@tekkare/romulus 0.1.250 → 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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/BarChart.vue +1 -0
- package/dist/runtime/components/ChoroplethMap.vue +4 -1
- package/dist/runtime/internal/geo/fr-regions.json +1 -1
- package/dist/runtime/utils/chart.d.ts +6 -3
- package/dist/runtime/utils/chart.js +15 -2
- package/layer/app/app.config.ts +6 -0
- package/layer/app/components/OipPubmedSummary.vue +223 -0
- package/layer/app/components/OipResourceDrawer.vue +331 -0
- package/layer/app/components/OipResourceDrawerLink.vue +40 -0
- package/layer/app/components/OipResourceField.vue +41 -0
- package/layer/app/components/OipResourceGenericCard.vue +54 -0
- package/layer/app/components/OipResourceSource.vue +50 -0
- package/layer/app/components/OipResourceTags.vue +65 -0
- package/layer/app/components/OipResourceView.vue +107 -0
- package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
- package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
- package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
- package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
- package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
- package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
- package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
- package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
- package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
- package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
- package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
- package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
- package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
- package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
- package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
- package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
- package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
- package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
- package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
- package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
- package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
- package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
- package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
- package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
- package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
- package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
- package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
- package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
- package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
- package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
- package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
- package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
- package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
- package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
- package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
- package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
- package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
- package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
- package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
- package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
- package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
- package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
- package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
- package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
- package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
- package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
- package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
- package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
- package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
- package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
- package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
- package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
- package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
- package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
- package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
- package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
- package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
- package/layer/app/components/oip-science/common/Form.vue +73 -0
- package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
- package/layer/app/components/oip-science/common/NoData.vue +16 -0
- package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
- package/layer/app/components/oip-science/common/Source.vue +31 -0
- package/layer/app/components/oip-science/common/TableTags.vue +279 -0
- package/layer/app/components/oip-science/common/Timeline.vue +104 -0
- package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
- package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
- package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
- package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
- package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
- package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
- package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
- package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
- package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
- package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
- package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
- package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
- package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
- package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
- package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
- package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
- package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
- package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
- package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
- package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
- package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
- package/layer/app/components/oip-science/euct/Locations.vue +102 -0
- package/layer/app/components/oip-science/euct/Products.vue +101 -0
- package/layer/app/components/oip-science/euct/Publications.vue +89 -0
- package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
- package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
- package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
- package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
- package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
- package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
- package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
- package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
- package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
- package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
- package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
- package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
- package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
- package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
- package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
- package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
- package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
- package/layer/app/composables/useOipChapterScroll.ts +43 -0
- package/layer/app/composables/useOipResourceHooks.ts +27 -0
- package/layer/app/composables/useOipResourceNav.ts +66 -0
- package/layer/app/composables/useOipResourceViews.ts +117 -0
- package/layer/app/composables/useOipResources.ts +83 -0
- package/layer/app/oip-drugs/PORTING.md +120 -0
- package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
- package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
- package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
- package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
- package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
- package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
- package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
- package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
- package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
- package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
- package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
- package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
- package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
- package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
- package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
- package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
- package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
- package/layer/app/oip-drugs/drugs.css +500 -0
- package/layer/app/oip-drugs/esIndices.ts +322 -0
- package/layer/app/oip-drugs/kinds.ts +264 -0
- package/layer/app/oip-drugs/locales/en.json +1571 -0
- package/layer/app/oip-drugs/locales/fr.json +1571 -0
- package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
- package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
- package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
- package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
- package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
- package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
- package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
- package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
- package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
- package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
- package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
- package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
- package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
- package/layer/app/oip-drugs/types/elastic.ts +113 -0
- package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
- package/layer/app/oip-drugs/types/es/directory.ts +54 -0
- package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
- package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
- package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
- package/layer/app/oip-drugs/types/es/portal.ts +130 -0
- package/layer/app/oip-drugs/types/es/retail.ts +107 -0
- package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
- package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
- package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
- package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
- package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
- package/layer/app/oip-drugs/utils/contributions.ts +36 -0
- package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
- package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
- package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
- package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
- package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
- package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
- package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
- package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
- package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
- package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
- package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
- package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
- package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
- package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
- package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
- package/layer/app/oip-science/PORTING.md +93 -0
- package/layer/app/oip-science/assets/smartSelection.json +428 -0
- package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
- package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
- package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
- package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
- package/layer/app/oip-science/composables/useEuct.ts +418 -0
- package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
- package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
- package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
- package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
- package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
- package/layer/app/oip-science/composables/usePubmed.ts +487 -0
- package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
- package/layer/app/oip-science/indexConfig.json +794 -0
- package/layer/app/oip-science/locales/en.json +483 -0
- package/layer/app/oip-science/locales/fr.json +483 -0
- package/layer/app/oip-science/science.css +1182 -0
- package/layer/app/oip-science/utils/a-openCard.ts +23 -0
- package/layer/app/oip-science/utils/b-openCard.ts +35 -0
- package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
- package/layer/app/oip-science/utils/f-types.ts +241 -0
- package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
- package/layer/app/oip-science/utils/matchMethod.ts +53 -0
- package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
- package/layer/app/oip-science/utils/sources.ts +15 -0
- package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
- package/layer/app/oip-science/utils/toPresets.ts +7 -0
- package/layer/app/oip-science/utils/valueTable.ts +126 -0
- package/layer/app/plugins/oip-science-i18n.ts +17 -0
- package/layer/app/utils/oipFicheRoutes.ts +118 -0
- package/layer/app/utils/oipResources.ts +440 -0
- package/layer/nuxt.config.ts +22 -2
- package/layer/public/reports/blincyto-has.html +292 -0
- package/layer/public/reports/blincyto-has.pdf +0 -0
- package/layer/public/reports/darzalex-has.html +279 -0
- package/layer/public/reports/darzalex-has.pdf +0 -0
- package/layer/public/reports/dupixent-has.html +217 -0
- package/layer/public/reports/dupixent-has.pdf +0 -0
- package/layer/public/reports/enhertu-has.html +283 -0
- package/layer/public/reports/enhertu-has.pdf +0 -0
- package/layer/public/reports/has-sein-2023.html +1639 -0
- package/layer/public/reports/ibrance-has.html +280 -0
- package/layer/public/reports/ibrance-has.pdf +0 -0
- package/layer/public/reports/imfinzi-has.html +297 -0
- package/layer/public/reports/imfinzi-has.pdf +0 -0
- package/layer/public/reports/keytruda-has.html +337 -0
- package/layer/public/reports/keytruda-has.pdf +0 -0
- package/layer/public/reports/kisqali-has.html +202 -0
- package/layer/public/reports/kisqali-has.pdf +0 -0
- package/layer/public/reports/opdivo-has.html +232 -0
- package/layer/public/reports/opdivo-has.pdf +0 -0
- package/layer/public/reports/ozempic-has.html +188 -0
- package/layer/public/reports/ozempic-has.pdf +0 -0
- package/layer/public/reports/tecentriq-has.html +315 -0
- package/layer/public/reports/tecentriq-has.pdf +0 -0
- package/layer/public/reports/yescarta-has.html +275 -0
- package/layer/public/reports/yescarta-has.pdf +0 -0
- package/layer/server/api/oip/es.post.ts +99 -0
- package/layer/server/api/oip/pdf.get.ts +70 -0
- package/layer/server/api/oip/reports.get.ts +98 -0
- package/layer/server/api/oip/resource-types.get.ts +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { defineAsyncComponent, type Component } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Le composant de chaque onglet de fiche, par type et par cle d'onglet (cf.
|
|
5
|
+
* OIP_RESOURCE_SPECS). Charges a la demande : une fiche ouverte ne tire que
|
|
6
|
+
* ses onglets, ce qui compte sur les reseaux lents des hopitaux.
|
|
7
|
+
*
|
|
8
|
+
* Contrat d'un onglet : props `id` (l'identifiant de route, cf.
|
|
9
|
+
* `spec.routeId`), `highlight` (termes a surligner) et `lang`. Ses liens vers
|
|
10
|
+
* d'autres fiches passent par useOipResourceNav, ses sources par
|
|
11
|
+
* useOipResourceHooks().
|
|
12
|
+
*/
|
|
13
|
+
const LOADERS: Record<string, Record<string, () => Promise<any>>> = {
|
|
14
|
+
PubMed: {
|
|
15
|
+
"summary": () => import("../components/OipPubmedSummary.vue"),
|
|
16
|
+
"authors": () => import("../components/oip-science/pubmed/Authors.vue"),
|
|
17
|
+
"clinical-trials": () => import("../components/oip-science/pubmed/ClinicalTrials.vue"),
|
|
18
|
+
},
|
|
19
|
+
ClinicalTrialsNCT: {
|
|
20
|
+
"study-description": () => import("../components/oip-science/ctgov/StudyDescription.vue"),
|
|
21
|
+
"investigators": () => import("../components/oip-science/ctgov/Investigators.vue"),
|
|
22
|
+
"locations": () => import("../components/oip-science/ctgov/Locations.vue"),
|
|
23
|
+
"sponsors": () => import("../components/oip-science/ctgov/Sponsors.vue"),
|
|
24
|
+
"publications": () => import("../components/oip-science/ctgov/Publications.vue"),
|
|
25
|
+
},
|
|
26
|
+
ClinicalTrialsEUCT: {
|
|
27
|
+
"study-description": () => import("../components/oip-science/euct/StudyDescription.vue"),
|
|
28
|
+
"products": () => import("../components/oip-science/euct/Products.vue"),
|
|
29
|
+
"investigators": () => import("../components/oip-science/euct/Investigators.vue"),
|
|
30
|
+
"locations": () => import("../components/oip-science/euct/Locations.vue"),
|
|
31
|
+
"sponsors": () => import("../components/oip-science/euct/Sponsors.vue"),
|
|
32
|
+
"publications": () => import("../components/oip-science/euct/Publications.vue"),
|
|
33
|
+
},
|
|
34
|
+
HCP: {
|
|
35
|
+
"informations": () => import("../components/oip-science/hcps/Informations.vue"),
|
|
36
|
+
"clinical-trials": () => import("../components/oip-science/hcps/ClinicalTrials.vue"),
|
|
37
|
+
"appels-a-projet": () => import("../components/oip-science/hcps/AppelsAProjet.vue"),
|
|
38
|
+
"pubmed": () => import("../components/oip-science/hcps/Pubmed.vue"),
|
|
39
|
+
"matched-labels": () => import("../components/oip-science/hcps/MatchedLabels.vue"),
|
|
40
|
+
},
|
|
41
|
+
Establishment: {
|
|
42
|
+
"informations": () => import("../components/oip-science/establishments/Informations.vue"),
|
|
43
|
+
"clinical-trials": () => import("../components/oip-science/establishments/ClinicalTrials.vue"),
|
|
44
|
+
"public-finance": () => import("../components/oip-science/establishments/PublicFinance.vue"),
|
|
45
|
+
"hcps": () => import("../components/oip-science/establishments/Hcps.vue"),
|
|
46
|
+
"matched-labels": () => import("../components/oip-science/establishments/MatchedLabels.vue"),
|
|
47
|
+
},
|
|
48
|
+
ClinicalTrialsInvestigatorsNCT: {
|
|
49
|
+
"informations": () => import("../components/oip-science/ctgov-investigators/Informations.vue"),
|
|
50
|
+
"clinical-trials": () => import("../components/oip-science/ctgov-investigators/ClinicalTrials.vue"),
|
|
51
|
+
"publications": () => import("../components/oip-science/ctgov-investigators/Publications.vue"),
|
|
52
|
+
},
|
|
53
|
+
ClinicalTrialsInvestigatorsEUCT: {
|
|
54
|
+
"informations": () => import("../components/oip-science/euct-investigators/Informations.vue"),
|
|
55
|
+
"clinical-trials": () => import("../components/oip-science/euct-investigators/ClinicalTrials.vue"),
|
|
56
|
+
"publications": () => import("../components/oip-science/euct-investigators/Publications.vue"),
|
|
57
|
+
},
|
|
58
|
+
ClinicalTrialsCentersNCT: {
|
|
59
|
+
"clinical-trials": () => import("../components/oip-science/ctgov-centers/ClinicalTrials.vue"),
|
|
60
|
+
"publications": () => import("../components/oip-science/ctgov-centers/Publications.vue"),
|
|
61
|
+
},
|
|
62
|
+
ClinicalTrialsCentersEUCT: {
|
|
63
|
+
"clinical-trials": () => import("../components/oip-science/euct-centers/ClinicalTrials.vue"),
|
|
64
|
+
"publications": () => import("../components/oip-science/euct-centers/Publications.vue"),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Les vues d'un seul tenant (`spec.view`, fiches de fr_drugs_app) et les
|
|
70
|
+
* fiches qu'un lien peut viser (cf. utils/oipFicheRoutes).
|
|
71
|
+
*
|
|
72
|
+
* - `drugs-<famille>` : la ressource rangee, par son `resourceId` (props `id`,
|
|
73
|
+
* parfois `by`). Une PUT, une MITM, un prix ou une AMM ouvrent la fiche
|
|
74
|
+
* marque sur leur onglet, comme fr_drugs_app.
|
|
75
|
+
* - `drugs-<fiche>-fiche` : la fiche elle-meme, telle que la page de
|
|
76
|
+
* fr_drugs_app la monte (props de sa route : `brand` et `tab`, `code`,
|
|
77
|
+
* `id`, `label`).
|
|
78
|
+
*/
|
|
79
|
+
const VIEW_LOADERS: Record<string, () => Promise<any>> = {
|
|
80
|
+
"science-phrc": () => import("../components/oip-science/resources/Phrc.vue"),
|
|
81
|
+
"drugs-brand": () => import("../components/oip-drugs/resources/Brand.vue"),
|
|
82
|
+
"drugs-put": () => import("../components/oip-drugs/resources/Put.vue"),
|
|
83
|
+
"drugs-mitm": () => import("../components/oip-drugs/resources/Mitm.vue"),
|
|
84
|
+
"drugs-price": () => import("../components/oip-drugs/resources/Price.vue"),
|
|
85
|
+
"drugs-ansm": () => import("../components/oip-drugs/resources/Ansm.vue"),
|
|
86
|
+
"drugs-indication": () => import("../components/oip-drugs/resources/Indication.vue"),
|
|
87
|
+
"drugs-pnds": () => import("../components/oip-drugs/resources/Pnds.vue"),
|
|
88
|
+
"drugs-jo": () => import("../components/oip-drugs/resources/Jo.vue"),
|
|
89
|
+
"drugs-association": () => import("../components/oip-drugs/resources/Association.vue"),
|
|
90
|
+
"drugs-contribution": () => import("../components/oip-drugs/resources/Contribution.vue"),
|
|
91
|
+
"drugs-national-assembly": () => import("../components/oip-drugs/resources/NationalAssembly.vue"),
|
|
92
|
+
"drugs-medicam": () => import("../components/oip-drugs/resources/Medicam.vue"),
|
|
93
|
+
"drugs-outpatient": () => import("../components/oip-drugs/resources/Outpatient.vue"),
|
|
94
|
+
"drugs-hospital": () => import("../components/oip-drugs/resources/Hospital.vue"),
|
|
95
|
+
"drugs-brand-fiche": () => import("../components/oip-drugs/fiches/BrandFiche.vue"),
|
|
96
|
+
"drugs-indication-fiche": () => import("../components/oip-drugs/fiches/IndicationFiche.vue"),
|
|
97
|
+
"drugs-pnds-fiche": () => import("../components/oip-drugs/fiches/PndsFiche.vue"),
|
|
98
|
+
"drugs-jo-fiche": () => import("../components/oip-drugs/fiches/JoFiche.vue"),
|
|
99
|
+
"drugs-association-fiche": () => import("../components/oip-drugs/fiches/AssociationFiche.vue"),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const cache = new Map<string, Component>();
|
|
103
|
+
|
|
104
|
+
export function oipResourceViewComponent(key: string): Component | null {
|
|
105
|
+
const loader = VIEW_LOADERS[key];
|
|
106
|
+
if (!loader) return null;
|
|
107
|
+
if (!cache.has(`view:${key}`)) cache.set(`view:${key}`, defineAsyncComponent(loader));
|
|
108
|
+
return cache.get(`view:${key}`)!;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function oipResourceTabComponent(type: string, key: string): Component | null {
|
|
112
|
+
const loader = LOADERS[type]?.[key];
|
|
113
|
+
if (!loader) return null;
|
|
114
|
+
const cacheKey = `${type}/${key}`;
|
|
115
|
+
if (!cache.has(cacheKey)) cache.set(cacheKey, defineAsyncComponent(loader));
|
|
116
|
+
return cache.get(cacheKey)!;
|
|
117
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
oipResourceAppUrl,
|
|
3
|
+
oipResourceSpec,
|
|
4
|
+
OIP_RESOURCE_APPS,
|
|
5
|
+
type OipEsCluster,
|
|
6
|
+
type OipResourceTypeEntry,
|
|
7
|
+
} from "../utils/oipResources";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Lecture des ressources pour les vues du layer : le proxy Elasticsearch
|
|
11
|
+
* commun (/api/oip/es) et le registre des types de l'API.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Une recherche dans un index declare par une vue (cf. OIP_RESOURCE_SPECS). */
|
|
15
|
+
export function oipEsSearch<T = any>(cluster: OipEsCluster, index: string, body: Record<string, unknown>) {
|
|
16
|
+
return $fetch<T>("/api/oip/es", { method: "POST", query: { cluster, index }, body });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Un document par son `_id`, ou null. */
|
|
20
|
+
export async function oipEsGet<S = any>(
|
|
21
|
+
cluster: OipEsCluster,
|
|
22
|
+
index: string,
|
|
23
|
+
id: string,
|
|
24
|
+
source?: string[],
|
|
25
|
+
): Promise<{ _id: string; _source: S } | null> {
|
|
26
|
+
const response = await oipEsSearch<any>(cluster, index, {
|
|
27
|
+
size: 1,
|
|
28
|
+
query: { ids: { values: [id] } },
|
|
29
|
+
...(source ? { _source: source } : {}),
|
|
30
|
+
});
|
|
31
|
+
return response?.hits?.hits?.[0] ?? null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Les documents d'un type decrit par le layer, par `_id`, pour lister ses
|
|
36
|
+
* elements dans la page d'un projet. Vide pour un type que le layer ne
|
|
37
|
+
* connait pas : l'app le relit elle-meme.
|
|
38
|
+
*/
|
|
39
|
+
export async function oipHydrate(type: string, ids: string[]): Promise<Record<string, any>> {
|
|
40
|
+
const spec = oipResourceSpec(type);
|
|
41
|
+
if (!spec || !ids.length) return {};
|
|
42
|
+
const response = await oipEsSearch<any>(spec.cluster, spec.kind.index, {
|
|
43
|
+
size: ids.length,
|
|
44
|
+
...(spec.kind.source ? { _source: spec.kind.source } : {}),
|
|
45
|
+
query: { ids: { values: ids } },
|
|
46
|
+
});
|
|
47
|
+
const docs: Record<string, any> = Object.fromEntries(
|
|
48
|
+
(response?.hits?.hits ?? []).map((hit: any) => [String(hit._id), hit._source]),
|
|
49
|
+
);
|
|
50
|
+
// Une contribution se titre par la marque, lue dans l'index HAS.
|
|
51
|
+
if (type === "FrPatientContribution") {
|
|
52
|
+
const { withContributionBrands } = await import("../oip-drugs/utils/contributions");
|
|
53
|
+
await withContributionBrands(Object.values(docs));
|
|
54
|
+
}
|
|
55
|
+
return docs;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Le registre change quelques fois par an : un appel par session suffit.
|
|
59
|
+
let registry: Promise<OipResourceTypeEntry[]> | null = null;
|
|
60
|
+
|
|
61
|
+
export function useOipResourceTypes(): Promise<OipResourceTypeEntry[]> {
|
|
62
|
+
registry ??= $fetch<OipResourceTypeEntry[]>("/api/oip/resource-types").catch(() => {
|
|
63
|
+
registry = null;
|
|
64
|
+
return [];
|
|
65
|
+
});
|
|
66
|
+
return registry;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Le lien vers la fiche dans son app d'origine, et le nom de cette app. Pour
|
|
71
|
+
* un type sans vue, l'URL du registre (deja absolue, remplie par l'appelant).
|
|
72
|
+
*/
|
|
73
|
+
export function oipResourceOrigin(type: string, id: string, lang: "fr" | "en") {
|
|
74
|
+
const spec = oipResourceSpec(type);
|
|
75
|
+
if (!spec) return null;
|
|
76
|
+
return {
|
|
77
|
+
name: OIP_RESOURCE_APPS[spec.app].name,
|
|
78
|
+
url: oipResourceAppUrl(spec.app, spec.detailPath(id), lang),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** La marque des contributions patients, lue dans l'index HAS (cf. oip-drugs/utils/contributions). */
|
|
83
|
+
export { withContributionBrands } from "../oip-drugs/utils/contributions";
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Porter une fiche médicament de fr_drugs_app dans le layer
|
|
2
|
+
|
|
3
|
+
Les fiches médicament (marque HAS et ses onglets, indication, PNDS, publication JO,
|
|
4
|
+
association, contribution, évolutions des ventes) vivent ici pour s'afficher **à
|
|
5
|
+
l'identique** dans toutes les apps du portail : la page de fr_drugs_app et le tiroir des
|
|
6
|
+
projets de n'importe quelle app montent le même composant. Source :
|
|
7
|
+
`~/Projects/fr_drugs_app/app` (lecture seule).
|
|
8
|
+
|
|
9
|
+
Le rendu de fr_drugs_app est la spec : on porte son code presque tel quel, en ne changeant
|
|
10
|
+
que ce qui l'attache à l'app. Pas de refonte. Même démarche que les fiches science
|
|
11
|
+
(`../oip-science/PORTING.md`), à lire pour l'esprit ; les règles ci-dessous priment.
|
|
12
|
+
|
|
13
|
+
## Où ranger
|
|
14
|
+
|
|
15
|
+
| Quoi | Où | Nom / import |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Composant | `app/components/oip-drugs/<dossier>/<Nom>.vue` | auto-importé sous `OipDrugs<Dossier><Nom>` (ex. `oip-drugs/heart/TextHighlight.vue` → `<OipDrugsHeartTextHighlight>`) |
|
|
18
|
+
| Composable, util, store porté | `app/oip-drugs/composables/*.ts`, `app/oip-drugs/utils/*.ts` | **pas** auto-importés : import relatif explicite. Garder les noms de fr_drugs_app. |
|
|
19
|
+
| Index ES | `app/oip-drugs/esIndices.ts` (copie de `types/esIndices.ts`) | `import { ES_INDEX } from "<rel>/oip-drugs/esIndices"` |
|
|
20
|
+
| Types ES | `app/oip-drugs/types/` (copie de `types/elastic.ts`, `types/es/*`, `types/heart/*`) | imports relatifs |
|
|
21
|
+
| Monographies | `app/oip-drugs/monographs/*.vue` (copie de `app/monographs/*.vue`) | `import.meta.glob` relatif |
|
|
22
|
+
| Rapports PDF/HTML | `layer/public/reports/` (déjà copiés) | même URL `/reports/...` |
|
|
23
|
+
|
|
24
|
+
Dans fr_drugs_app, un composant de `components/heart/x/y.vue` devient
|
|
25
|
+
`components/oip-drugs/heart/x/Y.vue` (même arborescence sous `oip-drugs/`, nom de fichier
|
|
26
|
+
avec une majuscule). Tous les lots suivent cette règle : un lot utilise le composant d'un
|
|
27
|
+
autre par ce nom, même s'il n'existe pas encore.
|
|
28
|
+
|
|
29
|
+
## Remplacements obligatoires
|
|
30
|
+
|
|
31
|
+
- **Racine** : chaque fiche, vue de ressource et composant monté seul (tiroirs téléportés
|
|
32
|
+
compris) porte la classe `oip-drugs` sur sa racine. Les classes globales de fr_drugs_app
|
|
33
|
+
(`main.css`, `heart/css/scoped.css` : `heart_*`, `oip_*`…) marchent dessous (cf.
|
|
34
|
+
`drugs.css`). Ne pas les réécrire. Les règles `.heart_app X` deviennent `.oip-drugs X`.
|
|
35
|
+
- **Elasticsearch** : `useHeartElastic()` → `useOipDrugsElastic()`
|
|
36
|
+
(`app/oip-drugs/composables/useOipDrugsElastic.ts`, mêmes fonctions). Les listes nommées
|
|
37
|
+
(`keyName`), que la page relisait dans `useResultStore`, sont dans `lists[keyName]` du
|
|
38
|
+
même objet. `$fetch('/api/es?index=…')` et `useElasticGeneral`/`useFetch('/api/es…')` →
|
|
39
|
+
`oipEsSearch('drugs', index, body)` (`app/composables/useOipResources.ts`). Index par
|
|
40
|
+
`ES_INDEX.X` (ids Directus). Les surcharges `esIndexFromConfig(runtimeConfig.public.x, …)`
|
|
41
|
+
prennent la valeur du catalogue.
|
|
42
|
+
- **Routes serveur** : `/api/pdf?url=` → `/api/oip/pdf?url=`, `/api/reports` →
|
|
43
|
+
`/api/oip/reports`, `/api/sources` → `/api/oip/sources` (déjà servies par le layer).
|
|
44
|
+
- **i18n** : clés sous `oipDrugs.` : `$t('heart.price')` → `$t('oipDrugs.heart.price')`
|
|
45
|
+
(fichiers `app/oip-drugs/locales/*.json`, copie complète de fr_drugs_app, ne pas les éditer).
|
|
46
|
+
- **Route** : jamais `useRoute()`, `route.params`, `route.query`. Ce que la page lisait dans
|
|
47
|
+
sa route devient une prop (`brand`, `tab`, `code`, `id`, `label`, `cis`, `indication`…).
|
|
48
|
+
Un changement d'onglet que la page écrivait dans `?tab=` devient
|
|
49
|
+
`emit('update:tab', key)` (prop `tab` + `v-model:tab`).
|
|
50
|
+
- **Liens** (`navigateTo`, `router.push`, `NuxtLink`, `localePath`, `heartOpenCard`,
|
|
51
|
+
`heartBrandPath`, `associationPath`) : `const nav = useOipResourceNav()` (auto-importé),
|
|
52
|
+
puis `nav.openApp('fr_drugs_app', path)` au clic et `:href="nav.appHref('fr_drugs_app', path)"`
|
|
53
|
+
pour garder le Ctrl/Cmd-clic (`@click.prevent` sauf touche modificatrice). `path` est le
|
|
54
|
+
chemin de fr_drugs_app, inchangé (`drugsBrandPath`, `drugsAssociationPath` de
|
|
55
|
+
`app/oip-drugs/kinds.ts`). Dans un tiroir, le layer empile la fiche si elle est connue
|
|
56
|
+
(cf. `app/utils/oipFicheRoutes.ts`) ; dans une page, il navigue. Liens externes inchangés.
|
|
57
|
+
- **Bouton Projet** (`hubr-project-add`, `HubrProjectAdd`) :
|
|
58
|
+
`const { projectButton } = useOipResourceHooks()` puis
|
|
59
|
+
`<component :is="projectButton" v-if="projectButton" :resource-id="…" type="…" :label="…" compact />`.
|
|
60
|
+
Ne jamais importer `useHubrProjects`, `useHubrResourceKinds`, `useElysiaApi`.
|
|
61
|
+
- **Sources** (`sourceContainerElastic`, `useHeartSourceUuids`, `HEART_SOURCE_UUIDS`) :
|
|
62
|
+
`<OipResourceSource :uuid="…" />` par uuid (le composant du layer lit `/api/oip/sources`
|
|
63
|
+
et ouvre le panneau Sources par le hook de l'app). Les uuids viennent de
|
|
64
|
+
`app/oip-drugs/utils/sourceUuids.ts` (lot P), plus de `route.path`.
|
|
65
|
+
- **Stores Pinia et `useState`** (`filterStore`, `sharedFiltersStore`, `resultStore`,
|
|
66
|
+
`elasticQueryStore`, `appConfigStore`, `CurrencyStore`, `countryMapping`,
|
|
67
|
+
`priceCurrencyStore`, `countryEdito`, `retail`, `outpatient`, `cardPrefs`,
|
|
68
|
+
`useHeartDetailTab`) : état local du composant, ou petit composable par instance dans
|
|
69
|
+
`app/oip-drugs/composables/`. Les données de référence (taux de change, table des pays)
|
|
70
|
+
deviennent un composable qui les charge.
|
|
71
|
+
`cardPrefs` / `useCardPrefOptions` → prop `prefs` avec les valeurs par défaut de fr_drugs_app.
|
|
72
|
+
- **Cadre de l'app** : retirer `useExportSource`, `useAlertSource`, `useViews`,
|
|
73
|
+
`useHeartLayout().setToolbar*`, `useAppLayout`, `definePageMeta`, `usePosthogTracking`,
|
|
74
|
+
`useHubrEmbedded` (toujours « embarqué » dans le layer) et l'export de la fiche
|
|
75
|
+
(`details/export.vue`, `les/export.vue`, `mitm/export.vue`) : l'en-tête et l'export restent
|
|
76
|
+
dans la page de fr_drugs_app, qui les passe au slot `actions` de la fiche.
|
|
77
|
+
- Surlignage de recherche lu dans un store : prop `highlight: string[]` (défaut `[]`).
|
|
78
|
+
|
|
79
|
+
## Contrats
|
|
80
|
+
|
|
81
|
+
- **Fiche** (`components/oip-drugs/fiches/*Fiche.vue`, montée par la page de fr_drugs_app
|
|
82
|
+
et par le tiroir) : props de sa route (`BrandFiche` : `brand`, `tab` ;
|
|
83
|
+
`IndicationFiche` : `code` ; `PndsFiche` : `id` ; `JoFiche` : `id` ;
|
|
84
|
+
`AssociationFiche` : `label`), plus `lang`, `highlight`. Elle rend son bandeau
|
|
85
|
+
(`RmPageHeader` : titre, onglets) et son contenu ; slot `actions` dans le bandeau pour
|
|
86
|
+
l'export et le bouton Projet de la page. Emet `update:tab`. Charge ses données elle-même.
|
|
87
|
+
- **Vue de ressource** (`components/oip-drugs/resources/*.vue`, montée par le tiroir d'un
|
|
88
|
+
projet pour un `resourceType`) : props `id` (le `resourceId`), `type`, `lang`,
|
|
89
|
+
`highlight`, parfois `by` (`"cis"`, `"local_drug_id"` : l'id est ce champ et non l'`_id`).
|
|
90
|
+
Elle relit le document et monte la fiche ou la carte qui convient.
|
|
91
|
+
|
|
92
|
+
Affichage tant que les données arrivent : `RmLoadingState` ; document absent : l'état
|
|
93
|
+
vide de fr_drugs_app, sinon `RmEmptyState`.
|
|
94
|
+
|
|
95
|
+
## Répartition (sept lots en parallèle)
|
|
96
|
+
|
|
97
|
+
Chaque fichier de fr_drugs_app a un seul lot propriétaire. Un lot n'écrit que ses
|
|
98
|
+
fichiers et utilise ceux des autres par leur nom cible (même API que l'original). Fichier
|
|
99
|
+
utile absent du tableau : le créer sous un nom préfixé par la lettre du lot
|
|
100
|
+
(`oip-drugs/utils/q-<nom>.ts`) et le signaler.
|
|
101
|
+
|
|
102
|
+
| Lot | Contenu (chemins fr_drugs_app sous `app/`) |
|
|
103
|
+
|---|---|
|
|
104
|
+
| P, socle | composants `heart/textHighlight`, `heart/elastic/sourceContainerElastic` (→ liste d'`OipResourceSource`), `heart/results/{matchExplain,prefRows,asmrCursor,indicCard,accesCard,evalCard,alertCard}`, `heart/searchPage/dynamicSource` ; composables `useHeartMatchExplain`, `useHeartSearchFields`, `useHeartSourceUuids` (→ `oip-drugs/utils/sourceUuids.ts`, uuids explicites), `useDirectusSources`, `useMergeByKey`, `usePageEmpty`, `useCardPrefOptions` (→ défauts) ; utils `heartStyleAndFormat`, `styleAndFormat`, `latestDate`, `heartStyleAnsm`, `associationFields`, `esHighlight`, `priceExport`, `exportSheets` ; stores `CurrencyStore` (→ `oip-drugs/composables/useCurrency.ts`), `countryMapping` (→ util) |
|
|
105
|
+
| Q, fiche marque (cadre et onglets HAS) | `pages/hubr/heart/details/[id].vue` → `fiches/BrandFiche.vue` (onglets par clé, `tab`) ; `heart/results/details/{monograph,hasHistory,opinionDrawer,recordList,recordDrawer}` ; `heart/les/{drawer,indicationDetail,card,source,sourceTag}` ; `useHeartLesIndication`, `useHeartDetailSearches` ; `app/monographs/*.vue` ; vue de ressource `resources/Brand.vue` (doc BRAND_HAS par `_id` → `brand_group` → `BrandFiche`) |
|
|
106
|
+
| R, fiche marque (prix et international) | `heart/results/details/{priceOverview,presentationDrawer,internationalOverview,internationalCountryCard,internationalPriceDrawer}` ; `useHeartIntlApprovals`, `useHeartReports` (→ `/api/oip/reports`), `useHeartBrandMatches`, utils `heartIntlPrice` |
|
|
107
|
+
| S, fiche marque (ANSM, MITM) et redirections | `heart/ansm/cisDetail` (+ puces CIS), `heart/mitm/details/overview` ; vues de ressource `resources/{Put,Mitm,Price,Ansm}.vue` : relire le document (`_id`, ou `by`), trouver la marque comme la page de fr_drugs_app (`put/[id]`, `mitm/[id]`, `prices/details/[id]`, `ansm/details/[id]`) et monter `BrandFiche` sur l'onglet `put`, `mitm`, `price`, `ansm` ; sans marque, ce que la page montre alors (au minimum sa carte : `heart/put/card`, `heart/mitm/...`, `heart/results/brandGroupCard`, `heart/ansm/brandCard`) |
|
|
108
|
+
| T, indication, PNDS, JO | `pages/hubr/heart/indication/[id].vue` → `fiches/IndicationFiche.vue` (+ `heart/results/details/price`) ; `pages/hubr/heart/pnds/[id].vue` → `fiches/PndsFiche.vue` ; `pages/hubr/heart/prices/jo/[id].vue` → `fiches/JoFiche.vue` (+ `heart/joCard`) ; vues `resources/{Indication,Pnds,Jo}.vue` |
|
|
109
|
+
| U, associations | `pages/hubr/heart/associations/[id].vue` → `fiches/AssociationFiche.vue` ; `heart/associations/{transcriptionCard,nationalAssemblyCard,nationalAssemblyFiche}` ; `useAssociationDetail`, `useAssociationExport` (la partie lecture), `useAssociationLookup` ; vues `resources/{Association,NationalAssembly}.vue` ; `resources/Contribution.vue` : **le détail de la contribution** (décision produit), en reprenant le contenu de `~/Projects/france_augmented_science_app/app/components/drugs/ContributionDetail.vue` mais avec les composants et le style de fr_drugs_app, et un lien vers l'association |
|
|
110
|
+
| V, évolutions des ventes | `hubr/projectMedicamEvolution` + `medicam/market/chart` (store `retail` → état local) → `resources/Medicam.vue` ; `outpatient/productEvolution` (+ `useElasticGeneral`, store `outpatient` → état local) → `resources/Outpatient.vue` ; `resources/Hospital.vue` pour `FrSusBrand` et `FrRetrocessionBrand` (**décision produit** : un graphe d'évolution par marque, au gabarit des deux autres, en reprenant les données et requêtes de `~/Projects/france_augmented_science_app/app/components/drugs/HospitalEvolution.vue`) |
|
|
111
|
+
|
|
112
|
+
## Compte rendu attendu de chaque lot
|
|
113
|
+
|
|
114
|
+
Fichiers créés, écarts de rendu volontaires, clés i18n manquantes, dépendances vers
|
|
115
|
+
d'autres lots, ce qui n'a pas pu être porté.
|
|
116
|
+
|
|
117
|
+
## Règles de texte
|
|
118
|
+
|
|
119
|
+
Aucun tiret cadratin (—) dans un texte visible : parenthèses ou virgules. Commentaires en
|
|
120
|
+
français sans accents dans le code, comme le reste du layer.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tout ce que les sept index disent d'une association, pour sa fiche (portage
|
|
3
|
+
* de useAssociationDetail de fr_drugs_app).
|
|
4
|
+
*
|
|
5
|
+
* Cinq index portent `association_label_regroup` et se lisent directement.
|
|
6
|
+
* Les deux autres ne l'ont pas : les seances du comite d'interface ANSM se
|
|
7
|
+
* rejoignent par `session_url` en passant par la liste des participants, et le
|
|
8
|
+
* texte des debats HAS par `has_code` + `transcription_url` en passant par la
|
|
9
|
+
* transcription. Les blocs ne partent qu'a la demande : il y en a deux cent
|
|
10
|
+
* cinquante par debat, et une fiche en cite huit.
|
|
11
|
+
*/
|
|
12
|
+
import type { Ref } from "vue";
|
|
13
|
+
import { ES_INDEX } from "../esIndices";
|
|
14
|
+
import type { EsIndexName } from "../esIndices";
|
|
15
|
+
import { ASSOCIATION_FIELDS, NATIONAL_ASSEMBLY_FIELDS } from "../utils/associationFields";
|
|
16
|
+
import useOipDrugsElastic from "./useOipDrugsElastic";
|
|
17
|
+
import { useAssociationHasBrands } from "./useAssociationExport";
|
|
18
|
+
|
|
19
|
+
/** Plafond des listes de la fiche. Aucune association n'en approche. */
|
|
20
|
+
const PAGE_SIZE = 200;
|
|
21
|
+
/** Plafond des blocs d'un debat. Le plus long en compte environ 400. */
|
|
22
|
+
const BLOCKS_SIZE = 500;
|
|
23
|
+
/** Plafond des participants d'une fiche : une vingtaine par seance. */
|
|
24
|
+
const ATTENDEES_SIZE = 1000;
|
|
25
|
+
|
|
26
|
+
export function useAssociationDetail(label: Ref<string>) {
|
|
27
|
+
const { queryCustomElastic } = useOipDrugsElastic();
|
|
28
|
+
|
|
29
|
+
const directory = ref<any>(null);
|
|
30
|
+
const contributions = ref<any[]>([]);
|
|
31
|
+
const transcriptions = ref<any[]>([]);
|
|
32
|
+
const sessions = ref<any[]>([]);
|
|
33
|
+
const nationalAssembly = ref<any[]>([]);
|
|
34
|
+
const loading = ref(true);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Toutes les ecritures du nom que l'annuaire connait pour cette
|
|
38
|
+
* association : son nom regroupe, et les appellations brutes des sources
|
|
39
|
+
* qu'il liste dans `association_label`.
|
|
40
|
+
*
|
|
41
|
+
* Filtrer sur le seul nom regroupe laissait 55 associations sur 159 sans
|
|
42
|
+
* aucune contribution a l'ecran : le regroupement de l'annuaire et celui des
|
|
43
|
+
* index sources ne sont pas le meme. L'appellation brute, elle, se retrouve
|
|
44
|
+
* de part et d'autre. A retirer le jour ou `association_label_regroup` sera
|
|
45
|
+
* aligne sur les six index.
|
|
46
|
+
*/
|
|
47
|
+
const knownLabels = ref<string[]>([]);
|
|
48
|
+
|
|
49
|
+
function labelsFrom(doc: any): string[] {
|
|
50
|
+
const raw = doc?.[ASSOCIATION_FIELDS.labelVariants];
|
|
51
|
+
const names = new Set<string>([label.value]);
|
|
52
|
+
if (typeof raw === "string") {
|
|
53
|
+
raw.split(";").forEach((value) => {
|
|
54
|
+
const name = value.trim();
|
|
55
|
+
if (name) names.add(name);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return [...names];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Les deux champs de nom des index sources, contre toutes ces ecritures. */
|
|
62
|
+
const byLabel = () => ({
|
|
63
|
+
bool: {
|
|
64
|
+
should: [
|
|
65
|
+
{ terms: { [ASSOCIATION_FIELDS.label]: knownLabels.value } },
|
|
66
|
+
{ terms: { [ASSOCIATION_FIELDS.labelVariants]: knownLabels.value } },
|
|
67
|
+
],
|
|
68
|
+
minimum_should_match: 1,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
async function hits(index: EsIndexName, body: Record<string, any>) {
|
|
73
|
+
const res: any = await queryCustomElastic({ index, customQuery: body as any, allAnswer: true });
|
|
74
|
+
return (res?.hits?.hits ?? []).map((hit: any) => hit._source);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Les autres associations presentes a chaque seance, par `session_url`. Une
|
|
79
|
+
* seance du comite d'interface reunit une vingtaine d'associations : la
|
|
80
|
+
* fiche les nomme, l'association affichee en moins.
|
|
81
|
+
*/
|
|
82
|
+
const sessionAttendees = ref<Record<string, string[]>>({});
|
|
83
|
+
|
|
84
|
+
/** Les seances suivies par l'association, via la liste des participants. */
|
|
85
|
+
async function loadSessions() {
|
|
86
|
+
const attendees = await hits(ES_INDEX.ASSOCIATION_SESSION_ATTENDEE_ANSM, {
|
|
87
|
+
size: PAGE_SIZE,
|
|
88
|
+
_source: ["session_url"],
|
|
89
|
+
query: { bool: { must: [byLabel()] } },
|
|
90
|
+
});
|
|
91
|
+
const urls = [...new Set(attendees.map((a: any) => a.session_url).filter(Boolean))];
|
|
92
|
+
if (!urls.length) return [];
|
|
93
|
+
|
|
94
|
+
const [found, others] = await Promise.all([
|
|
95
|
+
hits(ES_INDEX.ASSOCIATION_SESSION_ANSM, {
|
|
96
|
+
size: PAGE_SIZE,
|
|
97
|
+
query: { terms: { session_url: urls } },
|
|
98
|
+
sort: [{ session_date: { order: "desc" } }],
|
|
99
|
+
}),
|
|
100
|
+
// Toute la liste des participants de ces seances, en une requete : une
|
|
101
|
+
// requete par carte aurait fait autant d'allers-retours qu'il y a de
|
|
102
|
+
// seances.
|
|
103
|
+
hits(ES_INDEX.ASSOCIATION_SESSION_ATTENDEE_ANSM, {
|
|
104
|
+
size: ATTENDEES_SIZE,
|
|
105
|
+
query: { terms: { session_url: urls } },
|
|
106
|
+
}),
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
sessionAttendees.value = groupAttendees(others);
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Les participants regroupes par seance, l'association affichee retiree et
|
|
115
|
+
* les doublons de nom ecartes : une meme association peut figurer deux fois
|
|
116
|
+
* dans la liste sous deux ecritures qui se regroupent sous le meme nom.
|
|
117
|
+
*/
|
|
118
|
+
function groupAttendees(attendees: any[]) {
|
|
119
|
+
const mine = new Set(knownLabels.value.map((name) => name.toLowerCase()));
|
|
120
|
+
const out: Record<string, string[]> = {};
|
|
121
|
+
attendees.forEach((attendee: any) => {
|
|
122
|
+
const url = attendee?.session_url;
|
|
123
|
+
const name =
|
|
124
|
+
attendee?.[ASSOCIATION_FIELDS.label] || attendee?.[ASSOCIATION_FIELDS.labelVariants];
|
|
125
|
+
if (!url || !name || mine.has(String(name).toLowerCase())) return;
|
|
126
|
+
const names = (out[url] ??= []);
|
|
127
|
+
if (!names.some((value) => value.toLowerCase() === String(name).toLowerCase())) {
|
|
128
|
+
names.push(String(name));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
Object.values(out).forEach((names) => names.sort((a, b) => a.localeCompare(b, "fr")));
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function load() {
|
|
136
|
+
loading.value = true;
|
|
137
|
+
|
|
138
|
+
// L'annuaire d'abord : c'est lui qui dit sous quels noms chercher le
|
|
139
|
+
// reste.
|
|
140
|
+
const directoryHits = await hits(ES_INDEX.ASSOCIATION_DIRECTORY, {
|
|
141
|
+
size: 1,
|
|
142
|
+
query: { term: { _id: label.value } },
|
|
143
|
+
});
|
|
144
|
+
directory.value = directoryHits[0] ?? null;
|
|
145
|
+
knownLabels.value = labelsFrom(directory.value);
|
|
146
|
+
|
|
147
|
+
const [contributionHits, transcriptionHits, sessionHits, nationalAssemblyHits] = await Promise.all([
|
|
148
|
+
hits(ES_INDEX.ASSOCIATION_CONTRIBUTION_HAS, {
|
|
149
|
+
size: PAGE_SIZE,
|
|
150
|
+
query: { bool: { must: [byLabel()] } },
|
|
151
|
+
sort: [{ contribution_date: { order: "desc" } }],
|
|
152
|
+
}),
|
|
153
|
+
hits(ES_INDEX.ASSOCIATION_TRANSCRIPTION_HAS, {
|
|
154
|
+
size: PAGE_SIZE,
|
|
155
|
+
query: { bool: { must: [byLabel()] } },
|
|
156
|
+
sort: [{ session_date: { order: "desc" } }],
|
|
157
|
+
}),
|
|
158
|
+
loadSessions(),
|
|
159
|
+
// Les comptes rendus de commission portent deux listes de noms
|
|
160
|
+
// regroupes : les associations citees et celles de la fiche. Une
|
|
161
|
+
// association compte si elle est dans l'une ou l'autre, sous toutes ses
|
|
162
|
+
// ecritures connues.
|
|
163
|
+
hits(ES_INDEX.ASSOCIATION_COMMISSION_SESSION_NATIONAL_ASSEMBLY, {
|
|
164
|
+
size: PAGE_SIZE,
|
|
165
|
+
_source: { excludes: [NATIONAL_ASSEMBLY_FIELDS.text] },
|
|
166
|
+
query: {
|
|
167
|
+
bool: {
|
|
168
|
+
should: [NATIONAL_ASSEMBLY_FIELDS.associations, NATIONAL_ASSEMBLY_FIELDS.ficheAssociations].map(
|
|
169
|
+
(field) => ({ terms: { [field]: knownLabels.value } }),
|
|
170
|
+
),
|
|
171
|
+
minimum_should_match: 1,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
sort: [{ [NATIONAL_ASSEMBLY_FIELDS.sessionDate]: { order: "desc" } }],
|
|
175
|
+
}),
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
contributions.value = contributionHits;
|
|
179
|
+
transcriptions.value = transcriptionHits;
|
|
180
|
+
sessions.value = sessionHits;
|
|
181
|
+
nationalAssembly.value = nationalAssemblyHits;
|
|
182
|
+
loading.value = false;
|
|
183
|
+
|
|
184
|
+
// Apres coup : les cartes s'affichent sans attendre, le lien vers l'avis
|
|
185
|
+
// apparait quand la marque est resolue.
|
|
186
|
+
await loadBrands(contributionHits.map((contribution: any) => contribution.has_code));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** La marque de l'avis de chaque contribution, par code HAS (cf. useAssociationHasBrands). */
|
|
190
|
+
const { brands, load: loadBrands } = useAssociationHasBrands();
|
|
191
|
+
|
|
192
|
+
/** Le fil d'un debat, tour de parole par tour de parole. */
|
|
193
|
+
async function loadBlocks(hasCode: string, transcriptionUrl: string) {
|
|
194
|
+
return await hits(ES_INDEX.ASSOCIATION_TRANSCRIPTION_BLOCK_HAS, {
|
|
195
|
+
size: BLOCKS_SIZE,
|
|
196
|
+
query: {
|
|
197
|
+
bool: {
|
|
198
|
+
must: [
|
|
199
|
+
{ term: { has_code: hasCode } },
|
|
200
|
+
{ term: { transcription_url: transcriptionUrl } },
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
sort: [{ block_index: { order: "asc" } }],
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
directory,
|
|
210
|
+
contributions,
|
|
211
|
+
transcriptions,
|
|
212
|
+
sessions,
|
|
213
|
+
nationalAssembly,
|
|
214
|
+
sessionAttendees,
|
|
215
|
+
loading,
|
|
216
|
+
load,
|
|
217
|
+
loadBlocks,
|
|
218
|
+
brands,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portage de la partie lecture de useAssociationExport de fr_drugs_app : la
|
|
3
|
+
* marque de l'avis auquel une contribution se rattache. Les schemas de
|
|
4
|
+
* colonnes et la lecture des index lies pour l'export restent dans
|
|
5
|
+
* fr_drugs_app, qui garde l'export de la fiche.
|
|
6
|
+
*/
|
|
7
|
+
import { ES_INDEX } from "../esIndices";
|
|
8
|
+
import useOipDrugsElastic from "./useOipDrugsElastic";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* La marque de l'avis auquel se rattache chaque contribution, par code HAS.
|
|
12
|
+
*
|
|
13
|
+
* Les index associations ne portent que le code ; la fiche produit du hub
|
|
14
|
+
* s'ouvre, elle, sur le nom de marque. C'est l'index des avis qui fait le
|
|
15
|
+
* lien, son historique portant le code. Tous les codes partent ensemble, en
|
|
16
|
+
* une agregation : une requete par contribution ferait autant d'allers-retours
|
|
17
|
+
* qu'il y a de contributions. Requete a part : l'index des avis n'est pas un
|
|
18
|
+
* index associations.
|
|
19
|
+
*/
|
|
20
|
+
export function useAssociationHasBrands() {
|
|
21
|
+
const { queryCustomElastic } = useOipDrugsElastic();
|
|
22
|
+
const hasIndex = ES_INDEX.BRAND_HAS;
|
|
23
|
+
const brands = ref<Record<string, string>>({});
|
|
24
|
+
|
|
25
|
+
async function load(hasCodes: unknown[]) {
|
|
26
|
+
const codes = [...new Set(hasCodes.filter((code): code is string => typeof code === "string" && code !== ""))].filter(
|
|
27
|
+
(code) => !(code in brands.value),
|
|
28
|
+
);
|
|
29
|
+
if (!codes.length) return;
|
|
30
|
+
|
|
31
|
+
const res: any = await queryCustomElastic({
|
|
32
|
+
index: hasIndex,
|
|
33
|
+
customQuery: {
|
|
34
|
+
size: 0,
|
|
35
|
+
aggs: {
|
|
36
|
+
history: {
|
|
37
|
+
nested: { path: "brand_history" },
|
|
38
|
+
aggs: {
|
|
39
|
+
by_code: {
|
|
40
|
+
// `include` borne l'agregation aux codes demandes : sans lui,
|
|
41
|
+
// `terms` remonterait les codes les plus frequents de l'index.
|
|
42
|
+
terms: { field: "brand_history.has_code.keyword", include: codes, size: codes.length },
|
|
43
|
+
aggs: {
|
|
44
|
+
up: { reverse_nested: {}, aggs: { brand: { terms: { field: "brand_group.keyword", size: 1 } } } },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
} as any,
|
|
51
|
+
allAnswer: true,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const found: Record<string, string> = {};
|
|
55
|
+
(res?.aggregations?.history?.by_code?.buckets ?? []).forEach((bucket: any) => {
|
|
56
|
+
const brand = bucket?.up?.brand?.buckets?.[0]?.key;
|
|
57
|
+
if (brand) found[bucket.key] = brand;
|
|
58
|
+
});
|
|
59
|
+
brands.value = { ...brands.value, ...found };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { brands, load };
|
|
63
|
+
}
|