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.
- {eea_plotly-2.3/eea.plotly.egg-info → eea_plotly-2.5}/PKG-INFO +62 -1
- {eea_plotly-2.3 → eea_plotly-2.5}/README.rst +50 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/docs/HISTORY.txt +10 -0
- eea_plotly-2.5/eea/plotly/agents.py +116 -0
- eea_plotly-2.5/eea/plotly/configure.zcml +63 -0
- eea_plotly-2.5/eea/plotly/context_providers.py +168 -0
- eea_plotly-2.5/eea/plotly/generate.py +53 -0
- eea_plotly-2.5/eea/plotly/models.py +89 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/types/visualization.xml +1 -0
- eea_plotly-2.5/eea/plotly/prompts.py +103 -0
- eea_plotly-2.5/eea/plotly/restapi/chart/configure.zcml +13 -0
- eea_plotly-2.5/eea/plotly/restapi/chart/post.py +58 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/configure.zcml +2 -0
- eea_plotly-2.5/eea/plotly/restapi/controlpanel/__init__.py +0 -0
- eea_plotly-2.5/eea/plotly/restapi/summarize/__init__.py +1 -0
- eea_plotly-2.5/eea/plotly/restapi/summarize/configure.zcml +11 -0
- eea_plotly-2.5/eea/plotly/restapi/summarize/post.py +142 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/utils.py +1 -0
- eea_plotly-2.5/eea/plotly/skills.py +168 -0
- eea_plotly-2.5/eea/plotly/tests/test_leak_prevention.py +180 -0
- eea_plotly-2.5/eea/plotly/tools.py +95 -0
- eea_plotly-2.5/eea/plotly/version.txt +1 -0
- {eea_plotly-2.3 → eea_plotly-2.5/eea.plotly.egg-info}/PKG-INFO +62 -1
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/SOURCES.txt +14 -2
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/requires.txt +1 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/setup.py +1 -0
- eea_plotly-2.3/eea/plotly/configure.zcml +0 -30
- eea_plotly-2.3/eea/plotly/tests/base.py +0 -51
- eea_plotly-2.3/eea/plotly/tests/test_doctests.py +0 -31
- eea_plotly-2.3/eea/plotly/version.txt +0 -1
- {eea_plotly-2.3 → eea_plotly-2.5}/CONTRIBUTING.md +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/MANIFEST.in +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/docs/LICENSE.GPL +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/docs/LICENSE.txt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/README.txt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/behaviors.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/controlpanel.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview_adapter/adapter.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/preview_adapter/soer_miniature.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/browser/pt/visualization_view.pt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/controlpanel.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/blocks.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/dxcontent.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/deserializer/dxfields.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/interfaces.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/io_csv.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/io_json.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/bg/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/bg/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/cs/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/cs/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/da/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/da/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/de/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/de/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eea.pot +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/el/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/el/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/en/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/en/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/es/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/es/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/et/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/et/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eu/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/eu/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fi/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fi/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fr/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/fr/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hr/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hr/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hu/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/hu/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/is/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/is/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/it/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/it/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/kl/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/kl/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lt/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lt/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lv/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/lv/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/mt/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/mt/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/nl/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/nl/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/no/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/no/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pl/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pl/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/plone-manual.pot +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt_BR/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/pt_BR/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ro/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ro/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ru/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/ru/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sk/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sk/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sl/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sl/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sv/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/sv/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/tr/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/tr/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/update.sh +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/zh_TW/LC_MESSAGES/eea.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/locales/zh_TW/LC_MESSAGES/plone.po +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/permissions.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/browserlayer.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/controlpanel.json +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/controlpanel.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/metadata.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/registry.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/rolemap.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/default/types.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/browserlayer.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/controlpanel.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles/uninstall/registry.xml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/profiles.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/__init__.py +0 -0
- {eea_plotly-2.3/eea/plotly/restapi/controlpanel → eea_plotly-2.5/eea/plotly/restapi/chart}/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/get.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/controlpanel/reset.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/visualization/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/restapi/visualization/get.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/blocks.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/serializer/dxfields.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/setuphandlers.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/tests/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/tests/test_utils.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/upgrades/__init__.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/upgrades/configure.zcml +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea/plotly/utils.py +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/dependency_links.txt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/entry_points.txt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/namespace_packages.txt +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/not-zip-safe +0 -0
- {eea_plotly-2.3 → eea_plotly-2.5}/eea.plotly.egg-info/top_level.txt +0 -0
- {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
|
+
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" />
|