SHELLAPANDIANGANHUNGING commited on
Commit
67f58b4
·
verified ·
1 Parent(s): 6a95279

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -52
app.py CHANGED
@@ -10,6 +10,7 @@ import os
10
  import sklearn
11
  import kaleido
12
 
 
13
  # =================== PAGE CONFIG ===================
14
  st.set_page_config(
15
  page_title="PLN Audit Insight & Intelligence Dashboard",
@@ -325,27 +326,45 @@ with st.container():
325
  </div>
326
  """, unsafe_allow_html=True)
327
 
328
- # RIGHT — LOGO BOX
329
- with col3:
330
- logo_path = "pln.png"
331
- if os.path.exists(logo_path):
332
- st.image(logo_path, width=200)
333
- else:
334
- st.error("Logo not found")
335
 
336
- st.markdown("</div>", unsafe_allow_html=True)
337
 
338
 
339
- # =================== 1. Pie Charts: Temuan/Person by Company (PG & UM) - PERBAIKAN ===================
340
- st.markdown("<h3 class='section-title'>OBJECTIVE 1 - Company Reporting Activity: Who Reports the Most?</h3>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
- # Asumsikan df_filtered adalah data utama yang telah difilter
343
  df_local = df_filtered.copy()
344
 
345
  # Tambah kolom bulan
346
  df_local['created_month'] = df_local['created_at'].dt.to_period('M')
347
 
348
- # --- Langsung buat Area_Type PG / UM tanpa filter manual ---
349
  if 'temuan_kode_distrik' in df_local.columns:
350
  df_local['Area_Type'] = df_local['temuan_kode_distrik'].apply(
351
  lambda x: 'PG' if 'PG' in str(x).upper()
@@ -367,7 +386,6 @@ if 'temuan_kode_distrik' in df_local.columns:
367
  creators_by_company_month = df_area.groupby(['created_month', 'nama_perusahaan'])['creator_nid'].nunique().reset_index(name='unique_creators')
368
  # Gabung
369
  merged = findings_by_company_month.merge(creators_by_company_month, on=['created_month', 'nama_perusahaan'], how='outer')
370
- # Isi NaN dengan 0 untuk kolom yang mungkin hilang dari merge
371
  merged = merged.fillna({'findings_count': 0, 'unique_creators': 0})
372
  # Filter untuk menghindari pembagian dengan nol
373
  merged = merged[merged['unique_creators'] > 0]
@@ -392,34 +410,102 @@ if 'temuan_kode_distrik' in df_local.columns:
392
  avg_ratio_pg = calculate_avg_ratio_per_company(df_pg)
393
  avg_ratio_um = calculate_avg_ratio_per_company(df_um)
394
 
395
- # Fungsi untuk menentukan warna
396
- def get_color_map(company_series):
397
- pln_color = "#FFD700" # Kuning untuk PLN
398
- blue_colors = ["#1E90FF", "#87CEEB", "#B0E0E6", "#ADD8E6", "#E0F6FF"]
399
- color_map = {}
400
- for company in company_series:
 
 
 
 
 
 
 
 
 
 
401
  if 'PLN' in str(company).upper():
402
- color_map[company] = pln_color
403
  else:
404
- idx = len([c for c in color_map.values() if c != pln_color]) % len(blue_colors)
405
- color_map[company] = blue_colors[idx]
406
- return color_map
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
  # Plot
409
  col1, col2 = st.columns(2)
410
 
411
  with col1:
412
  st.markdown("<h5>Unit Pembangkit: Avg Monthly Finding by Company</h5>", unsafe_allow_html=True)
413
- if not avg_ratio_pg.empty:
414
- color_discrete_map_pg = get_color_map(avg_ratio_pg['nama_perusahaan'])
415
- fig_pg = px.pie(
416
- avg_ratio_pg,
417
- values='avg_monthly_ratio',
418
- names='nama_perusahaan',
419
- title='PG Area',
420
- color='nama_perusahaan',
421
- color_discrete_map=color_discrete_map_pg
422
- )
423
  st.plotly_chart(fig_pg, use_container_width=True)
424
 
425
  # AI Insight untuk PG
@@ -443,16 +529,8 @@ if 'temuan_kode_distrik' in df_local.columns:
443
 
444
  with col2:
445
  st.markdown("<h5>Unit Maintenance: Avg Monthly Finding by Company</h5>", unsafe_allow_html=True)
446
- if not avg_ratio_um.empty:
447
- color_discrete_map_um = get_color_map(avg_ratio_um['nama_perusahaan'])
448
- fig_um = px.pie(
449
- avg_ratio_um,
450
- values='avg_monthly_ratio',
451
- names='nama_perusahaan',
452
- title='UM Area',
453
- color='nama_perusahaan',
454
- color_discrete_map=color_discrete_map_um
455
- )
456
  st.plotly_chart(fig_um, use_container_width=True)
457
 
458
  # AI Insight untuk UM
@@ -702,7 +780,7 @@ st.markdown(
702
  margin-top: 1rem;
703
  }
704
  </style>
705
- <h3 class='section-title'>OBJECTIVE 3 — Frequency & Response Time: Who Reports Well? Who Executes Well?</h3>
706
  """,
707
  unsafe_allow_html=True
708
  )
@@ -791,11 +869,11 @@ avg_leadtime_per_executor = compute_executor_leadtime_by_pic(df_local)
791
  col_3a, col_3c = st.columns(2)
792
 
793
  with col_3a:
794
- st.markdown("<h5>3a. Average Finding by Division (Reporter)</h5>", unsafe_allow_html=True)
795
  if avg_ratio_per_nama.empty:
796
  st.warning("No data for reporter analysis by division.")
797
  else:
798
- sort_option_3a = st.selectbox("Show 3a:", ["Top 10", "Bottom 10"], key='sort_3a')
799
 
800
  # Urutkan data dari tertinggi ke terendah
801
  sorted_data_all = avg_ratio_per_nama.sort_values('avg_monthly_ratio', ascending=False)
@@ -854,11 +932,11 @@ with col_3a:
854
  st.markdown(insight_text, unsafe_allow_html=True)
855
 
856
  with col_3c:
857
- st.markdown("<h5>3c. Average Finding Rate per Reporter (Name)</h5>", unsafe_allow_html=True)
858
  if avg_rate_per_creator.empty:
859
  st.warning("No data for reporter analysis by creator_name.")
860
  else:
861
- sort_option_3c = st.selectbox("Show 3c:", ["Top 10", "Bottom 10"], key='sort_3c')
862
 
863
  # Urutkan data dari tertinggi ke terendah
864
  sorted_data_all = avg_rate_per_creator.sort_values('avg_monthly_rate', ascending=False)
@@ -921,11 +999,11 @@ with col_3c:
921
  col_3b, col_3d = st.columns(2)
922
 
923
  with col_3b:
924
- st.markdown("<h5>3b. Average Lead Time by Division (Executor)</h5>", unsafe_allow_html=True)
925
  if avg_leadtime_nama.empty:
926
  st.warning("No data for executor analysis by division.")
927
  else:
928
- sort_option_3b = st.selectbox("Show 3b:", ["Top 10", "Bottom 10"], key='sort_3b')
929
 
930
  # Urutkan data dari tertinggi ke terendah
931
  sorted_data_all = avg_leadtime_nama.sort_values('avg_monthly_leadtime', ascending=False)
@@ -982,11 +1060,11 @@ with col_3b:
982
  st.markdown(insight_text, unsafe_allow_html=True)
983
 
984
  with col_3d:
985
- st.markdown("<h5>3d. Average Lead Time by Executor (Name)</h5>", unsafe_allow_html=True)
986
  if avg_leadtime_per_executor.empty:
987
  st.warning("No data for executor analysis by nama_pic.")
988
  else:
989
- sort_option_3d = st.selectbox("Show 3d:", ["Top 10", "Bottom 10"], key='sort_3d')
990
 
991
  # Urutkan data dari tertinggi ke terendah
992
  sorted_data_all = avg_leadtime_per_executor.sort_values('avg_monthly_leadtime', ascending=False)
@@ -1043,6 +1121,7 @@ with col_3d:
1043
  st.markdown(insight_text, unsafe_allow_html=True)
1044
 
1045
 
 
1046
  ####OBJECTIVE 4
1047
  try:
1048
  from wordcloud import WordCloud
 
10
  import sklearn
11
  import kaleido
12
 
13
+
14
  # =================== PAGE CONFIG ===================
15
  st.set_page_config(
16
  page_title="PLN Audit Insight & Intelligence Dashboard",
 
326
  </div>
327
  """, unsafe_allow_html=True)
328
 
 
 
 
 
 
 
 
329
 
 
330
 
331
 
332
+ import streamlit as st
333
+ import plotly.graph_objects as go
334
+ import numpy as np
335
+ import pandas as pd
336
+
337
+ # =================== OBJECTIVE 1 - Company Reporting Activity (Polar Bar Chart) ===================
338
+ st.markdown(
339
+ """
340
+ <style>
341
+ .section-title {
342
+ text-align: center;
343
+ font-size: 1.5rem;
344
+ font-weight: 600;
345
+ color: #2c3e50;
346
+ margin-bottom: 1.2rem;
347
+ }
348
+ .ai-insight {
349
+ background-color: #f8f9fa;
350
+ padding: 12px;
351
+ border-left: 4px solid #27ae60;
352
+ border-radius: 0 4px 4px 0;
353
+ font-size: 0.95rem;
354
+ line-height: 1.5;
355
+ margin-top: 1rem;
356
+ }
357
+ </style>
358
+ <h3 class='section-title'>OBJECTIVE 1 — Company Reporting Activity: Who Reports the Most?</h3>
359
+ """,
360
+ unsafe_allow_html=True
361
+ )
362
 
 
363
  df_local = df_filtered.copy()
364
 
365
  # Tambah kolom bulan
366
  df_local['created_month'] = df_local['created_at'].dt.to_period('M')
367
 
 
368
  if 'temuan_kode_distrik' in df_local.columns:
369
  df_local['Area_Type'] = df_local['temuan_kode_distrik'].apply(
370
  lambda x: 'PG' if 'PG' in str(x).upper()
 
386
  creators_by_company_month = df_area.groupby(['created_month', 'nama_perusahaan'])['creator_nid'].nunique().reset_index(name='unique_creators')
387
  # Gabung
388
  merged = findings_by_company_month.merge(creators_by_company_month, on=['created_month', 'nama_perusahaan'], how='outer')
 
389
  merged = merged.fillna({'findings_count': 0, 'unique_creators': 0})
390
  # Filter untuk menghindari pembagian dengan nol
391
  merged = merged[merged['unique_creators'] > 0]
 
410
  avg_ratio_pg = calculate_avg_ratio_per_company(df_pg)
411
  avg_ratio_um = calculate_avg_ratio_per_company(df_um)
412
 
413
+ # Palet biru pastel konsisten (soft, harmonis)
414
+ pastel_blues = [
415
+ "#A8DADC", # light cyan
416
+ "#E2ECE9", # pale mint
417
+ "#CCE4E7", # soft sky
418
+ "#B5D9D9", # muted aqua
419
+ "#98C8D1", # gentle teal
420
+ "#7FB9C1", # calm blue
421
+ "#6BA9B3" # deep pastel teal
422
+ ]
423
+ pln_color = "#FFD700" # Kuning PLN
424
+
425
+ def assign_colors(df):
426
+ colors = []
427
+ blue_idx = 0
428
+ for company in df['nama_perusahaan']:
429
  if 'PLN' in str(company).upper():
430
+ colors.append(pln_color)
431
  else:
432
+ colors.append(pastel_blues[blue_idx % len(pastel_blues)])
433
+ blue_idx += 1
434
+ return colors
435
+
436
+ # Fungsi untuk membuat polar bar chart
437
+ def create_polar_bar_chart(df, area_name):
438
+ if df.empty:
439
+ return None
440
+
441
+ # Urutkan berdasarkan rasio untuk tampilan konsisten
442
+ df = df.sort_values('avg_monthly_ratio', ascending=True)
443
+ companies = df['nama_perusahaan'].tolist()
444
+ ratios = df['avg_monthly_ratio'].tolist()
445
+ colors = assign_colors(df)
446
+
447
+ # Hitung total temuan untuk menghitung sudut (proporsi)
448
+ total_findings = df_local[df_local['Area_Type'] == area_name].groupby('nama_perusahaan').size()
449
+ angles = []
450
+ total_angle = 0
451
+ for comp in companies:
452
+ count = total_findings.get(comp, 0)
453
+ angle = (count / total_findings.sum()) * 360 if total_findings.sum() > 0 else 0
454
+ angles.append(angle)
455
+
456
+ # Hitung sudut tengah untuk setiap bar
457
+ mid_angles = []
458
+ current_angle = 0
459
+ for a in angles:
460
+ mid_angles.append(current_angle + a / 2)
461
+ current_angle += a
462
+
463
+ fig = go.Figure()
464
+ fig.add_trace(go.Barpolar(
465
+ r=ratios,
466
+ theta=mid_angles,
467
+ width=angles,
468
+ marker_color=colors,
469
+ marker_line_color="white",
470
+ marker_line_width=1.2,
471
+ opacity=0.9,
472
+ hovertemplate="<b>%{text}</b><br>Avg Ratio: %{r:.2f}<extra></extra>",
473
+ text=companies,
474
+ ))
475
+
476
+ fig.update_layout(
477
+ title=f'{area_name} Area',
478
+ polar=dict(
479
+ bgcolor="white",
480
+ radialaxis=dict(
481
+ visible=True,
482
+ tickfont=dict(size=9),
483
+ gridcolor='lightgray',
484
+ title=dict(text='Avg Finding/Person', font=dict(size=10))
485
+ ),
486
+ angularaxis=dict(
487
+ visible=True,
488
+ direction='clockwise',
489
+ tickfont=dict(size=9),
490
+ showline=False
491
+ ),
492
+ ),
493
+ showlegend=False,
494
+ margin=dict(t=40, b=20, l=20, r=20),
495
+ height=400,
496
+ paper_bgcolor="rgba(0,0,0,0)",
497
+ plot_bgcolor="rgba(0,0,0,0)"
498
+ )
499
+
500
+ return fig
501
 
502
  # Plot
503
  col1, col2 = st.columns(2)
504
 
505
  with col1:
506
  st.markdown("<h5>Unit Pembangkit: Avg Monthly Finding by Company</h5>", unsafe_allow_html=True)
507
+ fig_pg = create_polar_bar_chart(avg_ratio_pg, 'PG')
508
+ if fig_pg:
 
 
 
 
 
 
 
 
509
  st.plotly_chart(fig_pg, use_container_width=True)
510
 
511
  # AI Insight untuk PG
 
529
 
530
  with col2:
531
  st.markdown("<h5>Unit Maintenance: Avg Monthly Finding by Company</h5>", unsafe_allow_html=True)
532
+ fig_um = create_polar_bar_chart(avg_ratio_um, 'UM')
533
+ if fig_um:
 
 
 
 
 
 
 
 
534
  st.plotly_chart(fig_um, use_container_width=True)
535
 
536
  # AI Insight untuk UM
 
780
  margin-top: 1rem;
781
  }
782
  </style>
783
+ <h3 class='section-title'>OBJECTIVE 4 — Frequency & Response Time: Who Reports Well? Who Executes Well?</h3>
784
  """,
785
  unsafe_allow_html=True
786
  )
 
869
  col_3a, col_3c = st.columns(2)
870
 
871
  with col_3a:
872
+ st.markdown("<h5>4a. Average Finding by Division (Reporter)</h5>", unsafe_allow_html=True)
873
  if avg_ratio_per_nama.empty:
874
  st.warning("No data for reporter analysis by division.")
875
  else:
876
+ sort_option_3a = st.selectbox("Show 4a:", ["Top 10", "Bottom 10"], key='sort_3a')
877
 
878
  # Urutkan data dari tertinggi ke terendah
879
  sorted_data_all = avg_ratio_per_nama.sort_values('avg_monthly_ratio', ascending=False)
 
932
  st.markdown(insight_text, unsafe_allow_html=True)
933
 
934
  with col_3c:
935
+ st.markdown("<h5>4b. Average Finding Rate per Reporter (Name)</h5>", unsafe_allow_html=True)
936
  if avg_rate_per_creator.empty:
937
  st.warning("No data for reporter analysis by creator_name.")
938
  else:
939
+ sort_option_3c = st.selectbox("Show 4b:", ["Top 10", "Bottom 10"], key='sort_3c')
940
 
941
  # Urutkan data dari tertinggi ke terendah
942
  sorted_data_all = avg_rate_per_creator.sort_values('avg_monthly_rate', ascending=False)
 
999
  col_3b, col_3d = st.columns(2)
1000
 
1001
  with col_3b:
1002
+ st.markdown("<h5>4c. Average Lead Time by Division (Executor)</h5>", unsafe_allow_html=True)
1003
  if avg_leadtime_nama.empty:
1004
  st.warning("No data for executor analysis by division.")
1005
  else:
1006
+ sort_option_3b = st.selectbox("Show 4c:", ["Top 10", "Bottom 10"], key='sort_3b')
1007
 
1008
  # Urutkan data dari tertinggi ke terendah
1009
  sorted_data_all = avg_leadtime_nama.sort_values('avg_monthly_leadtime', ascending=False)
 
1060
  st.markdown(insight_text, unsafe_allow_html=True)
1061
 
1062
  with col_3d:
1063
+ st.markdown("<h5>4d. Average Lead Time by Executor (Name)</h5>", unsafe_allow_html=True)
1064
  if avg_leadtime_per_executor.empty:
1065
  st.warning("No data for executor analysis by nama_pic.")
1066
  else:
1067
+ sort_option_3d = st.selectbox("Show 4d:", ["Top 10", "Bottom 10"], key='sort_3d')
1068
 
1069
  # Urutkan data dari tertinggi ke terendah
1070
  sorted_data_all = avg_leadtime_per_executor.sort_values('avg_monthly_leadtime', ascending=False)
 
1121
  st.markdown(insight_text, unsafe_allow_html=True)
1122
 
1123
 
1124
+
1125
  ####OBJECTIVE 4
1126
  try:
1127
  from wordcloud import WordCloud