Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
@@ -727,471 +727,555 @@ def generate_usage_html(usage_data: dict, days: int = 7):
|
|
727 |
daily_dates = list(usage_data['daily_usage_period'].keys())
|
728 |
daily_requests = [data['requests'] for data in usage_data['daily_usage_period'].values()]
|
729 |
daily_unique_ips = [data['unique_ips_count'] for data in usage_data['daily_usage_period'].values()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
730 |
|
731 |
model_usage_all_time_rows = "\n".join([
|
732 |
f"""
|
733 |
-
<tr>
|
734 |
-
<td>
|
735 |
-
|
736 |
-
|
737 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
738 |
</tr>
|
739 |
""" for model, stats in usage_data['all_time_model_usage'].items()
|
740 |
])
|
741 |
|
742 |
api_usage_all_time_rows = "\n".join([
|
743 |
f"""
|
744 |
-
<tr>
|
745 |
-
<td>
|
746 |
-
|
747 |
-
|
748 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
749 |
</tr>
|
750 |
""" for endpoint, stats in usage_data['all_time_endpoint_usage'].items()
|
751 |
])
|
752 |
|
753 |
-
daily_usage_table_rows = "\n".join([
|
754 |
-
f"""
|
755 |
-
<tr>
|
756 |
-
<td>{date}</td>
|
757 |
-
<td>{data['requests']}</td>
|
758 |
-
<td>{data['unique_ips_count']}</td>
|
759 |
-
</tr>
|
760 |
-
""" for date, data in usage_data['daily_usage_period'].items()
|
761 |
-
])
|
762 |
-
|
763 |
recent_requests_rows = "\n".join([
|
764 |
f"""
|
765 |
-
<tr>
|
766 |
-
<td>{datetime.datetime.fromisoformat(req['timestamp']).strftime("%Y-%m-%d %H:%M:%S")}</td>
|
767 |
-
<td>
|
768 |
-
|
769 |
-
|
770 |
-
|
|
|
|
|
|
|
|
|
771 |
</tr>
|
772 |
""" for req in usage_data['recent_requests']
|
773 |
])
|
774 |
-
|
775 |
html_content = f"""
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
.logo h1 {{
|
813 |
-
font-weight: 700;
|
814 |
-
font-size: 2.8em;
|
815 |
-
color: var(--text-primary);
|
816 |
-
margin-left: 15px;
|
817 |
-
}}
|
818 |
-
.logo img {{
|
819 |
-
width: 70px;
|
820 |
-
height: 70px;
|
821 |
-
border-radius: 12px;
|
822 |
-
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
823 |
-
}}
|
824 |
-
.container {{
|
825 |
-
background-color: var(--bg-darker);
|
826 |
-
border-radius: 16px;
|
827 |
-
padding: 30px;
|
828 |
-
box-shadow: 0 20px 50px rgba(0,0,0,0.4);
|
829 |
-
border: 1px solid var(--border-color);
|
830 |
-
}}
|
831 |
-
h2, h3 {{
|
832 |
-
color: var(--text-primary);
|
833 |
-
border-bottom: 2px solid var(--border-color);
|
834 |
-
padding-bottom: 12px;
|
835 |
-
margin-top: 40px;
|
836 |
-
margin-bottom: 25px;
|
837 |
-
font-weight: 600;
|
838 |
-
font-size: 1.8em;
|
839 |
-
}}
|
840 |
-
.summary-grid {{
|
841 |
-
display: grid;
|
842 |
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
843 |
-
gap: 20px;
|
844 |
-
margin-bottom: 30px;
|
845 |
-
}}
|
846 |
-
.summary-card {{
|
847 |
-
background-color: var(--bg-dark);
|
848 |
-
border-radius: 10px;
|
849 |
-
padding: 20px;
|
850 |
-
text-align: center;
|
851 |
-
border: 1px solid var(--border-color);
|
852 |
-
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
|
853 |
-
transition: transform 0.2s ease-in-out;
|
854 |
-
}}
|
855 |
-
.summary-card:hover {{
|
856 |
-
transform: translateY(-5px);
|
857 |
-
}}
|
858 |
-
.summary-card h3 {{
|
859 |
-
margin-top: 0;
|
860 |
-
font-size: 1.1em;
|
861 |
-
color: var(--text-secondary);
|
862 |
-
border-bottom: none;
|
863 |
-
padding-bottom: 0;
|
864 |
-
margin-bottom: 10px;
|
865 |
-
}}
|
866 |
-
.summary-card p {{
|
867 |
-
font-size: 2.2em;
|
868 |
-
font-weight: 700;
|
869 |
-
color: var(--accent-color);
|
870 |
-
margin: 0;
|
871 |
-
}}
|
872 |
-
table {{
|
873 |
-
width: 100%;
|
874 |
-
border-collapse: separate;
|
875 |
-
border-spacing: 0;
|
876 |
-
margin-bottom: 40px;
|
877 |
-
background-color: var(--bg-dark);
|
878 |
-
border-radius: 10px;
|
879 |
-
overflow: hidden;
|
880 |
-
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
|
881 |
-
}}
|
882 |
-
th, td {{
|
883 |
-
border: 1px solid var(--border-color);
|
884 |
-
padding: 15px;
|
885 |
-
text-align: left;
|
886 |
-
transition: background-color 0.3s ease;
|
887 |
-
}}
|
888 |
-
th {{
|
889 |
-
background-color: #1a1a1a;
|
890 |
-
color: var(--text-primary);
|
891 |
-
font-weight: 600;
|
892 |
-
text-transform: uppercase;
|
893 |
-
font-size: 0.95em;
|
894 |
-
}}
|
895 |
-
tr:nth-child(even) {{
|
896 |
-
background-color: rgba(255,255,255,0.03);
|
897 |
-
}}
|
898 |
-
tr:hover {{
|
899 |
-
background-color: rgba(62,100,255,0.1);
|
900 |
-
}}
|
901 |
-
.chart-container {{
|
902 |
-
background-color: var(--bg-dark);
|
903 |
-
border-radius: 10px;
|
904 |
-
padding: 20px;
|
905 |
-
margin-bottom: 40px;
|
906 |
-
border: 1px solid var(--border-color);
|
907 |
-
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
|
908 |
-
max-height: 400px;
|
909 |
-
position: relative;
|
910 |
-
}}
|
911 |
-
canvas {{
|
912 |
-
max-width: 100% !important;
|
913 |
-
height: auto !important;
|
914 |
-
}}
|
915 |
-
@media (max-width: 768px) {{
|
916 |
-
body {{
|
917 |
-
padding: 20px 10px;
|
918 |
-
}}
|
919 |
-
.container {{
|
920 |
-
padding: 20px;
|
921 |
-
}}
|
922 |
-
.logo h1 {{
|
923 |
-
font-size: 2em;
|
924 |
-
}}
|
925 |
-
.summary-card p {{
|
926 |
-
font-size: 1.8em;
|
927 |
-
}}
|
928 |
-
h2, h3 {{
|
929 |
-
font-size: 1.5em;
|
930 |
-
}}
|
931 |
-
table {{
|
932 |
-
font-size: 0.85em;
|
933 |
-
}}
|
934 |
-
th, td {{
|
935 |
-
padding: 10px;
|
936 |
}}
|
937 |
-
|
938 |
-
</
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
951 |
</div>
|
952 |
-
|
953 |
-
|
954 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
955 |
</div>
|
956 |
-
|
957 |
-
|
958 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
959 |
</div>
|
960 |
-
|
961 |
-
|
962 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
963 |
</div>
|
964 |
</div>
|
965 |
-
|
966 |
-
|
967 |
-
<
|
968 |
-
<
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
<h2>API Endpoint Usage (Last {days} Days)</h2>
|
1003 |
-
<div class="chart-container">
|
1004 |
-
<canvas id="endpointUsageChart"></canvas>
|
1005 |
-
</div>
|
1006 |
-
<h3>API Endpoint Usage (All Time Details)</h3>
|
1007 |
-
<table>
|
1008 |
-
<thead>
|
1009 |
-
<tr>
|
1010 |
-
<th>Endpoint</th>
|
1011 |
-
<th>Total Requests</th>
|
1012 |
-
<th>First Used</th>
|
1013 |
-
<th>Last Used</th>
|
1014 |
-
</tr>
|
1015 |
-
</thead>
|
1016 |
-
<tbody>
|
1017 |
-
{api_usage_all_time_rows}
|
1018 |
-
</tbody>
|
1019 |
-
</table>
|
1020 |
-
|
1021 |
-
<h2>Recent Requests (Last 20)</h2>
|
1022 |
-
<table>
|
1023 |
-
<thead>
|
1024 |
-
<tr>
|
1025 |
-
<th>Timestamp</th>
|
1026 |
-
<th>Model</th>
|
1027 |
-
<th>Endpoint</th>
|
1028 |
-
<th>IP Address</th>
|
1029 |
-
<th>User Agent</th>
|
1030 |
-
</tr>
|
1031 |
-
</thead>
|
1032 |
-
<tbody>
|
1033 |
-
{recent_requests_rows}
|
1034 |
-
</tbody>
|
1035 |
-
</table>
|
1036 |
-
</div>
|
1037 |
-
|
1038 |
-
<script>
|
1039 |
-
const modelLabels = {json.dumps(model_labels)};
|
1040 |
-
const modelCounts = {json.dumps(model_counts)};
|
1041 |
-
|
1042 |
-
const endpointLabels = {json.dumps(endpoint_labels)};
|
1043 |
-
const endpointCounts = {json.dumps(endpoint_counts)};
|
1044 |
-
|
1045 |
-
const dailyDates = {json.dumps(daily_dates)};
|
1046 |
-
const dailyRequests = {json.dumps(daily_requests)};
|
1047 |
-
const dailyUniqueIps = {json.dumps(daily_unique_ips)};
|
1048 |
-
|
1049 |
-
new Chart(document.getElementById('modelUsageChart'), {{
|
1050 |
-
type: 'bar',
|
1051 |
-
data: {{
|
1052 |
-
labels: modelLabels,
|
1053 |
-
datasets: [{{
|
1054 |
-
label: 'Requests',
|
1055 |
-
data: modelCounts,
|
1056 |
-
backgroundColor: 'var(--chart-bg-light)',
|
1057 |
-
borderColor: 'var(--chart-border-light)',
|
1058 |
-
borderWidth: 1,
|
1059 |
-
borderRadius: 5,
|
1060 |
-
}}]
|
1061 |
-
}},
|
1062 |
-
options: {{
|
1063 |
responsive: true,
|
1064 |
maintainAspectRatio: false,
|
1065 |
plugins: {{
|
1066 |
legend: {{
|
1067 |
labels: {{
|
1068 |
-
color: '
|
|
|
|
|
|
|
|
|
|
|
1069 |
}}
|
1070 |
-
}},
|
1071 |
-
title: {{
|
1072 |
-
display: true,
|
1073 |
-
text: 'Model Usage',
|
1074 |
-
color: 'var(--text-primary)'
|
1075 |
}}
|
1076 |
}},
|
1077 |
scales: {{
|
1078 |
x: {{
|
1079 |
ticks: {{
|
1080 |
-
color: '
|
|
|
|
|
|
|
1081 |
}},
|
1082 |
grid: {{
|
1083 |
-
color: '
|
|
|
1084 |
}}
|
1085 |
}},
|
1086 |
y: {{
|
1087 |
beginAtZero: true,
|
1088 |
ticks: {{
|
1089 |
-
color: '
|
|
|
|
|
|
|
1090 |
}},
|
1091 |
grid: {{
|
1092 |
-
color: '
|
|
|
1093 |
}}
|
1094 |
}}
|
1095 |
}}
|
1096 |
-
}}
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
new Chart(document.getElementById('dailyRequestsChart'), {{
|
1133 |
-
type: 'line',
|
1134 |
-
data: {{
|
1135 |
-
labels: dailyDates,
|
1136 |
-
datasets: [
|
1137 |
-
{{
|
1138 |
-
label: 'Total Requests',
|
1139 |
-
data: dailyRequests,
|
1140 |
-
borderColor: 'var(--accent-color)',
|
1141 |
-
backgroundColor: 'rgba(58, 110, 224, 0.1)',
|
1142 |
-
fill: true,
|
1143 |
-
tension: 0.3
|
1144 |
-
}},
|
1145 |
-
{{
|
1146 |
-
label: 'Unique IPs',
|
1147 |
-
data: dailyUniqueIps,
|
1148 |
-
borderColor: '#FFCE56',
|
1149 |
-
backgroundColor: 'rgba(255, 206, 86, 0.1)',
|
1150 |
-
fill: true,
|
1151 |
-
tension: 0.3
|
1152 |
-
}}
|
1153 |
-
]
|
1154 |
-
}},
|
1155 |
-
options: {{
|
1156 |
-
responsive: true,
|
1157 |
-
maintainAspectRatio: false,
|
1158 |
-
plugins: {{
|
1159 |
-
legend: {{
|
1160 |
-
labels: {{
|
1161 |
-
color: 'var(--text-primary)'
|
1162 |
}}
|
1163 |
-
|
1164 |
-
title: {{
|
1165 |
-
display: true,
|
1166 |
-
text: 'Daily Requests and Unique IPs',
|
1167 |
-
color: 'var(--text-primary)'
|
1168 |
-
}}
|
1169 |
}},
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1177 |
}}
|
1178 |
}},
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1186 |
}}
|
1187 |
}}
|
1188 |
}}
|
1189 |
-
}}
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1195 |
return html_content
|
1196 |
|
1197 |
@app.on_event("startup")
|
|
|
727 |
daily_dates = list(usage_data['daily_usage_period'].keys())
|
728 |
daily_requests = [data['requests'] for data in usage_data['daily_usage_period'].values()]
|
729 |
daily_unique_ips = [data['unique_ips_count'] for data in usage_data['daily_usage_period'].values()]
|
730 |
+
daily_usage_table_rows = "\n".join([
|
731 |
+
f"""
|
732 |
+
<tr class="hover:bg-slate-700/20 transition-colors duration-200">
|
733 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-200">{date}</td>
|
734 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-300">
|
735 |
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-500/20 text-blue-300">
|
736 |
+
{data['requests']:,}
|
737 |
+
</span>
|
738 |
+
</td>
|
739 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-300">
|
740 |
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-500/20 text-emerald-300">
|
741 |
+
{data['unique_ips_count']:,}
|
742 |
+
</span>
|
743 |
+
</td>
|
744 |
+
</tr>
|
745 |
+
""" for date, data in usage_data['daily_usage_period'].items()
|
746 |
+
])
|
747 |
|
748 |
model_usage_all_time_rows = "\n".join([
|
749 |
f"""
|
750 |
+
<tr class="hover:bg-slate-700/20 transition-colors duration-200">
|
751 |
+
<td class="px-6 py-4 whitespace-nowrap">
|
752 |
+
<div class="flex items-center">
|
753 |
+
<div class="w-2 h-2 bg-purple-400 rounded-full mr-3"></div>
|
754 |
+
<span class="text-sm font-medium text-slate-200">{model}</span>
|
755 |
+
</div>
|
756 |
+
</td>
|
757 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-300">
|
758 |
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-500/20 text-purple-300">
|
759 |
+
{stats['total_requests']:,}
|
760 |
+
</span>
|
761 |
+
</td>
|
762 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-400">{datetime.datetime.fromisoformat(stats['first_used']).strftime("%Y-%m-%d %H:%M")}</td>
|
763 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-400">{datetime.datetime.fromisoformat(stats['last_used']).strftime("%Y-%m-%d %H:%M")}</td>
|
764 |
</tr>
|
765 |
""" for model, stats in usage_data['all_time_model_usage'].items()
|
766 |
])
|
767 |
|
768 |
api_usage_all_time_rows = "\n".join([
|
769 |
f"""
|
770 |
+
<tr class="hover:bg-slate-700/20 transition-colors duration-200">
|
771 |
+
<td class="px-6 py-4 whitespace-nowrap">
|
772 |
+
<div class="flex items-center">
|
773 |
+
<div class="w-2 h-2 bg-emerald-400 rounded-full mr-3"></div>
|
774 |
+
<span class="text-sm font-medium text-slate-200">{endpoint}</span>
|
775 |
+
</div>
|
776 |
+
</td>
|
777 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-300">
|
778 |
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-500/20 text-emerald-300">
|
779 |
+
{stats['total_requests']:,}
|
780 |
+
</span>
|
781 |
+
</td>
|
782 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-400">{datetime.datetime.fromisoformat(stats['first_used']).strftime("%Y-%m-%d %H:%M")}</td>
|
783 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-400">{datetime.datetime.fromisoformat(stats['last_used']).strftime("%Y-%m-%d %H:%M")}</td>
|
784 |
</tr>
|
785 |
""" for endpoint, stats in usage_data['all_time_endpoint_usage'].items()
|
786 |
])
|
787 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
788 |
recent_requests_rows = "\n".join([
|
789 |
f"""
|
790 |
+
<tr class="hover:bg-slate-700/20 transition-colors duration-200">
|
791 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-slate-300">{datetime.datetime.fromisoformat(req['timestamp']).strftime("%Y-%m-%d %H:%M:%S")}</td>
|
792 |
+
<td class="px-6 py-4 whitespace-nowrap">
|
793 |
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-500/20 text-indigo-300">
|
794 |
+
{req['model']}
|
795 |
+
</span>
|
796 |
+
</td>
|
797 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-300">{req['endpoint']}</td>
|
798 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-slate-400">{req['ip_address']}</td>
|
799 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500 truncate max-w-xs">{req['user_agent'][:50]}...</td>
|
800 |
</tr>
|
801 |
""" for req in usage_data['recent_requests']
|
802 |
])
|
|
|
803 |
html_content = f"""
|
804 |
+
<!DOCTYPE html>
|
805 |
+
<html lang="en">
|
806 |
+
<head>
|
807 |
+
<meta charset="UTF-8">
|
808 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
809 |
+
<title>Lokiai AI - Usage Analytics Dashboard</title>
|
810 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
811 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
812 |
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
813 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
814 |
+
<script>
|
815 |
+
tailwind.config = {{
|
816 |
+
theme: {{
|
817 |
+
extend: {{
|
818 |
+
animation: {{
|
819 |
+
'fade-in': 'fadeIn 0.5s ease-in-out',
|
820 |
+
'slide-up': 'slideUp 0.6s ease-out',
|
821 |
+
'pulse-slow': 'pulse 3s infinite',
|
822 |
+
'bounce-gentle': 'bounceGentle 2s infinite',
|
823 |
+
}},
|
824 |
+
keyframes: {{
|
825 |
+
fadeIn: {{
|
826 |
+
'0%': {{ opacity: '0', transform: 'translateY(10px)' }},
|
827 |
+
'100%': {{ opacity: '1', transform: 'translateY(0)' }},
|
828 |
+
}},
|
829 |
+
slideUp: {{
|
830 |
+
'0%': {{ opacity: '0', transform: 'translateY(30px)' }},
|
831 |
+
'100%': {{ opacity: '1', transform: 'translateY(0)' }},
|
832 |
+
}},
|
833 |
+
bounceGentle: {{
|
834 |
+
'0%, 100%': {{ transform: 'translateY(-2px)' }},
|
835 |
+
'50%': {{ transform: 'translateY(2px)' }},
|
836 |
+
}}
|
837 |
+
}}
|
838 |
+
}}
|
839 |
+
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
840 |
}}
|
841 |
+
</script>
|
842 |
+
</head>
|
843 |
+
<body class="bg-gradient-to-br from-slate-950 via-slate-900 to-indigo-950 text-white min-h-screen">
|
844 |
+
<!-- Navigation Header -->
|
845 |
+
<nav class="bg-slate-900/80 backdrop-blur-md border-b border-slate-700/50 sticky top-0 z-50">
|
846 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
847 |
+
<div class="flex justify-between items-center h-16">
|
848 |
+
<div class="flex items-center space-x-4">
|
849 |
+
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
|
850 |
+
<i class="fas fa-robot text-white text-lg"></i>
|
851 |
+
</div>
|
852 |
+
<div>
|
853 |
+
<h1 class="text-xl font-bold bg-gradient-to-r from-blue-400 to-indigo-400 bg-clip-text text-transparent">
|
854 |
+
Lokiai AI
|
855 |
+
</h1>
|
856 |
+
<p class="text-xs text-slate-400">Usage Analytics Dashboard</p>
|
857 |
+
</div>
|
858 |
+
</div>
|
859 |
+
<div class="flex items-center space-x-2">
|
860 |
+
<div class="w-3 h-3 bg-green-400 rounded-full animate-pulse"></div>
|
861 |
+
<span class="text-sm text-slate-300">Live</span>
|
862 |
+
</div>
|
863 |
+
</div>
|
864 |
</div>
|
865 |
+
</nav>
|
866 |
+
|
867 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-8">
|
868 |
+
<!-- Hero Stats Section -->
|
869 |
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8" x-data="{{}}">
|
870 |
+
<div class="bg-gradient-to-br from-blue-500/10 to-blue-600/5 backdrop-blur-sm border border-blue-500/20 rounded-2xl p-6 hover:border-blue-400/30 transition-all duration-300 animate-fade-in">
|
871 |
+
<div class="flex items-center justify-between">
|
872 |
+
<div>
|
873 |
+
<p class="text-blue-300 text-sm font-medium">Total Requests</p>
|
874 |
+
<p class="text-3xl font-bold text-white mt-2">{usage_data['total_requests']:,}</p>
|
875 |
+
<p class="text-green-400 text-xs mt-1">
|
876 |
+
<i class="fas fa-arrow-up mr-1"></i>All Time
|
877 |
+
</p>
|
878 |
+
</div>
|
879 |
+
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center">
|
880 |
+
<i class="fas fa-chart-line text-blue-400 text-xl"></i>
|
881 |
+
</div>
|
882 |
+
</div>
|
883 |
+
</div>
|
884 |
+
|
885 |
+
<div class="bg-gradient-to-br from-emerald-500/10 to-emerald-600/5 backdrop-blur-sm border border-emerald-500/20 rounded-2xl p-6 hover:border-emerald-400/30 transition-all duration-300 animate-fade-in" style="animation-delay: 0.1s">
|
886 |
+
<div class="flex items-center justify-between">
|
887 |
+
<div>
|
888 |
+
<p class="text-emerald-300 text-sm font-medium">Unique Users</p>
|
889 |
+
<p class="text-3xl font-bold text-white mt-2">{usage_data['unique_ips_total_count']:,}</p>
|
890 |
+
<p class="text-green-400 text-xs mt-1">
|
891 |
+
<i class="fas fa-users mr-1"></i>All Time
|
892 |
+
</p>
|
893 |
+
</div>
|
894 |
+
<div class="w-12 h-12 bg-emerald-500/20 rounded-xl flex items-center justify-center">
|
895 |
+
<i class="fas fa-users text-emerald-400 text-xl"></i>
|
896 |
+
</div>
|
897 |
+
</div>
|
898 |
+
</div>
|
899 |
+
|
900 |
+
<div class="bg-gradient-to-br from-purple-500/10 to-purple-600/5 backdrop-blur-sm border border-purple-500/20 rounded-2xl p-6 hover:border-purple-400/30 transition-all duration-300 animate-fade-in" style="animation-delay: 0.2s">
|
901 |
+
<div class="flex items-center justify-between">
|
902 |
+
<div>
|
903 |
+
<p class="text-purple-300 text-sm font-medium">Active Models</p>
|
904 |
+
<p class="text-3xl font-bold text-white mt-2">{len(usage_data['model_usage_period'])}</p>
|
905 |
+
<p class="text-blue-400 text-xs mt-1">
|
906 |
+
<i class="fas fa-clock mr-1"></i>Last {days} Days
|
907 |
+
</p>
|
908 |
+
</div>
|
909 |
+
<div class="w-12 h-12 bg-purple-500/20 rounded-xl flex items-center justify-center">
|
910 |
+
<i class="fas fa-brain text-purple-400 text-xl"></i>
|
911 |
+
</div>
|
912 |
+
</div>
|
913 |
+
</div>
|
914 |
+
|
915 |
+
<div class="bg-gradient-to-br from-amber-500/10 to-amber-600/5 backdrop-blur-sm border border-amber-500/20 rounded-2xl p-6 hover:border-amber-400/30 transition-all duration-300 animate-fade-in" style="animation-delay: 0.3s">
|
916 |
+
<div class="flex items-center justify-between">
|
917 |
+
<div>
|
918 |
+
<p class="text-amber-300 text-sm font-medium">API Endpoints</p>
|
919 |
+
<p class="text-3xl font-bold text-white mt-2">{len(usage_data['endpoint_usage_period'])}</p>
|
920 |
+
<p class="text-blue-400 text-xs mt-1">
|
921 |
+
<i class="fas fa-clock mr-1"></i>Last {days} Days
|
922 |
+
</p>
|
923 |
+
</div>
|
924 |
+
<div class="w-12 h-12 bg-amber-500/20 rounded-xl flex items-center justify-center">
|
925 |
+
<i class="fas fa-plug text-amber-400 text-xl"></i>
|
926 |
+
</div>
|
927 |
+
</div>
|
928 |
+
</div>
|
929 |
</div>
|
930 |
+
|
931 |
+
<!-- Charts Section -->
|
932 |
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
933 |
+
<!-- Daily Usage Chart -->
|
934 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl p-6 animate-slide-up">
|
935 |
+
<div class="flex items-center justify-between mb-6">
|
936 |
+
<div>
|
937 |
+
<h3 class="text-xl font-semibold text-white">Daily Usage Trends</h3>
|
938 |
+
<p class="text-slate-400 text-sm">Last {days} days performance</p>
|
939 |
+
</div>
|
940 |
+
<div class="flex items-center space-x-2">
|
941 |
+
<div class="w-3 h-3 bg-blue-400 rounded-full"></div>
|
942 |
+
<span class="text-xs text-slate-300">Requests</span>
|
943 |
+
<div class="w-3 h-3 bg-amber-400 rounded-full ml-4"></div>
|
944 |
+
<span class="text-xs text-slate-300">Unique IPs</span>
|
945 |
+
</div>
|
946 |
+
</div>
|
947 |
+
<div class="h-64">
|
948 |
+
<canvas id="dailyRequestsChart" class="w-full h-full"></canvas>
|
949 |
+
</div>
|
950 |
+
</div>
|
951 |
+
|
952 |
+
<!-- Model Usage Chart -->
|
953 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl p-6 animate-slide-up" style="animation-delay: 0.2s">
|
954 |
+
<div class="flex items-center justify-between mb-6">
|
955 |
+
<div>
|
956 |
+
<h3 class="text-xl font-semibold text-white">Model Distribution</h3>
|
957 |
+
<p class="text-slate-400 text-sm">Usage by AI models</p>
|
958 |
+
</div>
|
959 |
+
</div>
|
960 |
+
<div class="h-64">
|
961 |
+
<canvas id="modelUsageChart" class="w-full h-full"></canvas>
|
962 |
+
</div>
|
963 |
+
</div>
|
964 |
</div>
|
965 |
+
|
966 |
+
<!-- Endpoint Usage Chart -->
|
967 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl p-6 animate-slide-up" style="animation-delay: 0.4s">
|
968 |
+
<div class="flex items-center justify-between mb-6">
|
969 |
+
<div>
|
970 |
+
<h2 class="text-2xl font-bold text-white">API Endpoint Analytics</h2>
|
971 |
+
<p class="text-slate-400">Distribution of requests across different endpoints</p>
|
972 |
+
</div>
|
973 |
+
</div>
|
974 |
+
<div class="h-80">
|
975 |
+
<canvas id="endpointUsageChart" class="w-full h-full"></canvas>
|
976 |
+
</div>
|
977 |
+
</div>
|
978 |
+
|
979 |
+
<!-- Data Tables Section -->
|
980 |
+
<div class="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
981 |
+
<!-- Daily Usage Table -->
|
982 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl animate-slide-up" style="animation-delay: 0.6s">
|
983 |
+
<div class="p-6 border-b border-slate-700/50">
|
984 |
+
<h3 class="text-xl font-semibold text-white flex items-center">
|
985 |
+
<i class="fas fa-calendar-alt mr-3 text-blue-400"></i>
|
986 |
+
Daily Breakdown
|
987 |
+
</h3>
|
988 |
+
<p class="text-slate-400 text-sm mt-1">Last {days} days detailed view</p>
|
989 |
+
</div>
|
990 |
+
<div class="overflow-x-auto max-h-96">
|
991 |
+
<table class="w-full">
|
992 |
+
<thead class="bg-slate-700/30">
|
993 |
+
<tr>
|
994 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Date</th>
|
995 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Requests</th>
|
996 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Unique IPs</th>
|
997 |
+
</tr>
|
998 |
+
</thead>
|
999 |
+
<tbody class="divide-y divide-slate-700/30">
|
1000 |
+
{daily_usage_table_rows}
|
1001 |
+
</tbody>
|
1002 |
+
</table>
|
1003 |
+
</div>
|
1004 |
+
</div>
|
1005 |
+
|
1006 |
+
<!-- Model Usage Table -->
|
1007 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl animate-slide-up" style="animation-delay: 0.8s">
|
1008 |
+
<div class="p-6 border-b border-slate-700/50">
|
1009 |
+
<h3 class="text-xl font-semibold text-white flex items-center">
|
1010 |
+
<i class="fas fa-robot mr-3 text-purple-400"></i>
|
1011 |
+
Model Statistics
|
1012 |
+
</h3>
|
1013 |
+
<p class="text-slate-400 text-sm mt-1">All-time model usage data</p>
|
1014 |
+
</div>
|
1015 |
+
<div class="overflow-x-auto max-h-96">
|
1016 |
+
<table class="w-full">
|
1017 |
+
<thead class="bg-slate-700/30">
|
1018 |
+
<tr>
|
1019 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Model</th>
|
1020 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Requests</th>
|
1021 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">First Used</th>
|
1022 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Last Used</th>
|
1023 |
+
</tr>
|
1024 |
+
</thead>
|
1025 |
+
<tbody class="divide-y divide-slate-700/30">
|
1026 |
+
{model_usage_all_time_rows}
|
1027 |
+
</tbody>
|
1028 |
+
</table>
|
1029 |
+
</div>
|
1030 |
+
</div>
|
1031 |
+
</div>
|
1032 |
+
|
1033 |
+
<!-- API Endpoints Table -->
|
1034 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl animate-slide-up" style="animation-delay: 1s">
|
1035 |
+
<div class="p-6 border-b border-slate-700/50">
|
1036 |
+
<h3 class="text-xl font-semibold text-white flex items-center">
|
1037 |
+
<i class="fas fa-plug mr-3 text-emerald-400"></i>
|
1038 |
+
API Endpoint Details
|
1039 |
+
</h3>
|
1040 |
+
<p class="text-slate-400 text-sm mt-1">Complete endpoint usage statistics</p>
|
1041 |
+
</div>
|
1042 |
+
<div class="overflow-x-auto">
|
1043 |
+
<table class="w-full">
|
1044 |
+
<thead class="bg-slate-700/30">
|
1045 |
+
<tr>
|
1046 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Endpoint</th>
|
1047 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Total Requests</th>
|
1048 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">First Used</th>
|
1049 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Last Used</th>
|
1050 |
+
</tr>
|
1051 |
+
</thead>
|
1052 |
+
<tbody class="divide-y divide-slate-700/30">
|
1053 |
+
{api_usage_all_time_rows}
|
1054 |
+
</tbody>
|
1055 |
+
</table>
|
1056 |
+
</div>
|
1057 |
+
</div>
|
1058 |
+
|
1059 |
+
<!-- Recent Requests -->
|
1060 |
+
<div class="bg-slate-800/40 backdrop-blur-sm border border-slate-700/50 rounded-2xl animate-slide-up" style="animation-delay: 1.2s">
|
1061 |
+
<div class="p-6 border-b border-slate-700/50">
|
1062 |
+
<h3 class="text-xl font-semibold text-white flex items-center">
|
1063 |
+
<i class="fas fa-clock mr-3 text-amber-400"></i>
|
1064 |
+
Recent Activity
|
1065 |
+
</h3>
|
1066 |
+
<p class="text-slate-400 text-sm mt-1">Last 20 requests in real-time</p>
|
1067 |
+
</div>
|
1068 |
+
<div class="overflow-x-auto">
|
1069 |
+
<table class="w-full">
|
1070 |
+
<thead class="bg-slate-700/30">
|
1071 |
+
<tr>
|
1072 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Timestamp</th>
|
1073 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Model</th>
|
1074 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">Endpoint</th>
|
1075 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">IP Address</th>
|
1076 |
+
<th class="px-6 py-4 text-left text-xs font-medium text-slate-300 uppercase tracking-wider">User Agent</th>
|
1077 |
+
</tr>
|
1078 |
+
</thead>
|
1079 |
+
<tbody class="divide-y divide-slate-700/30">
|
1080 |
+
{recent_requests_rows}
|
1081 |
+
</tbody>
|
1082 |
+
</table>
|
1083 |
+
</div>
|
1084 |
</div>
|
1085 |
</div>
|
1086 |
+
|
1087 |
+
<!-- Footer -->
|
1088 |
+
<footer class="border-t border-slate-700/50 mt-16">
|
1089 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
1090 |
+
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
|
1091 |
+
<div class="flex items-center space-x-4">
|
1092 |
+
<div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center">
|
1093 |
+
<i class="fas fa-robot text-white text-sm"></i>
|
1094 |
+
</div>
|
1095 |
+
<div>
|
1096 |
+
<p class="text-slate-300 font-medium">Lokiai AI Dashboard</p>
|
1097 |
+
<p class="text-slate-500 text-xs">Advanced Analytics & Monitoring</p>
|
1098 |
+
</div>
|
1099 |
+
</div>
|
1100 |
+
<div class="text-slate-400 text-sm">
|
1101 |
+
Last updated: <span class="text-slate-300" id="currentTime"></span>
|
1102 |
+
</div>
|
1103 |
+
</div>
|
1104 |
+
</div>
|
1105 |
+
</footer>
|
1106 |
+
|
1107 |
+
<script>
|
1108 |
+
// Update current time
|
1109 |
+
document.getElementById('currentTime').textContent = new Date().toLocaleString();
|
1110 |
+
|
1111 |
+
// Chart data
|
1112 |
+
const modelLabels = {json.dumps(model_labels)};
|
1113 |
+
const modelCounts = {json.dumps(model_counts)};
|
1114 |
+
const endpointLabels = {json.dumps(endpoint_labels)};
|
1115 |
+
const endpointCounts = {json.dumps(endpoint_counts)};
|
1116 |
+
const dailyDates = {json.dumps(daily_dates)};
|
1117 |
+
const dailyRequests = {json.dumps(daily_requests)};
|
1118 |
+
const dailyUniqueIps = {json.dumps(daily_unique_ips)};
|
1119 |
+
|
1120 |
+
// Chart options
|
1121 |
+
const chartOptions = {{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1122 |
responsive: true,
|
1123 |
maintainAspectRatio: false,
|
1124 |
plugins: {{
|
1125 |
legend: {{
|
1126 |
labels: {{
|
1127 |
+
color: '#e2e8f0',
|
1128 |
+
padding: 20,
|
1129 |
+
font: {{
|
1130 |
+
size: 12,
|
1131 |
+
weight: 500
|
1132 |
+
}}
|
1133 |
}}
|
|
|
|
|
|
|
|
|
|
|
1134 |
}}
|
1135 |
}},
|
1136 |
scales: {{
|
1137 |
x: {{
|
1138 |
ticks: {{
|
1139 |
+
color: '#94a3b8',
|
1140 |
+
font: {{
|
1141 |
+
size: 11
|
1142 |
+
}}
|
1143 |
}},
|
1144 |
grid: {{
|
1145 |
+
color: 'rgba(148, 163, 184, 0.1)',
|
1146 |
+
drawBorder: false
|
1147 |
}}
|
1148 |
}},
|
1149 |
y: {{
|
1150 |
beginAtZero: true,
|
1151 |
ticks: {{
|
1152 |
+
color: '#94a3b8',
|
1153 |
+
font: {{
|
1154 |
+
size: 11
|
1155 |
+
}}
|
1156 |
}},
|
1157 |
grid: {{
|
1158 |
+
color: 'rgba(148, 163, 184, 0.1)',
|
1159 |
+
drawBorder: false
|
1160 |
}}
|
1161 |
}}
|
1162 |
}}
|
1163 |
+
}};
|
1164 |
+
|
1165 |
+
// Daily Requests Chart
|
1166 |
+
new Chart(document.getElementById('dailyRequestsChart'), {{
|
1167 |
+
type: 'line',
|
1168 |
+
data: {{
|
1169 |
+
labels: dailyDates,
|
1170 |
+
datasets: [
|
1171 |
+
{{
|
1172 |
+
label: 'Total Requests',
|
1173 |
+
data: dailyRequests,
|
1174 |
+
borderColor: '#3b82f6',
|
1175 |
+
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
1176 |
+
fill: true,
|
1177 |
+
tension: 0.4,
|
1178 |
+
borderWidth: 3,
|
1179 |
+
pointBackgroundColor: '#3b82f6',
|
1180 |
+
pointBorderColor: '#1e40af',
|
1181 |
+
pointBorderWidth: 2,
|
1182 |
+
pointRadius: 4,
|
1183 |
+
pointHoverRadius: 6
|
1184 |
+
}},
|
1185 |
+
{{
|
1186 |
+
label: 'Unique IPs',
|
1187 |
+
data: dailyUniqueIps,
|
1188 |
+
borderColor: '#f59e0b',
|
1189 |
+
backgroundColor: 'rgba(245, 158, 11, 0.1)',
|
1190 |
+
fill: true,
|
1191 |
+
tension: 0.4,
|
1192 |
+
borderWidth: 3,
|
1193 |
+
pointBackgroundColor: '#f59e0b',
|
1194 |
+
pointBorderColor: '#d97706',
|
1195 |
+
pointBorderWidth: 2,
|
1196 |
+
pointRadius: 4,
|
1197 |
+
pointHoverRadius: 6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1198 |
}}
|
1199 |
+
]
|
|
|
|
|
|
|
|
|
|
|
1200 |
}},
|
1201 |
+
options: chartOptions
|
1202 |
+
}});
|
1203 |
+
|
1204 |
+
// Model Usage Chart
|
1205 |
+
new Chart(document.getElementById('modelUsageChart'), {{
|
1206 |
+
type: 'doughnut',
|
1207 |
+
data: {{
|
1208 |
+
labels: modelLabels,
|
1209 |
+
datasets: [{{
|
1210 |
+
data: modelCounts,
|
1211 |
+
backgroundColor: [
|
1212 |
+
'#3b82f6', '#8b5cf6', '#06b6d4', '#10b981', '#f59e0b',
|
1213 |
+
'#ef4444', '#ec4899', '#84cc16', '#f97316', '#6366f1'
|
1214 |
+
],
|
1215 |
+
borderWidth: 0,
|
1216 |
+
hoverOffset: 8
|
1217 |
+
}}]
|
1218 |
+
}},
|
1219 |
+
options: {{
|
1220 |
+
responsive: true,
|
1221 |
+
maintainAspectRatio: false,
|
1222 |
+
plugins: {{
|
1223 |
+
legend: {{
|
1224 |
+
position: 'bottom',
|
1225 |
+
labels: {{
|
1226 |
+
color: '#e2e8f0',
|
1227 |
+
padding: 15,
|
1228 |
+
usePointStyle: true,
|
1229 |
+
font: {{
|
1230 |
+
size: 12
|
1231 |
+
}}
|
1232 |
+
}}
|
1233 |
}}
|
1234 |
}},
|
1235 |
+
cutout: '60%'
|
1236 |
+
}}
|
1237 |
+
}});
|
1238 |
+
|
1239 |
+
// Endpoint Usage Chart
|
1240 |
+
new Chart(document.getElementById('endpointUsageChart'), {{
|
1241 |
+
type: 'bar',
|
1242 |
+
data: {{
|
1243 |
+
labels: endpointLabels,
|
1244 |
+
datasets: [{{
|
1245 |
+
label: 'Requests',
|
1246 |
+
data: endpointCounts,
|
1247 |
+
backgroundColor: 'rgba(59, 130, 246, 0.8)',
|
1248 |
+
borderColor: '#3b82f6',
|
1249 |
+
borderWidth: 0,
|
1250 |
+
borderRadius: 8,
|
1251 |
+
borderSkipped: false,
|
1252 |
+
}}]
|
1253 |
+
}},
|
1254 |
+
options: {{
|
1255 |
+
...chartOptions,
|
1256 |
+
plugins: {{
|
1257 |
+
legend: {{
|
1258 |
+
display: false
|
1259 |
}}
|
1260 |
}}
|
1261 |
}}
|
1262 |
+
}});
|
1263 |
+
|
1264 |
+
// Add hover effects to table rows
|
1265 |
+
document.querySelectorAll('tbody tr').forEach(row => {{
|
1266 |
+
row.addEventListener('mouseenter', function() {{
|
1267 |
+
this.classList.add('bg-slate-700/20');
|
1268 |
+
}});
|
1269 |
+
row.addEventListener('mouseleave', function() {{
|
1270 |
+
this.classList.remove('bg-slate-700/20');
|
1271 |
+
}});
|
1272 |
+
}});
|
1273 |
+
</script>
|
1274 |
+
</body>
|
1275 |
+
</html>"""
|
1276 |
+
|
1277 |
+
# Update the table row generation with better styling
|
1278 |
+
|
1279 |
return html_content
|
1280 |
|
1281 |
@app.on_event("startup")
|