eea.plotly 2.3__tar.gz → 2.5__tar.gz

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 (153) hide show
  1. {eea_plotly-2.3/eea.plotly.egg-info → eea_plotly-2.5}/PKG-INFO +62 -1
  2. {eea_plotly-2.3 → eea_plotly-2.5}/README.rst +50 -0
  3. {eea_plotly-2.3 → eea_plotly-2.5}/docs/HISTORY.txt +10 -0
  4. eea_plotly-2.5/eea/plotly/agents.py +116 -0
  5. eea_plotly-2.5/eea/plotly/configure.zcml +63 -0
  6. eea_plotly-2.5/eea/plotly/context_providers.py +168 -0
  7. eea_plotly-2.5/eea/plotly/generate.py +53 -0
  8. eea_plotly-2.5/eea/plotly/models.py +89 -0
  9. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/types/visualization.xml +1 -0
  10. eea_plotly-2.5/eea/plotly/prompts.py +103 -0
  11. eea_plotly-2.5/eea/plotly/restapi/chart/configure.zcml +13 -0
  12. eea_plotly-2.5/eea/plotly/restapi/chart/post.py +58 -0
  13. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/configure.zcml +2 -0
  14. eea_plotly-2.5/eea/plotly/restapi/controlpanel/__init__.py +0 -0
  15. eea_plotly-2.5/eea/plotly/restapi/summarize/__init__.py +1 -0
  16. eea_plotly-2.5/eea/plotly/restapi/summarize/configure.zcml +11 -0
  17. eea_plotly-2.5/eea/plotly/restapi/summarize/post.py +142 -0
  18. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/utils.py +1 -0
  19. eea_plotly-2.5/eea/plotly/skills.py +168 -0
  20. eea_plotly-2.5/eea/plotly/tests/test_leak_prevention.py +180 -0
  21. eea_plotly-2.5/eea/plotly/tools.py +95 -0
  22. eea_plotly-2.5/eea/plotly/version.txt +1 -0
  23. {eea_plotly-2.3 → eea_plotly-2.5/eea.plotly.egg-info}/PKG-INFO +62 -1
  24. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/SOURCES.txt +14 -2
  25. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/requires.txt +1 -0
  26. {eea_plotly-2.3 → eea_plotly-2.5}/setup.py +1 -0
  27. eea_plotly-2.3/eea/plotly/configure.zcml +0 -30
  28. eea_plotly-2.3/eea/plotly/tests/base.py +0 -51
  29. eea_plotly-2.3/eea/plotly/tests/test_doctests.py +0 -31
  30. eea_plotly-2.3/eea/plotly/version.txt +0 -1
  31. {eea_plotly-2.3 → eea_plotly-2.5}/CONTRIBUTING.md +0 -0
  32. {eea_plotly-2.3 → eea_plotly-2.5}/MANIFEST.in +0 -0
  33. {eea_plotly-2.3 → eea_plotly-2.5}/docs/LICENSE.GPL +0 -0
  34. {eea_plotly-2.3 → eea_plotly-2.5}/docs/LICENSE.txt +0 -0
  35. {eea_plotly-2.3 → eea_plotly-2.5}/eea/__init__.py +0 -0
  36. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/README.txt +0 -0
  37. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/__init__.py +0 -0
  38. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/behaviors.py +0 -0
  39. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/__init__.py +0 -0
  40. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/configure.zcml +0 -0
  41. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/controlpanel.py +0 -0
  42. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview.py +0 -0
  43. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview_adapter/adapter.py +0 -0
  44. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview_adapter/soer_miniature.py +0 -0
  45. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/pt/visualization_view.pt +0 -0
  46. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/controlpanel.py +0 -0
  47. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/blocks.py +0 -0
  48. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/configure.zcml +0 -0
  49. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/dxcontent.py +0 -0
  50. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/dxfields.py +0 -0
  51. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/interfaces.py +0 -0
  52. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/io_csv.py +0 -0
  53. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/io_json.py +0 -0
  54. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/__init__.py +0 -0
  55. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/bg/LC_MESSAGES/eea.po +0 -0
  56. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/bg/LC_MESSAGES/plone.po +0 -0
  57. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/cs/LC_MESSAGES/eea.po +0 -0
  58. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/cs/LC_MESSAGES/plone.po +0 -0
  59. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/da/LC_MESSAGES/eea.po +0 -0
  60. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/da/LC_MESSAGES/plone.po +0 -0
  61. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/de/LC_MESSAGES/eea.po +0 -0
  62. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/de/LC_MESSAGES/plone.po +0 -0
  63. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eea.pot +0 -0
  64. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/el/LC_MESSAGES/eea.po +0 -0
  65. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/el/LC_MESSAGES/plone.po +0 -0
  66. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/en/LC_MESSAGES/eea.po +0 -0
  67. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/en/LC_MESSAGES/plone.po +0 -0
  68. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/es/LC_MESSAGES/eea.po +0 -0
  69. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/es/LC_MESSAGES/plone.po +0 -0
  70. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/et/LC_MESSAGES/eea.po +0 -0
  71. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/et/LC_MESSAGES/plone.po +0 -0
  72. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eu/LC_MESSAGES/eea.po +0 -0
  73. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eu/LC_MESSAGES/plone.po +0 -0
  74. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fi/LC_MESSAGES/eea.po +0 -0
  75. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fi/LC_MESSAGES/plone.po +0 -0
  76. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fr/LC_MESSAGES/eea.po +0 -0
  77. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fr/LC_MESSAGES/plone.po +0 -0
  78. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hr/LC_MESSAGES/eea.po +0 -0
  79. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hr/LC_MESSAGES/plone.po +0 -0
  80. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hu/LC_MESSAGES/eea.po +0 -0
  81. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hu/LC_MESSAGES/plone.po +0 -0
  82. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/is/LC_MESSAGES/eea.po +0 -0
  83. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/is/LC_MESSAGES/plone.po +0 -0
  84. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/it/LC_MESSAGES/eea.po +0 -0
  85. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/it/LC_MESSAGES/plone.po +0 -0
  86. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/kl/LC_MESSAGES/eea.po +0 -0
  87. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/kl/LC_MESSAGES/plone.po +0 -0
  88. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lt/LC_MESSAGES/eea.po +0 -0
  89. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lt/LC_MESSAGES/plone.po +0 -0
  90. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lv/LC_MESSAGES/eea.po +0 -0
  91. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lv/LC_MESSAGES/plone.po +0 -0
  92. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/mt/LC_MESSAGES/eea.po +0 -0
  93. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/mt/LC_MESSAGES/plone.po +0 -0
  94. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/nl/LC_MESSAGES/eea.po +0 -0
  95. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/nl/LC_MESSAGES/plone.po +0 -0
  96. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/no/LC_MESSAGES/eea.po +0 -0
  97. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/no/LC_MESSAGES/plone.po +0 -0
  98. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pl/LC_MESSAGES/eea.po +0 -0
  99. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pl/LC_MESSAGES/plone.po +0 -0
  100. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/plone-manual.pot +0 -0
  101. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt/LC_MESSAGES/eea.po +0 -0
  102. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt/LC_MESSAGES/plone.po +0 -0
  103. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt_BR/LC_MESSAGES/eea.po +0 -0
  104. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt_BR/LC_MESSAGES/plone.po +0 -0
  105. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ro/LC_MESSAGES/eea.po +0 -0
  106. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ro/LC_MESSAGES/plone.po +0 -0
  107. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ru/LC_MESSAGES/eea.po +0 -0
  108. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ru/LC_MESSAGES/plone.po +0 -0
  109. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sk/LC_MESSAGES/eea.po +0 -0
  110. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sk/LC_MESSAGES/plone.po +0 -0
  111. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sl/LC_MESSAGES/eea.po +0 -0
  112. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sl/LC_MESSAGES/plone.po +0 -0
  113. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sv/LC_MESSAGES/eea.po +0 -0
  114. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sv/LC_MESSAGES/plone.po +0 -0
  115. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/tr/LC_MESSAGES/eea.po +0 -0
  116. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/tr/LC_MESSAGES/plone.po +0 -0
  117. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/update.sh +0 -0
  118. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/zh_TW/LC_MESSAGES/eea.po +0 -0
  119. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/zh_TW/LC_MESSAGES/plone.po +0 -0
  120. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/permissions.zcml +0 -0
  121. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/browserlayer.xml +0 -0
  122. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/controlpanel.json +0 -0
  123. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/controlpanel.xml +0 -0
  124. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/metadata.xml +0 -0
  125. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/registry.xml +0 -0
  126. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/rolemap.xml +0 -0
  127. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/types.xml +0 -0
  128. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/browserlayer.xml +0 -0
  129. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/controlpanel.xml +0 -0
  130. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/registry.xml +0 -0
  131. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles.zcml +0 -0
  132. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/__init__.py +0 -0
  133. {eea_plotly-2.3/eea/plotly/restapi/controlpanel → eea_plotly-2.5/eea/plotly/restapi/chart}/__init__.py +0 -0
  134. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/configure.zcml +0 -0
  135. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/get.py +0 -0
  136. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/reset.py +0 -0
  137. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/visualization/configure.zcml +0 -0
  138. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/visualization/get.py +0 -0
  139. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/blocks.py +0 -0
  140. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/configure.zcml +0 -0
  141. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/dxfields.py +0 -0
  142. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/setuphandlers.py +0 -0
  143. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/tests/__init__.py +0 -0
  144. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/tests/test_utils.py +0 -0
  145. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/upgrades/__init__.py +0 -0
  146. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/upgrades/configure.zcml +0 -0
  147. {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/utils.py +0 -0
  148. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/dependency_links.txt +0 -0
  149. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/entry_points.txt +0 -0
  150. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/namespace_packages.txt +0 -0
  151. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/not-zip-safe +0 -0
  152. {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/top_level.txt +0 -0
  153. {eea_plotly-2.3 → eea_plotly-2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eea.plotly
3
- Version: 2.3
3
+ Version: 2.5
4
4
  Summary: volto-plotlycharts backend support
5
5
  Home-page: https://github.com/eea/eea.plotly
6
6
  Author: European Environment Agency: IDM2 A-Team
@@ -24,6 +24,7 @@ Requires-Dist: plone.volto
24
24
  Requires-Dist: collective.folderishtypes
25
25
  Requires-Dist: plotly==5.24.1
26
26
  Requires-Dist: kaleido==0.2.1
27
+ Requires-Dist: eea.genai.summary
27
28
  Provides-Extra: test
28
29
  Requires-Dist: plone.app.testing; extra == "test"
29
30
  Dynamic: author
@@ -142,9 +143,69 @@ EEA_ - European Environment Agency (EU)
142
143
  .. _EEA: https://www.eea.europa.eu/
143
144
  .. _`EEA Web Systems Training`: http://www.youtube.com/user/eeacms/videos?view=1
144
145
 
146
+ Secret Scanning
147
+ ===============
148
+
149
+ This repository uses the Betterleaks GitHub Action to scan the current
150
+ repository content on every push and pull request. The scan uses the rules in
151
+ ``.gitleaks.toml`` and uploads a ``betterleaks-report`` artifact when a finding
152
+ is detected.
153
+
154
+ If the optional SMTP secrets are configured, failed scans also send an email to
155
+ the last commit committer. The workflow expects these repository or
156
+ organization secrets:
157
+
158
+ - ``SMTP_URL``
159
+ - ``SMTP_PORT`` (optional, defaults to ``25``)
160
+ - ``SMTP_EMAIL``
161
+ - ``SMTP_PASSWORD`` (optional if the SMTP server does not require authentication)
162
+
163
+ Port ``465`` is sent with direct TLS; other ports use the default SMTP
164
+ handshake. The email includes a short finding summary from the redacted
165
+ Betterleaks report, including the redacted matched line from each finding.
166
+
167
+ There are three common outcomes:
168
+
169
+ 1. Everything is OK. The ``Betterleaks / Scan for secrets`` check is green and
170
+ no action is needed. Regular references to runtime values are OK, for example::
171
+
172
+ token_from_cookie = request.cookies.get("auth_token")
173
+
174
+ 2. A real secret was found. The check is red and the workflow log asks you to
175
+ download the ``betterleaks-report`` artifact. Open the artifact from the
176
+ GitHub Actions run and check the reported file, line and rule. Remove the
177
+ committed value, move it to the proper secret store, and rotate it if it was
178
+ exposed. A report entry looks like this::
179
+
180
+ {
181
+ "RuleID": "secret-literal-assignment",
182
+ "File": "src/config.py",
183
+ "StartLine": 12,
184
+ "Secret": "[REDACTED]"
185
+ }
186
+
187
+ 3. The finding is a false positive. Keep the value only if it is clearly not
188
+ sensitive, such as a test fixture, placeholder, or public example. Add
189
+ ``betterleaks:allow`` on the same line and include a short explanation in the
190
+ pull request::
191
+
192
+ test_password = "admin" #betterleaks:allow
193
+
194
+ Do not add ``betterleaks:allow`` to real credentials.
195
+
145
196
  Changelog
146
197
  =========
147
198
 
199
+ 2.5 - (2026-07-13)
200
+ ---------------------------
201
+ * Change: fix: SonarQube report - refs #305404
202
+ [avoinea]
203
+
204
+ 2.4 - (2026-05-26)
205
+ ---------------------------
206
+ * Change: Implement eea.genai.summary customization
207
+ [razvanMiu]
208
+
148
209
  2.3 - (2026-04-14)
149
210
  ---------------------------
150
211
  * Change: Add unit tests for utils, io_csv, and io_json modules
@@ -101,3 +101,53 @@ EEA_ - European Environment Agency (EU)
101
101
 
102
102
  .. _EEA: https://www.eea.europa.eu/
103
103
  .. _`EEA Web Systems Training`: http://www.youtube.com/user/eeacms/videos?view=1
104
+
105
+ Secret Scanning
106
+ ===============
107
+
108
+ This repository uses the Betterleaks GitHub Action to scan the current
109
+ repository content on every push and pull request. The scan uses the rules in
110
+ ``.gitleaks.toml`` and uploads a ``betterleaks-report`` artifact when a finding
111
+ is detected.
112
+
113
+ If the optional SMTP secrets are configured, failed scans also send an email to
114
+ the last commit committer. The workflow expects these repository or
115
+ organization secrets:
116
+
117
+ - ``SMTP_URL``
118
+ - ``SMTP_PORT`` (optional, defaults to ``25``)
119
+ - ``SMTP_EMAIL``
120
+ - ``SMTP_PASSWORD`` (optional if the SMTP server does not require authentication)
121
+
122
+ Port ``465`` is sent with direct TLS; other ports use the default SMTP
123
+ handshake. The email includes a short finding summary from the redacted
124
+ Betterleaks report, including the redacted matched line from each finding.
125
+
126
+ There are three common outcomes:
127
+
128
+ 1. Everything is OK. The ``Betterleaks / Scan for secrets`` check is green and
129
+ no action is needed. Regular references to runtime values are OK, for example::
130
+
131
+ token_from_cookie = request.cookies.get("auth_token")
132
+
133
+ 2. A real secret was found. The check is red and the workflow log asks you to
134
+ download the ``betterleaks-report`` artifact. Open the artifact from the
135
+ GitHub Actions run and check the reported file, line and rule. Remove the
136
+ committed value, move it to the proper secret store, and rotate it if it was
137
+ exposed. A report entry looks like this::
138
+
139
+ {
140
+ "RuleID": "secret-literal-assignment",
141
+ "File": "src/config.py",
142
+ "StartLine": 12,
143
+ "Secret": "[REDACTED]"
144
+ }
145
+
146
+ 3. The finding is a false positive. Keep the value only if it is clearly not
147
+ sensitive, such as a test fixture, placeholder, or public example. Add
148
+ ``betterleaks:allow`` on the same line and include a short explanation in the
149
+ pull request::
150
+
151
+ test_password = "admin" #betterleaks:allow
152
+
153
+ Do not add ``betterleaks:allow`` to real credentials.
@@ -1,6 +1,16 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2.5 - (2026-07-13)
5
+ ---------------------------
6
+ * Change: fix: SonarQube report - refs #305404
7
+ [avoinea]
8
+
9
+ 2.4 - (2026-05-26)
10
+ ---------------------------
11
+ * Change: Implement eea.genai.summary customization
12
+ [razvanMiu]
13
+
4
14
  2.3 - (2026-04-14)
5
15
  ---------------------------
6
16
  * Change: Add unit tests for utils, io_csv, and io_json modules
@@ -0,0 +1,116 @@
1
+ """Agent configurations for eea.plotly."""
2
+
3
+ from eea.genai.core.interfaces import AgentConfiguration
4
+
5
+ PLOTLY_SUMMARIZER_SYSTEM_PROMPT = """\
6
+ You are an expert data visualization analyst working for the \
7
+ European Environment Agency (EEA).
8
+ You will receive information about an interactive chart built with \
9
+ Plotly.js, along with limited page metadata.
10
+ Your output is indexed in a semantic search system that feeds AI \
11
+ assistants, so prioritize topical keywords, qualitative descriptions, \
12
+ and clear context over stylistic flourish.
13
+ Do not use bullet points or markdown. Write in clear plain prose.
14
+ If you are unable to resolve the task answer with empty string.
15
+
16
+ ABSOLUTE RULE — no quantitative information of any kind:
17
+ - Forbidden phrasing includes any year ("2010", "2023"), decade \
18
+ ("the 2010s", "early 2010s", "mid-2020s"), year range \
19
+ ("2010-2023", "between 2015 and 2020", "from 2012 through 2032"), \
20
+ percentage ("40%"), currency amount ("EUR 12 billion"), count \
21
+ ("261 million tonnes", "around 50", "over 7,000"), or change verb \
22
+ implying magnitude ("tripled", "halved", "increased by", \
23
+ "dropped to").
24
+ - Use qualitative descriptors instead: "across recent decades", \
25
+ "over a multi-year period", "a small share", "a notable increase", \
26
+ "a downward trend", "a wide range of countries".
27
+ - The chart data is provided only as compact summaries (e.g. \
28
+ "[N numeric values]"). Do not invent specifics that those summaries \
29
+ do not contain.
30
+ If you accidentally include a number or year, rewrite that sentence \
31
+ qualitatively before returning.
32
+ """
33
+
34
+ PLOTLY_SUMMARIZER_TASK_PROMPT = """\
35
+ Write a 3-8 sentence (max ~150 words) plain-prose description of this \
36
+ chart, optimized for retrieval by semantic search. Cover:
37
+ - the environmental or policy question the chart addresses;
38
+ - the topic, indicator, or phenomenon shown;
39
+ - the chart type (bar chart, line chart, choropleth map, etc.);
40
+ - what the axes represent qualitatively (e.g. "years over recent \
41
+ decades", "European countries", "emissions in CO2 equivalent");
42
+ - the names of data series and qualitative groupings (sectors, \
43
+ regions, gases);
44
+ - geographic scope expressed in words.
45
+
46
+ Constraints:
47
+ - No numbers, ranges, min/max/mean, percentages, currency amounts, \
48
+ years, decades, or any other quantitative claim (see system prompt for \
49
+ the full forbidden list).
50
+ - No markdown, bullet points, or headings in the output.
51
+ - Do not preface with "This chart..." and do not repeat the page \
52
+ title verbatim.
53
+ - Match the output language to the content language.
54
+ - If there is not enough information, return an empty string.
55
+ """
56
+
57
+
58
+ class PlotlySummarizerAgent(AgentConfiguration):
59
+ """Chart summarizer agent for the EEA website.
60
+
61
+ Context is intentionally minimal: only the chart structure (axes,
62
+ trace names, qualitative summaries of the data) plus
63
+ ``generic_metadata_no_dates`` (title, description, language, geo —
64
+ no temporal coverage). The ``blocks`` enricher is deliberately
65
+ excluded so the model cannot read year/quantity leaks from the
66
+ surrounding page text.
67
+ """
68
+
69
+ system_prompt = PLOTLY_SUMMARIZER_SYSTEM_PROMPT
70
+ task_prompt = PLOTLY_SUMMARIZER_TASK_PROMPT
71
+ skills = ["plotly_knowledge"]
72
+ context_providers = ["generic_metadata_no_dates", "plotly_visualization"]
73
+
74
+
75
+ PLOTLY_GENERATOR_SYSTEM_PROMPT = """\
76
+ You are a Plotly.js chart generation expert working for the \
77
+ European Environment Agency (EEA). You generate complete visualization \
78
+ content including metadata (title, description, topics, temporal and \
79
+ geographic coverage) and a valid Plotly.js chart configuration.
80
+
81
+ Generation conventions:
82
+ - Set `layout.template` to the string `"__ACTIVE_THEME__"`. The full \
83
+ theme object is injected automatically after generation. Do NOT generate \
84
+ theme content.
85
+ - Do NOT set `yaxis.title`. Use `layout.title.subtitle.text` for the \
86
+ unit or measurement label (e.g. "Million tonnes CO2 equivalent").
87
+ - Use `hovertemplate` with `hoverinfo: "none"` and \
88
+ `hoverlabel: {namelength: 0}`.
89
+ - Multiple traces sharing the same x-axis should use the same `xsrc` column.
90
+ - For multi-series charts, create one trace per series, each with its \
91
+ own `ysrc` column.
92
+ - Always fetch a relevant template with `get_plotly_template` first and \
93
+ follow its structure. Add complexity on top of the template as needed, \
94
+ but preserve its conventions.
95
+ - Do not ask for clarifications or additional information. If you cannot \
96
+ generate a valid chart, return an empty result.
97
+ """
98
+
99
+ PLOTLY_GENERATOR_TASK_PROMPT = """\
100
+ Generate a complete visualization content based on the user's request. \
101
+ Include: a descriptive title, a 1-3 sentence description, relevant EEA \
102
+ topics, temporal coverage (years), geographic coverage, and a valid \
103
+ Plotly.js chart with 'data' (traces) and 'layout' (with title, axes, \
104
+ and template).
105
+ """
106
+
107
+
108
+ class PlotlyGeneratorAgent(AgentConfiguration):
109
+ """Chart generator agent for the EEA website."""
110
+
111
+ system_prompt = PLOTLY_GENERATOR_SYSTEM_PROMPT
112
+ task_prompt = PLOTLY_GENERATOR_TASK_PROMPT
113
+ skills = ["plotly_knowledge"]
114
+ tools = ["get_plotly_template"]
115
+ output_type = "eea.plotly.models.ChartGenerationResult"
116
+ max_iterations = 10
@@ -0,0 +1,63 @@
1
+ <configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://namespaces.zope.org/i18n"
2
+ xmlns:plone="http://namespaces.plone.org/plone"
3
+ xmlns:genai="http://namespaces.eea.europa.eu/genai"
4
+ i18n_domain="eea">
5
+
6
+ <i18n:registerTranslations directory="locales" />
7
+
8
+ <include file="profiles.zcml" />
9
+ <include file="permissions.zcml" />
10
+
11
+ <include package="eea.genai.core" file="meta.zcml" />
12
+
13
+ <include package=".browser" />
14
+ <include package=".restapi" />
15
+ <include package=".serializer" />
16
+ <include package=".deserializer" />
17
+ <include package=".upgrades" />
18
+
19
+ <interface interface="eea.plotly.interfaces.IPlotlyVisualization" />
20
+
21
+ <adapter
22
+ factory=".controlpanel.PlotlyControlpanel"
23
+ name="plotly"
24
+ />
25
+
26
+ <plone:behavior
27
+ name="eea.plotly.visualization"
28
+ title="EEA-Viz_Plotly"
29
+ description="Allows adding an interactive plotly chart."
30
+ provides="eea.plotly.behaviors.IPlotlyVisualization"
31
+ />
32
+
33
+ <!-- GenAI: Context provider for chart interpretation -->
34
+ <genai:agentContextProvider
35
+ name="plotly_visualization"
36
+ class=".context_providers.PlotlyVisualizationProvider"
37
+ />
38
+
39
+ <!-- GenAI: Plotly knowledge skill for chart generation -->
40
+ <genai:agentSkill
41
+ name="plotly_knowledge"
42
+ class=".skills.PlotlyKnowledgeSkill"
43
+ />
44
+
45
+ <!-- GenAI: Tool to fetch chart templates by label -->
46
+ <genai:agentTool
47
+ name="get_plotly_template"
48
+ class=".tools.GetPlotlyTemplateTool"
49
+ />
50
+
51
+ <!-- GenAI: Content-type-specific summarizer for visualization -->
52
+ <genai:agent
53
+ name="summarizer:visualization"
54
+ class=".agents.PlotlySummarizerAgent"
55
+ />
56
+
57
+ <!-- GenAI: Chart generator agent -->
58
+ <genai:agent
59
+ name="plotly_generator"
60
+ class=".agents.PlotlyGeneratorAgent"
61
+ />
62
+
63
+ </configure>
@@ -0,0 +1,168 @@
1
+ """Agent context providers for eea.plotly."""
2
+
3
+ import json
4
+ import logging
5
+
6
+ from eea.genai.core.interfaces import Enricher
7
+ from eea.genai.core.utils import Source
8
+ from eea.plotly.prompts import clean_layout
9
+ from eea.plotly.utils import sanitizeVisualization
10
+
11
+ logger = logging.getLogger("eea.plotly")
12
+
13
+ # Arrays with more values than this threshold get replaced with
14
+ # statistical summaries to keep the prompt within token budget.
15
+ _ARRAY_TRUNCATION_THRESHOLD = 200
16
+
17
+
18
+ class PlotlyVisualizationProvider(Enricher):
19
+ """Extracts Plotly chart data and adds it to the user prompt.
20
+
21
+ Reads deps.context.visualization, cleans cosmetic layout keys,
22
+ and truncates large data arrays to statistical summaries while
23
+ preserving the full Plotly JSON structure.
24
+ """
25
+
26
+ name = "plotly_visualization"
27
+ description = "Adds Plotly chart structure and data to the user prompt"
28
+
29
+ def user_prompt(self, deps):
30
+ context = getattr(deps, "context", None)
31
+ properties = getattr(deps, "properties", None) or {}
32
+
33
+ if context is None:
34
+ return ""
35
+
36
+ source = Source(context, properties)
37
+
38
+ viz = getattr(source, "visualization", None)
39
+ if not viz or not isinstance(viz, dict):
40
+ return ""
41
+
42
+ prepared = prepare_visualization(viz)
43
+ if not prepared:
44
+ return ""
45
+
46
+ viz_json = json.dumps(prepared, indent=2, default=str)
47
+ return f"### Plotly visualization\n\n```json\n{viz_json}\n```"
48
+
49
+
50
+ def prepare_visualization(viz):
51
+ """Return a cleaned copy of the visualization JSON.
52
+
53
+ 1. Normalizes via sanitizeVisualization() for backward compat.
54
+ 2. Cleans cosmetic layout keys via clean_layout().
55
+ 3. Truncates large data arrays (>200 values) to statistical summaries.
56
+ """
57
+ normalized = sanitizeVisualization(viz)
58
+ data = normalized.get("data", [])
59
+ layout = normalized.get("layout", {})
60
+ data_sources = normalized.get("dataSources", {})
61
+
62
+ if not data and not data_sources:
63
+ return None
64
+
65
+ result = {}
66
+
67
+ # Clean layout
68
+ if layout:
69
+ result["layout"] = clean_layout(layout)
70
+
71
+ # Process traces
72
+ if data:
73
+ result["data"] = [_truncate_trace(trace) for trace in data]
74
+
75
+ # Process dataSources
76
+ if data_sources:
77
+ result["dataSources"] = _truncate_data_sources(data_sources)
78
+
79
+ return result
80
+
81
+
82
+ def _is_numeric_list(values):
83
+ """True if every element is a real number (ints/floats, not bool)."""
84
+ if not values:
85
+ return False
86
+ return all(isinstance(v, (int, float)) and not isinstance(v, bool) for v in values)
87
+
88
+
89
+ def _looks_like_year_strings(values):
90
+ """True if strings parse as 4-digit calendar years.
91
+
92
+ String year arrays (e.g. ["2012","2013",...]) are otherwise treated as
93
+ categorical and would leak first/last samples through ``_summarize_array``.
94
+ """
95
+ if not values:
96
+ return False
97
+ for v in values:
98
+ if not isinstance(v, str) or len(v) != 4 or not v.isdigit():
99
+ return False
100
+ n = int(v)
101
+ if not (1800 <= n <= 2200):
102
+ return False
103
+ return True
104
+
105
+
106
+ def _truncate_trace(trace):
107
+ """Return a copy of a trace with sensitive/large arrays summarized.
108
+
109
+ Numeric arrays are always summarized (regardless of length) so raw
110
+ values cannot reach the LLM. Year-like string arrays are also always
111
+ summarized. Other lists pass through unless they exceed the truncation
112
+ threshold.
113
+ """
114
+ result = {}
115
+ for key, value in trace.items():
116
+ if isinstance(value, list) and (
117
+ _is_numeric_list(value)
118
+ or _looks_like_year_strings(value)
119
+ or len(value) > _ARRAY_TRUNCATION_THRESHOLD
120
+ ):
121
+ result[key] = _summarize_array(value)
122
+ else:
123
+ result[key] = value
124
+ return result
125
+
126
+
127
+ def _truncate_data_sources(data_sources):
128
+ """Return a copy of dataSources with sensitive/large columns summarized."""
129
+ result = {}
130
+ for col_name, values in data_sources.items():
131
+ if isinstance(values, list) and (
132
+ _is_numeric_list(values)
133
+ or _looks_like_year_strings(values)
134
+ or len(values) > _ARRAY_TRUNCATION_THRESHOLD
135
+ ):
136
+ result[col_name] = _summarize_array(values)
137
+ else:
138
+ result[col_name] = values
139
+ return result
140
+
141
+
142
+ def _summarize_array(values):
143
+ """Summarize an array into a compact description string.
144
+
145
+ Numeric arrays → ``"[N numeric values]"``. Year-like string arrays →
146
+ ``"[N year-like values]"``. Other arrays keep first/last samples and
147
+ unique count because labels like country codes are useful qualitative
148
+ keywords for retrieval.
149
+
150
+ Quantitative details (min/max/mean) are deliberately omitted: agents
151
+ that consume this output are instructed to avoid numeric claims, and
152
+ summary text cannot leak what is not present.
153
+ """
154
+ n = len(values)
155
+
156
+ if _is_numeric_list(values):
157
+ return f"[{n} numeric values]"
158
+ if _looks_like_year_strings(values):
159
+ return f"[{n} year-like values]"
160
+
161
+ first = values[:3]
162
+ last = values[-3:]
163
+ try:
164
+ unique_count = len(set(str(v) for v in values))
165
+ except Exception:
166
+ unique_count = "?"
167
+
168
+ return f"[{n} values, {unique_count} unique, first: {first}, last: {last}]"
@@ -0,0 +1,53 @@
1
+ """Chart generation using agent system."""
2
+
3
+ import logging
4
+
5
+ from plone import api
6
+
7
+ from eea.genai.core.agent import AgentDeps
8
+ from eea.genai.core.utils import get_executor
9
+ from eea.plotly.controlpanel import IPlotlySettings
10
+
11
+ logger = logging.getLogger("eea.plotly")
12
+
13
+
14
+ def generate_chart(prompt, data_sources=None, context=None, request=None):
15
+ """Generate a full visualization content from a natural language description."""
16
+ deps = AgentDeps(
17
+ context=context, request=request, data_sources=data_sources or None
18
+ )
19
+ result = get_executor().run_with_agent(
20
+ "plotly_generator", user_prompt=prompt, deps=deps
21
+ )
22
+ _inject_theme(result)
23
+ return result.model_dump()
24
+
25
+
26
+ def _inject_theme(result):
27
+ """Replace __ACTIVE_THEME__ placeholder with the actual theme object."""
28
+ layout = result.visualization.layout
29
+ if layout.get("template") != "__ACTIVE_THEME__":
30
+ return
31
+
32
+ theme = _get_active_theme()
33
+ if theme is None:
34
+ logger.warning("No active theme found; removing template placeholder")
35
+ layout.pop("template", None)
36
+ return
37
+
38
+ result.visualization.layout["template"] = theme
39
+
40
+
41
+ def _get_active_theme():
42
+ """Return the first non-hidden theme from the Plotly control panel."""
43
+ try:
44
+ themes = api.portal.get_registry_record(
45
+ "themes", interface=IPlotlySettings, default=[]
46
+ )
47
+ except Exception:
48
+ return None
49
+
50
+ for theme in themes:
51
+ if not theme.get("hidden"):
52
+ return theme
53
+ return None
@@ -0,0 +1,89 @@
1
+ """Pydantic models for structured LLM output of chart operations."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class VisualizationData(BaseModel):
7
+ """The Plotly chart configuration stored in the visualization field."""
8
+
9
+ data: list[dict] = Field(
10
+ description=(
11
+ "Ordered list of Plotly trace objects. Each trace must have a "
12
+ "'type' field, '*src' references to dataSources columns, "
13
+ "dereferenced inline data arrays, and 'meta.columnNames' mapping."
14
+ ),
15
+ )
16
+
17
+ layout: dict = Field(
18
+ description=(
19
+ "Plotly layout object with title, axis definitions, and "
20
+ "layout.template set to the active site theme."
21
+ ),
22
+ default_factory=dict,
23
+ )
24
+
25
+ dataSources: dict = Field(
26
+ description=(
27
+ "Dict of column_name -> array of values. Contains all data "
28
+ "columns referenced by traces via *src fields."
29
+ ),
30
+ default_factory=dict,
31
+ )
32
+
33
+ columns: list[dict] = Field(
34
+ description=(
35
+ "List of column metadata objects, each with a 'key' field "
36
+ "matching a dataSources column name. "
37
+ 'Example: [{"key": "Country"}, {"key": "Value"}]'
38
+ ),
39
+ default_factory=list,
40
+ )
41
+
42
+
43
+ class ChartGenerationResult(BaseModel):
44
+ """Result of LLM visualization content generation.
45
+
46
+ Represents a full visualization content type with metadata and chart data.
47
+ """
48
+
49
+ title: str = Field(
50
+ description="Content title for the visualization page.",
51
+ )
52
+
53
+ description: str = Field(
54
+ description=(
55
+ "Short summary describing the visualization (1-3 sentences). "
56
+ "Used as the page description / Dublin Core description."
57
+ ),
58
+ default="",
59
+ )
60
+
61
+ visualization: VisualizationData = Field(
62
+ description=(
63
+ "The complete Plotly visualization with traces (data), layout, "
64
+ "dataSources (raw dataset columns), and columns metadata."
65
+ ),
66
+ )
67
+
68
+ topics: list[str] = Field(
69
+ description=(
70
+ "List of EEA topic tags relevant to this visualization "
71
+ "(e.g. 'Climate change mitigation', 'Air pollution')."
72
+ ),
73
+ default_factory=list,
74
+ )
75
+
76
+ temporal_coverage: list[int] = Field(
77
+ description=(
78
+ "List of years covered by the data (e.g. [2018, 2019, 2020, 2021, 2022])."
79
+ ),
80
+ default_factory=list,
81
+ )
82
+
83
+ geo_coverage: list[str] = Field(
84
+ description=(
85
+ "List of geographic areas covered by the data "
86
+ "(e.g. ['Europe', 'EU-27', 'Germany'])."
87
+ ),
88
+ default_factory=list,
89
+ )
@@ -20,6 +20,7 @@
20
20
  <property name="klass">collective.folderishtypes.dx.content.FolderishDocument</property>
21
21
  <property name="behaviors">
22
22
  <element value="eea.plotly.visualization" />
23
+ <element value="eea.genai.summary" />
23
24
  <element value="eea.coremetadata.behavior" />
24
25
  <element value="eea.dataconnector.figure_note" />
25
26
  <element value="plone.namefromtitle" />