From ceae5b26fb30861a83831a7b4d3dc0b74b509f00 Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 25 Aug 2024 10:36:33 +0300 Subject: [PATCH] feat: quickchart (#1709) --- docs/images/chart_example_1.webp | Bin 0 -> 13894 bytes docs/images/chart_example_2.webp | Bin 0 -> 16418 bytes docs/mint.json | 1 + .../documentation/quickchart-provider.mdx | 71 ++++++ keep-ui/public/icons/quickchart-icon.png | Bin 0 -> 23002 bytes keep/api/routes/alerts.py | 23 -- .../providers/quickchart_provider/__init__.py | 0 .../quickchart_provider.py | 210 ++++++++++++++++++ poetry.lock | 15 +- pyproject.toml | 1 + 10 files changed, 297 insertions(+), 24 deletions(-) create mode 100644 docs/images/chart_example_1.webp create mode 100644 docs/images/chart_example_2.webp create mode 100644 docs/providers/documentation/quickchart-provider.mdx create mode 100644 keep-ui/public/icons/quickchart-icon.png create mode 100644 keep/providers/quickchart_provider/__init__.py create mode 100644 keep/providers/quickchart_provider/quickchart_provider.py diff --git a/docs/images/chart_example_1.webp b/docs/images/chart_example_1.webp new file mode 100644 index 0000000000000000000000000000000000000000..48692f0ac26510c28743da14916806aa7054725b GIT binary patch literal 13894 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 literal 0 HcmV?d00001 diff --git a/docs/images/chart_example_2.webp b/docs/images/chart_example_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..83d0105e67365abb3d67489365dd6dcba44baaa2 GIT binary patch literal 16418 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 VcR^i-RHzx&7i+NQkCFg@{|6a?PALEY literal 0 HcmV?d00001 diff --git a/docs/mint.json b/docs/mint.json index 193e77242..e8316d702 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -101,6 +101,7 @@ "providers/documentation/grafana-provider", "providers/documentation/http-provider", "providers/documentation/ilert-provider", + "providers/documentation/quickchart-provider", "providers/documentation/incidentio-provider", "providers/documentation/jira-provider", "providers/documentation/kibana-provider", diff --git a/docs/providers/documentation/quickchart-provider.mdx b/docs/providers/documentation/quickchart-provider.mdx new file mode 100644 index 000000000..d20a2360f --- /dev/null +++ b/docs/providers/documentation/quickchart-provider.mdx @@ -0,0 +1,71 @@ +--- +title: "QuickChart Provider" +sidebarTitle: "QuickChart Provider" +description: "The QuickChart provider enables the generation of chart images through a simple and open API, allowing visualization of alert trends and counts. It supports both anonymous usage and authenticated access with an API key for enhanced functionality." +--- + +# QuickChart Provider + +## Overview + +The QuickChart provider allows for the generation of two types of charts based on alert data within Keep's platform: + +1. A line chart that shows the trend of a specific fingerprint alert over time. +2. A radial gauge chart displaying the total number of alerts Keep received for this fingerprint. + +These charts can be used in various reports, dashboards, or alert summaries to provide visual insights into alert activity and trends. + +## Inputs + +- `fingerprint`: The unique identifier of the alert whose trend you want to visualize. This is required. +- `status`: (Optional) The status of alerts to filter by (e.g., firing, resolved). Defaults to all statuses. +- `chartConfig`: (Optional) Custom chart configuration settings in JSON format. Default settings will be used if not provided. + +## Outputs + +The output is a JSON object that includes URLs to the generated chart images: + +- `chart_url`: URL of the trend chart image. + + + + + +- `counter_url`: URL of the total alerts gauge chart image. + + + + + +## Authentication Parameters + +- `api_key`: (Optional) QuickChart API Key. The provider can be used without an API key, but for more advanced usage, such as generating more complex charts or handling higher request volumes, an API key is recommended. + +## Connecting with the Provider + +### Using QuickChart without an API Key + +The QuickChart provider can generate charts without the need for an API key. However, this usage is limited to basic functionality and lower request limits. + +### Using QuickChart with an API Key + +To unlock more advanced features and higher usage limits, you can use a QuickChart API key. Here's how to obtain one: + +1. Visit [QuickChart](https://quickchart.io/). +2. Sign up for a free account to get started. +3. Navigate to your account settings to find your API key. + +Once you have your API key, add it to the provider configuration in Keep. + +## Notes + +This provider is designed to offer flexible chart generation capabilities within Keep, enhancing how you visualize alert data and trends. It is ideal for users who want to quickly integrate visual representations of alert activity into their workflows. + +## Useful Links + +- [QuickChart API Documentation](https://quickchart.io/documentation/) +- [QuickChart Website](https://quickchart.io/) diff --git a/keep-ui/public/icons/quickchart-icon.png b/keep-ui/public/icons/quickchart-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fd2ad5c8db6d498f0df64a5ad7a5959d7179e654 GIT binary patch literal 23002 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 literal 0 HcmV?d00001 diff --git a/keep/api/routes/alerts.py b/keep/api/routes/alerts.py index 112d1ddc0..7b0938815 100644 --- a/keep/api/routes/alerts.py +++ b/keep/api/routes/alerts.py @@ -81,8 +81,6 @@ def get_all_alerts( @router.get("/{fingerprint}/history", description="Get alert history") def get_alert_history( fingerprint: str, - provider_id: str | None = None, - provider_type: str | None = None, authenticated_entity: AuthenticatedEntity = Depends(AuthVerifier(["read:alert"])), ) -> list[AlertDto]: logger.info( @@ -97,27 +95,6 @@ def get_alert_history( ) enriched_alerts_dto = convert_db_alerts_to_dto_alerts(db_alerts) - if provider_id is not None and provider_type is not None: - try: - installed_provider = ProvidersFactory.get_installed_provider( - tenant_id=authenticated_entity.tenant_id, - provider_id=provider_id, - provider_type=provider_type, - ) - pulled_alerts_history = installed_provider.get_alerts_by_fingerprint( - tenant_id=authenticated_entity.tenant_id - ).get(fingerprint, []) - enriched_alerts_dto.extend(pulled_alerts_history) - except Exception: - logger.warning( - "Failed to pull alerts history from installed provider", - extra={ - "provider_id": provider_id, - "provider_type": provider_type, - "tenant_id": authenticated_entity.tenant_id, - }, - ) - logger.info( "Fetched alert history", extra={ diff --git a/keep/providers/quickchart_provider/__init__.py b/keep/providers/quickchart_provider/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/keep/providers/quickchart_provider/quickchart_provider.py b/keep/providers/quickchart_provider/quickchart_provider.py new file mode 100644 index 000000000..de93d1202 --- /dev/null +++ b/keep/providers/quickchart_provider/quickchart_provider.py @@ -0,0 +1,210 @@ +# builtins +import dataclasses +import datetime +from collections import defaultdict + +import pydantic + +# third-parties +from quickchart import QuickChart + +# internals +from keep.api.core.db import get_alerts_by_fingerprint +from keep.api.utils.enrichment_helpers import convert_db_alerts_to_dto_alerts +from keep.contextmanager.contextmanager import ContextManager +from keep.providers.base.base_provider import BaseProvider +from keep.providers.models.provider_config import ProviderConfig +from keep.providers.providers_factory import ProvidersFactory + + +def get_date_key(date: datetime.datetime, time_unit: str) -> str: + if isinstance(date, str): + date = datetime.datetime.fromisoformat(date) + if time_unit == "Minutes": + return f"{date.hour}:{date.minute}:{date.second}" + elif time_unit == "Hours": + return f"{date.hour}:{date.minute}" + else: + return f"{date.day}/{date.month}/{date.year}" + + +@pydantic.dataclasses.dataclass +class QuickchartProviderAuthConfig: + api_key: str = dataclasses.field( + metadata={ + "required": False, + "description": "Quickchart API Key", + "sensitive": True, + }, + default=None, + ) + + +class QuickchartProvider(BaseProvider): + def __init__( + self, context_manager: ContextManager, provider_id: str, config: ProviderConfig + ): + super().__init__(context_manager, provider_id, config) + + def validate_config(self): + self.authentication_config = QuickchartProviderAuthConfig( + **self.config.authentication + ) + + def dispose(self): + pass + + def _notify( + self, + fingerprint: str, + status: str | None = None, + chartConfig: dict | None = None, + ) -> dict: + db_alerts = get_alerts_by_fingerprint( + tenant_id=self.context_manager.tenant_id, + fingerprint=fingerprint, + limit=False, + status=status, + ) + alerts = convert_db_alerts_to_dto_alerts(db_alerts) + + if not alerts: + self.logger.warning( + "No alerts found for this fingerprint", + extra={ + "tenant_id": self.context_manager.tenant_id, + "fingerprint": fingerprint, + }, + ) + return {"chart_url": ""} + + min_last_received = min( + datetime.datetime.fromisoformat(alert.lastReceived) for alert in alerts + ) + max_last_received = max( + datetime.datetime.fromisoformat(alert.lastReceived) for alert in alerts + ) + + title = f"First: {str(min_last_received)} | Last: {str(max_last_received)} | Total: {len(alerts)}" + + time_difference = ( + max_last_received - min_last_received + ).total_seconds() * 1000 # Convert to milliseconds + time_unit = "Days" + if time_difference < 3600000: + time_unit = "Minutes" + elif time_difference < 86400000: + time_unit = "Hours" + + categories_by_status = [] + raw_chart_data = defaultdict(dict) + + for alert in reversed(alerts): + date_key = get_date_key(alert.lastReceived, time_unit) + status = alert.status + if date_key not in raw_chart_data: + raw_chart_data[date_key][status] = 1 + else: + raw_chart_data[date_key][status] = ( + raw_chart_data[date_key].get(status, 0) + 1 + ) + + if status not in categories_by_status: + categories_by_status.append(status) + + chart_data = [{"date": key, **value} for key, value in raw_chart_data.items()] + + # Generate chart using QuickChart + return self.generate_chart_image( + chart_data, categories_by_status, len(alerts), title, chartConfig + ) + + def __get_total_alerts_gaugae(self, counter: int): + qc = QuickChart() + if self.authentication_config.api_key: + qc.key = self.authentication_config.api_key + qc.width = 500 + qc.height = 300 + qc.config = { + "type": "radialGauge", + "data": {"datasets": [{"data": [counter]}]}, + "options": { + "centerArea": {"fontSize": 25, "fontWeight": "bold"}, + }, + } + chart_url = qc.get_short_url() + return chart_url + + def generate_chart_image( + self, + chart_data, + categories_by_status, + total_alerts: int, + title: str, + config: dict | None = None, + ) -> dict: + qc = QuickChart() + + if self.authentication_config.api_key: + qc.key = self.authentication_config.api_key + + qc.width = 800 + qc.height = 400 + qc.config = config or { + "type": "line", + "data": { + "labels": [data["date"] for data in chart_data], + "datasets": [ + { + "fill": False, + "label": category, + "lineTension": 0.4, + "borderWidth": 3, + "data": [data.get(category, 0) for data in chart_data], + } + for category in categories_by_status + ], + }, + "options": { + "title": { + "display": True, + "position": "top", + "fontSize": 14, + "padding": 10, + "text": title, + }, + "scales": { + "xAxes": [{"type": "category"}], + "yAxes": [{"ticks": {"beginAtZero": True}}], + }, + }, + } + chart_url = qc.get_short_url() + + counter_url = self.__get_total_alerts_gaugae(total_alerts) + + return {"chart_url": chart_url, "counter_url": counter_url} + + +if __name__ == "__main__": + import logging + + logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()]) + context_manager = ContextManager( + tenant_id="keep", + workflow_id="test", + ) + config = { + "description": "", + "authentication": {}, + } + provider = ProvidersFactory.get_provider( + context_manager, + provider_id="quickchart", + provider_type="quickchart", + provider_config=config, + ) + result = provider.notify( + fingerprint="5bcafb4ea94749f36871a2e1169d5252ecfb1c589d7464bd8bf863cdeb76b864" + ) + print(result) diff --git a/poetry.lock b/poetry.lock index 57b5695ae..262c81c3e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3981,6 +3981,19 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "quickchart-io" +version = "2.0.0" +description = "A client for quickchart.io, a service that generates static chart images" +optional = false +python-versions = ">=3.7,<4" +files = [ + {file = "quickchart_io-2.0.0-py3-none-any.whl", hash = "sha256:c44b5fb4d6e957fb85db0926e691684795e9fe5d6819d33f2daea795a0f6a36b"}, +] + +[package.dependencies] +requests = ">=2.28.1,<3.0.0" + [[package]] name = "redis" version = "4.6.0" @@ -5069,4 +5082,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "fee3645d18637ff6a6c0f4d08e20abfb556bf3d5c301111168c678ed2dbc3ad2" +content-hash = "a5815b02a068725ecb0b29ebb62ed09ed54c595f8fca3c98183b9ebbfd169da8" diff --git a/pyproject.toml b/pyproject.toml index 2a60fbf23..9a999b3b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,6 +89,7 @@ alembic = "^1.13.2" numpy = "^2.0.0" pandas = "^2.2.2" networkx = "^3.3" +quickchart-io = "^2.0.0" [tool.poetry.group.dev.dependencies] pre-commit = "^3.0.4" pre-commit-hooks = "^4.4.0"