diff --git a/.streamlit/config.toml b/.streamlit/config.toml index 6bb3aa9..b3b75b8 100644 --- a/.streamlit/config.toml +++ b/.streamlit/config.toml @@ -5,5 +5,7 @@ backgroundColor="#2c3e50" secondaryBackgroundColor="#34495e" textColor="#ffffff" font="sans serif" +layout="wide" -layout="wide" \ No newline at end of file +[browser] +serverAddress = "reporting.stines.de" \ No newline at end of file diff --git a/app.py b/app.py index 429cc27..c126d8b 100644 --- a/app.py +++ b/app.py @@ -1,52 +1,145 @@ import streamlit as st +from streamlit_option_menu import option_menu import sites.services_reporting as sr import sites.userlist as us import sites.server as s import sites.tickets as ti import sites.lastrun as lr +from datetime import datetime +from dateutil.relativedelta import relativedelta +import mysql.connector +import pandas as pd +import os + # Page Settings -st.set_page_config(page_title="Reporting",layout="wide") +st.set_page_config(page_title="Reporting", layout="wide") + +start_date = datetime.today().replace(day=1) - relativedelta(months=1) +end_date = datetime.today().replace(day=1) - relativedelta(days=1) + +# Datumsformatierung +start_date_format = start_date.strftime("%Y-%m-%d") +end_date_format = end_date.strftime("%Y-%m-%d") # Load custom CSS def load_css(file_name): with open(file_name) as f: st.markdown(f'', unsafe_allow_html=True) -load_css('style.css') -# Define page functions +def get_customer_used_service(): + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + + mycursor = mydb.cursor() + + mycursor.execute(f"""SELECT c.companyname, c.customer_ID, cs.services_ID,s.name from Kunden.`customers.services` cs + JOIN Kunden.company c ON c.customer_ID = cs.customer_ID + JOIN Kunden.services s ON s.service_ID = cs.services_ID + where 1=1 GROUP by c.companyname , c.customer_ID, cs.services_ID, s.name """) + + myresult = mycursor.fetchall() + mydb.close() + return myresult + +def load_user_service_list(service_id, customer_id, start_date, end_date): + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + + query = f""" + SELECT COUNT(*) AS max_count + FROM ( + SELECT + u.user_id, + u.username, + active_users.last_active_timestamp, + active_users.status + FROM Kunden.`users` u + JOIN ( + SELECT + us.user_id, + us.`timestamp` AS last_active_timestamp, + us.status + FROM Kunden.`users.services` us + JOIN ( + SELECT + user_id, + MAX(`timestamp`) AS last_active_timestamp + FROM Kunden.`users.services` + WHERE `timestamp` <= '{end_date}' + AND customer_id = {customer_id} + AND service_ID = {service_id} + AND user_id NOT IN ( + SELECT user_id + FROM Kunden.`users.status` + WHERE status = 0 + AND `timestamp` < '{start_date}' + AND customer_id = {customer_id} + ) + GROUP BY user_id + ) max_timestamps + ON us.user_id = max_timestamps.user_id + AND us.`timestamp` = max_timestamps.last_active_timestamp + WHERE us.customer_id = {customer_id} + AND us.service_ID = {service_id} + AND us.status = 1 + ) active_users + ON u.user_id = active_users.user_id + ) AS max_count; + + """ + max_user_count = pd.read_sql_query(query, mydb) + mydb.close() + return max_user_count.iloc[0]['max_count'] if not max_user_count.empty else 0 + def home(): - st.title("Home Page") - st.write("Welcome to the Home Page!") + st.title("Dashboard") + edit_start_date = start_date.strftime("%d.%m.%Y") + edit_end_date = end_date.strftime("%d.%m.%Y") + st.subheader(f"Übersicht {edit_start_date} - {edit_end_date}") + previous_value = None + columns = None + c = 0 + for i in get_customer_used_service(): + if previous_value != i[1]: + st.subheader(f"{i[0]}") + columns = st.columns(3) + c = 0 + if not (load_user_service_list(i[2], i[1], start_date, end_date)): + st.info(f"Kunde {i[0]} - Service {i[3]} - Not Data found!") + else: + columns[c].metric(label=f"Aktive {i[3]} User", value=load_user_service_list(i[2], i[1], start_date, end_date)) + c +=1 + previous_value = i[1] +# Navigation bar using streamlit-option-menu +with st.sidebar: + selected_page = option_menu( + menu_title="Navigation", # required + options=["Dashboard", "Services Reporting", "User Filter", "Server", "Tickets", "Last-Run"], # required + icons=["house", "bar-chart", "filter", "server", "ticket", "clock"], # optional + menu_icon="cast", # optional + default_index=0, # optional + orientation="vertikal", # horizontal navigation + ) -if 'page' not in st.session_state: - st.session_state.page = 'Home' - -# Sidebar navigation -st.sidebar.title("Navigation") -if st.sidebar.button('Home'): - st.session_state.page = 'Home' -if st.sidebar.button('Services Reporting'): - st.session_state.page = 'Services Reporting' -if st.sidebar.button('User Filter'): - st.session_state.page = 'User Filter' -if st.sidebar.button('Server'): - st.session_state.page = 'Server' -if st.sidebar.button('Tickets'): - st.session_state.page = 'Tickets' -if st.sidebar.button('Last-Run'): - st.session_state.page = 'Last-Run' - -# Page display logic -if st.session_state.page == 'Home': +# Page display logic based on selected option +if selected_page == "Dashboard": home() -elif st.session_state.page == 'Services Reporting': +elif selected_page == "Services Reporting": sr.services_reporting() -elif st.session_state.page == 'User Filter': +elif selected_page == "User Filter": us.user_filter() -elif st.session_state.page == 'Server': +elif selected_page == "Server": s.server_filter() -elif st.session_state.page == 'Tickets': +elif selected_page == "Tickets": ti.ticket_filter() -elif st.session_state.page == 'Last-Run': - lr.user_filter() \ No newline at end of file +elif selected_page == "Last-Run": + lr.user_filter() diff --git a/apps/ticket_export/exports/RE2025.00012.1.docx b/apps/ticket_export/exports/RE2025.00012.1.docx new file mode 100644 index 0000000..eb19088 Binary files /dev/null and b/apps/ticket_export/exports/RE2025.00012.1.docx differ diff --git a/apps/ticket_export/exports/RE2025.00014.1.docx b/apps/ticket_export/exports/RE2025.00014.1.docx new file mode 100644 index 0000000..d81fbb8 Binary files /dev/null and b/apps/ticket_export/exports/RE2025.00014.1.docx differ diff --git a/apps/ticket_export/main.py b/apps/ticket_export/main.py index 83a0a43..c3a1ce1 100644 --- a/apps/ticket_export/main.py +++ b/apps/ticket_export/main.py @@ -25,10 +25,11 @@ def fetch_tickets_from_database(): # Tickets abrufen, inklusive customer_ID cursor.execute(""" - SELECT t.`number`, t.title, t.createdate, t.`type`, t.customer_ID, tct.firstdate, t.time, s.price, t.service_ID + SELECT t.`number`, t.title, t.createdate, t.`type`, t.customer_ID, tct.firstdate, t.time, s.price, t.service_ID, t.tags FROM Kunden.tickets t JOIN Kunden.`tickets.customer.timerange` tct ON t.customer_ID = tct.customer_ID JOIN Kunden.services s ON s.service_ID = t.service_ID + WHERE closedate >= DATE_SUB(CURDATE(), INTERVAL 32 DAY) ORDER by t.createdate ASC """) tickets = cursor.fetchall() @@ -136,7 +137,7 @@ def set_cell_border(cell, **kwargs): def fill_template(doc_path, output_path, data, tickets): doc = Document(doc_path) - + print("Korrekt") # Platzhalter in normalem Text ersetzen for paragraph in doc.paragraphs: for key, value in data.items(): @@ -161,8 +162,15 @@ def fill_template(doc_path, output_path, data, tickets): second_table = doc.tables[1] for ticket in tickets: row = second_table.add_row() + ticket_number, title, createdate, _, _, _, timerange, _, _, tags = ticket + + print(ticket[9]) + row.cells[0].text = ticket[0] # Ticketnummer - row.cells[1].text = ticket[1] # Tickettitel + if ticket[9]: + row.cells[1].text = f"{ticket[1]} - {ticket[9]}" # Tickettitel + else: + row.cells[1].text = ticket[1] row.cells[2].text = ticket[2].strftime("%d.%m.%Y") # createdate als String formatieren row.cells[3].text = str(ticket[6]) # timerange (time_unit) @@ -227,12 +235,16 @@ if __name__ == "__main__": service_ID = next(iter(set(ticket[8] for ticket in tickets))) price = next(iter(set(ticket[7] for ticket in tickets))) customer_ids = set(ticket[4] for ticket in tickets) + + print(tickets) + print(customer_ids) for customer_id in customer_ids: customer_data = fetch_customer_data(customer_id) customer_servicetime = fetch_customer_servicetime(customer_id) customer_price = fetch_customer_price(price, customer_id, service_ID) customer_tickets = [ticket for ticket in tickets if ticket[4] == customer_id] + print(customer_data) if not customer_data: print(f"Keine Kundendaten für Kunden-ID {customer_id} gefunden!") @@ -257,22 +269,21 @@ if __name__ == "__main__": "city": customer_data[4], "customernumber": customer_data[5], "year": datetime.now().year, - "ordernumber": "0001", + "ordernumber": "1", "startdate": startdate.strftime("%d.%m.%Y"), "enddate": enddate.strftime("%d.%m.%Y"), "today": datetime.now().strftime("%d.%m.%Y"), "price_per_minute": customer_price, "servicetime": customer_servicetime[0], "gesamt_time_unit": 0, - "price_ex_mwst": 0, + "price_ex_mwst": "0,00", "sl_time_unit": 0, "sl_minus_unit": 0, "zl_time_unit": 0, "time_unit_sum": 0, - "mwst_set": 0, - "sum": 0, + "mwst_set": "0,00", + "sum": "0,00", } - - output_path = f"apps/ticket_export/exports/RE2024.{customer_data[5]}.{data['ordernumber']}.docx" + output_path = f"apps/ticket_export/exports/RE2025.{customer_data[5]}.{data['ordernumber']}.docx" fill_template('apps/ticket_export/template.docx', output_path, data, customer_tickets) print("True") diff --git a/apps/ticket_export/template.docx b/apps/ticket_export/template.docx index 18a9b43..6fd4bf7 100644 Binary files a/apps/ticket_export/template.docx and b/apps/ticket_export/template.docx differ diff --git a/docker-compose.yaml b/docker-compose.yaml index 5c1bf9c..aa49bcb 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,12 +1,12 @@ -version: "3.5" services: streamlit: - build: . - container_name: streamlit - ports: - - 80:80 - networks: - - frontend + build: + context: . + dockerfile: dockerfile + ports: + - 80:80 + networks: + - frontend networks: frontend: diff --git a/sites/__pycache__/lastrun.cpython-39.pyc b/sites/__pycache__/lastrun.cpython-39.pyc new file mode 100644 index 0000000..ee9806b Binary files /dev/null and b/sites/__pycache__/lastrun.cpython-39.pyc differ diff --git a/sites/__pycache__/server.cpython-39.pyc b/sites/__pycache__/server.cpython-39.pyc new file mode 100644 index 0000000..887a935 Binary files /dev/null and b/sites/__pycache__/server.cpython-39.pyc differ diff --git a/sites/__pycache__/services_reporting.cpython-39.pyc b/sites/__pycache__/services_reporting.cpython-39.pyc new file mode 100644 index 0000000..901fa04 Binary files /dev/null and b/sites/__pycache__/services_reporting.cpython-39.pyc differ diff --git a/sites/__pycache__/tickets.cpython-39.pyc b/sites/__pycache__/tickets.cpython-39.pyc new file mode 100644 index 0000000..163a8ab Binary files /dev/null and b/sites/__pycache__/tickets.cpython-39.pyc differ diff --git a/sites/__pycache__/userlist.cpython-39.pyc b/sites/__pycache__/userlist.cpython-39.pyc new file mode 100644 index 0000000..84e7adf Binary files /dev/null and b/sites/__pycache__/userlist.cpython-39.pyc differ diff --git a/sites/server.py b/sites/server.py index 98d435b..9affea5 100644 --- a/sites/server.py +++ b/sites/server.py @@ -8,7 +8,7 @@ from dotenv import load_dotenv load_dotenv() -def get_filtered_server(customer_id, service_id, service_status): +def get_filtered_server(customer_ids, service_id, service_status, os_type, reporting): mydb = mysql.connector.connect( host=os.getenv("MYSQL_HOST"), user=os.getenv("MYSQL_USER"), @@ -18,23 +18,30 @@ def get_filtered_server(customer_id, service_id, service_status): # Prepare the base query query = f""" - select s.hostname,s.privat_ipaddress,s.public_ipaddress, s.ram, s.createdate, s.disabledate,s.customer_ID,s.server_ID,hc.name,hc.core + select s.hostname, s.privat_ipaddress, s.public_ipaddress, s.ram, s.createdate, s.disabledate, s.os, s.customer_ID, s.server_ID, hc.name, hc.core from Kunden.server s join Kunden.`hardware.cpu` hc ON hc.cpu_ID = s.cpu_ID WHERE 1=1 """ - if customer_id: - query += f"AND s.customer_ID = {customer_id}" + + # If multiple customers are selected, use the IN clause + if customer_ids: + customer_ids_str = ', '.join([str(id) for id in customer_ids]) + query += f" AND s.customer_ID IN ({customer_ids_str})" + if service_id: query += f" AND s.service_ID = {service_id}" if service_status: query += f" AND s.status = {service_status}" + if os_type: + query += f" AND s.os = '{os_type}'" + if reporting == "True": + query += f" AND licensekey IS NOT NULL" users = pd.read_sql_query(query, mydb) mydb.close() return users - def get_initial_data(): mydb = mysql.connector.connect( host=os.getenv("MYSQL_HOST"), @@ -60,7 +67,6 @@ def get_initial_data(): mydb.close() return service_ids, customers - def server_filter(): st.title("Server Filter :mag_right:") # Get initial data for widgets @@ -68,15 +74,17 @@ def server_filter(): # Combine service_ID and name for display service_options = initial_service_ids.apply(lambda row: f"{row['service_ID']} - {row['name']}", axis=1) - # Add selection widget for customer ID - selected_customer = st.selectbox( - 'Select Customer', - ["All"] + customers.apply(lambda row: f"{row['customer_ID']} - {row['companyname']} - {row['customer']}", - axis=1).tolist() + # Create a dictionary for customer selection + customer_dict = {f"{row['companyname']} - {row['customer']}": row['customer_ID'] for _, row in customers.iterrows()} + + # Use multiselect for multiple customer selection + selected_customers = st.multiselect( + 'Select Customer(s)', + list(customer_dict.keys()) # Display only companyname and customer ) - # Extract customer_ID from selected option - selected_customer_id = None if selected_customer == "All" else int(selected_customer.split(' - ')[0]) + # Get the corresponding customer IDs + selected_customer_ids = [customer_dict[customer] for customer in selected_customers] # Add selection widget for service ID selected_service = st.selectbox( @@ -96,13 +104,33 @@ def server_filter(): # Extract status from selected option service_status = None if selected_status == "All" else int(selected_status.split(' - ')[0]) + # Add SPLA server selection + reporting_box = st.selectbox( + 'Select SPLA Server', + ["Nein", "Ja"] + ) + + # Extract reporting status + reporting = None if reporting_box == "Nein" else "True" + + # Add OS type selection + os_box = st.selectbox( + 'Select OS Type', + ["All", "Linux", "Windows"] + ) + + # Extract OS type + os_type = None if os_box == "All" else os_box + # Add a button to apply filters if st.button('Apply Filters'): # Fetch filtered data from the database - filtered_data = get_filtered_server(selected_customer_id, selected_service_id, service_status) - + filtered_data = get_filtered_server(selected_customer_ids, selected_service_id, service_status, os_type, reporting) # Display the filtered data if not filtered_data.empty: st.dataframe(filtered_data) + st.text(f"CPU SUMME = {sum(filtered_data['core'])}" ) + st.text(f"Berechung der Core-Pakete: Anzahl der Cores({filtered_data['core'].count()}) * Core-Pakete aus SPLA (8) / 2") + st.text(f"Reporting Core-Pakete = {(filtered_data['core'].count())*8/2}".split('.')[0]) else: st.write("No data available for the selected filters.") \ No newline at end of file diff --git a/sites/services_reporting.py b/sites/services_reporting.py index 84ac954..094762c 100644 --- a/sites/services_reporting.py +++ b/sites/services_reporting.py @@ -4,11 +4,14 @@ import mysql.connector from datetime import datetime, date import os from dotenv import load_dotenv +import altair as alt load_dotenv() - def get_filtered_data(customer_id, service_id, start_date, end_date): + """ + Fetches the user count data grouped by month within the specified date range. + """ mydb = mysql.connector.connect( host=os.getenv("MYSQL_HOST"), user=os.getenv("MYSQL_USER"), @@ -16,9 +19,8 @@ def get_filtered_data(customer_id, service_id, start_date, end_date): database=os.getenv("MYSQL_DATABASE") ) - # Prepare the query query = f""" - SELECT DATE_FORMAT(sr.reportingdate, '%Y-%m') AS month, + SELECT DATE_FORMAT(sr.reportingdate, '%Y-%m') AS day, COUNT(DISTINCT sr.username) as count FROM Kunden.`services.reporting` sr JOIN Kunden.services s ON sr.service_ID = s.service_ID @@ -36,6 +38,163 @@ def get_filtered_data(customer_id, service_id, start_date, end_date): return service_reporting +def get_user_online(customer_id,service_id,start_date,end_date): + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + if service_id == 100: + user_info = "u.primarymail as username" + else: + user_info = "u.username" + query = f""" + SELECT DATE_FORMAT(sr.reportingdate, '%Y-%m') AS day, + sr.username + FROM Kunden.`services.reporting` sr + JOIN Kunden.services s ON sr.service_ID = s.service_ID + WHERE sr.customer_ID = {customer_id} + AND sr.service_ID = {service_id} + AND sr.username NOT LIKE '%admin%' + AND sr.username NOT LIKE '%test%' + AND sr.reportingdate BETWEEN '{start_date}' AND '{end_date}' + GROUP BY day, sr.username + ORDER BY sr.username; + """ + + user_online = pd.read_sql_query(query, mydb) + user_online_count= user_online.shape[0] + mydb.close() + return user_online, user_online_count + +def get_max_user_count(customer_id, service_id, start_date, end_date): + """ + Fetches the maximum user count within the specified date range. + """ + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + + query = f""" + SELECT MAX(user_counts.count) as max_count + FROM ( + SELECT DATE_FORMAT(sr.reportingdate, '%Y-%m') AS day, + COUNT(DISTINCT sr.username) as count + FROM Kunden.`services.reporting` sr + JOIN Kunden.services s ON sr.service_ID = s.service_ID + WHERE sr.customer_ID = {customer_id} + AND sr.service_ID = {service_id} + AND sr.username NOT LIKE '%admin%' + AND sr.username NOT LIKE '%test%' + AND sr.reportingdate BETWEEN '{start_date}' AND '{end_date}' + GROUP BY DATE_FORMAT(sr.reportingdate, '%Y-%m') + ) as user_counts; + """ + + max_user_count = pd.read_sql_query(query, mydb) + mydb.close() + return max_user_count.iloc[0]['max_count'] if not max_user_count.empty else 0 + +def get_active_users(customer_id, service_id, start_date, end_date): + """ + Fetch all active users for the given customer, service, and date range + based on the most recent activity and status. + """ + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + if service_id == 100: + user_info = "u.primarymail as username" + else: + user_info = "u.username" + query = f""" + SELECT + {user_info} + FROM Kunden.`users` u + JOIN ( + SELECT + us.user_id, + us.`timestamp` AS last_active_timestamp, + us.status + FROM Kunden.`users.services` us + JOIN ( + SELECT + user_id, + MAX(`timestamp`) AS last_active_timestamp + FROM Kunden.`users.services` + WHERE `timestamp` <= '{end_date}' + AND customer_id = {customer_id} + AND service_ID = {service_id} + AND user_id NOT IN ( + SELECT user_id + FROM Kunden.`users.status` + WHERE status = 0 + AND `timestamp` < '{start_date}' + AND customer_id = {customer_id} + ) + GROUP BY user_id + ) max_timestamps ON us.user_id = max_timestamps.user_id + AND us.`timestamp` = max_timestamps.last_active_timestamp + WHERE us.customer_id = {customer_id} + AND us.service_ID = {service_id} + AND us.status = 1 + ) active_users ON u.user_id = active_users.user_id + GROUP BY u.user_id, u.username, active_users.last_active_timestamp, active_users.status ORDER by u.username ASC; + """ + active_users = pd.read_sql_query(query, mydb) + user_active_count = active_users.shape[0] + mydb.close() + return active_users, user_active_count + +def get_user_not_online(customer_id,service_id,start_date,end_date): + mydb = mysql.connector.connect( + host=os.getenv("MYSQL_HOST"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + database=os.getenv("MYSQL_DATABASE") + ) + + query = f""" + SELECT u.username, ss.service_ID + FROM Kunden.users u + JOIN Kunden.`users.services` ss ON ss.user_ID = u.user_ID + JOIN ( + SELECT user_id, + MAX(CASE WHEN status = 1 THEN timestamp END) AS latest_active_timestamp, + MAX(CASE WHEN status = 0 THEN timestamp END) AS latest_inactive_timestamp + FROM Kunden.`users.status` + GROUP BY user_id + ) us ON u.user_ID = us.user_id + WHERE ss.service_ID = {service_id} + AND ( + (us.latest_active_timestamp IS NOT NULL AND + (us.latest_inactive_timestamp IS NULL OR us.latest_active_timestamp > us.latest_inactive_timestamp)) + OR us.latest_inactive_timestamp IS NULL + ) + AND u.username NOT IN ( + SELECT sr.username + FROM Kunden.`services.reporting` sr + WHERE sr.service_ID = {service_id} + AND sr.reportingdate BETWEEN '{start_date}' AND '{end_date}' + ) + AND u.user_ID != 95 + AND u.user_ID != 102 + AND u.customer_ID = {customer_id} + AND u.username NOT LIKE "%ad-test-user%" + """ + + not_active_users = pd.read_sql_query(query, mydb) + user_not_online_count = not_active_users.shape[0] + mydb.close() + return not_active_users, user_not_online_count + def get_initial_data(): mydb = mysql.connector.connect( host=os.getenv("MYSQL_HOST"), @@ -43,6 +202,7 @@ def get_initial_data(): password=os.getenv("MYSQL_PASSWORD"), database=os.getenv("MYSQL_DATABASE") ) + # Fetch unique service IDs and names service_id_query = """ SELECT DISTINCT s.service_ID, s.name @@ -71,61 +231,98 @@ def get_initial_data(): return service_ids, customers, date_range -def generate_month_range(min_date, max_date): - months = pd.date_range(start=min_date, end=max_date, freq='MS').strftime("%Y-%m").tolist() - return months - def services_reporting(): st.title("Reporting :mag_right:") + # Get initial data for widgets initial_service_ids, customers, initial_date_range = get_initial_data() - # Combine service_ID and name for display service_options = initial_service_ids.apply(lambda row: f"{row['service_ID']} - {row['name']}", axis=1) - # Add selection widget for customer ID + # Selection widget for customer ID + customer_dict = {f"{row['companyname']} - {row['customer']}": row['customer_ID'] for _, row in customers.iterrows()} + + # Selectbox with only the customer name and company displayed selected_customer = st.selectbox( 'Select Customer', - customers.apply(lambda row: f"{row['customer_ID']} - {row['companyname']} - {row['customer']}", axis=1).tolist() + list(customer_dict.keys()) # Display only companyname and customer ) - # Extract customer_ID from selected option - selected_customer_id = int(selected_customer.split(' - ')[0]) + # Get the corresponding customer ID + selected_customer_id = customer_dict[selected_customer] - # Add selection widget for service ID + # Selection widget for service ID selected_service = st.selectbox( 'Select Service', service_options.tolist() ) - - # Extract service_ID from selected option selected_service_id = int(selected_service.split(' - ')[0]) # Convert date range to datetime objects min_date = initial_date_range['min_date'][0] max_date = initial_date_range['max_date'][0] - # Generate month options for dropdown - month_options = generate_month_range(min_date, max_date) + # Date input for start and end date + start_date = st.date_input('Start Date', min_date) + end_date = st.date_input('End Date', max_date) - # Add dropdown for start and end months - start_month = st.selectbox('Start Month', month_options) - end_month = st.selectbox('End Month', month_options, index=len(month_options) - 1) - - # Convert 'YYYY-MM' to 'YYYY-MM-DD' format for SQL query - start_date = datetime.strptime(start_month, '%Y-%m').date().replace(day=1) - end_date = datetime.strptime(end_month, '%Y-%m').date().replace(day=1) - end_date = (end_date.replace(day=28) + pd.DateOffset(days=4)).replace(day=1) - pd.DateOffset(days=1) # last day of the month - - # Add a button to apply filters if st.button('Apply Filters'): # Fetch filtered data from the database filtered_data = get_filtered_data(selected_customer_id, selected_service_id, start_date, end_date) - # Sort the data by month - filtered_data = filtered_data.sort_values('month') + # Fetch max user count in the selected range + max_count = get_max_user_count(selected_customer_id, selected_service_id, start_date, end_date) + + # Sort the data by day + filtered_data = filtered_data.sort_values('day') - # Create a bar chart with the filtered data if not filtered_data.empty: - st.bar_chart(filtered_data.set_index('month')['count']) + # Highlight the max value in the chart + filtered_data['color'] = filtered_data['count'].apply(lambda x: 'red' if x == max_count else 'steelblue') + + # Create an Altair bar chart + bars = alt.Chart(filtered_data).mark_bar().encode( + x='day:O', + y='count:Q', + color=alt.Color('color:N', scale=None, legend=None) + ) + + # Add text labels to bars + text = bars.mark_text( + align='center', + baseline='middle', + dy=-10 + ).encode( + text='count:Q' + ) + + # Combine bars and text into a single chart + chart = (bars + text).properties( + title='Daily Service Usage with Highlighted Maximum' + ) + + # Fetch the data for users not online, online, and active users + not_user_online, max_count_user_not_online = get_user_not_online(selected_customer_id, selected_service_id, start_date, end_date) + user_online, user_online_count = get_user_online(selected_customer_id, selected_service_id, start_date, end_date) + active_users_data, user_active_count = get_active_users(selected_customer_id, selected_service_id, start_date, end_date) + # Create three columns for each DataFrame + col1, col2, col3, col4, col5 = st.columns([2,2,2,2,2]) + + # Display each DataFrame in a separate column + with col4: + st.subheader(f"{selected_service.split(' - ')[1]} - User not Online") + st.metric(label="1",label_visibility="hidden", value=max_count_user_not_online) + st.data_editor(not_user_online['username'],use_container_width=True, hide_index=True) + + with col2: + st.subheader(f"{selected_service.split(' - ')[1]} - User Online") + st.metric(label="1",label_visibility="hidden", value=user_online_count) + st.data_editor(user_online['username'],use_container_width=True, hide_index=True) + + with col3: + st.subheader(f"{selected_service.split(' - ')[1]} - Active Users") + st.metric(label="1",label_visibility="hidden", value=user_active_count) + st.data_editor(active_users_data['username'],key=active_users_data,use_container_width=True, hide_index=True) + + st.altair_chart(chart, use_container_width=True) else: - st.write("No data available for the selected filters.") \ No newline at end of file + st.write("No data available for the selected filters.") diff --git a/sites/tickets.py b/sites/tickets.py index 60ccc79..b1c026e 100644 --- a/sites/tickets.py +++ b/sites/tickets.py @@ -20,7 +20,7 @@ def get_filtered_users(customer_id, start_date, end_date): # Prepare the base query with date filter query = f""" SELECT number, title, createdate, time FROM Kunden.tickets - WHERE createdate BETWEEN '{start_date}' AND '{end_date}' + WHERE closedate BETWEEN '{start_date}' AND '{end_date}' """ if customer_id: query += f" AND customer_ID = {customer_id}" @@ -149,4 +149,4 @@ def ticket_filter(): st.write("No data available for the selected filters.") if __name__ == "__main__": - ticket_filter() + ticket_filter() \ No newline at end of file diff --git a/streamlit.sh b/streamlit.sh new file mode 100644 index 0000000..6f9039d --- /dev/null +++ b/streamlit.sh @@ -0,0 +1 @@ +streamlit run app.py --server.port=80 --server.address=0.0.0.0 diff --git a/style.css b/style.css index 6f30cc1..c2bec67 100644 --- a/style.css +++ b/style.css @@ -32,4 +32,20 @@ } .st-d7 , .st-d6 ,.st-d5 ,.st-d4 { border-color: #fcbb2e; +} + +.st-emotion-cache-1gwvy71{ + padding: 0; + padding-left: 30px; +} +.e1f1d6gn4 > .stButton > button{ + width: 100%; + justify-content: left; + border: 0; +} +[data-testid="baseButton-secondary"]:focus{ + background-color: #f4f3f366 !important; +} +[data-testid="baseButton-secondary"]:active{ + background-color: #f4f3f366!important; } \ No newline at end of file