From 960dcae608f543744e18ad45a0dbfc4d878766c3 Mon Sep 17 00:00:00 2001 From: Dominik Broj Date: Fri, 5 Jan 2024 08:05:31 +0100 Subject: [PATCH] Brojd/implement insights (#3583) # What this PR does - Use Grafana Scenes to add Insights as a separate page in OnCall - Add an option to run Prometheus instance via helm so that Prometheus Exporter feature can be used easily without the need of setting up Prometheus separately ## Which issue(s) this PR fixes https://github.com/grafana/oncall-private/issues/2382 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --- .github/helm-values.yml | 10 ++ CHANGELOG.md | 4 + dev/helm-local.yml | 8 + .../e2e-tests/insights/insights.test.ts | 65 +++++++ grafana-plugin/e2e-tests/utils/alertGroup.ts | 6 + grafana-plugin/e2e-tests/utils/navigation.ts | 5 +- grafana-plugin/package.json | 2 + .../src/containers/Alerts/Alerts.tsx | 2 +- .../MobileAppConnection.test.tsx | 2 +- .../MobileAppConnection.tsx | 2 +- grafana-plugin/src/navbar/Header/Header.tsx | 2 +- grafana-plugin/src/pages/index.tsx | 7 + .../src/pages/insights/Insights.helpers.ts | 4 + .../src/pages/insights/Insights.tsx | 170 ++++++++++++++++++ .../src/pages/insights/Insights.types.ts | 6 + .../scenes/AlertGroupsByIntegration.tsx | 111 ++++++++++++ .../insights/scenes/AlertGroupsByTeam.tsx | 111 ++++++++++++ .../src/pages/insights/scenes/MTTR.tsx | 74 ++++++++ .../insights/scenes/MTTRByIntegration.tsx | 126 +++++++++++++ .../src/pages/insights/scenes/MTTRByTeam.tsx | 116 ++++++++++++ .../scenes/MTTRChangedForPeriodStat.tsx | 68 +++++++ .../scenes/MTTRChangedForPeriodTimeseries.tsx | 107 +++++++++++ .../scenes/NewAlertGroupsDuringTimePeriod.tsx | 118 ++++++++++++ .../NewAlertGroupsForSelectedPeriod.tsx | 80 +++++++++ ...ertGroupsNotificationsDuringTimePeriod.tsx | 118 ++++++++++++ ...AlertGroupsNotificationsForPeriodTable.tsx | 113 ++++++++++++ .../NewAlertGroupsNotificationsInTotal.tsx | 113 ++++++++++++ .../insights/scenes/TotalAlertGroups.tsx | 79 ++++++++ .../scenes/TotalAlertGroupsByState.tsx | 133 ++++++++++++++ .../src/pages/insights/variables.ts | 138 ++++++++++++++ grafana-plugin/src/pages/routes.tsx | 5 + .../pages/settings/tabs/ChatOps/ChatOps.tsx | 2 +- grafana-plugin/src/plugin.json | 8 + .../src/plugin/GrafanaPluginRootPage.tsx | 4 + .../src/state/rootBaseStore/index.ts | 7 +- grafana-plugin/yarn.lock | 66 ++++++- helm/oncall/Chart.yaml | 4 + helm/oncall/charts/prometheus-25.8.2.tgz | Bin 0 -> 73490 bytes helm/oncall/values.yaml | 9 + 39 files changed, 1993 insertions(+), 12 deletions(-) create mode 100644 grafana-plugin/e2e-tests/insights/insights.test.ts create mode 100644 grafana-plugin/src/pages/insights/Insights.helpers.ts create mode 100644 grafana-plugin/src/pages/insights/Insights.tsx create mode 100644 grafana-plugin/src/pages/insights/Insights.types.ts create mode 100644 grafana-plugin/src/pages/insights/scenes/AlertGroupsByIntegration.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/MTTR.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx create mode 100644 grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx create mode 100644 grafana-plugin/src/pages/insights/variables.ts create mode 100644 helm/oncall/charts/prometheus-25.8.2.tgz diff --git a/.github/helm-values.yml b/.github/helm-values.yml index c6619a5f..9d50177d 100644 --- a/.github/helm-values.yml +++ b/.github/helm-values.yml @@ -4,6 +4,8 @@ base_url_protocol: http env: - name: GRAFANA_CLOUD_NOTIFICATIONS_ENABLED value: "False" + - name: FEATURE_PROMETHEUS_EXPORTER_ENABLED + value: "True" image: tag: latest pullPolicy: Always @@ -104,3 +106,11 @@ service: type: NodePort port: 8080 nodePort: 30001 +prometheus: + enabled: true + extraScrapeConfigs: | + - job_name: 'oncall-exporter' + metrics_path: /metrics/ + static_configs: + - targets: + - oncall-dev-engine.default.svc.cluster.local:8080 diff --git a/CHANGELOG.md b/CHANGELOG.md index cecdeb86..8cbca088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Move Insights to OnCall as a separate page ([#2382](https://github.com/grafana/oncall-private/issues/2382)) + ## v1.3.82 (2024-01-04) ### Added diff --git a/dev/helm-local.yml b/dev/helm-local.yml index 68674c37..7d6cf5b8 100644 --- a/dev/helm-local.yml +++ b/dev/helm-local.yml @@ -133,3 +133,11 @@ service: type: NodePort port: 8080 nodePort: 30001 +prometheus: + enabled: false + extraScrapeConfigs: | + - job_name: 'oncall-exporter' + metrics_path: /metrics/ + static_configs: + - targets: + - oncall-dev-engine.default.svc.cluster.local:8080 diff --git a/grafana-plugin/e2e-tests/insights/insights.test.ts b/grafana-plugin/e2e-tests/insights/insights.test.ts new file mode 100644 index 00000000..d83b9b59 --- /dev/null +++ b/grafana-plugin/e2e-tests/insights/insights.test.ts @@ -0,0 +1,65 @@ +import { test, expect } from '../fixtures'; +import { resolveFiringAlert } from '../utils/alertGroup'; +import { createEscalationChain, EscalationStep } from '../utils/escalationChain'; +import { clickButton, generateRandomValue } from '../utils/forms'; +import { createIntegrationAndSendDemoAlert } from '../utils/integrations'; +import { goToGrafanaPage, goToOnCallPage } from '../utils/navigation'; +import { createOnCallSchedule } from '../utils/schedule'; + +test.describe('Insights', () => { + test.beforeAll(async ({ adminRolePage: { page, userName } }) => { + const DATASOURCE_NAME = 'OnCall Prometheus'; + const DATASOURCE_URL = 'http://oncall-dev-prometheus-server.default.svc.cluster.local'; + + await goToGrafanaPage(page, '/connections/datasources'); + await page.waitForLoadState('networkidle'); + + // setup data source if it's not already connected + const isDataSourceAlreadyConnected = await page.getByText(DATASOURCE_NAME).isVisible(); + if (!isDataSourceAlreadyConnected) { + await page.getByRole('link', { name: 'Add data source' }).click(); + await clickButton({ page, buttonText: 'Prometheus' }); + await page.getByRole('textbox', { name: 'Data source settings page name input field' }).fill(DATASOURCE_NAME); + await page.getByPlaceholder('http://localhost:9090').fill(DATASOURCE_URL); + await clickButton({ page, buttonText: 'Save & test' }); + } + + // send alert and resolve to get some values in insights + const escalationChainName = generateRandomValue(); + const integrationName = generateRandomValue(); + const onCallScheduleName = generateRandomValue(); + await createOnCallSchedule(page, onCallScheduleName, userName); + await createEscalationChain( + page, + escalationChainName, + EscalationStep.NotifyUsersFromOnCallSchedule, + onCallScheduleName + ); + await createIntegrationAndSendDemoAlert(page, integrationName, escalationChainName); + await resolveFiringAlert(page); + }); + + test('Viewer can see all the panels in OnCall insights', async ({ viewerRolePage: { page } }) => { + await goToOnCallPage(page, 'insights'); + [ + 'Total alert groups', + 'Total alert groups by state', + 'New alert groups for selected period', + 'Mean time to respond \\(MTTR\\)', + 'MTTR changed for period', + 'New alert groups during time period', + 'Alert groups by Integration', + 'Mean time to respond \\(MTTR\\) by Integration', + ].forEach(async (panelTitle) => { + await expect(page.getByRole('heading', { name: new RegExp(`^${panelTitle}$`) }).first()).toBeVisible(); + }); + }); + + test('There is no panel that misses data', async ({ adminRolePage: { page } }) => { + await goToOnCallPage(page, 'insights'); + await page.getByText('Last 7 days').click(); + await page.getByText('Last 1 hour').click(); + await page.waitForTimeout(2000); + await expect(page.getByText('No data')).toBeHidden(); + }); +}); diff --git a/grafana-plugin/e2e-tests/utils/alertGroup.ts b/grafana-plugin/e2e-tests/utils/alertGroup.ts index 33ef2462..2711c896 100644 --- a/grafana-plugin/e2e-tests/utils/alertGroup.ts +++ b/grafana-plugin/e2e-tests/utils/alertGroup.ts @@ -98,3 +98,9 @@ export const verifyThatAlertGroupIsTriggered = async ( expect(await incidentTimelineContainsStep(page, triggeredStepText)).toBe(true); }; + +export const resolveFiringAlert = async (page: Page) => { + await goToOnCallPage(page, 'alert-groups'); + await page.getByText('Firing').nth(1).click(); + await page.getByLabel('Context menu').getByText('Resolve').click(); +}; diff --git a/grafana-plugin/e2e-tests/utils/navigation.ts b/grafana-plugin/e2e-tests/utils/navigation.ts index d7ac6fa8..2404eb65 100644 --- a/grafana-plugin/e2e-tests/utils/navigation.ts +++ b/grafana-plugin/e2e-tests/utils/navigation.ts @@ -2,12 +2,11 @@ import type { Page } from '@playwright/test'; import { BASE_URL } from './constants'; -type GrafanaPage = '/plugins/grafana-oncall-app'; -type OnCallPage = 'alert-groups' | 'integrations' | 'escalations' | 'schedules' | 'users'; +type OnCallPage = 'alert-groups' | 'integrations' | 'escalations' | 'schedules' | 'users' | 'insights'; const _goToPage = async (page: Page, url = '') => page.goto(`${BASE_URL}${url}`); -export const goToGrafanaPage = async (page: Page, url: GrafanaPage) => _goToPage(page, url); +export const goToGrafanaPage = async (page: Page, url = '') => _goToPage(page, url); export const goToOnCallPage = async (page: Page, onCallPage: OnCallPage) => { await _goToPage(page, `/a/grafana-oncall-app/${onCallPage}`); diff --git a/grafana-plugin/package.json b/grafana-plugin/package.json index 5aed5207..24b7386a 100644 --- a/grafana-plugin/package.json +++ b/grafana-plugin/package.json @@ -124,6 +124,8 @@ "@grafana/faro-web-tracing": "^1.0.0-beta4", "@grafana/labels": "~1.4.4", "@grafana/runtime": "9.3.0-beta1", + "@grafana/scenes": "^1.28.0", + "@grafana/schema": "^10.2.2", "@grafana/ui": "^10.2.0", "@lifeomic/attempt": "^3.0.3", "@opentelemetry/api": "^1.3.0", diff --git a/grafana-plugin/src/containers/Alerts/Alerts.tsx b/grafana-plugin/src/containers/Alerts/Alerts.tsx index f14bdecc..2a343933 100644 --- a/grafana-plugin/src/containers/Alerts/Alerts.tsx +++ b/grafana-plugin/src/containers/Alerts/Alerts.tsx @@ -147,7 +147,7 @@ export default function Alerts() { function showMismatchWarning(): boolean { return ( - store.isOpenSource() && + store.isOpenSource && store.backendVersion && plugin?.version && store.backendVersion !== plugin?.version && diff --git a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx index 321358e6..bd5c5461 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx @@ -54,7 +54,7 @@ const mockUseStore = (rest?: any, connected = false, cloud_connected = true) => cloudConnectionStatus: { cloud_connection_status: cloud_connected }, } as unknown as CloudStore, hasFeature: jest.fn().mockReturnValue(true), - isOpenSource: jest.fn().mockReturnValue(true), + isOpenSource: true, } as unknown as RootStore; useStore.mockReturnValue(store); diff --git a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx index 9518a2b8..82ec2674 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx @@ -176,7 +176,7 @@ const MobileAppConnection = observer(({ userPk }: Props) => { {isQRBlurry && } - {store.isOpenSource() && QRCodeDataParsed && ( + {store.isOpenSource && QRCodeDataParsed && ( Server URL embedded in this QR:
diff --git a/grafana-plugin/src/navbar/Header/Header.tsx b/grafana-plugin/src/navbar/Header/Header.tsx index c7878ae5..8b6d6764 100644 --- a/grafana-plugin/src/navbar/Header/Header.tsx +++ b/grafana-plugin/src/navbar/Header/Header.tsx @@ -35,7 +35,7 @@ const Header = observer(() => { ); function renderHeading() { - if (store.isOpenSource()) { + if (store.isOpenSource) { return (

Grafana OnCall

diff --git a/grafana-plugin/src/pages/index.tsx b/grafana-plugin/src/pages/index.tsx index 280c80e7..99278aa0 100644 --- a/grafana-plugin/src/pages/index.tsx +++ b/grafana-plugin/src/pages/index.tsx @@ -142,6 +142,12 @@ export const pages: { [id: string]: PageDefinition } = [ path: getPath('cloud'), action: UserActions.OtherSettingsWrite, }, + { + icon: 'cloud', + id: 'insights', + text: 'Insights', + path: getPath('insights'), + }, { icon: 'cog', id: 'test', @@ -180,6 +186,7 @@ export const ROUTES = { 'chat-ops': ['chat-ops'], 'live-settings': ['live-settings'], cloud: ['cloud'], + insights: ['insights'], test: ['test'], // backwards compatible to redirect to new alert-groups diff --git a/grafana-plugin/src/pages/insights/Insights.helpers.ts b/grafana-plugin/src/pages/insights/Insights.helpers.ts new file mode 100644 index 00000000..eb09252e --- /dev/null +++ b/grafana-plugin/src/pages/insights/Insights.helpers.ts @@ -0,0 +1,4 @@ +import { DataSourceRef } from '@grafana/schema'; + +export const getDataSource = (isOpenSource: boolean): DataSourceRef => + isOpenSource ? { uid: '$datasource' } : { uid: 'grafanacloud-usage' }; diff --git a/grafana-plugin/src/pages/insights/Insights.tsx b/grafana-plugin/src/pages/insights/Insights.tsx new file mode 100644 index 00000000..5441a5fb --- /dev/null +++ b/grafana-plugin/src/pages/insights/Insights.tsx @@ -0,0 +1,170 @@ +import React, { useMemo, useState } from 'react'; + +import { + EmbeddedScene, + SceneTimeRange, + SceneFlexLayout, + SceneControlsSpacer, + SceneRefreshPicker, + SceneTimePicker, + SceneVariableSet, + VariableValueSelectors, + NestedScene, +} from '@grafana/scenes'; +import { Alert } from '@grafana/ui'; +import { observer } from 'mobx-react'; + +import Text from 'components/Text/Text'; +import { useStore } from 'state/useStore'; +import { DOCS_ROOT } from 'utils/consts'; + +import { getDataSource } from './Insights.helpers'; +import { InsightsConfig } from './Insights.types'; +import getAlertGroupsByIntegrationScene from './scenes/AlertGroupsByIntegration'; +import getAlertGroupsByTeamScene from './scenes/AlertGroupsByTeam'; +import getMTTRScene from './scenes/MTTR'; +import getMTTRByIntegrationScene from './scenes/MTTRByIntegration'; +import getMTTRByTeamScene from './scenes/MTTRByTeam'; +import getMTTRChangedForPeriodStatScene from './scenes/MTTRChangedForPeriodStat'; +import getMTTRChangedForPeriodTimeseriesScene from './scenes/MTTRChangedForPeriodTimeseries'; +import getNewAlertGroupsDuringTimePeriodScene from './scenes/NewAlertGroupsDuringTimePeriod'; +import getNewAlertGroupsForSelectedPeriodScene from './scenes/NewAlertGroupsForSelectedPeriod'; +import getNewAlertGroupsNotificationsDuringTimePeriodScene from './scenes/NewAlertGroupsNotificationsDuringTimePeriod'; +import getNewAlertGroupsNotificationsForPeriodTableScene from './scenes/NewAlertGroupsNotificationsForPeriodTable'; +import getNewAlertGroupsNotificationsInTotalScene from './scenes/NewAlertGroupsNotificationsInTotal'; +import getTotalAlertGroupsScene from './scenes/TotalAlertGroups'; +import getTotalAlertGroupsByStateScene from './scenes/TotalAlertGroupsByState'; +import getVariables from './variables'; + +const Insights = observer(() => { + const { isOpenSource } = useStore(); + const [alertVisible, setAlertVisible] = useState(true); + + const rootScene = useMemo( + () => getRootScene({ isOpenSource, datasource: getDataSource(isOpenSource) }), + [isOpenSource] + ); + + return ( + <> + {isOpenSource && alertVisible && ( + setAlertVisible(false)} severity="info" title=""> + { + <> + In order to see insights you need to set up Prometheus, add it to your Grafana instance as a data source, + set FEATURE_PROMETHEUS_EXPORTER_ENABLED environment variable to true and then select your Data source in + the dropdown below. +
+
+ <> + You can find out more in + + documentation + + . + + + } +
+ )} + + + ); +}); + +const getRootScene = (config: InsightsConfig) => + new EmbeddedScene({ + $timeRange: new SceneTimeRange({ from: 'now-7d', to: 'now' }), + $variables: new SceneVariableSet({ + variables: getVariables(config), + }), + controls: [ + new VariableValueSelectors({}), + new SceneControlsSpacer(), + new SceneTimePicker({}), + new SceneRefreshPicker({}), + ], + body: new SceneFlexLayout({ + direction: 'column', + children: [ + new NestedScene({ + title: 'Overview', + canCollapse: true, + isCollapsed: false, + body: new SceneFlexLayout({ + direction: 'column', + children: [ + new SceneFlexLayout({ + height: 200, + children: [ + getTotalAlertGroupsScene(config), + getTotalAlertGroupsByStateScene(config), + getNewAlertGroupsForSelectedPeriodScene(config), + getMTTRScene(config), + getMTTRChangedForPeriodStatScene(config), + ], + }), + new SceneFlexLayout({ + height: 400, + children: [getNewAlertGroupsDuringTimePeriodScene(config)], + }), + new SceneFlexLayout({ + height: 400, + children: [getMTTRChangedForPeriodTimeseriesScene(config)], + }), + ], + }), + }), + new NestedScene({ + title: 'Integrations data', + canCollapse: true, + isCollapsed: false, + body: new SceneFlexLayout({ + height: 400, + children: [getAlertGroupsByIntegrationScene(config), getMTTRByIntegrationScene(config)], + }), + }), + new NestedScene({ + title: 'Notified alert groups by Users (based on all Integrations)', + canCollapse: true, + isCollapsed: false, + body: new SceneFlexLayout({ + direction: 'column', + children: [ + new SceneFlexLayout({ + height: 400, + children: [getNewAlertGroupsNotificationsDuringTimePeriodScene(config)], + }), + new SceneFlexLayout({ + height: 400, + children: [ + getNewAlertGroupsNotificationsInTotalScene(config), + getNewAlertGroupsNotificationsForPeriodTableScene(config), + ], + }), + ], + }), + }), + new NestedScene({ + title: 'Teams data', + canCollapse: true, + isCollapsed: false, + body: new SceneFlexLayout({ + direction: 'column', + children: [ + new SceneFlexLayout({ + height: 400, + children: [getAlertGroupsByTeamScene(config), getMTTRByTeamScene(config)], + }), + ], + }), + }), + ], + }), + }); + +export default Insights; diff --git a/grafana-plugin/src/pages/insights/Insights.types.ts b/grafana-plugin/src/pages/insights/Insights.types.ts new file mode 100644 index 00000000..912585a9 --- /dev/null +++ b/grafana-plugin/src/pages/insights/Insights.types.ts @@ -0,0 +1,6 @@ +import { DataSourceRef } from '@grafana/schema'; + +export interface InsightsConfig { + isOpenSource: boolean; + datasource: DataSourceRef; +} diff --git a/grafana-plugin/src/pages/insights/scenes/AlertGroupsByIntegration.tsx b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByIntegration.tsx new file mode 100644 index 00000000..07e2a102 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByIntegration.tsx @@ -0,0 +1,111 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getAlertGroupsByIntegrationScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(max_over_time(sum by(integration) (avg without(pod, instance)($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))[1d:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'Alert groups', + integration: 'Integration', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'Alert groups by Integration', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + custom: { + align: 'auto', + cellOptions: { + mode: 'gradient', + type: 'gauge', + valueDisplayMode: 'color', + }, + filterable: false, + inspect: false, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Integration', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx new file mode 100644 index 00000000..2df8f305 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx @@ -0,0 +1,111 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getAlertGroupsByTeamScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(max_over_time(sum by(team) (avg without(pod, instance)($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))[1d:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'Alert groups', + team: 'Team', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'Alert groups by Team', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + custom: { + align: 'auto', + cellOptions: { + mode: 'gradient', + type: 'gauge', + valueDisplayMode: 'color', + }, + filterable: false, + inspect: false, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Team', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTR.tsx b/grafana-plugin/src/pages/insights/scenes/MTTR.tsx new file mode 100644 index 00000000..a8849465 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/MTTR.tsx @@ -0,0 +1,74 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getMTTRScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'avg_over_time((sum($alert_groups_response_time_seconds_sum{slug=~"$instance", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$instance", team=~"$team", integration=~"$integration"}))[$__range:])', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'Mean time to respond (MTTR)', + description: 'Mean time between the start and first action of all alert groups for the last 7 days', + pluginId: 'stat', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'text', + value: null, + }, + ], + }, + unit: 's', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Value', + }, + properties: [ + { + id: 'displayName', + value: 'MTTR', + }, + ], + }, + ], + }, + options: { + colorMode: 'value', + graphMode: 'none', + justifyMode: 'center', + orientation: 'auto', + reduceOptions: { + calcs: ['lastNotNull'], + fields: '', + values: false, + }, + textMode: 'auto', + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx new file mode 100644 index 00000000..384a247c --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx @@ -0,0 +1,126 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getMTTRByIntegrationScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(avg_over_time((sum by (integration)($alert_groups_response_time_seconds_sum{slug=~"$instance", team=~"$team", integration=~"$integration"}) / sum by (integration)($alert_groups_response_time_seconds_count{slug=~"$instance", team=~"$team", integration=~"$integration"}))[$__range:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + cluster: true, + container: true, + id: true, + instance: true, + job: true, + namespace: true, + org_id: true, + pod: true, + slug: true, + team: true, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'MTTR', + integration: 'Integration', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'Mean time to respond (MTTR) by Integration', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'continuous-GrYlRd', + }, + custom: { + align: 'auto', + cellOptions: { + mode: 'gradient', + type: 'gauge', + valueDisplayMode: 'text', + }, + filterable: false, + inspect: false, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: 0, + }, + { + color: 'red', + value: 5400, + }, + ], + }, + unit: 's', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Integration', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx new file mode 100644 index 00000000..e193546f --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx @@ -0,0 +1,116 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getMTTRByTeamScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(avg_over_time((sum by(team) ($alert_groups_response_time_seconds_sum{slug=~"$instance", team=~"$team", integration=~"$integration"}) / sum by(team)($alert_groups_response_time_seconds_count{slug=~"$instance", team=~"$team", integration=~"$integration"}))[$__range:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'MTTR', + team: 'Team', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'Mean time to respond by Team (MTTR)', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'continuous-GrYlRd', + }, + custom: { + align: 'left', + cellOptions: { + mode: 'gradient', + type: 'gauge', + valueDisplayMode: 'text', + }, + filterable: false, + inspect: false, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + { + color: 'red', + value: 5400, + }, + ], + }, + unit: 's', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Team', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx new file mode 100644 index 00000000..11000fd7 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx @@ -0,0 +1,68 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getMTTRChangedForPeriodStatScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'avg(sum($alert_groups_response_time_seconds_sum{slug=~"$instance", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$instance", team=~"$team", integration=~"$integration"}))', + instant: false, + legendFormat: '__auto', + range: true, + refId: 'A', + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'MTTR changed for period', + pluginId: 'stat', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'blue', + value: null, + }, + { + color: 'green', + value: -10000000, + }, + { + color: 'super-light-yellow', + value: 0, + }, + ], + }, + unit: 's', + }, + overrides: [], + }, + options: { + colorMode: 'value', + graphMode: 'none', + justifyMode: 'center', + orientation: 'auto', + reduceOptions: { + calcs: ['diff'], + fields: '', + values: false, + }, + textMode: 'auto', + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx new file mode 100644 index 00000000..c5f37f2c --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx @@ -0,0 +1,107 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getMTTRChangedForPeriodTimeseriesScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'avg(sum($alert_groups_response_time_seconds_sum{slug=~"$instance", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$instance", team=~"$team", integration=~"$integration"}))', + instant: false, + legendFormat: '__auto', + range: true, + refId: 'A', + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'MTTR changed for period', + pluginId: 'timeseries', + fieldConfig: { + defaults: { + color: { + fixedColor: 'green', + mode: 'fixed', + seriesBy: 'min', + }, + custom: { + axisCenteredZero: false, + axisColorMode: 'text', + axisLabel: '', + axisPlacement: 'auto', + barAlignment: 0, + drawStyle: 'line', + fillOpacity: 54, + gradientMode: 'opacity', + hideFrom: { + legend: false, + tooltip: false, + viz: false, + }, + lineInterpolation: 'linear', + lineStyle: { + fill: 'solid', + }, + lineWidth: 1, + pointSize: 5, + scaleDistribution: { + type: 'linear', + }, + showPoints: 'auto', + spanNulls: true, + stacking: { + group: 'A', + mode: 'none', + }, + thresholdsStyle: { + mode: 'off', + }, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'text', + value: 0, + }, + ], + }, + unit: 's', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Value', + }, + properties: [ + { + id: 'displayName', + value: 'MTTR', + }, + ], + }, + ], + }, + options: { + legend: { + displayMode: 'list', + placement: 'bottom', + showLegend: false, + }, + tooltip: { + mode: 'single', + sort: 'none', + }, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx new file mode 100644 index 00000000..16033da3 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx @@ -0,0 +1,118 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getNewAlertGroupsDuringTimePeriodScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + disableTextWrap: false, + editorMode: 'code', + excludeNullMetadata: false, + exemplar: false, + expr: 'increase(max_over_time(sum by (integration) (avg without(pod, instance) ($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))[30m:])[1h:])', + fullMetaSearch: false, + instant: false, + legendFormat: '__auto', + range: true, + refId: 'A', + useBackend: false, + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'New alert groups during time period', + pluginId: 'timeseries', + fieldConfig: { + defaults: { + color: { + mode: 'palette-classic', + }, + custom: { + axisCenteredZero: false, + axisColorMode: 'text', + axisLabel: '', + axisPlacement: 'auto', + barAlignment: 0, + drawStyle: 'line', + fillOpacity: 80, + gradientMode: 'opacity', + hideFrom: { + legend: false, + tooltip: false, + viz: false, + }, + lineInterpolation: 'linear', + lineStyle: { + fill: 'solid', + }, + lineWidth: 1, + pointSize: 5, + scaleDistribution: { + type: 'linear', + }, + showPoints: 'auto', + spanNulls: false, + stacking: { + group: 'A', + mode: 'normal', + }, + thresholdsStyle: { + mode: 'off', + }, + }, + decimals: 0, + displayName: '${__field.labels.integration}', + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byValue', + options: { + op: 'gte', + reducer: 'allIsZero', + value: 0, + }, + }, + properties: [ + { + id: 'custom.hideFrom', + value: { + legend: true, + tooltip: true, + viz: true, + }, + }, + ], + }, + ], + }, + options: { + legend: { + displayMode: 'list', + placement: 'bottom', + showLegend: true, + }, + tooltip: { + mode: 'multi', + sort: 'desc', + }, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx new file mode 100644 index 00000000..f38abacb --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx @@ -0,0 +1,80 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getNewAlertGroupsForSelectedPeriodScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + disableTextWrap: false, + editorMode: 'code', + excludeNullMetadata: false, + exemplar: false, + expr: 'increase(max_over_time(sum(avg without(pod, instance) ($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))[1d:])[$__range:])', + format: 'time_series', + fullMetaSearch: false, + includeNullMetadata: true, + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + useBackend: false, + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'New alert groups for selected period', + pluginId: 'stat', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + decimals: 0, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'text', + value: null, + }, + ], + }, + unit: 'none', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Value', + }, + properties: [ + { + id: 'displayName', + value: 'New alert groups', + }, + ], + }, + ], + }, + options: { + colorMode: 'value', + graphMode: 'none', + justifyMode: 'center', + orientation: 'auto', + reduceOptions: { + calcs: ['lastNotNull'], + fields: '', + values: false, + }, + textMode: 'auto', + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx new file mode 100644 index 00000000..328da56b --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx @@ -0,0 +1,118 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getNewAlertGroupsNotificationsDuringTimePeriodScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + disableTextWrap: false, + editorMode: 'code', + excludeNullMetadata: false, + exemplar: false, + expr: 'increase(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"$instance"}))[30m:])[1h:])', + fullMetaSearch: false, + instant: false, + legendFormat: '__auto', + range: true, + refId: 'A', + useBackend: false, + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + title: 'New alert groups notifications during time period', + pluginId: 'timeseries', + fieldConfig: { + defaults: { + color: { + mode: 'palette-classic', + }, + custom: { + axisCenteredZero: false, + axisColorMode: 'text', + axisLabel: '', + axisPlacement: 'auto', + barAlignment: 0, + drawStyle: 'line', + fillOpacity: 80, + gradientMode: 'opacity', + hideFrom: { + legend: false, + tooltip: false, + viz: false, + }, + insertNulls: false, + lineInterpolation: 'linear', + lineStyle: { + fill: 'solid', + }, + lineWidth: 1, + pointSize: 5, + scaleDistribution: { + type: 'linear', + }, + showPoints: 'auto', + spanNulls: false, + stacking: { + group: 'A', + mode: 'normal', + }, + thresholdsStyle: { + mode: 'off', + }, + }, + decimals: 0, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byValue', + options: { + op: 'gte', + reducer: 'allIsZero', + value: 0, + }, + }, + properties: [ + { + id: 'custom.hideFrom', + value: { + legend: true, + tooltip: true, + viz: true, + }, + }, + ], + }, + ], + }, + options: { + legend: { + displayMode: 'list', + placement: 'bottom', + showLegend: true, + }, + tooltip: { + mode: 'multi', + sort: 'desc', + }, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx new file mode 100644 index 00000000..b7c4d722 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx @@ -0,0 +1,113 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getNewAlertGroupsNotificationsForPeriodTableScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(increase(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"$instance"}))[1h:])[$__range:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + username: false, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'Alert groups', + team: 'Team', + username: 'Username', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'New alert groups notifications for period', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + custom: { + align: 'auto', + cellOptions: { + type: 'gauge', + }, + filterable: false, + inspect: false, + }, + decimals: 0, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + unit: 'none', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Username', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx new file mode 100644 index 00000000..2e6564a6 --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx @@ -0,0 +1,113 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getNewAlertGroupsNotificationsInTotalScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + editorMode: 'code', + exemplar: false, + expr: 'sort_desc(max_over_time(sum by(username) (avg without(pod, instance)($user_was_notified_of_alert_groups_total{slug=~"$instance"}))[1d:]))', + format: 'table', + instant: true, + legendFormat: '__auto', + range: false, + refId: 'A', + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'seriesToRows', + options: {}, + }, + { + id: 'organize', + options: { + excludeByName: { + Time: true, + username: false, + }, + indexByName: {}, + renameByName: { + Metric: 'Integration', + Value: 'Alert groups', + team: 'Team', + username: 'Username', + }, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'New alert groups notifications in total', + pluginId: 'table', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + custom: { + align: 'auto', + cellOptions: { + mode: 'gradient', + type: 'gauge', + valueDisplayMode: 'color', + }, + filterable: false, + inspect: false, + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Username', + }, + properties: [ + { + id: 'custom.cellOptions', + value: { + type: 'auto', + }, + }, + { + id: 'custom.width', + value: 300, + }, + ], + }, + ], + }, + options: { + cellHeight: 'sm', + footer: { + countRows: false, + fields: '', + reducer: ['sum'], + show: false, + }, + showHeader: true, + }, + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx new file mode 100644 index 00000000..ebd3facd --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx @@ -0,0 +1,79 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getTotalAlertGroupsScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + disableTextWrap: false, + editorMode: 'code', + excludeNullMetadata: false, + exemplar: false, + expr: 'max_over_time(sum(avg without(pod, instance) ($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))[1d:])', + format: 'time_series', + fullMetaSearch: false, + instant: false, + legendFormat: '__auto', + range: true, + refId: 'A', + useBackend: false, + }, + ], + }); + + return new SceneFlexItem({ + $data: query, + body: new VizPanel({ + pluginId: 'stat', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'text', + value: null, + }, + ], + }, + unit: 'none', + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'Value', + }, + properties: [ + { + id: 'displayName', + value: 'Total alert groups', + }, + ], + }, + ], + }, + options: { + colorMode: 'value', + graphMode: 'none', + justifyMode: 'center', + orientation: 'auto', + reduceOptions: { + calcs: ['lastNotNull'], + fields: '', + values: false, + }, + textMode: 'auto', + }, + pluginVersion: '9.5.2', + title: 'Total alert groups', + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx new file mode 100644 index 00000000..7d36795b --- /dev/null +++ b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx @@ -0,0 +1,133 @@ +import { ThresholdsMode } from '@grafana/data'; +import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; + +import { InsightsConfig } from 'pages/insights/Insights.types'; + +export default function getTotalAlertGroupsByStateScene({ datasource }: InsightsConfig) { + const query = new SceneQueryRunner({ + datasource, + queries: [ + { + disableTextWrap: false, + editorMode: 'code', + excludeNullMetadata: false, + expr: 'sum by (state) (avg without(pod, instance) ($alert_groups_total{slug=~"$instance", team=~"$team", integration=~"$integration"}))', + fullMetaSearch: false, + legendFormat: '__auto', + range: true, + refId: 'A', + useBackend: false, + }, + ], + }); + + const transformedData = new SceneDataTransformer({ + $data: query, + transformations: [ + { + id: 'joinByLabels', + options: { + value: 'state', + }, + }, + { + id: 'organize', + options: { + excludeByName: {}, + indexByName: { + acknowledged: 1, + firing: 0, + resolved: 2, + silenced: 3, + }, + renameByName: {}, + }, + }, + ], + }); + + return new SceneFlexItem({ + $data: transformedData, + body: new VizPanel({ + title: 'Total alert groups by state', + pluginId: 'bargauge', + fieldConfig: { + defaults: { + color: { + mode: 'thresholds', + }, + mappings: [], + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: null, + }, + ], + }, + }, + overrides: [ + { + matcher: { + id: 'byName', + options: 'firing', + }, + properties: [ + { + id: 'color', + value: { + fixedColor: 'red', + mode: 'fixed', + }, + }, + ], + }, + { + matcher: { + id: 'byName', + options: 'acknowledged', + }, + properties: [ + { + id: 'color', + value: { + fixedColor: 'dark-yellow', + mode: 'fixed', + }, + }, + ], + }, + { + matcher: { + id: 'byName', + options: 'silenced', + }, + properties: [ + { + id: 'color', + value: { + mode: 'fixed', + }, + }, + ], + }, + ], + }, + options: { + displayMode: 'gradient', + minVizHeight: 10, + minVizWidth: 0, + orientation: 'vertical', + reduceOptions: { + calcs: ['lastNotNull'], + fields: '', + values: false, + }, + showUnfilled: true, + valueMode: 'color', + }, + pluginVersion: '9.5.2', + }), + }); +} diff --git a/grafana-plugin/src/pages/insights/variables.ts b/grafana-plugin/src/pages/insights/variables.ts new file mode 100644 index 00000000..1d121789 --- /dev/null +++ b/grafana-plugin/src/pages/insights/variables.ts @@ -0,0 +1,138 @@ +import { DataSourceVariable, QueryVariable } from '@grafana/scenes'; + +import { InsightsConfig } from './Insights.types'; + +const DEFAULT_VARIABLE_CONFIG: Partial[0]> = { + hide: 0, + includeAll: true, + isMulti: true, + options: [], + refresh: 1, + regex: '', + skipUrlSync: false, + sort: 0, + type: 'query', +}; + +const getVariables = ({ isOpenSource, datasource }: InsightsConfig) => [ + // Selectable + ...(isOpenSource + ? [ + new DataSourceVariable({ + name: 'datasource', + label: 'Data source', + pluginId: 'prometheus', + value: 'grafanacloud-usage', + }), + ] + : []), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'instance', + label: 'Instance', + text: ['All'], + value: ['$__all'], + datasource, + definition: 'label_values(${alert_groups_total},slug)', + query: { + query: 'label_values(${alert_groups_total},slug)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'team', + label: 'Team', + text: ['All'], + value: ['$__all'], + datasource, + definition: 'label_values(${alert_groups_total}{slug=~"$instance"},team)', + query: { + query: 'label_values(${alert_groups_total}{slug=~"$instance"},team)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + refresh: 2, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'integration', + label: 'Integration', + text: ['All'], + value: ['$__all'], + datasource, + definition: 'label_values(${alert_groups_total}{team=~"$team",slug=~"$instance"},integration)', + query: { + query: 'label_values(${alert_groups_total}{team=~"$team",slug=~"$instance"},integration)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + refresh: 2, + }), + + // Non-selectable + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'alert_groups_total', + label: 'alert_groups_total', + datasource, + query: { + query: 'metrics(alert_groups_total)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + text: ['oncall_alert_groups_total', 'grafanacloud_oncall_instance_alert_groups_total'], + value: ['oncall_alert_groups_total', 'grafanacloud_oncall_instance_alert_groups_total'], + definition: 'metrics(alert_groups_total)', + hide: 2, + includeAll: false, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'user_was_notified_of_alert_groups_total', + label: 'user_was_notified_of_alert_groups_total', + datasource, + definition: 'metrics(user_was_notified_of_alert_groups_total)', + query: { + query: 'metrics(user_was_notified_of_alert_groups_total)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + hide: 2, + refresh: 2, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'alert_groups_response_time_seconds_bucket', + label: 'alert_groups_response_time_seconds_bucket', + datasource, + definition: 'metrics(alert_groups_response_time_seconds_bucket)', + query: { + query: 'metrics(alert_groups_response_time_seconds_bucket)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + hide: 2, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'alert_groups_response_time_seconds_sum', + label: 'alert_groups_response_time_seconds_sum', + datasource, + definition: 'metrics(alert_groups_response_time_seconds_sum)', + query: { + query: 'metrics(alert_groups_response_time_seconds_sum)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + hide: 2, + }), + new QueryVariable({ + ...DEFAULT_VARIABLE_CONFIG, + name: 'alert_groups_response_time_seconds_count', + label: 'alert_groups_response_time_seconds_count', + datasource, + definition: 'metrics(alert_groups_response_time_seconds_count)', + query: { + query: 'metrics(alert_groups_response_time_seconds_count)', + refId: 'PrometheusVariableQueryEditor-VariableQuery', + }, + hide: 2, + }), +]; + +export default getVariables; diff --git a/grafana-plugin/src/pages/routes.tsx b/grafana-plugin/src/pages/routes.tsx index a224529d..048c83ec 100644 --- a/grafana-plugin/src/pages/routes.tsx +++ b/grafana-plugin/src/pages/routes.tsx @@ -1,6 +1,7 @@ import EscalationsChainsPage from 'pages/escalation-chains/EscalationChains'; import IncidentPage from 'pages/incident/Incident'; import IncidentsPage from 'pages/incidents/Incidents'; +import Insights from 'pages/insights/Insights'; import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks'; import SchedulePage from 'pages/schedule/Schedule'; import SchedulesPage from 'pages/schedules/Schedules'; @@ -66,6 +67,10 @@ export const routes: { [id: string]: NavRoute } = [ component: CloudPage, id: 'cloud', }, + { + component: Insights, + id: 'insights', + }, ].reduce((prev, current) => { prev[current.id] = { id: current.id, diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx index 824e9191..c4cb87b7 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx @@ -47,7 +47,7 @@ class ChatOpsPage extends React.Component { const { activeTab } = this.state; const { store } = this.props; - if (!this.isChatOpsConfigured() && store.isOpenSource()) { + if (!this.isChatOpsConfigured() && store.isOpenSource) { return this.renderNoChatOpsBannerInfo(); } diff --git a/grafana-plugin/src/plugin.json b/grafana-plugin/src/plugin.json index 2dc75d5b..8fe77354 100644 --- a/grafana-plugin/src/plugin.json +++ b/grafana-plugin/src/plugin.json @@ -94,6 +94,14 @@ "action": "grafana-oncall-app.other-settings:read", "addToNav": true }, + { + "type": "page", + "name": "Insights", + "path": "/a/grafana-oncall-app/insights", + "role": "Viewer", + "action": "grafana-oncall-app.other-settings:read", + "addToNav": true + }, { "type": "dashboard", "path": "dashboards/oncall_metrics_dashboard.json", diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx index ec89e331..8f87baaa 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx @@ -26,6 +26,7 @@ import NoMatch from 'pages/NoMatch'; import EscalationChains from 'pages/escalation-chains/EscalationChains'; import Incident from 'pages/incident/Incident'; import Incidents from 'pages/incidents/Incidents'; +import Insights from 'pages/insights/Insights'; import Integration from 'pages/integration/Integration'; import Integrations from 'pages/integrations/Integrations'; import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks'; @@ -181,6 +182,9 @@ export const Root = observer((props: AppRootProps) => { + + + {/* Backwards compatibility redirect routes */} Dc zVQyr3R8em|NM&qo0POv1ciXm>I1ZoJ`V<&BGl^S^lI%EXG$);LoTSaKxp>)W`^@U; zav&0tP*Vg8fO6C%&u9NH+(>`~DN2@|q{n=bSR}Bqu>tHGHa1d9=7`Tw#`?!INO`b? z^Y|azeFlTU;KBX-^8dkLQ2zg5|KY=b4DTNvJlH>cFx=n&$6$DHe{k>*FxWC4-X~)m z(tixD+*Y}BU&#ZLgmB0)N!X|Z0HhpGV900LIKZSI;}9hbjlfX~!x`%B2LoTBi4HuRu9R%UrfPXNaph_!F<`Dp?Ka4 z1rX-I{W)cvA~**pl>C80-Ws;`X9uSE7epB*$p|d=J1|YHpTYg_f?+2@DM})ggcz|= zrw2lkM3~DWFh-QmVFIU!1}K5!7)2eR@v}d60H7!(40A%41$w^z1{!BfNAc z;2Rf_i)@T~j6;rkbHphQSp}VHk*y+i7z_`~SXqz~5}_V?pAyPFq^c{pRSfS3`}Z3# zOfxo{LXIxsvI#${*jBOI4+L@%Vj;z;Lhd971TX^-%t<1e!pRh{CF5ugU=jh2=Lj%F zF=8Ns9F8GFL1#t;-c^(Y1|1v<@a7|hmm0l{AsUi|qlDKl8LKOE$T*^X$Qa^wk@J{L zNiQ599*)O{;lcd}L7GfE7if7&Xe8HA(UnfmqHae_(tH8l{r>SV7(NOHy}{ki9O8sS zoFFOzpgF|x2uv|dXg0>=f2ZPwz&ana{994{FPO|2Ml(-o^QkC2$CR)bE`k}1{+eO5 z@YMSMaXej8tAzwIgy#RtDNGpV0Zh}pton43#WA#6p@eV-lZaD<*#D-4aYTX$6?h?v zImeyyMC>rhP$e;%xt+s@Ru#4ySXtCs39HJAReheXYHw*V^fW;nvA%~@@ntGlYMRD4 zlrpMQtenH(yI{Zbxy9#K|5Wt9n1mNwZexZ1HyjQgl=Z)Z!w0we-%UJaJ?93rm_gpm zO=u_kd22VPZw!2GUZ)PR--+-PF+Kup2KNsijJ`X7@KN;Wy9fIRQ4~FV5QYd&4&m_O z_`#!thx-s6+=t_b!^3EN7>48T?nmSB{=@y~a58@Q9UKf%XNnR;AxF^&boU4Q2fg92 zH+=YZ_}%FK!_n}5@M!Za}SBNsFh(F1W<>;W!UU2^PouM$Jm@iLAjKifUqHVbmn828!P6yB7 z6rE&oe1<}bI2(cAe(&t$UWKmw-b=GM?lD=S+v)7cj^acbS_>Saqc9{{!et%R>yXS- zlAwgMzz{|DB!7RGfk_s}OYmm~V?4nqlC^`*&Q8aLD%~%d`qHW4^isUgsCMNK+iwFt zeCo)TJ3HV_$^pqZAQJ#HPUZr%upNKO6>V^I@?N zDAJZsM@UAJyh0qllx0=#cXq%lWnD6GOp*znzJe(TXE2!}CN1WZH)n4ZVDR(lOHttz zO;-7%NGgONq271Gc{BRN)o+2CBO}1b z9LYaR#ZQjT&VGG!`qVA|oRaw{e+4uSKcS@Kv+npnOJem8R++%!Xr9$nR64s1&Omc0uiPt z5tP13#E8d2N-8aJ*Z0t=EFH39Bs19;K(R;4-5Ew-$Qj)xG;FXXzzYLsX z1;!cn3JfF707ZXhh;b#MVm!w=VGHRr>RVniu`VMLvVO)8?M*Wrp}tTyGLCw=jO^Qk zlfP9c6&l9Qj_qq!@2i|gLQ$iqifEa$nVz9|saXG5D;q}BDCt-%XsvZfDd~QdO|?@$ zlpJOVKyVyqj3atVVg&Aq0kK{@!AXRZ>8>i@GrizM!2vyRDLB5=79p@|0Gvpnp}r~Y zkEJj~47_>$^8XBlcXq(*CnGRMArylD5`hGvNP_~Gn9l^WJk?|Q^XbdG4BYGXwb!J( z3wlCb2J?(@FhyJnS(50Qx*t6l&2a)!iWfLWQ>7VboVifgsc1`3b4jv2Fh&zX5ujO; zh<->&vKT{L4z(6$dR)aL)&4oWK+IZ-;F8b_(3610tYzSGMnFWQoSeZ0GWZ?k=&?O8 z&N#S)33niI0+b{|fJm}p3dm(*5p@sXgmIyGC+)Esxz#{bjkqo^0`Rpx#F(Yyq ztwGUB5=jt~6qN|VFhoj$64OGUW%e?ZbULw&AJdNL>$CTm2~t@T>9yV!m?JXL_zALi zniq#Es6>=FQ=ATnJ*>=;_Dosz_?UvO!!PGr(llg6bTl|3M0h{$A0Y z;RuD09vd#qKu?GwFc&K^EZz#0zRbHNfS=H^|BIXvmor3>_$@RpF@_p8cxR;XKYv2Y z@87BMeh=qqj0|oSG|UG8z$F@6GDNGP^f3t?;hy&G96y4LvBv{hZZP_8@ZHcU7X*RzJ0+BtS}m!dB>+bHTHSZP zrj&3Jl6VB(9-mlWT)FR~tX5I-S@%}ET7FSponA>L%9LX!_sGcctw3#cr_(m*% zUYIdX=Dlf5#xQO%4T|{(KK88se)s@z62@7CKsPVb%W2y)Xm>#XK7EoE-`YKS*Hq~p znB(b;gP4#D24Z}nB&NYi&%y484h z%Y?)Cf{;&1c!4O85no#G`}OGb^^4a(jAThQ;9`IimZfrgEmAs@9w~N#(^T=1pT1+N z^J0t_!(ez24081eReE07Z96;Q?F>ohSxV6azXy!T!6EKP4R2f#r)-cmDg%r*aN0jY}4k=kD z)E>1qdlLkCM$uz96|B;HWSmeqMS*-9P{f78N0ML;-}gA4qsIq>d0tpKMuG{B(PQcK zQ7`Pcv*kSQ0E5$Wfox%|sJ=+=ll(H$k)m=nDeiE|KzvbVpaNc3v#Ehg823Ul zPX!~9GF~*IFl4~~Amr{0flG=x>cN??vtlv@_kKNkx$DQE1lovS$kjHG_M>G~$vW9Z zl6Yio!LFX4b&P}1axMC2nQTpomI!i~dt#SL^Bpfso(Q3x6!TqnAccIU!AMm`=`32Q zONCOz#ClL}*C6+J8jZ;KE`&~}z~=7G4gg9+&i5pKB$r?!{f7w`RyqMJq%cK*LpnvA zS+mI=PF3h!O05NqM_@R&A;u(`f-K=U7J^R!)J{q{c^M#?51#l&GCl&s0jtMQ?)wOZ zn`UvzzpcJtl9W-~*{KO=m2OZ=CzZRzyzfks(ytwg=7ghjIVX!9HSz9O@k%?bucJw- z#?@-^6hT{M++K@fDw?Lm-`W5a3yx&`oJ`IMjS!V%c7BPIh+K}qfNdQQ^j=yd)F>kv zllx-YZ3q?^R)vYFW7bu!4;oljXXfzzx%GZ1<1TbBj-^$pxV2zCh^h_ymw15^#8_WY zAIrbaIHWvFg_=J`mJrv)ad zC7fW6VT}J$8gfb^2b5dEn#GL>{!-eED&YAxqS`_8!D$Ub=)sZ!drGlX(k?(QFtV>0dU5B3NB z!TtVVzmMlaz9A_vdYtt54E0Q_FvUHb^a=nyfop$9V~CTfEFOx+dt}msiK%KFgC*?D z9)TfnJzcE`H=CyAz&Epa+&=1N1BqC%FX1c7W7dPA!0fIV>bq@FD+jDqq9NQ_r8o4I zMPW#ywi9hwW{)@}ltEJJ!D?iMU{Mi^l53iXC!mcLM>kW7j@sxl^)Um* z$+*0i0;exLa_|-S?`p|GY-lCL3F9yUyia`7~`4J`Ywg}c1h?(zV%(qJLxiuaxZI8 zjJn|at{l<*`X;Bs7={-piQJGj2}rFD#^N5;Go@Na4SdE>uRy286nOjc%81tD*N=ba2cL)Azh|Rz?UhKMzi{@psK@T zr$}|hST}_6qZmUb)QUJJm&|NYqJ+!Sl$~`$lN+P;je_f<@%0xGfzsYu>AvbqKs}g5y#j@PZcP?gIkQ}P8oL*0S!g^aVsHey&;HC{{87e2 zu&Ti$|1Vyn$pnRb1iG)unL-bJ6w4b?>&0vG>^%xIj_wVE2a-YTI@D{_7E%%c$QYhV zpX?S$rhxTQ63KH#Ikm)(GZafFlD!=#+Mfu0i!V)4^Se}xla$0{x&$nhQMe&V#2hb% znxaXt`A}=B2lvK^!(9WSp_3f43)ZJu(zDL8=I#>IOQUgrCr3ffBr{6ARAQmA zuSatPUQB*e-xOu+(6$7?SneBfWLOBIdup4a%Q&GI-JsoYsh$|0xKRI9*o)Pfc@}e= z#;BLKRE*$G(Y%xrGTyr67j2_^D}zKb_I@UV_fB#s#?5dXLp--&=?HF7T>5378q2*Y zG`lV!%Lql|xN=!^-y4Q({?Q*~ghw0n8@Q79b6u?g*0)gR83JS~T4)d+m)xUP~y z?`X`0yl_-Lxy=jsj4;^~q~q1V$uEFV5G@lp$Dz^+873y^b*<>sf+9Ud5jQ+lq4r8( zW@6EM;1t0~I%MA@AzHt?Rh>Mps^jg6*Uk=E8*WZ)IYaD7e{hQa%rHf)cI&{Mti@c_ z7Q`Mn`Qdf5Cl{+2TsVAthU&ka%BCX4sutkXHo6R`sB#~(jY&%17X3KvfYOfER z;lCV3RQw)+?|#7RgE~`gW+3fTA0f3L+Ol8&X}|iu#3(oc-CoyMt=rwxdxmF5+eB;O#>|j%Py1*J6I90#kAzMnR%q8L{_MTkn6~Tmzz1diSaBYEmi6-@ z+0(~lN8mRQp#}IId}<)@YfB7ViL5g0$_`dmdXjS7;Si7n`DUr@eKl&*sDAaDB1V`9 zR;&)Plo|9(@zRbmD&%MR%#GJ(W0nDsKMbzyHY$ zW@N*`YDQLsHyT*Qn=%jUI5uC^2-fUd7m-G3ae4*Xk(4rGfk=HHPUW^ve#BpTxwYR@ zMhw9i8OrLTF%+3WP@DdZn^``NVA)zg9(age<%k$x#TyzSiXvg>EsH~^7KfBU8!~)b zd%o=>@wVa2kRxeKHDgQqSyl-}P~KN5c!53$H9b8!&bMVOjMUtMnHX!#LL#>p^hr6G z5Z3!MLlozSqXig)nb&Jo!DTpjG#KWecBCM%zvN0}L5{nu&Ic2tgTWO&c|Y#RVbQyll`57ri!GJ zqP?7=_9XO?(;j6HRo&xiRaJzVa3y_GSVb|ZU?UT(BWMMed3s0{sMmrD%BN>}BS};o zfwA73R(m-zJ*Hx(dJ>Pq&Bw#^Ovo87W2&B&=eaC>7S526A3wf*d(r_#2W6EPsz*;n zheqIFP$_VND3(eJNg_4^hm`_vO+e!P%BSZL#~DR$XB4p+iK7uXsFXR&q+iM^BUYiI zKpl?G_Y#W3!*YSQMa*RRlOIK=h#x~eAhJZQZ#=CM4}eLvNyP{CTq-&;Xqcsq=i0A+ zs$I)t_hg&ki>i&nE(_Ca1P%v-V*7cH=JGVt;c)+zj-bxbY+q0Mur8={6Le5yoF5F5cRV zNd+}qAFbGcJ$2keIuo@9rBB2@$CUA3F`xaYDynn&kmC@>u@FH~!i-*ES5Zw0HByGg z5vmRum>(k`Ph{qWgml)%v-#7qHh)1>HYhEuql^=&`WREq!zcYYN?~5pWdD(y9}5j# zvd*H6%F-vl6it>jI{?8iCr5yc|3D!x2NUJ$juriL4`F%k%T}$4*%wFigzxD91$7>5 zVG`D`-8z1-1F|%N9BJ2-z}2`uth1>xRyiVp-t^5sX*-~@;jb(K6N>xvbtwj+tSo!Z z%qjajN|=h)vevuYOP8gr#oH4KKyVP;&)q(dhBM@pNZ2r7i!jiOv!}xh^gg^*aJ~$p zgarylz_Q5%n+PHmLgRu}p-mCtc8r7BgH)8H_!>SZC_JO@|yuOoDM;G-H#cFXfHShX6 z`B6ojmP60v`xhrxIv=6eK7lbz!n}9o*D6i_sZ5rZ2X^>uBBmUmM|rryYvV>#(Kg41 zh(#>c77ZxdeDWGkwWd?O;q+nxGU>yIw)qY2SwGBf+hb|K?B!fc?gHn=Ptm84B#GeV z^2}21Z`D?|JTV}1ipXdVW2IqqX&~txnJ|IaNR!iJ%)Afq^fblN^I0?~9J_ zInEMV7VHPHLcDsS`?W5TjS0Oi2Wkv8g1+l)?~c z^)z4YpP7|DCg$DJu$;$Q*zAC*kh4}Q7`1?2B#aAw%<>pPSNUi+nZ9g`5o!YCdYdsvCtBbVFer>3PyP$}Ik zE-GwAdpAUD!$A&C?i^%H#w4YXsz^eIo6AJDs+yG-8QjAG3gn4-#f|r*Dz;l}JS9aa zeW4K|o%>hJv#rJa-`In-KE6AzSu{LPT&Z0SwFX%wzSyR#3K&8G@?!!IsN5;4b2UDXa){snE z4LGr&N>aTG3O=Eo=Mt#hyd>q@muKL=z*wCm<}y>JOqQT0rpY$onkq8aMaVN4>xFGW zE#`rLkr&5ja^~O|bYk6F;9QPmyqrbx|0+l)n73f%Ib3NV(?sa37DOunqe|f5G;MV} z_dUv}nt}(=EHQ^YEU?n^ra2*a!)J(IVutqebbJD+b_OYMkIQ5Kd3^q;;7 z_I)w7JZAc(J?TOyrRE6q7m)U8mY8#=B`<9uWnB1!GHZbdNoT$E>vkRP_OMpwuZzmqLuFvJV-e^w9 znl{t;38A#e_1VDIT|OWRO9mv_x#9s9R4H;{6y*s?L>(#DEoQC!XqF^|Q6xv%H0AQa zX8a1GYU$?Z-fH*g-1z@~J3pTz4$rM+nICuC1J#eZF-C%w$s3J@K?x<`^Z5Y|o z)*wAa??p-2{U;b?Cz~Bx|Kz@vglS8eVwwXweBq@Os5^YH~iMva6X>v z5dkF9BI5jB@a?Y05?5bEUA;4OeqI8d#xRs&ZzDV9fJyPj7UAgr{eyKylfbJgRCGT| zsDcWr+QL0gA)htj+lMLcFNX38Pu9OP?5l*z=9a!zvZxorXn`4_w?gSwDBTLBTcPyt zC6qL;ao4gmie%`Nq%cV&^cZn@GwQ*&R_!C5wqH3FOC07%aaOgc6hn-xy8501F<+G{ zj>Hk7=FU+0P2MjeIGpCDGh|Bum%BEKhSB}uqx&WKV9k7@<{betX_Y6uDkKyU=DlqS zGnOINe{gVcE9%SrxFJ#BH$vQ=*stU_GZy&{04MUUZk3Ew##1qK))*&K#FV?d0P~xM zbDI0qf4{fA^pmOs=cNg=YtKz@iIG*JG^HX+QtN9m7S^a?a&D5neAF?u=PJnTd|Vn} zOwK8qVv>CHd?C#D7SeF$dvA6+&-pTK)9zm+LAy;m7ZsOJb^qe6x)>(k4$;R-EH%k& zNw_RbD*<^JO>xCnp4f~CSQ4na-RvUp4S45H6V=eW5qKk*t8TW~T#^~#RP2p9?L#KT z7LO1i@Ad$CAEH!-fOui|SWGV8S;vG_Jph^9jR=}>6B_Bek&OGCp?t3?r;3CrL|deE z1bKE--Y1AG66wUlQdW!R)gB7?FEA&J%OvZf=SbzKe^;nV?@U~gvuoFYuV5QhY*U@C zI3XOoGd$v*ofiYMCZL4!Do`P&)DD;mTD8Q>pMUw-@F5L+MU7ue|GQv&uH|o=-b`@`R{w; zzk03e&Tw>Re+$ZKiy;&ItfCD%$eim^cZOTUs77O6V|QiKfpW7Ewr|+px!JUBJ0EBJ zmQxbdsq<@e+cGb`DqBo0t9sjitNULdQ-?wH&5AOkC5Q!lT8K4u(n}o{(jS3I$N$x+ ze@aDLLkHA4+^y<%tGcbLy2-iLPJgRhym2@$f6Fh?I2f?6N%3n?xNcVayH&UkTXx_ zB+A6x`b~(l^ZoblfB&0K%oh4ZE_;4&6j6$itG5os4Yr2HdYZ4m%xud(U=C|9jJ_kyT-stTY1Ri_ ztVC^grdTm%kA{Q&cdL%6$W$)dRW4EzZH<2mUF&mi4Ya`FX0`=ZAhMo?T5o)|vRrn*#A5GBgYtS+xN}XnH978h7EXu z&DUqVOB4DE*0V_;x?YRv3^ISf(+QIK(?80@cPK*9$LA2oDEfFzgpS8i)P}czrrjE& zh*o!Pq(Y~TLFw4bu~BUzxut_>0FDxKF`1pHN}jUHt8f@zP`p62NEEHWsuVghH1z1? zh0sydHE3FY(ppI|Twk_6?AzX1r4--lvR{-g`$g@V+^{BmGkYbsTIj77nnStOLT|Ou zTP<|07XM~i=#D;CUnE&8Vt__jek2%K$qXC$m>g*V8RW)zzni)rUp{hCvbv)2i-pZZYjkpR(lPauci)6>yAW=ERJliAcQ#CKs zjvhxVD^ro9%^YH|Zg=gN0|ONWW2bgTI6D+O-tn#MPt-#z?<)8KT6+p^7yT@9Ctn>F z+iDt>(WBw;P|V09F(uU5zwWNl~Y-~sYiJMM9j;M?7Q zMc?X|E_tJ{)6pqki>r5vJgPDgt{@qifY)}hA}e~HH?n+ThrD?{v@UZNK=L0^0iiPy z- zBpMY-9E+t3BgmmyCYq%AyesEX>&eFTXS<@lpSvgD<#!Huc65v8bRL-imW{(16tm09 zPI)Ze1WP!NW%fyX(uoQ^S)(}VCk9F-k(0S9Rn9zyyrT;dOF^Vd)#Ta1ug%>7Cv$|$+UtP0kT@iK7S*Uk* zq%qf%X>fbS7)iF&#$YfQJh*>f z{y!KDO8-9?9Ngdk$MF8)!Grz72gCjSe+-6)!@f)u5lp(3QlIv!P%SlTHDoX1Z99;c z6N*}638zhE-B&x4cgp*v`qta|ik5g&G|*aPdoswZDTp@E!H+Q6sT9mV(|EPStLt?eMZony*Xsc#W>f*;>_63m zDWht`aUJQK!H|saB#YxSmAh`E4pp6yU!e8WVr-qQk*~80mQD0kccaG1yY1ert+j4h zYp!z@Z&Q)I0#Cgjrv)9u@TLe{xbHs)N!Od{mJ9m}T(zEotUBm#Y*?#X0H47p)na(P zbN#AJOWs#|%^Wztrg1Y@s=9$=+dYT+YS`A(tkpEO-M%em@EUHIF=XiH)0bD!NIgJx zO6*e-OXcs4j>Fz^BV;~@NehOt1~ozxfMHO6-DIn5h_-0r1oGL|;a*KoXw{02cYhwk zDQiEhYm7i~d)X#DyIm9JKJ~4GUE!A4Xk5`e<;$nI-s^k~n

v9IxRPz0MsJr55P> znP1}MqQzubOGDVUUmLwg^E8HS*z`KHYqdjnbFjJN_N9z3yUrF?I7~MqIHyoDq_$vd zRtV;3zG88-nei>>wZQsE!nlwG*F}{V3FgN}E?fhA<&n8A>|43muLym+fTuAT!xrAX z?Qm+efN`ddR+#f)H{(_<9ZI{=M)hg8FGD!Z2;)aFhHT68NWj(6&0Cgs&AUijo}evv zi?%X7*4P!=($+%j-Jh+r*JhXJM&>+DrWCOobECi(RzO|T8xi_5?9;DMyDBYSd8c0J z95=ITEjZcDw3Iek)Rl&=#A+a)y;X2-V&ShdGgi^>3hO?`3zQ(nPAD0p=jwu$w=;^^ zjKnRRFtwpjE87xEEQaw@6nmV`TWse9Q7pHnND{SRZ<{wHk4`dnBgA{VLS5RdiN+oL zjl3KQiO?B}QOHSqPe&`rdHg9?8vl(6E;-1rNVHD$L6d!&7hY|rR^YY?_t-3$<8LpY zG2=Ly9>);RZ-(K=H>eHFg>l{Ji>@?xB^z$Lf!R$hamdjmi_Z{mwF9x;#7ERHOg(ci98<@lQKPPYjaV%PG-RN(tIkX=77IA(ngUQA7 z`*qEs>u?RPjs&U$|3w;=S0XUw+uv$TDVpH-Ti@MHDJF7*F@}s?2i9baenc?Zw#!-q zK9dQWwu>=EFv2%w8@Y{LqjBUmbe-0b+tM|gM{Z-+V;@l@&p_7)zE;4m7oCRYgrigB zIpk9TyvF)aV1K2!P2|p6aAn?-W1(wqozSZSyf+d|6bXB=Dadg{KB2*$HR-_{f?i;T zN%A9RoX}i<6tNcyD~u zL}ssgadI=GH$6Q$UVGS9IPum!dDoW06?9fo+B|K}n(3t~7S8&Zv@&2pqbIWY>zCqH ziwTULz!)as&8SJox)_gzVCUX#@Cq^+Yy@#F`@1pt%|m;w7LzaB18OC>9n_8 z!BX(QGLFEvyM=od1qeqdgmj%qy>+Y^4gRf+OXcVnndWHAO>jtgmYy`8a`~z~z>~IK zol3J;;&W*$x65|h)FF1)(CAgxNU@>wb$f}w7%h5+U=%k+a#fnU9vQX{nd?y6iWqC9 z=GS4c8a~*7IC;Hw1$_n0)&k!=Ehv^ZvWZzx!LYS5kYm`Y+T;XF`{jwQ`Xpa;v^+8 znJ&*#iePk15;0})w$BoA7LB)SOD)873}Jkfal*t>zOkd9*Pws4o#!3@FXz0zm5yk| zvHyldK6%+<*0XEY83psSv`o70r8S%3)n+W5itWe6+umB% zWL|qitFmZaUE;G*WpEX8IY2feofnUFrc?Q8XI5@rqkZYE8MvD=w<~C=6orIF*C-7o z*eif+qjBiuQDNvmgMLMQp(X)QJLqj&$;;h*2tsoXs7hN`vVdZ z1Wt;3@)S=7Axz}ek1`TjU*RpUfBx_R1i$EeVr0=k3yHw;&Vo;$%yypIpd+q5arv09_fhY!HaLB}%FElsjGmV6Eb;M1o-pmM4Lje|VWSbSiMFwiiA zn1nFy2*`L+#$2D|54712dY?XZo(tqi#+kY{8yV#G)b)UhIzOMjyvKF{q)6c00~97x zR0mz200*Bwb@aU|0&VeAR76RX{~0rZeCG#!NqBWkem;E(#!Eo6BoXxFGsF;aGoDrZ0_KrqlbSI};F|y7$ytHDKT8xF@#v|~8 zgE?FR4lfXZV1h2e948q^3`_`>V{d~6FHZDD{Q0S^AeW8*M>4s>FChh+gc(+pw+UtS!0#a+|Hz)AQFyub!32m6E9K%#T4ArfE>zI~-ur7Zo21o|%Mk7P*ux z$hTLmXl{>vTy7A2`qZsmX6Bkt30o~J)0cdPt5#m$7ZZk*1V z*Q~4b8k82Np{=Pd4%GD(7>8WVYK*s~4V9UrQnOpvW4xpKg=#Ym6XS2E)2tYmzl~lg0w5fHN?I42%&<^05-!SLaR# z42ywm7#++(gi?w^F%SSwIHKU+}*%^YR!m^430Y%O6P15o=Z(+^mv0gB=@`|1sg0Ge|w+RdbzH-`?s-j8piBv zv}~?xyti%vT^FL;x0E&)II9h()f{76woZ* zClt&HMF1xgLg!NN5~_e%ZiNoA2NsxNK9X{Zjrx7}{As_##?A( z<^KP}gVO&0gTY{UyZ?U^&xa5FZ@>c2M}Q#?COAfdjXa(UvV=1<0^juIn&^MidGDRD8)qX@O$V2OV9mC(JyY_!vd6C?M^?;8@m_P7&jNNUUU~;3MEPOG5DAK>ms6 zXW3+e--B+i047#yPni?_Z#u^^=1QTYkQ5j%5M^TFLMCiSX^ZBF19>*98GTu>XfmZZ z;SP(j42}Y5>Qy`ZR?fQo}Z0?>OeubkNprbT3V&tPjC4`;@HIz^;v#2*-y0p+ya{fvwq`XHaZD=ouAiWG`rKs+|%6l)%08ai7 z&SSrcRE&g|o_aSlwrE*{mM-bcEZw4?a@COv*2JpCEY7FR$P=ZMV|{EF0Sm*u%w^I{G$Fh(KF7y^vU5%?+Zgmx!j6h}rBA`H3RX6{vJ! zbQ7(s!`H;1+ z->cb@TC{&XLaSiK(5xvmVDyvYK*Uz_ifx%D}ML`;&KKVb&R4p zS%`^|QqSxE2RY;TXr5W5fch*<544N6{3$e~yy~bQix3dXIkp z_Fw-BN`G`$1KO?W?kVCKRUT)Urj(=<%f*#qgMnZ!#U;Sa-L=XU5K4xM^x&}mVpwNj zv_mOT*YTxte{Y4gr;4hrz$V0WL7Ys3i|>@Genl)(5=Hri3}bmALo=?P8yvhC3s&Qd z`O(P>U5EugLI%2uGO`D5FRc&ttI#SoMjU#`uqjFC7=|7i$k&_oR8`IRj8OcST9nrC zxxH#G+eDA1np-B3iEgV>$$TjjO0g9@k?w_iE9m8&=DXk?L-Pfq$7G%gS=s&mF&GBJ zN5OmPQmraA4sQ!~cQqp7X|<+QG2>F+0B4KpEgE%7JA+)RjNWsU$fmuuR*fy5nDGOc z0mjv~N$p5tb|#BBZTx9b+&ARG?ncbYLXBmq+-@^3^1CA%P&IX4l&aXM6AJEm(J%Tb zkmoc*V#>+sx^Z{a;MC3B?^@5?6~fU+Hgo4-3v!*9>k0p^WYTJU*$=DtkN{K%GKJDD zbab=RXijz+FUd-kyExk^=czueTc>OZ<`0juRYPZhCb#DnXoQ(I2C$%ZG=qb@DO387(e8-|#Tp3N%3y-@S8`oX%cL8ZZr9xLF*R1+)G z^hRn1dUBPFlZ>Hf`HgjsdjZ%|+hfw`mTmc@qE_xC5}!W^PC$3Ba$L)*`w_4-#$1+i zM6=zs$3hVigtuSb<7q4#y0Z%`1kB9y{i^n&eCdkQm(?iL`JeTC3wA-HO#biz%rnN{ zlK;m@;y`X%M%CqSUBcp>Z1#7R*(B4+v(!@i`Shik!#J6?@hjNZz7(_3dRv)~dSck9 zj2>5qqxhOr&IS7z$Z@yc1sRRO9Wj#-cN^$tH12|r`OrCls$-$|0UC?78i`W`qlSTy zpSL*<@>ZYnFo>7Uqag998wB~WJO&D;GX&z5ZvsU@D;DRT_2|6i;VDefAv#J-Y-k<=7>`q79~x1YW@NxQA%*ai#LJ{ z`44TjSrwG5n!~h09PxFEc>cGLHTe%2c3IocVraU>%Zh1OON|`MzKS@kL4P~AMwo?U zf#`A-Ryk?ooZ{}V{P9iaduc8L5kpIKs z{e4&d?+^C(Z}b1$$g`3BFRZ)W&!izdQPc9vP!w1u)5OW9dqCmpW=E4a<&bNIY>mq; zFiwG-?u`*+#$uN`42jVxnh4B{Fc^W_t(6V{Rb6Z&lH4-yujKLb|M@n|zWiCi{~sI< z9+d3A`wtIp?Y|p&H2?Q=aiuK3f~g5&@iXwn&@gCI{u=nQs_TTn>PqN6r|{V5^(uVR zu9L^a*1*h-nAHrrY6We=lnr5E9Fxl)OK_SZ-m?;vjzD(;V}`og2}TH)&aK=prhQ)* zXt@XOECe-d=a(>CWrla-^pYjPr%xlB9=hH~plP!2i5YHg$I2yNZRw*ioY9}^WjWWn z8G>m{#!#NRR6Q+?uKEl<8noSbqkkpcq6ZJG4Feyjic1z~FXs8Bc&APM`q}Atiq)u; zrc{Z1jd&DZ$s7%eVAK)|C75xW`vz%dnmxRq+{E* zZQI(hZEMH2Z96-*ZQHhOdveab^UTNj5#7D2R@K|H5(mkoQ$R0!i;ifoyx>Xn-s`qy zQM#xubB1hb6Fi^Nx<0(3irO97ni@3tPtGAaQ$*kkV!DS(G|}(l`*=BdCozBJP{HM< zHH_)hF*MRkJq0jyiQ^I|d?$>GBiBiVDbff3cUAs~2}QYId6Q#1cbpG*efpnm`OIDv z;=iyQM+g6e=-sgl&enCp8DwAumcAQbK5q?q&{2$R$0t$8z@~Dw5Uns#PUmuzYrrEV zo|Sge*Q-zM(yWu^(L>FZ8?-hPx8JbR&19_#PKdT;D>_7x6>Cm{gg0NPg{5!r;U-Ro zRpbh69*OypIQdERGk^>BaI>@e*Ruy|Mr7cOlF*xb!qiBZNBe>|i<{GIl~og8y}{E? z?ncDM=iT}bRkfEDpP%W+&#t}wj{)b;M_}Mb%k$44o{S}Eo4PEg*4DqPV+@)X?K<8J zefLLvQ_||)K)(7)fRj=_MXRAU%$dFXM%OKXiguhfTiISye4r@>^1I{mQ9`n%{Tp1f zT=hWn>-gui?+*e7#W(F{-NMc98+9=JOGW7FFki9i>TwgPZZTaWj^0NZM9-5~spU~` zCfJ(Js7V-97KA=(lIcq<^Cl&t@z@II;YH%~yDQ9yIJxaJ|@w zBb>>r|o&o&#(%Z%8S|j2(Ae{r&LlwfecmpX$nG_l{J2ExQHit0gOQVA6QT zz9Yp_i&R4)M!n;{N6@tK62wzgvvPQW6(h0~GX)M|1vKLG=CjV{X0_^5s|qADK9~^j zF2M!HCVYk#Bmxd%QtPq$1Ce^)uA(O-a&(8Cn;EpDe{HU0HQ4K*TfSr*-`yQnZ`M?( zVnRkDXkyh@M->D*MYv{UZc`)Ya2v-Re=}5tQo8idxeBGs?HB$?E(h3B92q57s6zD@b9WAt8IcKlWTKPBNaRwC~Zi zR1vUgmXL)i;j32b2@pFc4AntpY9V*A)SOyWB$*VUDf0#Y0m)SdKJ~UxPyd*6`C7b| zg?*`5s1O2_&`C{H2kOd+>J(C-?|=M@e^*<>B@g5xuJv^C`wpHmo}OPi4}6985nzA} z%>61?!Jd_p`jsszu5NFR8nuyy-Gk5FwwARYW7PpcXsObiieJ*ha4c<0@PhqTUU@nH zLhHKv1DHB;BvRI0m~1-yieawI?s@wHHrIQ;gX91e$YL;=9OF}gfgD<*S`#Q#G&Be) zbPJdi+D7K%6{bx$*B_#HpfDa%bzLr5ckyf64|RuhRMnx8&QYi0e*8Fb4PRp+`-k(-+(EiuQ3i)Bj* zpKlPr&cAF`Khq6L>ZbTEL z%0xGBsIte3VU^7+9T%VE> zRH%;5psg|2mJ}6ocgDwAMSQBVtQ2gxUjRsWMZcPaBMTcm!9zXDK~}p|7gsB!jiXpq)_hHUe$@Xg37`2{5~NOb&U1 z17Q;5C&I`t?<#*U;-|Xlmb+-Zv6~t|yij+LJ%9rIQZ5CIMV@4g`VCam&B#k6l$uUV z=77^zN}2dk7O3Z-*?^x9(&?TN=MX2{E%$Dh-tK~{66 z0lcqK76Q#U4E;!2U5^>Sn7dWofURXVEz&yAp;DKtHZ4gRe!ba)f14!V!r#ws?73}Ha-tjJwZDJ@r z>j=t<83bI@gEeHg*CEnwpKy}&cxO1-y4SqWoiwW=Z9YYO1sy^$M9Ix-0PVI)rV^}@X3ap>U|0%qWK}9`c5{WTge^xp zM8y^o`|%&)4)>yHiz!JdLnfV^BB5>E#Gt=Wb;OmHk=8Q4ae9=olJ~&D`4>#Lf8&Os zK(&kBxXku5Sgd(bU@h=+l$g}K29KuQks#E#&?F7YUjW|b#r))Qd0YqGfq>4fPNLkZ z4p5xui-;N)1tnWj&$lizyhF(P-APx;QUn@YI2N9?8==iY-*l-ZTW|s!mR>Velxotb zt(6@ceLh&YhSbA5=|G2%kdC_o4xc_*PMucRky}{AR^&pG zk5$eXJeB%{b*+-#CaZJAEx)|sICl7=V_!a74vD)sWo|(j zr>i{>yhOiS`gRT8K3@hteuwCutg0E;*3At>qMiHuEr+lsqh&^9~=(6@*Lnd{^xG7-`350U}1dSA|P zb=qHB#kYT~fQj2a7lkXf1SV3DhkyH|JNca?%%qMaxb?Zdq?a&j>f&yj*YQVaIT zhoqCy9t=1NO>HE68YXq!6_`P+<%Qj72Rv)8ZN^0Ga_>~XA@8VFqmj3(+d%Cw6M_(0x3Bn%ViB6 zk##olp_~|lC->1kgWhimhYGqiGm11g%QP!*G6X_t>+NN%ZZpEr%fEg81x40kQyDlw z!KMkUEp8uZr+#YUly`^r8|OOryUiu2c$hjF?XnGxSd!RKO&rahWOe1#dPv1i={sQ4 zj#GnK-k=Sp|F}EAUdGCv~XthkeovBw& zO=_-$rg<6Ji)^gLp_#cNJE0#v9IGv_HMJita4WCZPK@{}eJ@g6ze?uZGSLO9)PDy0 z4BozX2KuhPde|QpgWUh8dgxChS8r~41&m=t7d>poC6PV(cj)%f4?qXDa3iJg^|o}6 zntZ9i7x>y?>n!Vv!kalyH2*$XKG(B;n@`L4^E0=Iee~z$hM$At;paE7q4oVAOZE6e zZ>r-RdxjSIK0+-wH8?;kU;n|jiI(B*%J{v8+f|-ar=bFIF_)=T?@shclVS)Or;sut z!fW;3qYwFE4-?t|X33je77}Icz%*&HV}!r^?dfSt_AE~jesc=g=q7`G+kWP=B3xxs zuN30ovm{);M~lEXU=KPO{eCUHTcb3#buHuRU$WCgweP2EBE^?w=2GcLF>-2*-7sgh zDVS8h=iQ?B-Tq;#n_$CX*wM~9*ejs=_ zcz8H&0GYJ*-?lq8EAU?_h*Y&(T1;_A;br)3>-*eH6T7FoiJz(<@1q&2x1U>sp>J%w z&0*i)o5tYk!*`hnLto{NrJl&Nhwb=sklX=BC66=%+xCOCzwE)!PGQYlo_#!RXnJ@@ z>Zl;-`i~u;P`cGAYB(1Bm+F|F@`H6xa~k+NO4R04L;04l@A$;|a#{5ad!63dg-(0X z1_`g|ItkWag{n=61-Fz^pg(1^l^j6E92R&nO`2WhbLa@U=KyzoCTUTga1+LqlApHdU<+j;D(V4Ia#LoFwe5}3$*sOE!#P^4 zTIG0_mmP5)DeoMr#73>@SDz%MZS|XiXg<)aLiyl}s*?&M&aN{~I%I=QYc%glbEm`R zOt!z3M=}k{E!iv|?KMk*ps!)ff&3+5x%81L*d`Na!d8_-#r7U)ZZb6rxktfM;|o>e zPk>=0{D{puv?)rR%F0vR!A`6!h&K7xdc$Q58(mR)cWonbhnh?Fg-;du)PCR_3O;cE zJu~ntl)rPa-FYIr?Nw01ua#Lwp{RS^M5<3`?yRM(FZJ4lF!Z1&28GK^p^tt=zTSk% zo{%34(ecGG8{!tgcTeB+=M<06x9?My-1EZZf8R;!@7%qw(3a01vYe@ho?aG{FCh>V z)@3P>1v0d1(Pfm>E030==I)Q}O-7Y24%?1)__o4QTk&tloG}ygZh^bgYOPHW;7WO{_EUO7{I6W5_BiXE9zkinL6uYnCJQc%dWgD4mL z!cx73l*BgHYh*UbW+=Ghuh=eY_oBPL`8(e0reMky!47x!^|0qF&#kBi-@yj%3EIul26y}@Gj#5PPd?>>GGSrm z@9J{as*kHxopcY5fo>!lh*wPb$ND8QyGM`EoqjX--MwsW6^vPeo_t51b2{mPBI{k= zy>l{mSO(hI*4PXv&O^U*T^235c8#jv{l=8Ktu$*Ny3Y(9;gf#Jjd*m(nzL3MqDs0~ zmSQJ1+P8MT8rcZj3mUl;R@+)TZ2}67 zR#y4?)$MqwGw%&Flf3UFDqCC(pd*-6kww3Hbg|s?kfvrCr&~J}6tzP>i zZ8Xm=hPmXv_UDh5=hd^gn%2Yd@5M4CyaNl{6@>Yg(~ z)`p25IB8n`qj~YCrr7ass@1*|gOjUle07qCVgvC~PZ+b996Qj=4@hoR8^;uNWs=WS zP8e@3Sv3O+{t8GAE6AaGZhE=Dm<|XeVQ`}K#`!)+m#OWH!KkN`ibW$yuqBg{bwMaV?Rg!GpCZGX8k zSXy;E_!6j!BXs%o|5w`1kk$a+RVohzV;q3KBdM*y^rg+%uB=5@F`E%-TcmTwRkx1g zsH>?P!)4i2GJ@+rAXP+P=J80Ic>iRlx`g{qB%d{0Yt~Fqanxv930DGGci*b;Dt;)7 z@ZRsJ;x}s0O&`*zsb>;$%+6OZf`F%Jc_GKz(lP$9ZEKB8lp4mVrkAP8MAbq% zxXP?_jkZ8vyT|k|7N50|uP*T3Jl)zbu5NAVob7E*f`B&zx2ovIZ3=3;K-)S1bDIxo zcO&RMm)qLz?!Qrt+&KNnXuYv!;Kay7l+iPd z+`ES7?*O}|$=Y{;A-F;=*@C3+l93Ixw6{~n+V(A|`!+sgm40YR>AdK?+8nu>+B%rg zhlO0cQSW5SB>UXNe{b>-DfD03%ibN2#|%<=-`R{PTdEP)YX0d6 z5;>h5;{$zk-`s!pTW>m3-bMgq#Q6<0PO4^Brov8bsIXh}N~Iz3Ok-52!P25HX*OLL z@mI!NEIMRi**55ga)u+)j-YGNV{_T4ZN+WQau!kJJrw6}b#puJi+icztye_gAZ;|Dc|eNgddnJ)q@9B16O6qQ(|2G8aCnakThPs zIx!^S?Dv2r_O`qMTu?I|9khKL^uFGPqH*z0XbVkBl%Q;J#+H7mqg`t)1L7oEJ(J>f zN-9_K__9FfRj+(3M~lEESR$)RY_BrIV@O-0!jqOZCb}vbTcBfF$~=e0#?qHG_2`NG5DDc|#7;%^@NwuX<) zn4yI^;Pk&z90Ub3OvmQ7uT5prdj+&J$rf`#{`F$UNxQp%HsqFoR=Avr&H!gVgY_r~OJL@6bErI>(J*{75; zs?RD6N}UPfGh@Xy8a^tsDSPk+$=Y{cU4iMRnF|7x^~3T`#|4dI#+mSJQpC{hr8E@$<)vjCkl8^crgN^r3^Aq+ z8&@hO>>9U&Q$!GsK$GwqFMFnmWaGx=m9bp)@}iao5t*F*jMxGkc9TmQ|M5qXKm=u! zm7j&o$;rAm8-+>(Vo$LNw=}i0cv5)_dNSojT zXbv+FNdtMmE+R=_VWvjq>nR9V7%QwV_t?-l=>DRyaC}5 zs;wDUxEC*ZI9^$qk)7!#16|ep`7P+?bbFvp7TUDTb$;L6@Z<2&RQ-H~g15Z&*dDF{ zlE1;6I{8rheEQf8yN`=~b$j>cKDxdDfb`&wkVN)cHOuAUdcAZ6HoY;nmy%!JV_LBSt^Oo&6S zBn*gfi8HZHe-tO&Ma9Q(4wfk;LVzUg1Y-=iRj`D(&6p!FDUgvc>o1hj+!5O$s6w?E zU_zJ_|B*(BN|8d(4GG>htH!$E@FZ_o&sW&K`&>xeC1B;1TPD>GJuj3_uasy|UXXt|B%}>_R9&)-t7locgdjc>J(NjQIyd4Z#`mG0 zC)y}iHvy3O#{d*z5@}->^hfFKj%#!1kOvGg{yd}WXIXzGjzm(Ktn%TT;P62Lc_p6> zy`FBR+Z6|)-T|`!gos6oI+u2xVH)K9Qvo^RKU@j2@^67?B*pr;{U5jqHIGsmGawkN ztf;*z9@%?+xxJt7*XGt5{+rhOy2gdILa{E$AT3sVjCt@xk5Z7K1I@1_oQYQBvqsOU`A5>Z0Epwai*tVMy@S!ybWm~ zH^5f{@pOVf?)}vd)<(UKFwsCxTb^U8p=DVHrQU1?C?yf(aoROI)4bfnb$isj5H(dC zS18^3*z5&)04?H~HL2BN=D&I6w2jnPAN-d}EfaIs9YtmKO29c0D*irdou5d&R$j0|7|>25yhrX)kn5=qIR5zQ2J9_u*!q7 zg(4#n)KCBgNx3;lP~wi7#6G?BKWm#E5CBcjgv=jsyL?n^(I#@&@Ywdw|Jj2MznWWNs6=BNW zNtRkMMeIv?sIp(PqvhU}kjJH!Kh$|5pguyVRC zkKhFr&%zcL45ji}OfFP|Y5bOxlJZs-VAW6%VeOv|RgYBVu5fQt{JsYC3D78kZtfEi z9Q)@e1Lo{{z%ip)wH&&?r5MGJ8?t0zNDk-K?}o`k6Dn_z*uFTxmB)r)Vhx^Fnyk>0VnF1svzZ4AXfW|79 z`|oGoQXqj_GKKCU&*jfJbrL$zJb9H;DrE)9?liVbEJ-oh6P~O@79;X=0bKg%er6)Q>6so1l&|}%4fJ{n<7yI1GT{s?kPinRS+J86 z-~y>$iKR%pom&d^{^OCTQImuPW{2?u45~jE z`3scLj%c7qhCK3-cW_4?#eVo{So$yq^zOk;Tl} zfW1=mp-z;hi5&VV-{#2&SI~u+!6x!r;iua6zb&r}+{rKtM@XL(9_z8Wh&65!CgVss zs4W`aicHOsA|-P%1DN_`E|sSNp*^C)BoYVlqszLiPR?*B-vT|l!*h*CjqiOQ;wQ&$_WX-ji3@$1VX#-#xfi* z&7>H8sHNx4bM{0_BG03pp+nwGQ09}+7}KGJXanvbW4b?|lcoz{z#xE4paBs-vJN*% zD@K!+NAyu`(ZLGGQ>r4-1E0E4LJ7?F->1!73GJP*ah5eyqSDD@=b?cHgJvQ?WERTG z+gu3+kt?+P%P*ox7CnAd53u%MC#6XPZFNJYI?2hXWr%fFA##-bR5!4oeNhlXzqI(! z56f-!z};~q z#GBMkkC(jZgxdXKU+*b(+U%xc=f1JqiO*B-gyw)VyUtQ?DqOse@Sa?H3BAAvSBRP1 zMo*VgK3<$Qgoq_a}#cn;)ChE+__00N>&K0Y4cBTs9uv05PMGnxwOJ2 zuL0_pon()PZAXwswhgt4vBXx7v8V$TLw?su~d87LY4>x|11N&xpfG~H6O&=CSu{c1J{qr^SH>?k3FL64S28#9CRbj#vV;bK=vg#yjb5rZip;s*E_ z=1eS0LD_qh_4#`m$^hrPIa@)wvB)jl9aKiA$Df*oAyvB1uQ_vY5ZL0FTNdy#Vx*1` zYpS~u03F$f9|6VPv`M7JzPxHm`lq%(Yu(yPC;&|;U`#v+V=JspwB}lBrOt>cCqz7^ zm^l&>vi>jV^Nq3-@?&%b~pwd?fO6Xvl|6t+2*Q9w5+WO-bjhh$SIae?+R` z=sohC5uHN>*$xTX{Zph?`<9uG z8?T`w-N8(QOm`aXK#@N_Qw%ZZ8+@nzRXkrFChe2MiU#_TVbQ9sk z`U8+(RSBpigQ1SuT=87VrOoYs@^y-2+?0QX6A(xgK1hj#yW(MTx0+8G;Dza}K-z~W zoh@0BKumCh=AItcHPV-kD&tH~amCi|XAG2Pb$&L=-2OIkz9&b;p}PeVOP#s6zdd8a96EaVUi>X@{jG7K_$>X|2j|eO@mx_HcwaXM-=1(4a~^s^6di$bP%zjM!|n6d zuxhP#Lu?`p9(^+!n#OUArTkU8EPvOg45%*Ms0*zL_cARjJ)(FR^9&br=V+wqIpQmq zQy(ojt1C2F~MB9@4ME_S_{o2U8!MnSO3TyGo|UO@pqQ;@jRe3p?FWhhDs zsF1r*&J~u%Cy=kH`26w9W=L>m24aHn)@qBlyMI$|YAxIPcQXL>7RVLRxvN#9=!Nkk{g49&2C6 zV5z2kbDuc}^%VQ^VRcL%VBI-F$%#Zz_H;v@;ooLfu0j}euMiQ>)}ou;f}Y?D$^zMa z%tJ2~fWB#!ETJhG(Z7f0q0_sS;3EC`a4I3yAAuSw$e_)_w<0Nk?qD*Kf1Oz+ph?qF zd4S{+qMmTid>9-;%3=V`6#|hvMNzVJtKOG`NOGC0y|ZY|L&p6-5DS7;9}XRxgBsk* z$kg24fpGapt$+iBG&E=)wTX28Ng54hmeUrFs5up%>yz)FO6Rp^s!(PiLbP;iqHmUs zL={C3u}FbPn~mhOpy!=nSk)RVl{o7Y&#-FL9}1BKW!GA3P$7;6N#a?^rYxV$(8YW8 z8CgEntHk%O=0LpNA4MjR@hA$CccvCYil=8R(B31*PJR813;t~jdqRC{f3GZ5P!l?* z*vBE}lG-hO5-&VtYuWr06Qdr)Lg$zqHFoedx2akUFR`BaJsm;M87NO>Cg75 zEE%uTe3rfU>c|fx=DXm;SEaC&N>Sd1lzWE+P+1F9THsip$EZvGTSr!VRRw7{rYA@} zZ8|cG%7}yoG%lk0TfpeTTPTQd+EW`nQx5wuM=pvM8{RD-4{9Kx#K*H1Xbid3tPciB zXgBpN`1`%aW6(W@3$QktQs!rikWFy0O@9o0*IiB<$BqB^#{skb_HGaMG8C0s0Vvs3 z;7p=Ms3Pv`8UorS^Xsxru!*M+pP5_hZF~r332)A%i=U+Qa3h&KFqoo5gf$=nv0@aV zDWafk>V%F%Kk1A#NIUhII5;Jr4Y=^h$#nJKE)o3J^X8R+9P7G~v9{W3@~%qzA2d*n z*`j>rJ0uZeh7OrQNwpb)(WKXa%)P>koX#UW#41R?llt`(#%UbPbvFv40!f(Fo-F0H zWN4<+*hbke6#zkCa6I_yS3rnOo20DSs0OdHW=G1hb?B~L%8D5ObdEj5jEGcC8TM`x zgKM$!IZtiLwD%y_auRb)Q?LFo%~=co zv+cz)u7KzLvH>u=39oEX0>tRv&qr%}7%@u^gaQd27zbaSgbyfI6an^U;d7#LqQFS+ z;v5wQ0>5FUbF9RJDkGwT2 zEl;C2a!1c~_C#2(t&-%`YXdnSz=t=u3<;z&YdEWzHP%RwW?7LZTts!ZPHbr zy-uO|mqj>fM*(Fs_KVVh^hScn80OxcyWYO0GInm3c&q~|+C;@aEkq?AOKgx2I;&wu z&th%N;uoQ_s|&6MYr4m)G97n5y$R{ zhPqLY(|`*Ros=@>KqoZBx{Z;sz*c9Jt}sB~QEt67G@HQH>S>GTS2~esuf?nk$7S+v zHYNOIQU&c z&5oCxl9%h~u^!fmYK~2ezsD22dUk~}gEBkcfUQ*cWUUd?R|KoU_*F-*3oaIpV1$zr ztupv_a>##*rQqPO`yT*i^sI!5QdN4|WKbI1mv$>;QK;X_=1>|#XyUaTm$(wzdXMUWGqAr}PzS87hsM)XqbJ6}xq5&?s_uD435qdN#yavINJ?ps zCIKr+f_lJ%1^R_Nxd2TCVOr#sZ+apjg~csBvvO&|CH6^!nEp8005??mO|=bKIwFh& zks~XRD0HoeS=1^_X!k(|A<%7iwU8Bvz|-4cZ&#zPl~O`v@N`l^1w~RTpk+H*v1d^u zLpe-V!^tNk(uaT4?Yop7_;hA1V^REV0Xx)j)L+b(cB$%xFBc+qSgNhYiN|YmkrmbL zd(eLv6`tN}9TK-vf9KOB8lG%_jeU7}wz~7h+u8AS!~QuT%gM+P%qA#dVVP4i#H$yn zXXKQy;4u64VX&nB{o8}_tvpc0sFr_m7q2f~M2V%scYBunAJX}q@eZ}O)5xJQ$|XOP z7#`KBA`3xhE;EVou(z#jA_bdmYy>z-8=nwcmvEv*^J~e%I4DsEmU@YAZ zfwMdLCp$jVM`iJPN|Fho_(PhpjxL0(Qg-O_pkBh{1Ql(4z7a#RPy74ZJ>z?Z&YxX{ zI5Os&G+H@Pw6VGqZuTpI**awBmm9g4fG+%ic>eYaO0fcD^fE4ML3bWi718OKmzy7> zVqJg|#yae$mU??3Qlm)UL(D%UGr$?e%``q-bo*)3yk1B1C^KSZ&uT`(!AS0JOe(O+ zR!7-sbyuLVfpfW(r|bBq8p1!g7Wez!mBl|FFE4m22mE|KucvdgUp?Vwk)=&fMj4Y( zTt=ESxQW~tmIoHnB+0b*+E(m1RaT;b`aq&WSuMW!ojyl}j3Q%GZWV$}h{ICj-1(Sl zNC|ZX{~}J$0-15FEBA?Sd9KQzWwNVBjAtI2M>7o>4eWyj1`*R8A^BqLJ=NnmoW{b2 zD91!_))i%e8>LfWRegEPQi=&uSk?Jk>T_A_1d_3y@nCG>9ZA>(z$$3Sedt79 zuIscE*EOYnx$m=T4i13^$7Q4xM(93tahQfDsRj;H&Py?xv12m z^mkSoWmlCGU{C&;6p4=+D-4Cd;lCY6aW{Pip!O4)s1q~Jy#)Q}``QKkOo=kx%i`2S zZbKhUWSGqm)w@7!6dXa*q6wsk2V90XX3V~4el6uC%mPywj||!hXRCSE_m!s}E)a>~ zVV}`&yK(0t3+(c~Erx%V`BQ;I<%uH`EWn>%|JD~|ddlY8-{~2fH1yzP1t(;B}hdVIZ>(Cg&1D0K&#dVUPCe& z3NuNB&%XruIT3UstE+fBddOMA_>+x!(9qHp4AQ3ATGduNpVYDO#OvI|-BkX$cy86$ zRw^OMtVE^jNnk-XodTgdZEE>Yo3KDm_v;NsT7xUhn=lbOT?q!~4-Ylhu z=Ox$p^cIc08_0JrVIW6id5<4vfmDGN)$)^&9y4S3dw4{+?iV{b6m0@U@{p`lw+xrF zzT+=lkwK3jC$I-@638XCt57g)rN&1keQDqr?1i* zY%I1eu<;M*^$c4FEg&M!c&8O9K&{t~xaiDKUArg9G^N~3C9 zWQJ$%o1Y$@u>pIUhXx}}qm7j*mG@VMg3;l@EE51de`S#hg->dKpk?M4(gGt8Ba2RF zd1ua*XH;{Akfh%oRyOJ5N#}+@uSkz5+l7ATGhoGYf+ zk9uM}N*At#Af|<}<6)7&>{O+b%(5y<1Fz%Msnm&Ct7wyJ66iEYB3ao8o?r}-qOk;1 z<6D+AUcm*{=_Zqcz>2tRy3UFfy!#3s4hoB2L{KG2+&J>f2bEM%MWptlk*9kCgX zg#0&V(RF zr-{wV=EcA`jT09gUfzlb^@s&K#Ps-wh7n{F5-PeoT3z8s*jzkuZflj_c;#t?Dw3R? z{~{*sE7&dU1vXUt=7sx+mr9(WbB@RSPHqNXZ)et`1Gq zLHt#^Ohgt*z$8@09ElmhH)bd_bPAZ6Y=b!{hjxM|SCgOX>|8rYkgk#ibv1t@h3$MTOWvE%9%r z^=Udq)+=`D;-nGL;iz|$eh1AC&5sTVb?Dg}X@+4|zy%331~R1>35hCrk6%VNyBygdY_c4E@naxniU>r#k;)>dEaw0C( zvndYc2?O3o*uP#ON^W{NA16nMU!N;K4@;xb9xqSRvSY?_wjbNLg?Udft;!ScLPh4Y zCMeYjhY#D_i0_G`$J;R`(@05FNkTSNYplQdXvu3@HFcPSJY?s)*scH!g`RY%3e;i- z+k7Y;sA7*8sF8N+vNiR@k4xr^OEtZxO;md&-$#ft>NfBDL3uyoRxfl-m(h7R?RVVY z=@1oFu#?c1@Kk;noW`gImJOSA9RAge2P7;p>sr{C1&emjQhNQAe4O7jhp_F!dtzlLzQ;sE-xLCdloPJ{xe~e^G;54uuxRUeX5531RR)v1Y<8Yg=)id_Br0K zT|;K5B{Og^!*Z0{Cw+3A?PzWjB^m++9wI-i#vJL2dbk|wTGPG7>bBfGXI7Uckl<4& zS@zuj&49phFF|*(*9;E;&gNnwPB^1sTq(WBTctsh|RT zc1f~Ye=;ZJwdvHdS+xn2D6TrPRi3`H8owfXn|K!NCwih=~7qLuq|>QsOD> zvTfxp`q+wDlxMhHTn%7hlYW|;_NEOENPhSfsC*hQcrwI6mYJ;*wOs2>ZQu;2y26KY z{aPhD(tLT;?}v!f!vDRb#g3L`UF!Xyj~gBcN4YABG`TVpt73S#ysa*@4>5uzI_BAV z>(-oKIXad!T{X^jNmz#Borg@%n?MI$ESOHjTrdz%Vm1T_J)jzB6)SC(XexEa-Bbli zE!vLNy5c|+cfo<|A(yFL|D2(Dc@X4+pWzM_r3F7K+_HZ~5{flIIpP(=VqCoCu`dnA zsS@)oPxamRIvOupEVleR_rtUBYvZ$r{XXl! zXXQEv*gFpsKGd?xxw}cgxWOQ4&f+>258io9r!wt<$272AR>#+=^Ij#M`hLRhZoFld zMhQSnVAkSc*_;a8=zebf4|p|w86D(};H#sa^46JVcWVvKROkoR5L?v3?Jt`$dW8`q z@qr8Ex}tf_OEp<1dt=^z!SSV$16e_KCNIeqJlFCF=cG_(tV@au)0|9^I-?!5e1O5x z&cotkn{6|dVYJ>5V3^y;bC43iGi;2yscOi1cBz!UV6=rh%aIXJ8zg@ZnC={THBBPj zjAw4*jBh9739RT}&z`!9sP_;p1Kq@X!ZxK&z|W9+iPqxZOPgWHF!n<{>f8N$%2XH_ zulL=eM7&2_TXD)1l*sXA%Otk}SsDd%+TDQma-5h>&YORms?l8Uqp%O>E(=z74qO*q zmiWdCj2lxJ_H7z2A#*_OE8i2C^SyX`%Ze$k#}eTs5;iLfCDKN%jfZB!mJKm1`ng@? zU2qqgx9|RI=-OIzGLgxk>-xSfuYHqNzOC4|u5&~Wjd@Krz*9xoz~5NqTOgNJdKq>b z&DDLYQwnAL)$CwSkupDkql8Fklr3(SkUASVcCj3hJ6%Y#ETEkQCV;DDZ1pTasfx4X5YW z`B8p=h1FgNddWMk2J@h$WPb#qPKQqT8ro+gst+(FxF4L za8?00lWeRjS|aHxBXY0ag>yqKx)%MzeTZ&WnkfX$+9$V42L|MAQ;U_+g;;u2 ze2|=OlVF;Y^e>%8WYLZwTO?3heb{hJ@@8t)R>^i4DV#_`)a0&WyM zc3>o4(F)W}@T^9K-->}#Zf|6jLDa=6_8I&YM5pQ*i-bi^1C0JV|Hj4!zW6tM?ss#; z=9}L0jnc`@=$q&DXP5nV%C%?PLr-^%%eAsuYX~H;edndfIxP5I{Cf}n>(9pn`Pazk z&)!3cr)&3jE&;@HFQ6@d7X=`gywcIcrho?_UF*lkrPYCKrUw6a)+&dDKI17;h8Rn1}^=Z%)-9mNO&I|X(i;~%Ymd*1G-p!f2V>#-cY0(dp%FF)#Xt)f-h4tuTgLdy7NK~pF-9~4 zBBEu=o`V~aJ zgzotTR)cNSJ(;t!*17*f);UCJ@`PWyY}@LxZQIpltIM`++qP}nwrzBm&2Q>A|2b#o zoY`b9au>0PjQigE+~qG?d1z4{IwPcEnZit}3OK3YUL3RQ7^Us5{SeR(j}x%kZQfK+ zJ%i>^?1#Z$p$?bTQkK>3`rXVr+{j`G6H0I`;M0>t&vd(0*3%Hv2?!tQ=@vEYRg8(F z(xLi)$p1{NW@pzLVp#FltZ~P%Orp9}A9#GMM?C9fVvqxMZuD^Q(L|A$T!nt8!ygLV>kCPt-&YyqBP_{6d5J9zmR?j#TXmUCg?fO>-guIg#aCHe=`Pe<68k ze&YGPwC;nL`^ij}e35z`L5d&m1{_zN_(%M`7%&tVid{Uk1sm1<$EC+X#+PNR-~7k5 zH+wrH<|}S-8;;SN0bQc>N!htJ=1w#epWPxxs)&wl6X%`39&Acms0%(dUiP&L<~y4s z-7%vJxnKL)sLO2yr@S)goqrF;aG$ÐL*BLQ8aDtg)~9MxIyk@90Xtsl{ElyBhV5 zHJia0rjhr*E1|6%+p)W#FyKGS#!m~J=k_qoIIK*biAye2Zj5I8{0u#CXPxM@)^Bsm zP1tdK-E;(++yi|5b2#TaZ?;}KVJ-Mx&&3KxoaXJoV+J&n=UGFLb=bONkV?ORUGI0( zgpBL3x>XBQ)_5M9Gxn-7TIZR=*Kb?}9als$aL#{=x-rcmb;>x$Hsu6zgSNc1U5fnN z*hW4 zs&|e4;upLm>S@;;-c}A&9~1wjjl*{ctm72FeQ{l^6@|GzP%Rf(VHHMNW04fvY@jL4 zizy%fIS7JO4^Gd(LF-}$XkOpMJzNN3a%SDIkU68nAK6~_`~Wdobr+@ zcQjkH&ziA`AV=PB3va@3WsEp;ET(`>S8jY*uWx@|*@!UwWQNlci|$t-^tW@3e>0V? zq137|VX$+rU@P_d;zQmOf3OQe6SH(usP4gs!UuVnm&P_+Q2TFAry6|W;kHXo(fOxm zN}FVMGgJ>BzAC0s2^cjo}bgCGRKQ_|X!rDM_dlrC_{S z5O!26uDZ3;I~4>E)b^c_u+5sEI+qU?O*RHs@y_oq^{eM<&#^1563fU~IaH?Qq%d9%B8gz^r%hY%|@ns6^2|4pgZ5ws-+)vxu<&=&N_RJ;QOI=7aI{Hl9@8?s& z8)VJ`_-_Hp+u+uNI?4|AaFApzOi5svC%94|6LoMEq8%Z0s14G35Y3A?7(*il|AOCh z*AmG+ zKV^W;7Tt8I>{q%e?L1m=_G6bq}97=*t~B7*5RJgw@ER|xJz<2 z=vJWLQMr1_NdDj+WJ?jr$;v^Ikf7NGhSV@z;_CiGfwK5MA5e6;JZVYZB6aPPcQena zDHTX0LoVKgNST(&64^BF>DatYGc3FL*rZZs)2`EHBib~M)1!M~mKvx_^qLtV7wT9* zr;A`5{L^*gs83lJsZ;F+yfa*(JTn&ZNo;5-2)yk?q6O)M&Yu&n1rRpk?7ppRMJQG9 zH(dmLZkzY1H^XRKfzp<*YP$t=x2SDR>lx6~*w_Y=UN=D)Qu~xI^y-r>xUO2`GTlzy zu>aE|TT9c@J@h0AIHMu?$2$8~*IAqJ*&*dHmk-_Q%~vgR(n+G@uKsZUy|NIae7qv- zx@%MMEv#~i^sG0a>rANZ z%dxUy9DztJHvQBdKG0NAL~P{@r-~>3g#^7Joeq#yLzqM*6T7jZVz>1p^^ z!eZ3?QI?c>j`9W-dSZ~Qpfb}!+0E(r@6C9_!A=S|9}l-_ndOP&&%RFx=4=X(CZ6iZ z4?Gs>3BRg!9WY#C|KYwGLFSnQ7>BG7=?4hb^ymzK6>^EFqmfS7R-YZljK@9dy0ZaJ zZ-R_Hq|<^Fa0j$?yJ=7)EZ3969@LR$Unj4jI&Gd00X{at$D{4V4yW9I_~>7fO|(Uo z+69M_HZEU1F_&bX7MrV8;~&!zh@N+Rp4LtuGlJ=%xEU)AhX>cU)z@yE=$k+?0S)1% zd`QA~|HjowP|ICvU?%^&`_H3J9;Jw0{UHwmS3syOydZF2J>hB@)wUdj;xRHTa0+{r zv2^|7SlbruwsI(?-4YfxrdKH=E#&HnR$ZiQOnDsh+v6Qx&=PwMVO_U$b{-`{nR+kf zP!bEWIBN-4n_&0jRYs_u8KEd#6IZ5mh0f%6APGKuD&9j2VDvnY{%wiS9Z4NRX9GD%C|PCAo+5bvBXavUID^o(P| zc<$zX(VD-+pOGYi1~M}?)JRT7WeTHSKv3~ z4aDdCr8J3`faCH^Q6$(3!qWFJ9==}CtuPz7j1usn|KfDuUScZFB&p*!Bw-1b%FU3ZoCFlEQ2OcQ*6 z%zR8;Jpj&?OEd6=XF+mI)SSW!Oge`+?DqONVeXr}ANa+K7S0!fAa4QYtd)QtcHH-_ z{xbn@H^3ZlP}KAF)ffHzBL3m!M_lw5%l?bXSiY$*ntn&!$KEf|jDZMXoL=Hz8b1gl z`TZk%BnPLUOBEWG$W%S18QpwtQ+P`U>YM8@Vw=0&2`=g0U*RSL>H9fClF-EFu}cTV zB~r^sYH|bVD|z$GJv@HuxHy;npj}n5J$&Ug3g2-N!_Lmu(VJy@eRu@MMkfjC%hdra zsj^SVnZ=NxM^*y$L|IkT`?^q94NWRJ@VnhU--izeTW&XbyBqxU0Q8$T_&h& z1h~5u10VPG5Ol@nJt*lyAS(Nl*{30J*5{^A@_<)%ApgM*e{=%GFgI3>BOX_Np48Ev ztwu`^DB?(in2D)u+VjNY$8==r(tpXc?n=k8;V0}BcI%V~*3q^BQ@j8=7?j1}>`k;f zPI$J2vtyL_oZYWAVL!w^0l;NO__-GwdQ zh;?ixtOLC}rmc)Su$5MA2fD;S6$iQxd6nCwmgpxVUiSlAeb|b`8y5&Ye%vHK6 ze47bYsFN650K0?q%Zp`MU}H)9`-9iaVUij9*rMBz(CWAkGVc0j?WAw)qe%PhhCKJ- z;egL^5YuQppXG!}dHD81SWbr1%*fFW3eClR&dVV+a7se=3$9|8S9g{oKi0g=#8*=k zzgp@R?h<5!xob<#Uc5JvmnJcuw-eFtenj!V2^#Y)B%2h1kay`!Pixq$irH=-RxA98 z(@GFdI4LTIlGu8&2IKu+>gi>aTJ}gnFxpPO8e5n%{lv4eMbILJGw{T%GR8I7?iAHT zyr9vqRI-8Ti%(H5t`i41!~EhvWonC(a3lDu%MCiNh73aq&N{Og7Sy%3p1ES0>qw~U3Tmp9 zq@o^m%~dM#zJ8~zMS*!o?bBVma*Q9KuAr{HK`1lR(C_1vErhHG4oF6@H*pUp zp=mQcs|6`|Jdd`aZ~z}wvA||Fy34*#vkGz6E=0s|Dbj*=WH_}`T(&y4e@D+oLxxVRnY=wKR%XH41#wuxymFbuCxWwG&5nyMa4-fYqj@y9eid{cFwvWO~Oo(=jH_ z>U6xEWfEqlCb#}m#dwT21AI&YDd%OAqdN4EG4nBuD2BYICus2mTkaxwjQ30y0y2!j zg$nyqmJHi~arj_j_qKjSp-n!(<|4&;Gc~h5n>};`>kfE09%)n3+)9z|?Ad#M(2e>)`@bdgjuyzdi`qF*_ znC9X%9#MBiZYOd^PfX;xO(&V_c!GF{7co9VX%3ZFzLUh66L3L*OD9q8&}_I$k++hjnKxnMI^Y8biT(xZ&nxB}?WVk>e)RcRulh zRU&5o3_NTCNoScu$3v{Cd}=Y*KiCCLVqH={@r+q_$Xpv-(n=uebE}-g{7&@#VE<7o zdE`2GE2TPDdOtn#b|3e`sd1G7EsuVF0HK`k8@^t^e(Lw$@X}nOo5aF3$9wi(RHq1? z2TCAhj)?PRr)OG^^Wqvi6Sk+2ZSlyptDZ`ruN?0f$V*R0tgI&IDtrrV1-xnq-}xFl z=)<~dgWGb2|M~*FmMNdGqg!T+Lt7OsG5>Mf5}RcUo(L1a?-9|>Y9mr*8QetxD6L-> zbBxdiA(EHD4ir}7-|$aX7rBH*bnsrieaoq@6SVtMVGfMWlrO;*BVJ> z9yENMCQ6Kzudt`ts=k|D$z>6{a})i-Zj;8|e310hX5!8yI>)>9BtSXU1g_fWi!8w9 zId4I4=i3Qzcl7Ze>wrLR!~$hS^O1OJ_acRTQ84DYC7CgMZ!x!YFHU(Xwejh9-?_@- z_%qzD9~tjNP2q7YuyabdA?)AXhH2^gNp4&jzM8NTb)D0{Ca8T41N4*-{tbTrxDD=f z9U(`5lXAt)kb?F+NvQ@ap}h{0Y~YIw?F_g&BI$pfk}<5?SG!GS*2=C)RCXOf=_^c{ zMsGF+QgtS_DyECLTOo(!*x0BYCp^pJH2$g%q7iU)y;i#d4nRifGOU>JzO+a|UsJb& z+eI#yRMo0vE8QhDwVs^Q5ry7_EU)8_1pZ6&EvP$ifuQYyZFU!SF_zu>ja z53Uxz)Q1dh{^iFIF7=?ZP?XUOY@-Xc`Wzp_V-a~=V_ZhA>PP4`boDUi={W_kVC@LM zzG3)#0$4cu3I(j01Ju6z63R+PEF#*MwPn;f$aru%F+lHLTJUtm`%Jsp>%Ay;H$9=7 zFJ_d4pUVyZYPL|C^nA%wNPFJ+(Z!o%#>YqwZ2pjQD)Rs@gnOHQcE=ZgW0pJ{`6`ol zjV0Q+Aa%1;+u6%+YRk*S==Y^vUA}jtj8avWZ>wPR5(1rGTs>FNp{YG1c)RIA_(YXG1i~`Tk4d z<`^o&C;EfVY_GgoHA8OYjgM&gGTve1EgPpzEqicUd-zpVR5oX|QncsQ6WiaZGZySd zZ^X;?nH4ZESyB)9Jb~-~9urU40RL1(ze`-b5h6#0oI;;+Muoxv)dhg8dq5N?P|d;5 zq6l>AzW}zkx_SV9zvierzCQ>Sy1+fg$eZ-E>>O^*Z63PvVBn4>Yd|H==uWO9DvlMF zVHG`o#)r*a57jnJ69#Hb^%$q<$+^s`smoU(NAbRW#kW<^-jEIoED2G+hmG#PHw?vg zE3pM$ldWkI+_Sz`d*Mu?!$(I@aVreR5~d!j)aGp<#_6BeI#`jcFOigH^^F-+Ja$H! z)#e*-K4{Rv(vWt}qQ#lCN&)$3C(K?u2|Rn@!8@F-X{J3_*xQYh+HuZ2#&%)W+BL!O zENPI4b*^-XNpvRcVnsM)Arlb<|M$WG0*<{AU}Vqz@TR4NY=Yp`3_nci*E%x=9dsqutUfWi*Zh^W-A5H(J}p@GC{B zO0WiR1lQcqX=_=grP{ZsBr>)zKm;L7PV_4_Ev9ehR3Bwl)15Xdq}N3=W7DRcM&=sq z=4l&TKxu;L?IPb*Mj5*7m?pug6L6bAi$ODBmzOoF+X<@>UzOg)3@Zt_kA(GJ{}Tg= zcMz=e!?t5APMrdGp;;Y~)vGZIM~PUYonn4oDap5eWW{6_2P&-J~8dD z&W(%JVzW!TQn-Y(*IbdRyx-(Qb!(0M<7i@*42>n@&Bh>InZFm^g<}WYxiJYYl?cAW zHMS}=hL&ubqXoT&JqKptt7pucaq;NMo6(a^HrVS6e374QzrU=)+?ixr-#FE^q?s7X zV?;)yzf^>bwH?*OYNVt_)c`yG))1J_w>(OZOf^+*nbR%n3cc-@Vszgi0T!#!(lRe} zt~SF@3UbBuVKG`t+jaYx$IPE~X4J@mvtlatWI>*15_MXvJ2F|L+vQv3mh`tRvAGx< z6MHUgE;Dy7MxjSiw&ZD6wsMxZwQ77N@i&D@{Z^ZghZF`52BR{`#D?bXu3Ch(#LD4L zj*2)C#Z4OYoDHXoI!SqV-?G7AtSJsKh8m2YuZM=?5l_KYSyfK)0VrG|LS|?uwGgt) zHd11h5$M~qO?RuiyqSS|n!R{p5`Tu7j8#)|b8CIbwsPK9agb(|Kq!cq{Yj zi1HH!`|6?zC`#62G42-kegoT@!IAWFopD3OYK`W&LvhNz3&Aqic#FYuxND0mg;<@! za_QFMOsW!L`RX)CHd_X8Y+RVVgW1=$hFHbli9ef2rtm1NTj#k!@(#Vvl}Wlf#&Xr0 zbx|{`1=a`5R_)RgV!f*(;%bMlHOGuG&(v@x=thi7F0apTXK7(5rW%H&$422fvb zv2t6Yix?7hsgvPB+;Fbcg<3akk5*?+HB}~2w&AihJopdqYSI^JYZC_OimMss2hTf8 zEnCeo+HCH2K;3OAd|cp}I!+pK+s(kZJvc^M;3_J;kgVK>nzLjNE~H;&gg4f|2yVP_ z{sO_yZBFaXwEh`)v&O{%HX?X3H-6e*gX=C?JWZ?qONXUCy$J6F4n(2q(F^c;4oiBfkE-{LZ@`` z$g_@Se%0<7`jO)d(k}3`=dw0iY%9IY$4iQ{X6CGVwa&DH#$Yk;^l)$ux*T`Ok!ht z^2uYIDOHQFU6Mvr=&8Cwrb+(_%6R14UoZFeal->?1W36a^{m;yfu~?Os*W*^Rw?pT z0r>?Hq*^22ay#H^vl9^GT{sSR{J!XmPr##@Q$f(X7CH=uyKOnk9|VwH$PG?kTanK~ z5nAdM+#pmH%*Lu{g3TTn%Ds&2R(}uGINp+0YtA=ge|Y)h#olxztKm6nYgjx)TrJx% zY>r`T(%W+EJDZ)q{8g>OmvVlr6rtb^Ekx{KrGerhHqW@jXZq?5Bns_>NKBBRWgDK^ z5vF&w#4f^{J_*G&L$;1n7#6+WvrSA~05zAUw_RT)K!t)GpijpJ_*ET02Ngp&^g}P# z-|E`$Ukt&@Q6Im%JK(McuqVgMHmD+n+RYC4S`e<4{R!do)o(e@+K$oIcNzZ880+Vv zTu=~E#*%6kSgu-?Bur0nDBH){$$5WQOYQlR3+tAAzHopy$}d&`#H-1><%|b!dT}tLo3D{N+vF{ z$&8h5u^cOlS$RuY$U*I*y7Qc*j1vM|W5&6njF_WF!1&zUnYYNWqbz0>u!aYmc9%%+ zTO|j7CIf)>Q*RJc;3xPoLigQO46bRc_D$(rmJ1Jgg2^6(n}NOj zX#q|7-4wOwv%VJbid((x6Kpo+Lz)0td=5iB(sEO#wrl9J;_KUE`|!noRpy2y zj)Kye4^q(+N=(4EqvsA0>Cil6JM~nCPLs?c?E;o3S0$$=|0U!-0{%ogU8<^fLYX zVBCsYK0}2KienDlG050lBPm)Fev&*!8%Xv$saHmx2(*_h)Q#*doC8vD?9GA)i(A0W zf5%L$kXlllns%tV3XfwVJv&O-=T`wmQm5&gUCbxpy&?FGj~R;ROT+c2K;;zR<0Fn> zcNEZ*P$phKMnJ6fmxtU#tzO`=ne?JaLQ+nNaDyHG0+}fVEqP}>2j-|p zJ%l}#aElGb|4v4fuJ_e#Dfxwq;xGNIzjxS!7eAHuL$ahrjWGWu)+lKiHh^wFDSGzp zkT8nGR7uteJc6x5D4~Z!AbL^@HWQ8ZTSBc~vmZv=u|O}mgv4{O$S-=fkWqtCn-awX z&GXgGDLD5j;g*4s2WxkF;nJO*2QXxCV_5GuOZ;;GA;p6#GXGB(;%t7i!P)ZnzuMj}oA;l~kFy%UAm`yc&$q))KO~$M$wJs!K zL_a`nkT8@LOOj3<=y(Z#m)b#3X5&K zyg!d^G;7)XZ+a_lihk|&KbSV8&%47h_Fx*%9xhkXyH7um$bbx)ORO_eTS!fOu*>GHUhwj%qOY?apX^hFVvufWo{;P!Ccp|GK&60X-I_o4f z3_Zn6z!VjPG%5;gjpJ>$;LLc-E65!3rvu1&m+UQDW?58>HfE$zE;2vyH^)4BILcdg zLip+O)?y@WsG_a;tO(ASOh~TgFp2PxtfbVp+10H7Cy$gEDYQ9Xev8z=(e_zH8#tYq zPN=1~46_Lb;$U2>L_H>IMw2{t)X%ksy{th{MFml>_X7%C2d7a6*<^QrqwuuKq< z*RMK0gtcACr}xMyylKwoQb=f{YjbHQ!za<|pI={)&8gXQl9-M3SN_?2fsdRrei>op zfP={wqkyzYH|VJ$!GbWg*D_&gl`>NnDd9}YpPEOKh;XewSg0l~+{$7jm6;Hx7a7$P zlKW~c%;&PRxHXa#Wv@p_XPxGIZ!6<9 z`4}KfEFa&P)fNnVx6Fu;)x93xmo6w2qU4_wc66OwkhqZOLp)!EDf{Hh`DbzFd7DUgQf$NGu#?El0wCQ&Ml)NsL?)YJY4R1ZoV}?3WB}kSG5>(ZJaJ~lcq*f zX(9SMjaZ`bB$|c^aU~$X3Sf%DXB__-`&C)2Jg(~>L`gz5g@Fh9+#f1_0^n(~cY~ID z6JSv+PP_%nB;>9EKQ^b2gAv%mGOzOjcb`K$WH%uCCj@VA?UWMx@^o6X~GpB|T z_(bt%dgG*wI~1a1Cf{Fu*iXU`nWOWX@^kmLBVUF_6UmZgb8IbJ94D(C?hSgPx_N*T9VL3qGpXPEl z?-oRJ*6=8jJF9s*6?i7wT5p_xl4dY@#nPZE*-fu_)DdiLlkls4h~B0gvSr7K6OrBlcVFW;0GIo_th z$0|2SwCJRXtq(s4XRFvFt=ft!;E4NVpt1p=fPH7ApavHIDnJDFA zD<%A?pQGPMLM*HpjG7w(=71WmKmB8S?R0^it$h;noI$NU<^64KLIr~Y?I@izY5rf9 zp(Yn-SA|mcHwCb@z-a_^FuT=lw305VEVL4fus;Ntf;*6K}v}crfkMTXL9S@d_SZQ^}(xhr8l$(Ol&|*Q_xJp`Q?0 zYmZNxs7D^UhYg!2<$cSqD^KDT3uslSmSk^Ht<}g+vQ0K(Jj3&Kce#2HzOn3r^!$@f zttLuiaPeeC9*Ik*`qcgQWfc6D!_b9piCOHmZRz>|{}=?EOkXoR)P3tbe`kpbZl%Ri{g*fotD-rhmu-XkS@tu8e-YWsF z(SiaAC`Lt1+o6jaOL2n>EjU)`vS~_nW6owdBY9ZGc#AlqQe~26He?NA!fOH}=Aee} z@yo|Z4O~B{bPb^e`hq~@AKHIs!vm@k^#v<)w#XFS9ZtHJRn1*p2-#8cD6#;?##2$V zHxNYp9pyt7*u63v`J{M2`ER;vWs#%iAgB)g;1yi>ywY!K&BOXI3Ku<*OZAqj;P|)b zFw}-9q_!0YW9{1J;QZwF) z@NJUQ2i5*|v87~F*e3nja>WT@7qqTT_B7^cqqI_MoAJcQd@2NcOaB?>`=Pg+oy>n;idPKsPI_MbzcR-pb9g;Zl!T&WyDj2c6D zmnsuM$%H}!p60i7;n)C@36I&SbUbs~YS(~D0tkdj@ryE-%T1e(XN+>mjV(16j!BH& zD%%*Q-P&Tih(^t)64J|PKO!M?F7h}6{%$~j_Xm-tT5o1+s>iQHH{s(}rk*+0$)m^o znPdFJ9`+6u$~Vui`4r2r-ZI;AWtQS{f1CruQ3dyD(|;jw7P1RI)BkqjOU+N4%P~zz z*xH^D^?Cv=e14IbiVCnL^lKC!v;5wF#O~#;u)zEf6}8l08Wq9vOlihi9X3{iRKc)+ z@TH3?H=hezhoTh*Nzg57LJ3`)&`5-N?%F>V^D3XWxV*5Cyxd-OFI+r*oYpf#`j3Z3vayntt zVu+~K8>aDAI^M)i8HT~zMZHfrxH|9)f&FgNNsz)fnPb*e!Ho^^Ic_rfT6RCvi-AN47pa{93Li z#}TMAd#3(Qb=B%65igQ26tEcQS=iz3Ak;~t-_{`{;+mCPQ!Z0N`MQh;O4>V2V5ZYd z+g3)Il>UQOgf0CvUdqfx`b|c4;Z$9Iscstyak>iF`@b~MB?d~$Zr_a4?Bqc z8A2hWKUCm_8I+38awA#v+i(x$!kHaRzZd(U_)-&1NWmf}Vp>Oh4RfGnC496~)w}wT ztr0QhKS23b3(r2Jv5{W%3*wgtP?j(~fDED!eyQt1G`CrmLPCl>WQRKGu>6R2zt@M7 zu!)G(gr(DzB07ck-1xE0LYPHExQ>!TxY3YMsW*xs!9F21gC*;eBeq1SE1=15je<07gQbn9)%dakXl_U)@$f+o*_>9sbFGnWEvl;wHdK|KI|(N^I~ui15htFy8nq(XorHs%5GoRG=r1cWeV~5gkcIlcc(C4a>s1f*|H>K-02QBWQ*#2mISwK`c;Bbfwj` zN2szoD&=pfYe4#sP|8cmM=mo*3^gg+I&gixsl_)DyM-l8nVI7S9^&aLQSt?#50FG_->7T5P3I>As zy&l0CQmGl(x^S+SfnY$*b;79fEKXC_YgQN6{b>LsGF#e)@>yF;6fF)Z$qm!(Kr!=I9unat}wp?7flEs1LU$AL$s zBm*!6K?gzp$>0R5)*H50gUh|5b)V895t&`UX1Uv4woo1a?ht3@TEQIM@-;PJ!H}VN ze#USpGPC1~=V^1zSN__lDc7C!ahHJbmj*RL<=7CR!~yrXufn+1jq}u|z0ICqh2P8( z<5zIM)X2e^m-I0KfhNCS-vK>kM+az$Ma>dFLfmp&ZuD@{;)vxX3|uwCnRr6?fiU z#!bD`uDk6Sq~?HcsNZvxtIc!I(me9Aj*|=NB$+sxY;>ybay%_cPd=jJz!cg>^puX+ z-;gqm4Zg6`iWv~PHK|`9Y1}}`lTNE^CZ0Rgpw#bG$*0aT%t^mPl#nk`kC47>BMJ%2 zUWMweN7o>BQkf}beRq|0wsNz%0j@SAE&;cWayowU_PklJ()Bm$uFRcb_F*DQv@j@{ z-#J}lG=y99TZb2{q{-_b0S;o= zeU;k{H>T069orV6o~j${A|sfeB^4B^jf&*kcrNr4@Q^)Hkyw5ISa~RLeYIyd>gWAw z4*0J-#>tnzTSETz51e!pRh(w8?8Z_^W~%+{=<)9D7KcL0yBcvr$Hq z_us76Qoc0i(G8InnS(?iH~MheVfP)EBJh%l(6AQ7*a?3WkzEYVTyf2Xp#q;JMesU8MWZx7_7wPj7)TJ;1v|Tjyxg9yeuUhahJpcsfdD z%>wkA5Sic}<9H%wPq)1S@HwxF4QK{<@PqKEwKZt$I3O7RU(=t~kS~W&M{OTXxyBwF z8-1N_uUC!T6ZLskZAvW$Y;(=(n#b$x+||AHRbd0#K+0V6CabO-JYKAUac#H2Ah0`Hg_N8C)nQBn$wxR z*wISo2Z{u`@H9yph9;O3Cb_KvJ!W;PuR#m}*-IGG$Fl{{VNy7<-mmIh-&LPu z`Z93lbsAR7OLJ4HxPM^%@7{w_ zBnpq7$Y{_QH+k~*tMNT9oFBavfX~%1cwFikqBH{Wb0+|Kss z_xS3-2iOW)r~$0a@YXtMNbE_NWw;_rf_-Ejp@ZpFqTw zm=ZX=)-TCMs;XA5NbBjcO6jt+9(=peyFblAba(=8e&+40l~;fd#24EKf!*;V zTZ_z6r3p(HR=39b4oG=Akz|is>U$VpAHCgPC&rt`;6nvT_*KO~d0~TQ-uw3!9+Oah zb#}Hj%N-^P68$KGVe$v3UrF3_>dLQNli>qAz!rG7rPujlCTqE2bs#QHf*+MGB5_LB zSy2ZRdXPRp76^B{^0HoE4eSUD7wf>ntr#ImbVY{nY6Tq|EtB&Vg9MWXHh1l+)s+N% zVq%C8Xwe|I`+hFi({6?>3j$Vp7fy?n5>=8R_9u&{^xk>@-|Nc@VA$vE{Z+_iwZ{WH zNhGgX4P?S)ED1U2R$m9OkF3n4&zF4K+V!pxs}=3(%4nUWfOAR61B3j%J-(to!l*5ff$Ru-mhF8`U<1J5XUft zsoBZPgU(+6d2%=)>-5(V!Ggd)lOsmfJ)~~+g4BXocaPwGL<&JDG6b8diiU#B*5(OA zB^}R71NFfit5qjTc)Zx{a4HlWnuafDseN-z?6xp_S1x^*YHZIkiAPpH;Z@>v=R_)!-wvj67Gn#)Pqkl;7Eht6^omcy{bpRx8mfTl?ei^SQh& z(YS21Tg{UA=kF~_o;ELe z3=Pr2E2qy2}IbKcuf zEX&Yt>vG_MsE+Ji0~Ax;xzQ~6d3{O3e%qL<>SQ^u#*24+hj0hQ=B+uTycf( z1g)}Uu{(6c8Fb0Tyl#u=T4T2gs0XE|>Zw$*M0AU|mqB_X3zHnYsVQSYy=i)GyS(lB zH1T=JmIsv!!Qu)GS-Glfm$ud^7U4Mra&d^wm-{+m$zr~o0aALE@UcLw=5-0IC<|hP z{s|7AU8goZuRd7HBRvAzX-?M?3SjKkM<8o>PC4CG6G96wd*!D@8T~}@Rva=gr-AlQVFgbqJl;B%= z!fv9@vg6-gq{k2xA39rPW> zQ?38TaKF=on;pmuLQ9QW}w3aXGx_l{JUxBC%_kFQ;#%HTT-DC$g z;UjA!ne;>9y1Ah4L3XH{a{QwFL~KL*7RVg5i?uKgx!se$_tQ{(Aus<%Y*PWmi9N0L zmH%6-AHnHP*lAnlP*nj*nTMx)G2FQ+!R~x6>C+ZvnH+{-qUYYfb&5 zsSxk9g1nf}hRwB3{&c;~lPbLVO@y_WIZ{K70^eN&ksUqO?o=}ofSoL=p_BHb6NtVW zz{sxd8t}D0@I9mrxXE&#ooi7L9|4^_B~#R%ees#oxSOG04SnB&?e}{&8~W0}w#d!~*FD2t|!V)Bod zRtfrS4;F13Ov?CQ0H8o$zY{0mHVV)MqX5;lKh;HqfHm0H))slrk|iW!*6$}aO>j$$ zpOpc4bOWoW)`^$15=vdpocq>xZpyd!?)d&}Z4nF{Z)%9dD%nA6U!8$U6rc4md|c~n zd1h@!>(J^!@ItIHT=yL24hEUF^ya$X4v$TU@#RPU+q_;MQs2K@$_M<=$jt&qor^!)-Sh30lWjvJB%wnQYyy<+PIA_Hjq`fvNxp@`h2TXJB|A=c zZ}CTBkwBqvt11-gvX8dGdj34my7Zq#=EA!D8F1D4uRo~8e;o|A`rik68qR+pQmHwv zo&np^f1uWq36ufVW5ZVHyCv%7diO!n?}IAWnPp|5){OzO974VEN=%S6qjMUQ35kZv z*}XfxFG3{qwSmyEYXUB3_CAU~rZ%PflN!U!0!2DqduHa>Re)L^HxH zkI4`{ev@-?CcnI8$r)qfF>uV+G|6vD<)F+_atq({G?nL?MBrGbNNoi9SiQx`q_{!v zqVHKYq7h^q*Xj5(#sr#3K!LTFoY7>63P-ycz8RuthldA;9ztWTf(t%qqsEU&4$}8D zCj6EQGBd;byEuq!pKtQ4%l~=mJ27DI<^FfD|Ga+xyUqXpFwZyU|Let!{c6Et<+5Hk zV5~~E4XQd>`T{bGne2C^?5FOS{YO3`x=`=~wdGyiTnSP&5gZ*OPG)nGy=1c#X9RWr zfd;|gFzEaJP7@}OtjbLX{#eb@;}4~9ZIv?QMvw$%)|4wkZxuPjA6Jh9l*sZXJ2>#6 z_m6W<;H9Yvu@qGB^v#3We(QbnXL;^M|J%g?U#b5)tls}U>u>M>ALjW((f_X3v78p3WZXTw@VVJ0FaD zN@2jFkT&9LYa|;nvQRd_AY;idY}C5_k%m@BLIYYKg$R=wOE_siQ-w-ULdR*!n=mTi zZh(_b7IEK56@b^r%0}k6%WAPe?$CZL#jUnTM3jeYPO{r2o2m?XeawKqvP9rGWf4ST z4RK7e);O9fG?_G^w+Q_Dh-$On_M=x`Ry9NQCTwjR>!Wusq+l~L0Ktvh6Klq@h$b#e zZ9+u>-2fRnqe5MN-({~kVAjWBmdhhsGPx$BDPtcMmCto>g4PQ=no%y7kIs$0e8AxH zep$2Y_o5K6?lO=ClnpR&9EE6mG+J%73hWJ#W6>Q)MgmyB-Be1bT#!U4E1fXLbap31 znjzjDvwKBq6MR?{Sqsz=SfI8>N!>y;@3SVj8)0|10bFMs)xYhNo+q%vUx6Yzn zv`k%Cn_$8+Jdp%)9YIjnTFSf2kk`j-j$;~q3!!60!yEJj$UPPUhtsu=aczdV2D~+X zDh%bL)sc(q=aVT(gr-GHkrV7ANis60FxjXS&AC(o)2KxW@ ziU0Mp>i+j>&HwM<+1CH#VV;Wqf7t~<^?IODAz!@&u)6<8b>;tVHOtdliu^(`e_v)n z_oQ}jRHJWbCuDCH=vdvr)HM0!E10`o7Ld|Kfbs%g)?U>u#abgRv_;z75~*=$+g@!y z+VX}4&zkh#gg#j73U~$mKNvi%`F}ruwzdCxkY_<;Cz$0wg(r2RsZJ%;uGs?TVTMy1 zE24NWISz+!gcgD7K`5)aJ|*ItQX&X zVK>$Rvn0Zdt^Bs#$giwf3Hq`i9=e%^nBnL<9OEP;*~zKi-KBTU3$<3Svn2R>Q>Ak_ znx4UJP`z3As|in}Qh56r^T2rPGhN^E#&yTWr<^h($H^PwpO)i zXx-ACvX2k1$L!fGK+e;xQQq(T*-ZY!N;1DE`|rVi&HwY@V1FzBJ;c*YLiNeZC;*m( z04<&GzLsCk9Xwa==(n16MQ(032}k4=j{BazYBia4}wT25JXQER;N@-FS$g;$}ZO1<=&&Zx_X>j+j@?-FH}?ULOM z<>0Ed*G`N<8=|4zpe<;jVU%jw!WPGlT@m`Q4PFL>8$@Fbv`X2-?>$VcwZ1OL8MgV6 zS5GWW1j*(&E|4^DO3IuTHJ9rvn?@Fv>xIh8SF~Sxy$Xp&7;9q!shxhxoIx;eR<&=+ zZQ8P8j6VvI?iYp9f##}h;?`QbQ4w?Hr(LvbU#&YJ1+4oRjuF8b z$u8JOl6+5NXIv^DRt?YsI1Tu$;uj92bCc4ej?;N1{oMjy(mXIrRP>b~@P;912D9Y} zTb{7x30t190Fcd>H-Dy81uCt!Js9=w&z1<|v@T zajWVfO9Z9~$xd}V73U^~w31h(;p;RqB-uHrJ$bRCH^7!qsv!Lr*He<9QBGq> zIG5t^Bc2c*cwKZcr5sY|LDqCWC2@=dlHCQ z>OJ{iUKjn0Gs<#~PF@{zFGw>c587TpBZ7OXT*iL+BT~s$`~U3t=+&F!U>0rG#)|!a zxc{`W{|C>v=f4Mfx~M+i=tmOI&`Y@-Jntmo0>?3jyp!)63gZb$#D|@!5Gfz_dJ`(9 z`3Q``3p3${MYS7vUbl;WAOf9{lyNFpc8gAuG4uZZZ@!NNxe+l%&gB^m`YLNX)K-%hEt-Bvy`v zF1KPhWUqfp=<igd~C^%sC4w7KZuAlCS_k z4-%0WDnO!D0>Ny9Qr9gWD?4vXw&{L4$uKb7Mcs!2VxR9&62`33*DV6ZN|71A|abkN<6F-e{NQRy_2+;cp5wIsU{h-O1ec0(vi13TI zzI?k2#cgN1xfyP?^>@)N%QHlDbQ?4Aa9%?>H;H!>a;;ZN0lqSOf}#0A&=2~ar~Ws3 zBHl!5=lel_4{<^ieGR8LnUI)G)+eaFQOrg?jkmua^m{LVJbL@X@#}X#1hYuZ+6@09 z=WePz)>#6L;?&05^f`^>V!xOYGvJZ#Z^Evj^q9$Wxa_C~GxWSC=vBtjlte}}gvMZX z&G*rc`r`nx49Np;hG3)qn$wI#yWSJzga-UbZqYTCpXI)uqW~!Cs(=$XE{+!zJvCZdkzc#gwR}m6zZcO+F zxt74tvDjUj0bSKAUs*>52zx<7klxjfxT?Ur3eb1Ah!Y{~*IJ-OWqx?IhlHfpYHeN9 zIMzY>LH?JOl*wkHgoML@MP3%0fs8s{orZUZDXm}%$8f7>Mf9qB1^h8ET zZ-!-8d!~NRAn9Y9#Yz(@xgphd;K3j9T=hqJ>gPX)s3a^RKDm(;on$>AmTM<}YGeQL ztZM%;kj1y>zXy0K?xHvobc{oDG>U0R5>AHbD3#PsN)=6ol8=0dKI>~EMYj}ZRRMA| zRmrn>h#Ck+BNuZIWu?#)`yNhH`*RRH5Bgq2c$iV(q(gM1j<1TdNLo_BN2LYrO_{`W zDMme6u-nX*Bsv}E4kvN?NX~=nOY&#MaIorscrx!5_p2Hr;HOD!GLn~ z=@Uvbnusy#%>SKtP@qJcBg%-S9?qLb!l(^>{t# z{RgjG1QCSqz69p0d=4ET!Uk^uVs3s*FTE~$o6klhLu`y#9HA-Ypznb5v1BjuFDy^y zC}VLv!r@1+m|8~*2D2>3fD<9>4EkP1=2Uk2M+FmN5Deu9ZxXW+j)yR~7fc@LQ&6K} zLBUMuR-KKQn37B}T#l}%G@PQG6QpX_q6Y;44sP=ip->ek2xl3-mA(6<*^;gB1>xB{Tu6isz%#qG)Pgel5mMC(1n6H=VpIar z9#Yo6a>rNCfr1&GfKraRI}(Hjdtg_TgA^xkNg?E^ltf5OGnP-37M9}~G3iFoC>P}l zKVDp%o(En@uQ2qCgJm>SG007ahx!~`rzmBb(tKcrVn6}r{a|nif-gdRiZw}CLKI-0 zUQ;gQ+AQM1pfIKgXA{tu!lZ?bQvhvuT^+;_`M!v`AJTM6GN0#E5Wa_eKV~Xl^d2H2 zRIA)*zEj3I9mPbp$U(<5B52I=Btlb?HOsxtBhk${p2$&!qzgycL@h|!S{I$CB&6e8 z#7UTEk|JpBk76WPt}-S4s^YMExm~SF1MCcuQHKGQ$&_Ai=afaewgCx{+z18kjEswp zl%vvs#!g`Vk@xH{`zecjt5ND1knoD!h+dZy0eZaZ7s=*)TvYWOv$^v!Vng)li&yfx zkuT(W>7suTl=B?N@oizohY|*anCpQwY|7S$ArcJ51kZ`mnqj#aLzeK6vYcz+;grcu zBZpnif6hr3C~l2o&Ja!HKIQ~;Zb?YEAs$(tASSo_oMtRhYZJ(NOofPvhHlZ>Ga4`S zh9*>wd4NvFlB1)Dp=+F!dgD~f6Rp)OtC2PQm?p9ij(*8Gl$gmT5)u;5adr#l1NwGH zUDxDUd@HvujL=vToX5qGKMpL|88s1Bx(J17E;(C&W?sz53~0c!r*EjI`l;xRrq{Z{ z%qx{X*ndMkPm`$<0n)D3>J0SKL|FAqfZ)Fk`hBk)>{q|CVR&^}Wpi2h{>P9|?Ok#d z$0vy(*%-sp((ToPJ~hX#?#SYjc+vD#j^lHI!;h+bje^QD-{TpLsnQ^xoX($h_K^Jl zsa6^40=I?+c1|-gCYgl{T;`kzgwb1-u}|Bj$<;OVx+Y~NV#@9VxiOo)E=m|8sz6Gz z8I?k&m$-l$NWa%bM+G%;*0d{*PO(-w7gDKUu&QkKT8|`i*tp%UY-2uzKM?x)=r3g>nk<7k9A4Ucj$ zwF6Q0^Y+Tl>6*9#@h@UJ{B-sjSgpP@fC=VPmdBBt;#^L!B#W>OXfn~np=)-EhGK`g zI7yf#sY7(wm{cx+YLKZCjz@4!pr*zs_vKs}A0N5&V5oS2&eW9{N2i6tYQ7&F90q;G zCP!>eO1P^_h&Rrq0n!n@?S4R&a0g11Yzo(4z7+97w4753;4$cPX9jBV=&Y^|9Tk5X zrc@bDW7EB(nI5d>&Z5YIo!y!_uQO%mASuD&1wke%0(8Sy*egaLiA-Y8QR+)1WzMul z7jPJoRFJ4N-n?jIi2Al^z1T}lZZ0D8Rd|o-Xa2p$<*ZwP>b#qs{Ih z7aXEVY4K_I540cbKi7*eA=e};p$9Fs!Y(GsL>?C{c5u1=P$)`{`Wi6U3|Vz@ekg3g z%Dawk7oD=mY#+1MiVb5BA%;un({@J2!B^w~i*{^KAPZ>x1h>Z6?ZXV;0viU=Ap`vo z;%r7L@~Awr$c>bN?qW`|TNKM}p;x>*hmX-)NoS7@3$zjT(9WRW@Bd{NjmVTrN{S>R zv86|9kmUK)!HjaGP0u1UvU0gT9P?|c>{^wDOF7!9q+$i2Jpe9=fS~GdO9_{PaUK^Y zG9c2r1`GC7Tp6pWJOo5^JSLz3P-|1+|Gh*y;X+FHdwQ^7rOFe=h`0n;)k8SBMKMbz zB-3h}8J$dpI!2Di@>nky@BjN6{hBMR6|Ym)6Iiz%r~sL7R){`b^wt`0&_uTP}oNyZJ8VdJXZ+9`_=OY|rxUJxu4vs5b%6LvZ4 z!fo2bR}~~H2+47><0B7v@Hf0>%Ii2-EDh;t`8u-DY9UB9FSoavcfmkEsdT3qCHhGD z3mYkpBzI(a0qX7282UC?D6OA^xZ5+?VobYrQPjxAPU;=7%ZCl38Iv`cGqaDS)ZFRW z#5B6vcSfYv>R>SL(N;5pm5pKKs4E#9RdStbU~iZtxXG9e8;ohKmPN4%noN2n64vXQ zh8M{C@^lChm0)e=3c-xvNS^{!R@KsY*%jhUs#2w+gpkPGHPEEcCKfhhm{-Y+T1#Lj z1iG@3B1F!jd#71oP&r67D4T$Vkt8I&2}xuv5;?_uoKm^h_Mi5;3geID*6bF4KJew1 ztR8`MSgB&bE?%Ek`Gi_j;IdJS(v&!=*ZL7kZ%EfJNe-hZWOd3b0KVoKav@Jn51N;HLKC%`Irrv*G^W zZ-)VTMKei@HPPT!im>|nhM6s$fK^*3CKjX=c7anDvQ%-f)<+r8|4A7`Dg7vkoN@lY z|NH;wn-N3EY@A8T;b6GXlj11x{2E$P&wsVEPiAO%j9b= z@GRxV1sG&fxV&=O$%w>3a4YTUI;pxC0NGX_|0J(c4NuDrIN=#}<62Y*r;KrZx2cH! z|NY6nDKVN7~m`7@njEeVWiEJ${y5OaF)J!quQNTj!ry)`JNS}!1bC#96w zFjnZ`<_2Y^a^t{_%MG|vR;tBSfF$>LiZeVTf@EAX`wY*xOAWN8?(4^RF zzpxRkQhgCA0aK3f9Mc$<*_>in(3h8DmR=UL^zs)rx&&d&o-x|XBaV>(*cXV3TvCxz zbWbnU>xCM#E_&nY%vy{*O(ujF;#9NB%}7FDQ+-v@TWRw8m9$nBb(Lmb_oS>c8)ieU zUe;ADMr~tF)qUXCpr~2{)7EOL`#`l(OV#Zn<7c4`R?3y`Tu704a*OC#p02&}+ah1- z83Uh*pOpU{0(%zw+a!ay`6;nPWQYcySG{iiikGcl^Q!ghUbKFdYgYG?wTtwX>3-kb znc7R!!+!G>Yw1$ft#HtHZdtW!hN^R}CB1?(<4T}2M@m)ZE0KrvUsmu@r$^Y?TI*7Q zp^jFnk4DBM%12N~z@r?1?IEn-P>$@`&+!^FHeJ@_&Di2ru5Q)RU10tLoLb^l#uCnn z_PVEHB_SIaa~+B9+2P^AQ@Nq?wydv8S2pwI}lJl}sSnC+4j0>bM z+CBM0-`?wPcwKZp#Y!Eo%BC5U^aGu|DwHiy@$}?XQR$7^Q`%1KF$X(!uo^{bsi2+S zt~_w|WYr#g)q0%5?nZus1;*bPa!|Fs0JBGsW~S^K95!Lsa0$%1cj{M0&S7#ivwCx& zOwEYwVTvcPtMwoierYU&`g`W|N9QEI4bZ8vztVQKI8(^IWeM3sKQb;Zm^F@4boV=| zjs|cR0Adde2;^%?T9`6|fa7gqf!SkhAsuHkrP5)E+FWfGGZ!qw6VjWrIG+*T>%tKE zrsqoc0`E?XSHo&YfcqbDPA3V%Xd;OW+{};16kPIoI3-aYgNJP%6Yh1pp82TkEwt|T z$h?F%$oh-E$}^fw&MlY|2mr9XXgwy5BGO>n0WqXg79Ew% ze!A#;dIRJHF37k%oBr@0$A&-{k6vEnhW4Exj^)L{@tF96`D9K*VJ(M%Zioy!#wV+> zbgoL)>11JxWB;lX&#tj!miSp)80Wf3Y3?~IbfcPTmhD!Q7}j`mgCy6etAdIHBql`vo5lR~1mX=GoyciiDO35|9QKCZmqwK*D;aT+Tspkn6 zldRYgJ|b|BIYe)nrWr~NQS94e$r5-O_tT6rkZxkk`CAa&m{M-(vr8AH)gs!gx^(+k zFbQk+D?3}ZqgCVR`_>CSh$#Qa1r|I|0gEC(BH+IJ;=gt>Jo_9omVO>bpLeF8`RDm= z^d9@aefXTrXXJnW&*(3S{3qR&|2}E}F2yx))lj^KWw$SSS)MpblWH$CueA5n!1MKX zEf#LUG))gBYxo(s3Giy$TeE2XJ~NSp|DjeFhzxM8^>_29{-XGf`egss-;CU;1c1+K zD>}7YZYBBU=L*aETw=QoQ#hs*@IX*o7=V%}klidkRy;lauD-m%Y0 zMil!|IGGg=I?8@h8RqK}y;6zcAb1)a07`1rsEa?Hp$BIKN3`69%)I(z^g&;_AQM9ruEYybuoT5* zp?`2=0MaItY;|2>qZY$Ci6!cABwNrL6BTtw7sJ9e8&wu2LE%?ZXm`jg6}M2Dagrw{i zRA?OGxG#}fG#epK%&lYDDkV&FI5#SVnb#aGS^JKabY?W1S_I7b)KQpOC_}KrAzW}3 znlZD#kCI5`Dpq4skBeL@=s8Dob%%*>l!*$7oY62-E-6VVu(46=meq?!S2*Y!?V{TP zO;)#11SM|M0=%k+)OCkX1A}db1ko!njPGgKDu8pgH}yTok_e z@D6ogC_2BR2ncKV{})*x-oCpyJ`co=XxGN7{11oE_bdMY2M31-TmS!ucm@H4;H<=f z0~>_Vtufz_pE)rx|M<|;q0=?Lq|5En&Id>97{<*_rK zfp(shvrp46k82ZObZhA3sBm2@ZHWKcTCk3*Ew=ONyH^m1v@&?0ExU(J5Tj1B+4YO| z02QpgIvnFIAz=k)I@BxZ`@VeXEZl&l8v$p_b$+It3NrzN{pUeH=m&%0-v<4@G1G;> zCk~m$=F1T^>-en1*l7`PH+L{C7DeaY`~Ch_p>8sO9qCVE?e+ugHH- z_Yb!6--A4#KJ}iUIh_sRB5h1#A}RcfnH-jIN`~l357YBcY$z{e3$jxjjYx<5@5fzqu;JOY5XcBpiZTpcf2$LCYhw4`YO_@Pl z_`S^p=xj*x??=#?;fwY&T;OPjMFFYuW`$?z<(kNu@L&JVeFy+nf~6X5zuubvEUiLk zdgUI{wv}>fVT$#7QKhuSbNgwAYQ-E^`{oY2`r9}Meg@T)6frt~>7dSKXZ=m4Vr?mN zfHhDy>G34aCdM{&12U@F#Cof`9H^v%Rk?146Fec2Ke|;3+RW@~%em%CHa|cScBNh@ z#hLKgxFwcO2Cr7-jXT(Dn>chcW zy{;$cZ6NXGVkT{Kh4JpJ9ur5v-CYDcaR=g9oHQ)sMa`2 z@Fn0=Y?>Tn8tY(<@OsR$5se~}1n4KO5eQV=M{u|&IOMOr0g=Aid#Se3OZ2 zG3F0E;O*rg6}GZ5kb~9>>X?9$_V}LVv7n9;#I=y72@2V4L=%&pWF3y-=t&xgLCsF4 zDi$v+;7&`4H7NYs$%%$js5(tN!vxxD-(g+De~6Ad02_isCDqYbr|hQVTODe2P6W4X z$@QmFn(K;TXlBA~PufvDtik)TyRJY{f@~%~!^wo8oscCV7LAO;sfKEAc8f&3bd1QaXh+Y!PAE=1~i9US+`NMb9DeLo_lhjx5AHT?*9xO}wYyR}x(`L)pOjBsf51tQDIZs}{|g`qB|6fpOg0j-tRrj{>XDUynW_p3p~ zR_RvBiEr-?B^)_ekCu;vLZEulSP8JD#-M)tXfF&Q_Ekpalw5P?LQ#U!_Ht0SshR3X zFKLD6U|Op}V6AR#fq3w(7xDJl__-_nAJHVD$z&-TSVjL21_u@T|Lp1W?fvhAJijyg z|D9S+B{p!6q}<^tYuj~ra=D+8F$8VUG-GI834) zmMyv?Uj}ZubhcX_gVGpRf<_f38=^^VJka%-tp!=IWgCJk6x!-`&>LKki_*00ntYdT z)rFgu#BGUGB%g2rdXIFyQbC0|)<;SkC13Z7$mxe}@3u6Oy=D*>TY6&xI!*VqETuQIPS zkKHILE#VTMv2s0=-C~F?UY@Sn4z*Ap&Yac)W;iI6>IK!JRisK$Hfn>s=H`+sYsKEG zFR^lPkA^yW{ax;%M^h-}F28bbb#DR(V!b(cc`AU`o)T3aXQAqnYQ5HqF6^61J<_Yb zHS1;>A1ET6eENj)G$q-JUaR6&FpY6h7Ot0u(p7+!cq$7)mC99t)B~_Agi|eA1yzmO zvJgQt3Doyb$-EQTIfBOAa>F$4*4Y#1%o9(y?usdzJ3aridTm{e_HeChMvRzjb zL5t8cmEBpnjV-EgDs9f)0Ar0JLG6504u{r!hH}G-6S z)Q({d=rok6OlCz<+AP+#m&3sQGp340se~Pz2T77BM?6Pc;46YaAI6ONFO#Ga&0iX7{^xFt%X-z zSE_?vRtqgx8=``qv8=EkYFI?oZ_9V>VuHVDFqgIcJ0u8eE|v90@AuP1I=&;lYgv08 z8n|FLG`w0ZBSJb16!tOKx z$Wtydf@d#kv197eeXmvJy|yw;g2J!@2yw$pmnXs}x8PP*QO z5NrFeRN0>r9E<61ot9dg1)3If#8)wv%HPsOXegzEMqu?>|8VJ1`LOL4TjwzBcr&el zwdOf_DTa5xFQ6)_04pug6-T!&=(n zDq?Xb^-!B*(lYFddZtth^0vA5r470eH~tmP%IMBWMDy7Yy)hBh?~36cYE+x`KyA>p z=}yXcuBy1nxYm_GZ4gw|%GebtRyM$Kor|s3C!{5j3Sc|PJ=!<@A#PqeVN;Wv3*6~i?Z2v<_1Dkg?TKlpF|Y_$J%a$uF~c(n|$%Kvx2KdA2i!~Xtu z|3Ac2;s1JOmFC=)WnEc77oy3e=F0olt^cQx5FWK~95d`1-@o6OPV=B@`&GdqU2UtY zHtEG}N-U6L0I7eRB<5GjqVosxQd>(z**;(K*@*r-(WTlP09VleXHWO5^#AGe=TEov z{~?}FZ6cyA-~$fbX|7ikc5Vnedg(~^5weUh9)xVxn-3s{onj-W7REQ@An{K7^$b@``H#mqDZ&hD&0%fKplSTP)n!q6>+==4F~gRStM zZaIDiiW?oUU~3H{_^dR7rQ7j)VuUr_fL&`1tQcYl7R-M`QK;==69R|RH+uJJai;Wc zZP)LXEvGEHQ1JwvRbF|ycH0uZit29_SguUfQv1?&p;b)i9TWVuoR}AkPf62n?*i@t z@(6Pp9_220`|7@(zZ=^pI3pQ&v#V=g2aoEfhrma~wJTWr>LJ=M#T9byo88wBfd`W< zIwlF~_d~P{7bQPsv9sRwoBLYx5Jh*6ofgcS`)Xmz5|&-i8Q}uY+`F~PhkI?WEFtK& zH0`{nb`(dyP?kV0v2$MES6dkY0g5KB!?^SEA=aYG3Uk(kN$t~}@KNISk3yvTCHYKw z|F#j;p>6?iOk+W^GnGTL1QNoIEQeUC?7)PKduJq~8N>=(7tu=n4Y**2XWZq3^7n@0 z{@+(0vDx{br~Vo-fLG~%`~7PC$HV6b+w=c}JfDjAk4>>3OYc&i9&1g&PVf?^ctm3= zDB;1;>4`q*^WaC!<-sqe;cag|7!iR7y;Jbt1Y<{~8t&{ChkZ9dV(Z9}b+v$8ly{01 zjPBz&uY(1b!}2~RH-aQEJdZzk4Vx+&qjxD7ks*Fc>NX z;LD;<*1gLqa9Y^U_-ICxAu?99Gkh~d&khd{4&^Z`PmcIcoao!PE;=O{;8-6@-J)-DE}*y?Yp@5k znl!UAa3lpTmBCYXbt2&e_A;_GmX*;Qb0q;iWSb`10=QbynDARJ$jmH`t=wUrjrhOg zRWie?MS+$2zh^c3@27{`{4WpkwDDzHd&hH+}BH|FOt>mH7U#q!3tKxyV zXxdOpXq`Cn4)Vgi7=0`j7;NIM`;!~$k-jQzZ4&saDdg9~1^Zgj0Vnp?K5EXyzs>)- z;d2-I-_{3k1^s`z->;tk4xb$kw)FoYp2hxuvrU};W_R`c)12d5d;k3|T>oajL%+Y- zYNx*nby(^6H(TWO*Jf6PP2z|cG~Kjeh1G+^8cJ< zf`&LwS%h=Jc!*=N$F ze`FzM4gMcYNj#&Igk@yCHdgTe{^8)T!vFXChg<)z2YI^a6bnJJgd@R_nq71~B?%hk zGzRnA6o()2gz&)AX39KI;T|9HDT!k=iP=agNoX?JgHwv2a{`fRocB11ye>+J&P=wG zW@JonNTjyxzwHJHvZJvCYD$z)N-`AFgalsj>iqItu#9+J^peeHEI~iNJVz1DxED;Q z=)u1VeJ>dOmi6FY^I|&b$$!jGK2LfDpb-u~<|!J}m~ih&z^_y9Nif16y(fW~rQVbO z<#o}|IHN4*=;YNg_X2I*?gca=xTnfx?3X_x1z2PMpB*2)dUG7iqIKI?vHzbveO~qd z8SMAB{{Ij1bkV7uZ}gT$1RWcR&U3z)MS#XELsOhZ*El0^YIt|96#}XdClMOmq9<=p z{y+4QWC@A+9=gt`5F|k~K|e6H-_tmsOyt2p*G8cfutLEy9(W#*8CZ{K#F*eB!zoAD z#r1s&*$+E9b>4)EX+DB`1!rl?g?wrHu!|xPIXDrBB?zPU|6z6C7EVbWzKgWCvv$G| zQ-a?ANaC3)&WF98?6@njPyBPl~TijcESrTm{mN+N?z)5ky$4P}6= zueQ4bRY^gzMbEoDCj`B>-)jBrMJ(j-1OB;`g?hWNI%I=#VMHvk+-ubj*dQ61u{q&L7LrS{SjMvHlqDn) z9AVB`NU<=S7F?KMYXNQ~h6>0@D z_oKUrW$1ko@b+uAT!drY-9ssCc0>}MXE4uZ(8ZL{3|*P~#49wWB#t=3VaPJMXX9I0 z9J~|BwO)^b+63Rr^}erm!hzfgFQ<@QLKapLzi2WU!b*X}vXyLT+E)`MORy#7JQF1? zvhJPryvRQ?E-u(hqqS0CyIt+K1mxcHfF`_zML>Tudk=^b`cAEJ&75d5SwJpmA~M#4 zoqGwSqZ=0Rd@&`8*ZOMD7@8!RO@J;K;)DPRw_HgwMwTjLF(BCy5tzm#LW&9qzK7o~vKPf*DNRB^R?)|+3e>9reRqpE5yF10d9kU5awX}n6)vx7EJ;)v zvs;9btfHHefv%##Pe{9Fn4yz=J8aT0;w#R7e6C7GI6dTOOg}m z3UxZ@gYxKgVq6)M5G9BgkQO!3Q6N+VEbHz&Awn%H*s_%sUp7|6uU6 z>i<7@I@tREKgd(@Ly*VTF%FHQD5fDvI2od&RLUIwe$a0!lzikv^jY5$E9pT=(C5HO zH3!eLc!-uCZVJKDRZ*2(O!hsTrjEx+@GRK(BErLrrb_HTLO)8`RY`b~aTg6BXGMEc zCJ|kVP|q#@k=$OhEQ08s?t9;9z>|kBB{@PlGQ%_;q6x)GmX9d==S04ctynqBek+QH z(~NRKaT0KW^N9SD3p^f=Fejy&A$%>WWsC`qlSqm+{GTb~g0LVeLdpF`GAac1YVw`i z8++sKuQ4>r^Ao3OEM;BF5^rv1Xg_!wY`Mqp_GwN3*OvN={Q4xVk}zdX#- zMX$6MfIjAz38uRQ?ENq02&4Zzdh^;JvuuWiAQ9Z2$^x%Q7~_nfInJm&cEh#uh@cd6 zP9j7T!PH6ES`!AI2bWND8WDtnR3Vz6G{zx8Y`g#&!1D^H>F`Qxpn$9ybNeCcbQ<6a zhIl^Wf{I)a00D}rJdTx7m~77;Ts>o8vK%N_hPjZV0&?-4B@STwx(pfo-iBb6Wv+^b z=+_+IN-n#A@*>nlbc_-v5GMi^yisZGlmfbt-IR)%L?G@@pjm{{JdRBSfRpiCCQdWL zNg`xl-=)g)1f?uORmZ%#+aP-X!SmcR1(n_PUlVTFFL1gWNtKub>6Gs79w{jyrHmFt zX$0v}qq|dZq;xkU1!>sml#txR_c^a~en0=kbzh(N{kiyXsvSkexZ|EZbP`vnkX%+A zG;cB_9c75DMlQd@2MywAX<5k0AoXLU?bPv0jDiG?mNeEDsQrf zXl9U4=I51~s=O2th0CU)Q$690Q^K2}Ywi+ed|jzNMtDk`xa|L^vZdCqY^t7^>|9| z%(ggWRP!z9_I+W(dU^e>ID=DcaerUkOkDdh+?SxvR4MJ-&Bio~6GT{7yb9PQ^ zH^+`DM^g?p=@sm>p}l|VK;Fs@vNO1otGYX(dITagU(Gn8b)iUwpKG~$X&j=jm%k?? zjV->(dekwH=a0&O*d{0#&y>cpjX;csVx4AVo}jke*#wjZWa~d-?rw-LsaD^g9Of0^ z(oX#0LvtYYlQQR_FSZLVwj-|(`<9F#$On%A69r!g?EndrX+vyzu1%oiWB862NePbQ z(%=4&q&5RptV;SoktY3;u(CrsdvSjS%3FTMM6|upp8vzkH=lU<7Z!t#4UAoWEj#iT z4-n6P(n-WXATVp|keX}r;$=tc&~U>)>R16HTS01|LVR@o*B6d?$Mw@L-8?uA4kUN} za8MdGh<29q#Snp|<_-|P7*V<4;xwSF$iBs*^e=y?t3+Vx@_sdW-9*!E+$F8@l>(7f zBJEmEQUVKk8njC!U$S(0!BQElcoGy-6{u5|+)_k8;%P#d41Sth$)c!?3oMzW?fqV; z=_!SeCdjAVj$SDc%&My=LrTrDsAxlmscK6e`^f)7R6`;4aafJ-mEOm(0-5P(1D?%e zDW}3bn&1S<9~Gm9MLsNuQW$WNMS!b3B=Abynt&kFIs2(w2ZI&id{2OGj{Jd4c6+M5 zpnwrUnnMG2OVfjz_N(a`#|vPum%g=co2pWtuv4XtUF$J{JAfW_@Zx3rBgqxoIG!<1 zxoeESjx0}K7N-gh3sz-#A*vOv<ojj7-5rz4cp2Q5%uf)`Ozk?$hK90dj~2Cz?D6UZOdvXvwEWc)_aJ?OzL zGe7<~)Era@aemgG=mpAQnY-bHV02pIdgxwKfccUQtlc@;k3u5H`LtxSOM=4bUckg#d0K8IvpT39($3l#lP+G3En=5j zg0vJkB2=g_1dSwupAjQMdK+;m)mg?XJv$rHHu8GKve8bGizmm9Ev&t_U9Fl$K{&u< zmbz#JXXXs`1Rfo__lAc3KYp~BM;2ej63_AOa89gd2Yk6!(Vi!Ctxqu@Q50U)@eLr( zkVF(m!{H#NIH8RfrHa+}-417hVe<_>{-yy}zg8IB@Dd6R2SfRpB6PzGBgsh=|Gi=^ zhkZsgsj)3sG(~sd@`rmQBTYv{7cmR=pb|l6G+{I{G$yJXVC}vXfd&jgEKzpRzf4L1 z{6%&WuhezbX@LZ&rJSLdPOx$K2eyLJd4axZT)MPnyz0#E#AQuL1R`Kxf)Fjvm{n{} z!|?qs)CxKN`t2Bg+xZ4zg8oGnqX+WIIYoEKyWzD%I{i3#Y_0ZZ7p)G7 z5Mjcg;P)u$S}$Fm2_AjPlUXb;86UfiBYwFL(-a0RuxKa2g&F(Pctv2Lp z19jjNsy2|lPgVk_tsT5uiHT1d8jQ;8@Y_n4xj^rT=6PDICHQej5O}l<@ufh1(#^rZ zNm)GqiCt5}$1TE@h}xyoY+?VLn@=QbmejdpemB1l*$GB+-=@km2A@CY;hl}D9|liE zepc_+hk))F3ugoq!*v!sT*Za9relyLNZ+UaJ2JpTpvRw%yWMw1!C$ zXy0p)d{}6gHfL_Y!=}&>y|ClVr)8kNv2Y}Q#=Re~%7oqn?Z)tTscw6EE;+-ZS7Tuv zp4G7TF~Z#h<#1PnzGWOG-b({5RYt-H#U+jS;_PrAJ7U|iq z5TBfODT67JWKY~A*|+`Z`_MhN<^2D4bEFkvuv!Uaw*Zkj%CIuv-3c>@xB(P%LYJ6@ zaeGDh9Q}209E;JNbDWzpFyXCUEH#m;k`3x{nM-hIl%Utj_^s_rYCMy))@dM8`YOe2 zg=Wk+spY_sLYA|!Fw7b8wa_+tw;J6uXa;0@>tRp#-nGL?fO$DFVaLhp2T!~3 z(n9+zXQ;)Gpa}E8h$dz^8cp)yOrE$lO&R@we9$uMUeyO%z>#x~*T%>C?#w4r; zF>kAF1p%k_-Lt#~)D|HkDTimcf-lsWl0qI(1`J22CH$O2>FENW+n9Km6Do@f&GaL$p27hbz-2zVG?p6gQ^ds3lT$q&2TcDjxm z_HN`(jq!9P-Y_Qk6&oK_D&oZ#Z8P4|NaYMY96+(5z$dVXvk|>Gjebz}GDh!d^P}N1 zG@E=b!nn;I?wpYH!|1MAlMxJ}LO5w&_022ap5s6`Olg^x57AMAVp-IVAP`lP5FVb9 zo9xroPOgv(D&#UrT$idQ9j*mUVySLkkj$&p{9WO0LHde9sZuWH^hNa#hRXf2H@Bw? zf);vg?TNJMCe%;v4>eN8bOHg#PIT|4@a$_+&PBI2k{!v0p)I-MFM1X>db~lKnCOz?Frugez^w5fAN+aRB=CUt!Kb<(2e>gS@X3iy>mC3w&0z9eFq96rqu z(79`R^2tSAba{y+FUB8OFCPxfuh3qTPXPKTFn7M8L{~A}_fWDY`ViS!)QWsAqJTfS zH?MSEl94?dF#56E&h|1HuOu~;ihIzHOUWgw%`73l#Y_&{uEL8~w(QV(Nac2S`s_lh ze!|GOoiaQ=qn6eT_w8JHaVtLc|!Irib%>i)u9Fs3q1s`Aq zSpvu?b{al+uKmU7l1PXZNM3jj8dhb(&nua-&2F7asAM)2dzg1fVt%ewoW)i2n-rwH z9uHK+**129R$=7ZAUQ#*Xtw!gk(q`7XG*@5*_(bhnzVwkl170*m(cs8UE2{O) z(I7(KwVWa-Q&sV>!E>W~Zjc02N+jLlDS+d8vg8?)T zURP#0$XM3ZW@37kZuMD5Pxw~Y_f!_9a@%UMW%n{sb^V&V3pIIv5H|}`Dlh6nO~si0 z%3pkUzEtOOI?vW6l8DiBYQfQv*Egw;YeKK^Mb6Y8-`Y4{owaO>U!Q*3{z^y4b@7JC zE4-)^JQ709cRZY9-w-|Ym;%oH;Wy7zeTd<%H+PS?nc7H=LCX{Fu|Xt3AR@@PjS8fr z%^6uSo1dAdft`+Ca(?uo&EZm^6f}=$2h9uVI(5 zEp&K?`Mg#HS~A%GCYv$$A>h2;rUdO%rk`2o&GfAFqn*Pn%=b;3sy2^Sr)15zOYk*V zt-b_V-dK{$?X&!!Ekx!Eh=V`Re54yB8@*S`wBd+w)ER95q!%9jzow9Wz=j%4EHnSF z;3tQJAI}eENN?q2XoKdm^n^kIv&>B|bygxr8A78GQ2Paafk3+mMY`I6aBI)!r>If( zD1_J>GA9Y+AjEwj`1C%{8HM^O^LrrBKVfI4bd~Z=8g76e^2H2v=)=uB)^99X5N7}B z(Ds$jn=jXjl%c-dsn&~FIXpWDwwK?(?9ha7K0zc0-61N&;q9y z{iGRYp8Mfd^L-6J`~$J?_^t=G1538%)`A z$TXXyFd^jj5so~fY$_7B`#W=gJWY-2b=dzN;;at;?xWI|<#N$|^c!)|GC87Leif`2 zdO70Q*+?+ILlXrVBLcOHjn$hz=|ITS3VM4jK994P=4inyxbWpO?4ptIUrPlhG%arm zqi72!Iz3Tqex(>1LQ7x23875QLMOJ>Gi(4%HkPeypyG9gf(M`ii(oG-uEn`AB z_bt}!jRvkakA%z2vy~`jsa6q4ZF)i zb~F9EGStWu4s??aZ6eJh{b`>uFQKG;MUu3rA>T8Fvxw&5ga!~d0n6|aoH6#xj8A5~ww3VmK&E&x7)LmHwyavo0W$RG0VSTg7?9uJ#pgy z^|rmcyhetSlYg2j&l%>Zeq2Dt?rEp~MXNGXtDWu2?zo+x+n?XcP%cy|ym3@o!Vn5? zzsXwwSN|#Z8mG1#Q_7Z43ASAIv@QQ7cM_XX`QT4~e^fd4;jexOPVTC0C^2F4gNGF# zq4OWlx@xS@+{$ z#4=a6+o@w1>xgvR(Q`bPl>PCPOf!;JNOsrqe#%N&^@8;v+`YRp4?L*+q&8e~v2#S# zNHP_Jwb40E zxtbUuX01d9HNu{o z7#XK|IP?5YAh|8`E*UffBvM!}*eFx8iA^mBn@PkeoVOjQc;S`}d`46B+EN3*=A!xi z$eBeWhO?aCUxA4azFS)Qh;Q*WFP%RAg5Kz0J2Ln2{=COrm&hu)q1O1`mzq~w!AIR} zI4r8TIz`m_!+S&34za;sxk$ zpp2B{Q;6l4Fv$FzkX={EJy(09c*b{ePWL_ z+n=`;6B95FGJwCand5IM`UC_EROpFCKa1ICjy-5D%G=EAGJC$q?p z{C`kH8#$KCzL`LC9FKDGYw@WAE;8RWkezsBcHiLhT<2 zIacjz0t(dPNu?b-I2=hPAN{WoRN`Hp`xBTA*TI*k6k##vY;U&JGEYK)9D?IJWcD)D&U)-JMMXCzyMw5 zhcdbrm6AsE2c1H=zZ04f9QC;V)L2)Ga@BZBgv(d|y;1Bi$t46;!(J?{S%+S}UwAH) zyxF*sQ|=^AV`cXB0M`74;c+qSQDUQH+OIi-rYafba=X_1gm+ROW%>&DWV8W48xASJ zzA!!6^2hV&yA=+D9}7{XTCCWkmpQPKuR8=&)D~>6m&Ue#n4$v>e2fVjXZzBe4U9l$ zei4;#oy2UM)P;P8YS+hRDv&iTxz&T0<>}wvJTr4vHd3x@n9V7A*S({Biu)xq1XiJR z`@EUA*6!r_RG=qqYwcap`~K$5lA?s>P?JI!67l*&On|uxxhZ zI689%&DQZp;=(2_=CWu_HBWLq3-TUwB5o3yAC4iR9u<7;Jsf+zUZ4yrqhKwv6-is~ z-^E;ZmU}sTMSAmI;T=d5VmHX&S zQYoCEjs9ckVZh1Pa#XNyu+q{8(!>j{5ou z+$@EX8k*58-M2)J{{F?Y68FbtDDan{nL$lo?dUy?Kk=$5(Ql52#(00>Qj|FUz{(z~ zd);sD5p}xvc=d>q%W=4^HQ;Y{$m79u9GhOOtj8wa=CnHDqJU}9RG7ZZQ1uzjutI>E z4x6p{)Uud-g*Mk}v`quORj%~*o5b`pT-o{Bkh6Kk$o|9GJQslhXfU_H&@_f9u(Eb*+PgA!G3!*XL9;JB z`*E4pLm&-^PRcY=BEu=hCqvTP7hXvGO{lUkf!py{b()uLq|29l zqTo#e%+zt=%U*L(n{e)0#@&Zpt?f<#i*}ntP|Eq*%#PtxWn1@#(c|aHQoyIGox8nf z_YKd_etJDtElt$M9?y?9c=IyOk9v-;5O>MBtHI%8y>sreX%X z6UEll49tm^&?6Sc!byh_;a6M}xzLh{!i4z&Q(v5gS!4-+xgCE5O^H?#&zKWZgwYcb ztE<9+G&s*7;DN}45 z1|9fu2Cm)O(EZc;niBS<4B6ybub`~J(-7mGw4D%pEQr(pPj=tev9p>KWL3JFWF02s zuY?X%1DY`1)>cu{V>8|jLT!HErGD>o$lpcs zcPm90#MNc0+Se8$58P-RYKidm&;>kDD;|FjBoD)h|UIDLo*=1_@wc;BB` zDAXhnBxPgg_$KcHy+wU~baJd2n9FJBk@H9CT$AP2OU6xckHUoz21)JM6b#SUjr&1Y zU3C4v;@IA{@;m)>mA;rmttd?L-6}j$e@IOFj)jFqIM)UjE7^{Djxt@3-nQ2u>7r|@ z0$zvz()y-to_ju#@|bbvYH&s=IkjTru3y?tSl+eq)t2FhW!yU!C{FDkqb1R*a0cUf0nH3fOw~Bys_LgOYMz*){yX=$(0nlWPJ1 z$5>Z>SC;3IU#7cPT=Q1a$Y(Xxb>_hfVw1p1?afG#u%Fx+c(Vmx?8n`{YvuNRxAML6 zhatjfYe|WQ=m_^A@5C>NOI%#)2OU=lw2+)R%85olZ|s`0pZj~_fd+a(iCvVYWDpx!ek!4VMmZnc$;D{}YVzRKu{>FI2VH;1-gi-KKsDY4 z8HgcdspiBOkZrnTdE^@y#dIXt#Kig4#I#k(8m1T1)JNg<9Q9zcbQ{tAAEGYv*Srz(RQvE4qRDXZSg0w=f@#J;1MP z;gRa}UDENOOJmYfCj++(gBPDz(I-ZxSyO@+FMy%X#y>-BAy`-O6ShFVN$yd`Nqm8e z7{z|ewNROvm@zq5Z=qH$Ps+s6;))~$`!rel&nf?4+#Fv4Wn;tsI&H$Q;XXkx^^YB1 zu5#Y;pITD%X#WFA1OICP literal 0 HcmV?d00001 diff --git a/helm/oncall/values.yaml b/helm/oncall/values.yaml index 97119654..3306f005 100644 --- a/helm/oncall/values.yaml +++ b/helm/oncall/values.yaml @@ -708,3 +708,12 @@ ui: tag: dev # Additional env vars for the ui container env: {} + +prometheus: + enabled: false + # extraScrapeConfigs: | + # - job_name: 'oncall-exporter' + # metrics_path: /metrics/ + # static_configs: + # - targets: + # - oncall-dev-engine.default.svc.cluster.local:8080