Risk Analysis Dashboard
Introduction
This notebook provides comprehensive risk analysis tools for project management, including:
- Risk Matrix Visualization
- Temporal Analysis
- Category Distribution
- Mitigation Effectiveness
- Resource Allocation Analysis
- Predictive Analytics
Setup and Data Loading
# Initialize required modules and libraries
alias Resolvinator.Notebooks.Setup
setup = Setup.setup()
import Explorer.DataFrame
alias Explorer.Series
alias Resolvinator.{Repo, Risks, Resources}
alias Resolvinator.Analytics.RiskAnalyzer
# Setup visualization tools
Kino.VegaLite.setup()
# Create interactive inputs for analysis parameters
date_range = Kino.Input.date_range("Date Range")
category_select = Kino.Input.select("Risk Category", ["All" | Risks.list_categories()])
impact_threshold = Kino.Input.number("Impact Threshold", default: 5)
Data Preparation and Validation
# Fetch and validate risk data
risks_with_associations =
try do
Risks.list_risks(includes: [:mitigations, :impacts, :requirements])
|> Enum.map(fn risk ->
%{
id: risk.id,
name: risk.name,
category: risk.category,
probability: risk.probability,
impact: risk.impact,
status: risk.status,
description: risk.description,
mitigation_count: length(risk.mitigations),
total_impact_score: Enum.sum(Enum.map(risk.impacts, & &1.score)),
resource_requirements: length(risk.requirements),
created_at: risk.inserted_at,
updated_at: risk.updated_at,
days_open: Date.diff(Date.utc_today(), Date.from_iso8601!(risk.inserted_at))
}
end)
|> DataFrame.new()
rescue
e in RuntimeError ->
IO.puts("Error loading risk data: #{inspect(e)}")
DataFrame.new([])
end
# Display interactive data table with filtering
Kino.DataTable.new(risks_with_associations, keys: [:id, :name, :category])
Risk Matrix Analysis
# Create Risk Matrix Heatmap
matrix_data =
risks_with_associations
|> group_by([:probability, :impact])
|> summarise(count: count())
|> collect()
VegaLite.new(width: 400, height: 400)
|> VegaLite.data(matrix_data)
|> VegaLite.mark(:rect)
|> VegaLite.encode_field(:x, "probability", type: :ordinal, title: "Probability")
|> VegaLite.encode_field(:y, "impact", type: :ordinal, title: "Impact")
|> VegaLite.encode_field(:color, "count",
type: :quantitative,
scale: [scheme: "reds"],
title: "Number of Risks"
)
|> VegaLite.config(view: [stroke: nil])
|> VegaLite.properties(title: "Risk Matrix Heatmap")
Temporal Analysis
# Risk Creation Timeline with Impact Overlay
timeline_data =
risks_with_associations
|> mutate(month: date_trunc(created_at, "month"))
|> group_by(:month)
|> summarise(
new_risks: count(),
avg_impact: mean(total_impact_score)
)
|> arrange(desc: :month)
|> collect()
# Create dual-axis chart
VegaLite.new(width: 600, height: 300)
|> VegaLite.layers([
VegaLite.new()
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "month", type: :temporal)
|> VegaLite.encode_field(:y, "new_risks", type: :quantitative)
|> VegaLite.encode_field(:color, value: "steelblue"),
VegaLite.new()
|> VegaLite.mark(:line, stroke: "red")
|> VegaLite.encode_field(:x, "month", type: :temporal)
|> VegaLite.encode_field(:y, "avg_impact", type: :quantitative)
])
|> VegaLite.properties(title: "Risk Creation and Impact Trends")
Category Analysis
# Category Distribution and Impact Analysis
category_analysis =
risks_with_associations
|> group_by(:category)
|> summarise(
count: count(),
avg_impact: mean(total_impact_score),
avg_mitigations: mean(mitigation_count),
avg_requirements: mean(resource_requirements)
)
|> arrange(desc: :count)
|> collect()
VegaLite.new(width: 500, height: 300)
|> VegaLite.data(category_analysis)
|> VegaLite.mark(:bar)
|> VegaLite.encode_field(:x, "category", type: :nominal)
|> VegaLite.encode_field(:y, "count", type: :quantitative)
|> VegaLite.encode_field(:color, "avg_impact", type: :quantitative)
|> VegaLite.properties(title: "Risk Categories by Count and Impact")
Predictive Analysis
# Simple prediction model for risk escalation
high_risk_factors =
risks_with_associations
|> filter(impact == "high" or probability == "high")
|> summarise(
avg_days_to_escalate: mean(days_open),
avg_mitigations_needed: mean(mitigation_count),
common_categories: mode(category)
)
|> collect()
# Display risk factors
Kino.DataTable.new(high_risk_factors)
Export Analysis
# Export analysis results
Form.new(
[
export_format: Kino.Input.select("Export Format", ["CSV", "JSON", "Excel"])
],
submit: "Export Analysis"
)
|> Kino.render()
|> Kino.listen(fn %{data: %{export_format: format}} ->
case format do
"CSV" ->
risks_with_associations
|> collect()
|> DataFrame.write_csv("risk_analysis_export.csv")
"JSON" ->
risks_with_associations
|> collect()
|> Jason.encode!()
|> File.write!("risk_analysis_export.json")
"Excel" ->
risks_with_associations
|> collect()
|> DataFrame.write_xlsx("risk_analysis_export.xlsx")
end
end)