yusiff commited on
Commit
72a5728
·
verified ·
1 Parent(s): dcbd202

Update views/leaderboard.py

Browse files
Files changed (1) hide show
  1. views/leaderboard.py +110 -0
views/leaderboard.py CHANGED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """🏆 Leaderboard — الترتيب العام + منصة الأوائل + ترتيب الأقسام."""
2
+
3
+ import streamlit as st
4
+
5
+ from auth import require_manager
6
+ from data_layer import get_repository
7
+ from logic import department_ranking, leaderboard
8
+ from ui import page_header, sidebar_note
9
+
10
+ require_manager()
11
+
12
+ repo = get_repository()
13
+ lb = leaderboard(repo)
14
+
15
+ page_header("🏆 لوحة الترتيب", "الترتيب حسب الرصيد التراكمي للنقاط")
16
+
17
+ # ---------------- الفلتر ----------------
18
+ departments = ["كل الأقسام"] + sorted(lb["department"].unique().tolist())
19
+ col_f, col_i = st.columns([1, 2])
20
+ with col_f:
21
+ selected = st.selectbox("🏢 فلترة بالقسم", departments)
22
+
23
+ view = lb if selected == "كل الأقسام" else lb[lb["department"] == selected].copy()
24
+
25
+ if selected != "كل الأقسام":
26
+ # ترتيب داخل القسم مع إبقاء الترتيب العام ظاهرًا
27
+ view = view.reset_index(drop=True)
28
+ view["dept_rank"] = view.index + 1
29
+ with col_i:
30
+ st.info(f"عرض **{len(view)}** موظف في قسم **{selected}** — الأرقام بين قوسين هي الترتيب العام.")
31
+
32
+ # ---------------- منصة الأوائل ----------------
33
+ if selected == "كل الأقسام" and len(view) >= 3:
34
+ st.markdown("### 🎖️ منصة الأوائل")
35
+ first, second, third = view.iloc[0], view.iloc[1], view.iloc[2]
36
+
37
+ def podium_html(row, place: int, medal: str) -> str:
38
+ crown = '<div class="podium-crown">👑</div>' if place == 1 else ""
39
+ return (
40
+ f'<div class="podium podium-{place}">{crown}'
41
+ f'<div class="podium-medal">{medal}</div>'
42
+ f'<div class="podium-name">{row["name"]}</div>'
43
+ f'<div class="podium-dept">{row["department"]}</div>'
44
+ f'<div class="podium-points">{int(row["current_points"]):,}</div>'
45
+ f'<div class="podium-dept">{row["level_emoji"]} {row["current_level"]}</div>'
46
+ f'</div>'
47
+ )
48
+
49
+ # ترتيب بصري: الثاني — الأول — الثالث
50
+ st.markdown(
51
+ '<div class="podium-wrap">'
52
+ + podium_html(second, 2, "🥈")
53
+ + podium_html(first, 1, "🥇")
54
+ + podium_html(third, 3, "🥉")
55
+ + "</div>",
56
+ unsafe_allow_html=True,
57
+ )
58
+
59
+ # ---------------- الترتيب الكامل ----------------
60
+ st.markdown("### 📊 الترتيب الكامل")
61
+
62
+ if view.empty:
63
+ st.warning("لا يوجد موظفون في هذا القسم.")
64
+ else:
65
+ for _, row in view.iterrows():
66
+ rank = int(row["dept_rank"]) if "dept_rank" in view.columns else int(row["rank"])
67
+ overall = f' <span class="card-sub">(عام #{int(row["rank"])})</span>' if "dept_rank" in view.columns else ""
68
+ highlight = " me" if rank == 1 else ""
69
+ st.markdown(
70
+ f"""
71
+ <div class="rank-row{highlight}">
72
+ <div class="rank-num">{rank}</div>
73
+ <div style="font-size:1.4rem;">{row['level_emoji']}</div>
74
+ <div class="rank-name">{row['name']}{overall}
75
+ <div class="rank-dept">{row['department']} · {row['current_level']}</div>
76
+ </div>
77
+ <div class="rank-points">{int(row['current_points']):,}</div>
78
+ </div>
79
+ """,
80
+ unsafe_allow_html=True,
81
+ )
82
+
83
+ st.markdown("---")
84
+
85
+ # ---------------- ترتيب الأقسام ----------------
86
+ st.markdown("### 🏢 ترتيب الأقسام")
87
+ st.caption("الترتيب حسب **متوسط** نقاط الموظف الواحد — حتى لا تتفوق الأقسام الكبيرة لمجرد عدد أفرادها.")
88
+
89
+ depts = department_ranking(repo)
90
+ max_avg = depts["avg_points"].max() or 1
91
+
92
+ cols = st.columns(min(len(depts), 5))
93
+ for i, (_, row) in enumerate(depts.iterrows()):
94
+ with cols[i % len(cols)]:
95
+ medal = ["🥇", "🥈", "🥉"][i] if i < 3 else "🏢"
96
+ st.markdown(
97
+ f'<div class="card" style="text-align:center; min-height:150px;">'
98
+ f'<div style="font-size:1.7rem;">{medal}</div>'
99
+ f'<div class="card-title" style="font-size:.92rem;">{row["department"]}</div>'
100
+ f'<div style="font-size:1.5rem; font-weight:700; color:#F5C542; direction:ltr;">'
101
+ f'{row["avg_points"]:.0f}</div>'
102
+ f'<div class="card-sub">متوسط النقاط</div>'
103
+ f'<div class="card-sub" style="margin-top:.3rem;">'
104
+ f'{int(row["members"])} موظف · {int(row["total_points"]):,} إجمالي</div>'
105
+ f"</div>",
106
+ unsafe_allow_html=True,
107
+ )
108
+ st.progress(float(row["avg_points"] / max_avg))
109
+
110
+ sidebar_note()