territories-dashboard-lib 0.1.38__py3-none-any.whl → 1.1.1.dev10__py3-none-any.whl

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.

Potentially problematic release.


This version of territories-dashboard-lib might be problematic. Click here for more details.

Files changed (63) hide show
  1. territories_dashboard_lib/geo_lib/admin.py +2 -0
  2. territories_dashboard_lib/geo_lib/migrations/0003_geofeature_color_column_geofeature_size_column.py +23 -0
  3. territories_dashboard_lib/geo_lib/models.py +12 -0
  4. territories_dashboard_lib/geo_lib/payloads.py +7 -21
  5. territories_dashboard_lib/geo_lib/views.py +58 -53
  6. territories_dashboard_lib/indicators_lib/enums.py +61 -37
  7. territories_dashboard_lib/indicators_lib/methodo_pdf.py +6 -1
  8. territories_dashboard_lib/indicators_lib/migrations/0004_alter_indicator_min_mesh.py +18 -0
  9. territories_dashboard_lib/indicators_lib/migrations/0005_auto_20251203_1621.py +124 -0
  10. territories_dashboard_lib/indicators_lib/models.py +7 -4
  11. territories_dashboard_lib/indicators_lib/payloads.py +14 -1
  12. territories_dashboard_lib/indicators_lib/query/commons.py +90 -104
  13. territories_dashboard_lib/indicators_lib/query/comparison.py +8 -3
  14. territories_dashboard_lib/indicators_lib/query/details.py +8 -13
  15. territories_dashboard_lib/indicators_lib/query/histogram.py +0 -1
  16. territories_dashboard_lib/indicators_lib/query/indicator_card.py +12 -7
  17. territories_dashboard_lib/indicators_lib/query/top_10.py +12 -12
  18. territories_dashboard_lib/indicators_lib/query/utils.py +9 -0
  19. territories_dashboard_lib/indicators_lib/table.py +15 -12
  20. territories_dashboard_lib/indicators_lib/views.py +49 -59
  21. territories_dashboard_lib/superset_lib/logic.py +24 -25
  22. territories_dashboard_lib/superset_lib/migrations/0002_alter_filter_mesh.py +18 -0
  23. territories_dashboard_lib/tracking_lib/enums.py +2 -0
  24. territories_dashboard_lib/tracking_lib/migrations/0005_alter_page_cmp_territory_mesh_alter_page_submesh_and_more.py +28 -0
  25. territories_dashboard_lib/tracking_lib/migrations/0006_alter_event_name.py +18 -0
  26. territories_dashboard_lib/tracking_lib/payloads.py +4 -2
  27. territories_dashboard_lib/tracking_lib/views.py +7 -6
  28. territories_dashboard_lib/website_lib/conf.py +28 -0
  29. territories_dashboard_lib/website_lib/context_processors.py +5 -6
  30. territories_dashboard_lib/website_lib/migrations/0005_mainconf_meshes.py +20 -0
  31. territories_dashboard_lib/website_lib/models.py +12 -0
  32. territories_dashboard_lib/website_lib/params.py +34 -22
  33. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/anchor.mjs +43 -0
  34. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/comparaison/page.mjs +7 -9
  35. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/details/page.mjs +2 -7
  36. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/dom.mjs +0 -15
  37. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/enums.mjs +13 -10
  38. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/side_panel.mjs +1 -15
  39. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/theme/page.mjs +7 -9
  40. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/js/pages/indicators/track-visible-indicators.mjs +121 -0
  41. territories_dashboard_lib/website_lib/static/territories_dashboard_lib/website/react/indicatorMap.bundle.js +1 -1
  42. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/layout/base.css +12 -0
  43. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/comparaison/[theme]/page.html +4 -3
  44. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/anchor.html +14 -0
  45. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/geo_params.html +3 -3
  46. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/indicator-card.html +14 -8
  47. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/select_territory.html +32 -0
  48. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/side_panel_geo.html +8 -35
  49. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/details/page.html +9 -8
  50. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/methodo/methodo.js +28 -0
  51. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/methodo/page.html +40 -0
  52. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/themes/page.html +4 -3
  53. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/sitemap/page.html +9 -0
  54. territories_dashboard_lib/website_lib/templatetags/other_filters.py +6 -3
  55. territories_dashboard_lib/website_lib/views.py +100 -0
  56. {territories_dashboard_lib-0.1.38.dist-info → territories_dashboard_lib-1.1.1.dev10.dist-info}/METADATA +2 -2
  57. {territories_dashboard_lib-0.1.38.dist-info → territories_dashboard_lib-1.1.1.dev10.dist-info}/RECORD +60 -49
  58. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/side_panel_methodo.css +0 -29
  59. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/side_panel_methodo.html +0 -45
  60. territories_dashboard_lib/website_lib/templates/territories_dashboard_lib/website/pages/indicators/components/side_panel_methodo.js +0 -19
  61. {territories_dashboard_lib-0.1.38.dist-info → territories_dashboard_lib-1.1.1.dev10.dist-info}/WHEEL +0 -0
  62. {territories_dashboard_lib-0.1.38.dist-info → territories_dashboard_lib-1.1.1.dev10.dist-info}/licenses/licence.md +0 -0
  63. {territories_dashboard_lib-0.1.38.dist-info → territories_dashboard_lib-1.1.1.dev10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,43 @@
1
+ function enableAnchorLinks() {
2
+ document.querySelectorAll("button.anchor-link").forEach((button) => {
3
+ button.addEventListener("click", () => {
4
+ const anchor = button.dataset.anchor;
5
+ window.history.replaceState(undefined, undefined, "#" + anchor);
6
+ navigator.clipboard.writeText(window.location.href);
7
+ setTimeout(() => {
8
+ // to close the tooltip and not trigger it again when reloading the URL
9
+ button.parentElement.click();
10
+ }, 500);
11
+ });
12
+ });
13
+ }
14
+
15
+ function trackAnchorLinks() {
16
+ const buttons = document.querySelectorAll('button[data-track="true"]');
17
+ if (buttons.length === 0) return;
18
+
19
+ const observer = new IntersectionObserver((entries) => {
20
+ entries.forEach((entry) => {
21
+ if (entry.isIntersecting) {
22
+ fetch("/api/tracking/event/", {
23
+ method: "POST",
24
+ body: JSON.stringify({
25
+ indicator: entry.target.dataset.indicator,
26
+ event: "vue-resume-indicateur",
27
+ }),
28
+ });
29
+ observer.unobserve(entry.target);
30
+ }
31
+ });
32
+ });
33
+
34
+ window.addEventListener(
35
+ "scroll",
36
+ () => {
37
+ buttons.forEach((button) => observer.observe(button));
38
+ },
39
+ { once: true }
40
+ );
41
+ }
42
+
43
+ export { enableAnchorLinks, trackAnchorLinks };
@@ -2,10 +2,12 @@
2
2
  import { callData, getSearchParams } from "./data.mjs";
3
3
  import { delaySpinner, getClosestIndicator } from "../dom.mjs";
4
4
  import { handleFilterClick, initializeFilters } from "../filters.mjs";
5
- import { addSidePanelListener, openMethodoSidePanel } from "../side_panel.mjs";
5
+ import { addSidePanelListener } from "../side_panel.mjs";
6
6
 
7
7
  import { exportImageAsync } from "../export-graph.mjs";
8
8
  import { exportToCSV } from "../export.mjs";
9
+ import { enableAnchorLinks, trackAnchorLinks } from "../anchor.mjs";
10
+ import { trackVisibleIndicators } from "../track-visible-indicators.mjs";
9
11
 
10
12
  Chart.register(
11
13
  Chart.CategoryScale,
@@ -43,14 +45,6 @@ document.querySelectorAll(".indicator-card").forEach((indicatorCard) => {
43
45
  callData(indicator);
44
46
  });
45
47
  });
46
-
47
- indicatorCard
48
- .querySelectorAll('button[aria-controls="slide-panel-methodo"]')
49
- .forEach((button) => {
50
- button.addEventListener("click", () => {
51
- openMethodoSidePanel(indicator, button);
52
- });
53
- });
54
48
  });
55
49
 
56
50
  document
@@ -105,3 +99,7 @@ document.addEventListener("DOMContentLoaded", () => {
105
99
  });
106
100
  addSidePanelListener();
107
101
  });
102
+
103
+ enableAnchorLinks();
104
+ trackAnchorLinks();
105
+ trackVisibleIndicators();
@@ -7,7 +7,7 @@ import {
7
7
  updateFiltersReminder,
8
8
  updateUrlWithFilters,
9
9
  } from "../filters.mjs";
10
- import { addSidePanelListener, openMethodoSidePanel } from "../side_panel.mjs";
10
+ import { addSidePanelListener } from "../side_panel.mjs";
11
11
 
12
12
  import { exportImageAsync } from "../export-graph.mjs";
13
13
  import { exportToCSV } from "../export.mjs";
@@ -33,12 +33,7 @@ initializeFilters(indicator);
33
33
  callData(indicator);
34
34
  makeMap(indicator);
35
35
  makeSankeyGraph(indicator);
36
- const openMethodoButton = document.querySelector(
37
- 'button[aria-controls="slide-panel-methodo"]'
38
- );
39
- openMethodoButton.addEventListener("click", () => {
40
- openMethodoSidePanel(indicator, openMethodoButton);
41
- });
36
+
42
37
  document.querySelectorAll(".filter-tag").forEach((button) => {
43
38
  button.addEventListener("click", async () => {
44
39
  await handleFilterClick(indicator);
@@ -1,17 +1,3 @@
1
- function fillMethodoSidePanel(indicator) {
2
- const container = document.getElementById(
3
- "slide-panel-content-methodo-markdown"
4
- );
5
- container.innerHTML = indicator.methodo_html;
6
- const exportButton = document.getElementById("methodo-export-button");
7
- exportButton.dataset.indicatorName = indicator.name;
8
- exportButton.dataset.indicatorTitle = indicator.title;
9
- document.getElementById("slide-panel-methodo-indicator-title").textContent =
10
- indicator.title;
11
- document.getElementById("slide-panel-methodo-theme-title").textContent =
12
- indicator.theme;
13
- }
14
-
15
1
  function getClosestIndicator(element) {
16
2
  const indicatorId = element.closest(".indicator-card").dataset.indicator;
17
3
  return JSON.parse(document.getElementById(indicatorId).textContent);
@@ -81,7 +67,6 @@ function delaySpinner() {
81
67
  export {
82
68
  delaySpinner,
83
69
  getClosestIndicator,
84
- fillMethodoSidePanel,
85
70
  makeIndicatorDataScript,
86
71
  getIndicatorDataScript,
87
72
  getIsAlternativeUnit,
@@ -2,11 +2,12 @@ const THOUSANDS = "k";
2
2
  const MILLIONS = "M";
3
3
 
4
4
  const MeshLevel = {
5
- National: "fr",
6
- Region: "reg",
7
- Department: "dep",
8
- Epci: "epci",
9
- Town: "com",
5
+ fr: "fr",
6
+ reg: "reg",
7
+ dep: "dep",
8
+ epci: "epci",
9
+ com: "com",
10
+ aom: "aom",
10
11
  };
11
12
 
12
13
  const COLORS = {
@@ -76,16 +77,18 @@ const PATTERNS = [
76
77
 
77
78
  function getMeshLevelTitle(meshLevel) {
78
79
  switch (meshLevel) {
79
- case MeshLevel.Region:
80
+ case MeshLevel.reg:
80
81
  return "Région";
81
- case MeshLevel.Department:
82
+ case MeshLevel.dep:
82
83
  return "Département";
83
- case MeshLevel.Epci:
84
+ case MeshLevel.epci:
84
85
  return "Intercommunalité";
85
- case MeshLevel.Town:
86
+ case MeshLevel.com:
86
87
  return "Commune";
87
- case MeshLevel.National:
88
+ case MeshLevel.fr:
88
89
  return "France entière";
90
+ case MeshLevel.aom:
91
+ return "AOM";
89
92
  }
90
93
  }
91
94
 
@@ -1,22 +1,13 @@
1
- import { fillMethodoSidePanel } from "./dom.mjs";
2
-
3
1
  let buttonWhichOpenedPanel = null;
4
2
 
5
3
  function closeSidePanel() {
6
4
  const sidePanelGeo = document.getElementById("slide-panel-geo");
7
- const sidePanelMethodo = document.getElementById("slide-panel-methodo");
8
5
  sidePanelGeo.classList.remove("tdbmd-slide-panel--show");
9
- sidePanelMethodo.classList.remove("tdbmd-slide-panel--show");
10
6
  if (sidePanelGeo.style.display === "block") {
11
7
  setTimeout(() => {
12
8
  sidePanelGeo.style.display = "none";
13
9
  }, 500);
14
10
  }
15
- if (sidePanelMethodo.style.display === "block") {
16
- setTimeout(() => {
17
- sidePanelMethodo.style.display = "none";
18
- }, 500);
19
- }
20
11
  document.querySelectorAll(".validate-territory-btn").forEach((btn) => {
21
12
  delete btn.dataset.comparison;
22
13
  });
@@ -73,11 +64,6 @@ function releaseFocusTrap() {
73
64
  document.removeEventListener("keydown", focusTrapListener);
74
65
  }
75
66
 
76
- function openMethodoSidePanel(indicator, button) {
77
- fillMethodoSidePanel(indicator);
78
- openSidePanel(button, "methodo");
79
- }
80
-
81
67
  function addSidePanelListener() {
82
68
  const button = document.querySelector(
83
69
  'button[aria-controls="slide-panel-geo"][data-comparison="false"]'
@@ -100,4 +86,4 @@ function addSidePanelListener() {
100
86
  });
101
87
  }
102
88
 
103
- export { openMethodoSidePanel, addSidePanelListener };
89
+ export { addSidePanelListener };
@@ -13,7 +13,7 @@ import {
13
13
  handleFilterClick,
14
14
  initializeFilters,
15
15
  } from "../filters.mjs";
16
- import { addSidePanelListener, openMethodoSidePanel } from "../side_panel.mjs";
16
+ import { addSidePanelListener } from "../side_panel.mjs";
17
17
 
18
18
  import { exportImageAsync } from "../export-graph.mjs";
19
19
  import { exportToCSV } from "../export.mjs";
@@ -21,6 +21,8 @@ import { exportToCSV } from "../export.mjs";
21
21
  import { updateHistoryDOM } from "./history.mjs";
22
22
  import { updateMainValueDOM } from "./main-value.mjs";
23
23
  import { updateStatisticsDOM } from "./statistics.mjs";
24
+ import { enableAnchorLinks, trackAnchorLinks } from "../anchor.mjs";
25
+ import { trackVisibleIndicators } from "../track-visible-indicators.mjs";
24
26
 
25
27
  Chart.register(
26
28
  Chart.CategoryScale,
@@ -117,14 +119,6 @@ document.querySelectorAll(".indicator-card").forEach((indicatorCard) => {
117
119
  toggleIndicatorUnit(event.target);
118
120
  });
119
121
  });
120
-
121
- indicatorCard
122
- .querySelectorAll('button[aria-controls="slide-panel-methodo"]')
123
- .forEach((button) => {
124
- button.addEventListener("click", () => {
125
- openMethodoSidePanel(indicator, button);
126
- });
127
- });
128
122
  });
129
123
 
130
124
  document.addEventListener("DOMContentLoaded", () => {
@@ -162,3 +156,7 @@ document
162
156
  button.removeAttribute("disabled");
163
157
  });
164
158
  });
159
+
160
+ enableAnchorLinks();
161
+ trackAnchorLinks();
162
+ trackVisibleIndicators();
@@ -0,0 +1,121 @@
1
+ function trackVisibleIndicators() {
2
+ // Track the most visible card over time
3
+ let currentMostVisibleId = null;
4
+ let mostVisibleCounter = 0;
5
+ let lastTrackedIndicator = null;
6
+ const REQUIRED_CONSECUTIVE_CHECKS = 3; // 3 seconds at 1 check per second
7
+
8
+ function resetTracking() {
9
+ currentMostVisibleId = null;
10
+ mostVisibleCounter = 0;
11
+ }
12
+
13
+ function fetchTracking(indicatorId) {
14
+ fetch("/api/tracking/event/", {
15
+ method: "POST",
16
+ body: JSON.stringify({
17
+ indicator: indicatorId,
18
+ event: "attention-sur-resume-indicateur",
19
+ }),
20
+ });
21
+ }
22
+
23
+ function getMostVisibleIndicatorCard() {
24
+ const indicatorCards = document.querySelectorAll(".indicator-card");
25
+ const visibilityData = [];
26
+
27
+ indicatorCards.forEach((card, index) => {
28
+ const rect = card.getBoundingClientRect();
29
+ const windowHeight =
30
+ window.innerHeight || document.documentElement.clientHeight;
31
+ const windowWidth =
32
+ window.innerWidth || document.documentElement.clientWidth;
33
+
34
+ // Calculate the visible portion of the card
35
+ const visibleTop = Math.max(0, rect.top);
36
+ const visibleBottom = Math.min(windowHeight, rect.bottom);
37
+ const visibleLeft = Math.max(0, rect.left);
38
+ const visibleRight = Math.min(windowWidth, rect.right);
39
+
40
+ // Calculate visible area (only if card is actually visible)
41
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
42
+ const visibleWidth = Math.max(0, visibleRight - visibleLeft);
43
+ const visibleArea = visibleHeight * visibleWidth;
44
+
45
+ // Calculate total card area
46
+ const totalArea = rect.width * rect.height;
47
+
48
+ const visibilityRatio = visibleArea / totalArea;
49
+
50
+ let isVisible = visibleArea > 0;
51
+ // Check if visible area is at least 20% of total area
52
+ if (visibilityRatio < 0.2) {
53
+ isVisible = false;
54
+ }
55
+ // 40% for the first card
56
+ if (index == 0 && visibilityRatio < 0.4) {
57
+ isVisible = false;
58
+ }
59
+ if (isVisible) {
60
+ visibilityData.push({
61
+ card,
62
+ visibleArea,
63
+ totalArea,
64
+ });
65
+ }
66
+ });
67
+
68
+ // No visible cards - reset tracking
69
+ if (visibilityData.length === 0) {
70
+ resetTracking();
71
+ return;
72
+ }
73
+
74
+ // Sort by visible area (descending)
75
+ visibilityData.sort((a, b) => b.visibleArea - a.visibleArea);
76
+
77
+ const mostVisible = visibilityData[0];
78
+
79
+ // Check if there are other visible cards and if the most visible is at least twice as visible
80
+ if (visibilityData.length > 1) {
81
+ const secondMostVisible = visibilityData[1];
82
+ if (mostVisible.visibleArea < 2 * secondMostVisible.visibleArea) {
83
+ resetTracking();
84
+ return;
85
+ }
86
+ }
87
+
88
+ const indicatorId = mostVisible.card.getAttribute("data-indicator");
89
+
90
+ // Check if it's the same card as before
91
+ if (indicatorId === currentMostVisibleId) {
92
+ mostVisibleCounter++;
93
+
94
+ // Add the class only after 3 consecutive seconds
95
+ if (
96
+ mostVisibleCounter >= REQUIRED_CONSECUTIVE_CHECKS &&
97
+ indicatorId !== lastTrackedIndicator
98
+ ) {
99
+ fetchTracking(indicatorId);
100
+ lastTrackedIndicator = indicatorId;
101
+ }
102
+ } else {
103
+ // Different card - reset counter
104
+ currentMostVisibleId = indicatorId;
105
+ mostVisibleCounter = 1;
106
+ }
107
+ }
108
+
109
+ // Start tracking only after first scroll
110
+ window.addEventListener(
111
+ "scroll",
112
+ () => {
113
+ setInterval(() => {
114
+ getMostVisibleIndicatorCard();
115
+ }, 1000);
116
+ },
117
+ { once: true }
118
+ );
119
+ }
120
+
121
+ export { trackVisibleIndicators };