Enviar errores a slack#119
Merged
germankay merged 10 commits into116-show-csv-calidator-errorfrom Feb 17, 2026
Merged
Conversation
…d activity XML processes
avdata99
reviewed
Feb 17, 2026
| required = h.required_organisation_csv_files() | ||
| pre = h.validate_required_csv_folder(Path(tmp_dir), required) | ||
| if pre: | ||
| h.notify_slack_iati_error("organisation", dataset, pre) |
Member
There was a problem hiding this comment.
The iati-generator extension should not depend on the push-errors extension; therefore, we cannot use its helpers (or anything else) from it. The way to send Slack messages is by using log.critical. This way, when the iati generator is used with push-error, it will send a message, and when it's not, nothing happens except for an error log.
avdata99
reviewed
Feb 17, 2026
| @@ -0,0 +1,151 @@ | |||
| import urllib.request | |||
Member
There was a problem hiding this comment.
We don't need to test anything with this PR.
We can remove this file.
avdata99
reviewed
Feb 17, 2026
ckanext/iati_generator/helpers.py
Outdated
Comment on lines
+837
to
+917
|
|
||
| def notify_slack_iati_error(kind: str, dataset: dict, error_dict: Any) -> bool: | ||
| """ | ||
| Send a Slack notification when an IATI compilation error occurs. | ||
| """ | ||
| if not _is_slack_enabled(): | ||
| return False | ||
|
|
||
| webhook_url = toolkit.config.get("ckanext.iati_generator.slack_webhook_url") | ||
| if not webhook_url: | ||
| return False | ||
|
|
||
| try: | ||
| normalized = normalize_iati_errors(error_dict) if error_dict is not None else { | ||
| "summary": None, | ||
| "items": [], | ||
| "raw": [], | ||
| } | ||
|
|
||
| payload = _create_slack_payload(kind, dataset, normalized) | ||
| req = urllib.request.Request( | ||
| webhook_url, | ||
| data=json.dumps(payload).encode("utf-8"), | ||
| headers={"Content-Type": "application/json"}, | ||
| method="POST", | ||
| ) | ||
| with urllib.request.urlopen(req, timeout=5): | ||
| pass | ||
|
|
||
| return True | ||
|
|
||
| except Exception: | ||
| log.error("Slack notification failed for IATI error.", exc_info=True) | ||
| return False | ||
|
|
||
|
|
||
| def _is_slack_enabled() -> bool: | ||
| raw_enabled = toolkit.config.get("ckanext.iati_generator.slack_enabled") | ||
| return toolkit.asbool(raw_enabled) if raw_enabled not in (None, "", " ") else False | ||
|
|
||
|
|
||
| def _create_slack_payload(kind: str, dataset: dict, normalized: dict) -> dict: | ||
| summary = normalized.get("summary") or "IATI compilation error" | ||
| items = normalized.get("items") or [] | ||
| pkg_name = (dataset or {}).get("name") or "" | ||
| pkg_title = (dataset or {}).get("title") or pkg_name | ||
| pkg_id = (dataset or {}).get("id") or "" | ||
| ns = normalize_namespace((dataset or {}).get("iati_namespace", DEFAULT_NAMESPACE)) | ||
| dataset_url = _get_dataset_url(pkg_name) | ||
|
|
||
| details_block = _format_error_details(items) | ||
|
|
||
| header = f":warning: IATI {kind} compilation error" | ||
| body = ( | ||
| f"*Dataset:* {pkg_title} ({pkg_name})\n" | ||
| f"*Package ID:* {pkg_id}\n" | ||
| f"*Namespace:* {ns}\n" | ||
| f"*Summary:* {summary}\n" | ||
| f"*Details:*\n{details_block}" | ||
| ) | ||
|
|
||
| if dataset_url: | ||
| body += f"\n*Link:* {dataset_url}" | ||
|
|
||
| return {"text": f"{header}\n{body}"} | ||
|
|
||
|
|
||
| def _get_dataset_url(pkg_name: str) -> str: | ||
| site_url = toolkit.config.get("ckan.site_url") or "" | ||
| return f"{site_url}/dataset/{pkg_name}" if site_url and pkg_name else None | ||
|
|
||
|
|
||
| def _format_error_details(items: list) -> str: | ||
| lines = [] | ||
| for item in items[:5]: | ||
| title = item.get("title") or "Error" | ||
| csv_file = item.get("csv_file") or "unknown file" | ||
| details = item.get("details") or "" | ||
| line = f"- {title} · {csv_file}: {details[:350]}..." | ||
| lines.append(line) | ||
| return "\n".join(lines) if lines else "- (no details)" |
Member
There was a problem hiding this comment.
Suggested change
| def notify_slack_iati_error(kind: str, dataset: dict, error_dict: Any) -> bool: | |
| """ | |
| Send a Slack notification when an IATI compilation error occurs. | |
| """ | |
| if not _is_slack_enabled(): | |
| return False | |
| webhook_url = toolkit.config.get("ckanext.iati_generator.slack_webhook_url") | |
| if not webhook_url: | |
| return False | |
| try: | |
| normalized = normalize_iati_errors(error_dict) if error_dict is not None else { | |
| "summary": None, | |
| "items": [], | |
| "raw": [], | |
| } | |
| payload = _create_slack_payload(kind, dataset, normalized) | |
| req = urllib.request.Request( | |
| webhook_url, | |
| data=json.dumps(payload).encode("utf-8"), | |
| headers={"Content-Type": "application/json"}, | |
| method="POST", | |
| ) | |
| with urllib.request.urlopen(req, timeout=5): | |
| pass | |
| return True | |
| except Exception: | |
| log.error("Slack notification failed for IATI error.", exc_info=True) | |
| return False | |
| def _is_slack_enabled() -> bool: | |
| raw_enabled = toolkit.config.get("ckanext.iati_generator.slack_enabled") | |
| return toolkit.asbool(raw_enabled) if raw_enabled not in (None, "", " ") else False | |
| def _create_slack_payload(kind: str, dataset: dict, normalized: dict) -> dict: | |
| summary = normalized.get("summary") or "IATI compilation error" | |
| items = normalized.get("items") or [] | |
| pkg_name = (dataset or {}).get("name") or "" | |
| pkg_title = (dataset or {}).get("title") or pkg_name | |
| pkg_id = (dataset or {}).get("id") or "" | |
| ns = normalize_namespace((dataset or {}).get("iati_namespace", DEFAULT_NAMESPACE)) | |
| dataset_url = _get_dataset_url(pkg_name) | |
| details_block = _format_error_details(items) | |
| header = f":warning: IATI {kind} compilation error" | |
| body = ( | |
| f"*Dataset:* {pkg_title} ({pkg_name})\n" | |
| f"*Package ID:* {pkg_id}\n" | |
| f"*Namespace:* {ns}\n" | |
| f"*Summary:* {summary}\n" | |
| f"*Details:*\n{details_block}" | |
| ) | |
| if dataset_url: | |
| body += f"\n*Link:* {dataset_url}" | |
| return {"text": f"{header}\n{body}"} | |
| def _get_dataset_url(pkg_name: str) -> str: | |
| site_url = toolkit.config.get("ckan.site_url") or "" | |
| return f"{site_url}/dataset/{pkg_name}" if site_url and pkg_name else None | |
| def _format_error_details(items: list) -> str: | |
| lines = [] | |
| for item in items[:5]: | |
| title = item.get("title") or "Error" | |
| csv_file = item.get("csv_file") or "unknown file" | |
| details = item.get("details") or "" | |
| line = f"- {title} · {csv_file}: {details[:350]}..." | |
| lines.append(line) | |
| return "\n".join(lines) if lines else "- (no details)" |
None of this is required, the push-error extension knows how to send messages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fixes https://github.com/okfn/ckan-bcie/issues/475