Skip to content

Commit 0e5c984

Browse files
committed
Initial commit of HR demo files
1 parent 211a331 commit 0e5c984

19 files changed

+2989
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Copyright (c) 2025 Oracle and/or its affiliates.
2+
3+
The Universal Permissive License (UPL), Version 1.0
4+
5+
Subject to the condition set forth below, permission is hereby granted to any
6+
person obtaining a copy of this software, associated documentation and/or data
7+
(collectively the "Software"), free of charge and under any and all copyright
8+
rights in the Software, and any and all patent rights owned or freely
9+
licensable by each licensor hereunder covering either (i) the unmodified
10+
Software as contributed to or provided by such licensor, or (ii) the Larger
11+
Works (as defined below), to deal in both
12+
13+
(a) the Software, and
14+
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15+
one is included with the Software (each a "Larger Work" to which the Software
16+
is contributed by such licensors),
17+
18+
without restriction, including without limitation the rights to copy, create
19+
derivative works of, display, perform, and distribute the Software and make,
20+
use, sell, offer for sale, import, export, have made, and have sold the
21+
Software and the Larger Work(s), and to sublicense the foregoing rights on
22+
either these or other terms.
23+
24+
This license is subject to the following condition:
25+
The above copyright notice and either this complete permission notice or at
26+
a minimum a reference to the UPL must be included in all copies or
27+
substantial portions of the Software.
28+
29+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35+
SOFTWARE.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# HR Goal Alignment Chatbot
2+
3+
This project provides an AI-powered HR assistant suite built using Oracle Cloud Infrastructure's Generative AI, Oracle Database, and Streamlit.
4+
5+
This asset lives under: `ai/generative-ai-service/hr-goal-alignment`
6+
7+
---
8+
9+
## When to use this asset?
10+
11+
This chatbot system is designed to support employee–manager goal alignment, self-assessment reflection, course recommendations, and meeting preparation through conversational AI. It’s especially useful during performance review cycles and organizational alignment phases.
12+
13+
---
14+
15+
## How to use this asset?
16+
17+
See the full setup and usage guide in [`files/README.md`](./files/README.md).
18+
19+
---
20+
21+
## License
22+
23+
Copyright (c) 2025 Oracle and/or its affiliates.
24+
25+
Licensed under the Universal Permissive License (UPL), Version 1.0.
26+
27+
See [LICENSE](./LICENSE) for more details.
28+
29+
---
30+
31+
> ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT ANY REVIEW. USE AT YOUR OWN RISK.
32+
33+
34+
## Disclaimer
35+
36+
ORACLE AND ITS AFFILIATES DO NOT PROVIDE ANY WARRANTY WHATSOEVER, EXPRESS OR IMPLIED, FOR ANY SOFTWARE, MATERIAL OR CONTENT OF ANY KIND CONTAINED OR PRODUCED WITHIN THIS REPOSITORY, AND IN PARTICULAR SPECIFICALLY DISCLAIM ANY AND ALL IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. FURTHERMORE, ORACLE AND ITS AFFILIATES DO NOT REPRESENT THAT ANY CUSTOMARY SECURITY REVIEW HAS BEEN PERFORMED WITH RESPECT TO ANY SOFTWARE, MATERIAL OR CONTENT CONTAINED OR PRODUCED WITHIN THIS REPOSITORY. IN ADDITION, AND WITHOUT LIMITING THE FOREGOING, THIRD PARTIES MAY HAVE POSTED SOFTWARE, MATERIAL OR CONTENT TO THIS REPOSITORY WITHOUT ANY REVIEW. USE AT YOUR OWN RISK.
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import streamlit as st
2+
from urllib.parse import quote, unquote
3+
4+
from org_chart_backend import check_goal_alignment, fetch_goals_from_emp, mapping_all_employees
5+
6+
try:
7+
import graphviz
8+
except ImportError:
9+
st.error("Please install graphviz package: pip install graphviz")
10+
st.stop()
11+
12+
st.title("Organization Chart")
13+
st.markdown("Use the sidebar to adjust settings and select employees for details.")
14+
15+
# Get org chart data
16+
mapping, employees_df = mapping_all_employees()
17+
if not mapping:
18+
st.warning("No data available to display.")
19+
st.stop()
20+
21+
# Get all employee names
22+
all_employees = sorted(set(mapping.keys()) | {emp for values in mapping.values() for emp in values})
23+
24+
# Sidebar controls
25+
with st.sidebar:
26+
st.header("Chart Settings")
27+
orientation = st.selectbox("Layout", ["Top-Down", "Left-Right"], index=0)
28+
zoom_level = st.slider("Zoom Level (%)", 50, 200, 50, step=10)
29+
30+
# Direct employee selection
31+
st.markdown("---")
32+
st.header("Select Employee")
33+
34+
# Preloaded selectbox with all employees
35+
selected_employee = st.selectbox(
36+
"Choose an employee:",
37+
[""] + all_employees, # Add empty option at the top
38+
index=0
39+
)
40+
41+
if selected_employee:
42+
if st.button("View Details"):
43+
st.session_state.selected_employee = selected_employee
44+
# Update URL parameter
45+
st.query_params["emp"] = quote(selected_employee)
46+
st.rerun()
47+
48+
# Store selected employee in session state
49+
if "selected_employee" not in st.session_state:
50+
# Try to get from URL params first
51+
params = st.query_params
52+
emp_param = params.get("emp")
53+
if emp_param:
54+
if isinstance(emp_param, list):
55+
emp_param = emp_param[0]
56+
st.session_state.selected_employee = unquote(emp_param)
57+
else:
58+
st.session_state.selected_employee = None
59+
60+
# Create the graphviz chart
61+
def create_org_chart():
62+
dot = graphviz.Digraph(format="svg")
63+
64+
# Set orientation
65+
if orientation == "Left-Right":
66+
dot.attr(rankdir="LR")
67+
68+
# Set global attributes
69+
dot.attr("graph")
70+
dot.attr("node", shape="box", style="rounded")
71+
72+
# Add all nodes
73+
for node in all_employees:
74+
if node == st.session_state.selected_employee:
75+
dot.node(node, style="filled,rounded", fillcolor="#ffd966")
76+
else:
77+
dot.node(node)
78+
79+
# Add all edges
80+
for manager, subordinates in mapping.items():
81+
for subordinate in subordinates:
82+
dot.edge(manager, subordinate)
83+
84+
return dot
85+
86+
# Create chart
87+
chart = create_org_chart()
88+
89+
# Generate SVG for zoomable display
90+
svg_code = chart.pipe(format='svg').decode('utf-8')
91+
92+
# Create HTML with zoom and pan capabilities
93+
html_chart = f"""
94+
<div style="border:1px solid #ddd; width:100%; height:500px; overflow:auto;">
95+
<div style="transform:scale({zoom_level/100}); transform-origin:top left;">
96+
{svg_code}
97+
</div>
98+
</div>
99+
"""
100+
101+
# Display the chart with HTML component
102+
st.components.v1.html(html_chart, height=520, scrolling=True)
103+
104+
# Show details section for selected employee
105+
# ------------------------------------------------------------
106+
# 📋 Employee profile & goal alignment
107+
# ------------------------------------------------------------
108+
if st.session_state.selected_employee:
109+
emp = st.session_state.selected_employee
110+
with st.spinner("Loading employee details...", show_time=True):
111+
# ── Fetch data ────────────────────────────────────────────
112+
manager = next((m for m, emps in mapping.items() if emp in emps), None)
113+
reports = mapping.get(emp, [])
114+
v_align, h_align = check_goal_alignment(employees_df, emp, manager)
115+
goals_df = fetch_goals_from_emp(employees_df, emp)
116+
117+
# ── PROFILE HEADER ───────────────────────────────────────
118+
with st.container(border=True):
119+
head, metrics = st.columns([3, 2])
120+
121+
with head:
122+
st.header(f"👤 {emp.splitlines()[0]}")
123+
title = emp.splitlines()[1][1:-1] if len(emp.splitlines()) > 1 else ""
124+
st.caption(title)
125+
126+
rel_emp = ""
127+
if manager:
128+
m_name = manager.split('(')[0]
129+
rel_emp = rel_emp + f"**Manager:** {m_name} "
130+
if len(reports) > 0:
131+
rel_emp = rel_emp + f"&emsp;|&emsp; **Direct reports:** {len(reports)}"
132+
133+
st.write(rel_emp, unsafe_allow_html=True)
134+
135+
with metrics:
136+
# side-by-side animated metrics
137+
if v_align:
138+
st.metric("Vertical alignment", f"{v_align}% ", f"{100-v_align}%")
139+
if h_align:
140+
st.metric("Horizontal alignment", f"{h_align}% ", f"{100-h_align}%")
141+
142+
# animated progress bars look slick and let the user
143+
if v_align:
144+
st.progress(v_align / 100.0, text="Vertical goal alignment")
145+
if h_align:
146+
st.progress(h_align / 100.0, text="Horizontal goal alignment")
147+
148+
# ------------------------------------------------------------
149+
# 🎯 Goal quality & list (robust, no KeyErrors)
150+
# ------------------------------------------------------------
151+
SMART_COL = "smart" # original column
152+
LABEL_COL = "Quality" # new column for the emoji chip
153+
154+
# --- 1. create a Boolean mask -------------------------------
155+
mask = goals_df[SMART_COL].astype(str).str.lower().isin({"yes", "true", "smart"})
156+
goals_pretty = goals_df.copy()
157+
goals_pretty[LABEL_COL] = mask.map({True: "✅ SMART", False: "⚪️ Not SMART"})
158+
goals_pretty.drop(columns=[SMART_COL], inplace=True)
159+
160+
# move the chip to the front
161+
first = goals_pretty.pop(LABEL_COL)
162+
goals_pretty.insert(0, LABEL_COL, first)
163+
164+
# --- 2. quick KPI -------------------------------------------
165+
total, good = len(goals_pretty), mask.sum()
166+
pct_good = int(good / total * 100) if total else 0
167+
168+
c_num, c_bar = st.columns([1, 4])
169+
with c_num:
170+
st.metric("SMART goals", f"{good}/{total}", f"{pct_good}%")
171+
with c_bar:
172+
st.progress(pct_good / 100, text=f"{pct_good}% SMART")
173+
174+
st.divider()
175+
176+
# --- 3. style just the chip column --------------------------
177+
def chip_style(val):
178+
return (
179+
"background-color:#d4edda;font-weight:bold"
180+
if "✅" in val
181+
else "background-color:#f8d7da;font-weight:bold"
182+
)
183+
184+
styled = goals_pretty.style.applymap(chip_style, subset=[LABEL_COL])
185+
st.dataframe(styled, use_container_width=True, hide_index=True)

0 commit comments

Comments
 (0)