Spaces:
Running
Running
Update pages/Statistics.py
Browse files- pages/Statistics.py +117 -81
pages/Statistics.py
CHANGED
@@ -14,14 +14,8 @@ import requests
|
|
14 |
# Importowanie funkcji z utils/functions.py
|
15 |
from utils.functions import get_phone_info, get_stats, get_history, get_fake_numbers
|
16 |
|
17 |
-
#
|
18 |
from streamlit_extras.metric_cards import style_metric_cards
|
19 |
-
from streamlit_elements import elements, mui, html
|
20 |
-
|
21 |
-
# Definiowanie 艣cie偶ek do plik贸w JSON
|
22 |
-
FAKE_NUMBERS_FILE = os.path.join('data', 'fake_numbers.json')
|
23 |
-
HISTORY_FILE = os.path.join('data', 'history.json')
|
24 |
-
STATS_FILE = os.path.join('data', 'stats.json')
|
25 |
|
26 |
# Definiowanie t艂umacze艅 dla zak艂adki "Statystyki"
|
27 |
page_translations = {
|
@@ -67,7 +61,7 @@ page_translations = {
|
|
67 |
'no_data': "Keine Daten zur Anzeige verf眉gbar.",
|
68 |
'download_button': "馃摜 Daten als CSV herunterladen",
|
69 |
'select_date_range': "W盲hle einen Datumsbereich:",
|
70 |
-
'recent_days': 30
|
71 |
},
|
72 |
'English': {
|
73 |
'page_title': "馃搳 Statistics",
|
@@ -89,19 +83,94 @@ page_translations = {
|
|
89 |
'no_data': "No data available to display.",
|
90 |
'download_button': "馃摜 Download data as CSV",
|
91 |
'select_date_range': "Select date range:",
|
92 |
-
'recent_days': 30
|
93 |
}
|
94 |
}
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
def main(language):
|
97 |
translations = page_translations.get(language, page_translations['English'])
|
98 |
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
st.title(translations['header'])
|
100 |
st.markdown(translations['description'])
|
101 |
|
102 |
# Pobieranie danych z plik贸w JSON
|
103 |
-
|
104 |
-
|
|
|
|
|
|
|
|
|
105 |
|
106 |
# Kluczowe metryki
|
107 |
total_analyses = stats.get("total_analyses", 0)
|
@@ -122,29 +191,39 @@ def main(language):
|
|
122 |
|
123 |
st.markdown("---")
|
124 |
|
125 |
-
# Dodanie interaktywnego filtra daty w
|
126 |
-
st.
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
|
|
|
|
|
|
|
|
139 |
|
140 |
# Filtracja historii na podstawie daty
|
141 |
if history:
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
# Wy艣wietlenie tabeli historii analiz
|
150 |
st.markdown(f"### {translations['history_title']}")
|
@@ -188,9 +267,9 @@ def main(language):
|
|
188 |
# Trend oszustw w czasie
|
189 |
st.markdown("### " + translations['frauds_over_time'])
|
190 |
fraud_over_time = df_filtered.groupby(df_filtered['timestamp'].dt.date)['phone_number'].count().reset_index()
|
191 |
-
fraud_over_time.rename(columns={'phone_number': '
|
192 |
-
fig_trend = px.line(fraud_over_time, x='
|
193 |
-
labels={'
|
194 |
fig_trend.update_traces(line=dict(color='firebrick'))
|
195 |
st.plotly_chart(fig_trend, use_container_width=True)
|
196 |
|
@@ -219,7 +298,7 @@ def main(language):
|
|
219 |
fraud_countries.columns = ['country', 'counts']
|
220 |
|
221 |
# Dodanie kod贸w kraj贸w
|
222 |
-
fraud_countries['iso_alpha'] = fraud_countries['country'].apply(lambda x:
|
223 |
fraud_countries = fraud_countries.dropna(subset=['iso_alpha'])
|
224 |
|
225 |
if not fraud_countries.empty:
|
@@ -238,52 +317,6 @@ def main(language):
|
|
238 |
|
239 |
st.markdown("---")
|
240 |
|
241 |
-
# Dodatkowe Wizualizacje
|
242 |
-
st.markdown("### " + translations['heatmap_title'])
|
243 |
-
# Heatmapa oszustw na podstawie lokalizacji
|
244 |
-
if not fraud_countries.empty:
|
245 |
-
# Przygotowanie danych geograficznych
|
246 |
-
# U偶yjemy szeroko艣ci i d艂ugo艣ci geograficznej kraj贸w
|
247 |
-
country_coords = {}
|
248 |
-
for country in fraud_countries['country'].unique():
|
249 |
-
if country == "Unknown":
|
250 |
-
country_coords[country] = (0, 0) # Centrum 艣wiata
|
251 |
-
else:
|
252 |
-
try:
|
253 |
-
country_obj = pycountry.countries.lookup(country)
|
254 |
-
# U偶yjemy 艣rednich szeroko艣ci i d艂ugo艣ci geograficznej
|
255 |
-
geocode_url = f"https://restcountries.com/v3.1/name/{country}"
|
256 |
-
response = requests.get(geocode_url)
|
257 |
-
if response.status_code == 200:
|
258 |
-
data = response.json()
|
259 |
-
lat = data[0]['latlng'][0]
|
260 |
-
lon = data[0]['latlng'][1]
|
261 |
-
country_coords[country] = (lat, lon)
|
262 |
-
else:
|
263 |
-
country_coords[country] = (0, 0)
|
264 |
-
except:
|
265 |
-
country_coords[country] = (0, 0)
|
266 |
-
|
267 |
-
fraud_countries['lat'] = fraud_countries['country'].apply(lambda x: country_coords.get(x, (0,0))[0])
|
268 |
-
fraud_countries['lon'] = fraud_countries['country'].apply(lambda x: country_coords.get(x, (0,0))[1])
|
269 |
-
|
270 |
-
# Tworzenie Heatmapy
|
271 |
-
fig_heatmap = px.density_mapbox(
|
272 |
-
fraud_countries,
|
273 |
-
lat='lat',
|
274 |
-
lon='lon',
|
275 |
-
z='counts',
|
276 |
-
radius=10,
|
277 |
-
center=dict(lat=20, lon=0),
|
278 |
-
zoom=1,
|
279 |
-
mapbox_style="stamen-terrain",
|
280 |
-
title=translations['heatmap_title']
|
281 |
-
)
|
282 |
-
fig_heatmap.update_geos(showcountries=True, showcoastlines=True)
|
283 |
-
st.plotly_chart(fig_heatmap, use_container_width=True)
|
284 |
-
else:
|
285 |
-
st.info(translations['no_data'])
|
286 |
-
|
287 |
# Gauge Chart - Procentowy udzia艂 oszustw
|
288 |
st.markdown("### " + translations['fraud_percentage'])
|
289 |
fig_gauge = go.Figure(go.Indicator(
|
@@ -308,3 +341,6 @@ def main(language):
|
|
308 |
}
|
309 |
))
|
310 |
st.plotly_chart(fig_gauge, use_container_width=True)
|
|
|
|
|
|
|
|
14 |
# Importowanie funkcji z utils/functions.py
|
15 |
from utils.functions import get_phone_info, get_stats, get_history, get_fake_numbers
|
16 |
|
17 |
+
# Importowanie dodatkowych komponent贸w
|
18 |
from streamlit_extras.metric_cards import style_metric_cards
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
# Definiowanie t艂umacze艅 dla zak艂adki "Statystyki"
|
21 |
page_translations = {
|
|
|
61 |
'no_data': "Keine Daten zur Anzeige verf眉gbar.",
|
62 |
'download_button': "馃摜 Daten als CSV herunterladen",
|
63 |
'select_date_range': "W盲hle einen Datumsbereich:",
|
64 |
+
'recent_days': 30
|
65 |
},
|
66 |
'English': {
|
67 |
'page_title': "馃搳 Statistics",
|
|
|
83 |
'no_data': "No data available to display.",
|
84 |
'download_button': "馃摜 Download data as CSV",
|
85 |
'select_date_range': "Select date range:",
|
86 |
+
'recent_days': 30
|
87 |
}
|
88 |
}
|
89 |
|
90 |
+
# Mapowanie polskich nazw kraj贸w na kody ISO alfa-3
|
91 |
+
country_name_mapping = {
|
92 |
+
'niemcy': 'DEU', # Niemcy
|
93 |
+
'polska': 'POL', # Polska
|
94 |
+
'francja': 'FRA', # Francja
|
95 |
+
'w艂ochy': 'ITA', # W艂ochy
|
96 |
+
'hiszpania': 'ESP', # Hiszpania
|
97 |
+
'stany zjednoczone': 'USA', # USA
|
98 |
+
'wielka brytania': 'GBR', # Wielka Brytania
|
99 |
+
'unknown': None,
|
100 |
+
'nieznany': None
|
101 |
+
# Dodaj inne kraje w razie potrzeby
|
102 |
+
}
|
103 |
+
|
104 |
+
def get_iso_alpha3(country_name):
|
105 |
+
country_code = country_name_mapping.get(country_name.lower())
|
106 |
+
if country_code:
|
107 |
+
return country_code
|
108 |
+
else:
|
109 |
+
# Je艣li nie znaleziono w mapowaniu, spr贸buj u偶y膰 pycountry
|
110 |
+
try:
|
111 |
+
country = pycountry.countries.lookup(country_name)
|
112 |
+
return country.alpha_3
|
113 |
+
except LookupError:
|
114 |
+
return None
|
115 |
+
|
116 |
+
def set_theme_variables():
|
117 |
+
if st.get_option("theme.base") == "dark":
|
118 |
+
background_color = "#31333F" # Kolor t艂a dla trybu ciemnego
|
119 |
+
text_color = "#FFFFFF"
|
120 |
+
border_color = "#4A4A4A"
|
121 |
+
else:
|
122 |
+
background_color = "#FFFFFF" # Kolor t艂a dla trybu jasnego
|
123 |
+
text_color = "#000000"
|
124 |
+
border_color = "#E0E0E0"
|
125 |
+
|
126 |
+
custom_css = f"""
|
127 |
+
<style>
|
128 |
+
:root {{
|
129 |
+
--background-color: {background_color};
|
130 |
+
--text-color: {text_color};
|
131 |
+
--border-color: {border_color};
|
132 |
+
}}
|
133 |
+
</style>
|
134 |
+
"""
|
135 |
+
st.markdown(custom_css, unsafe_allow_html=True)
|
136 |
+
|
137 |
+
def inject_custom_css():
|
138 |
+
custom_css = """
|
139 |
+
<style>
|
140 |
+
/* Stylizacja kart metryk dla obu motyw贸w */
|
141 |
+
[data-testid="metric-container"] {{
|
142 |
+
background-color: var(--background-color);
|
143 |
+
padding: 1rem;
|
144 |
+
border-radius: 0.5rem;
|
145 |
+
border: 1px solid var(--border-color);
|
146 |
+
box-shadow: 0px 2px 4px rgba(0,0,0,0.1);
|
147 |
+
}}
|
148 |
+
[data-testid="stMetricLabel"], [data-testid="stMetricValue"] {{
|
149 |
+
color: var(--text-color);
|
150 |
+
}}
|
151 |
+
</style>
|
152 |
+
"""
|
153 |
+
st.markdown(custom_css, unsafe_allow_html=True)
|
154 |
+
|
155 |
def main(language):
|
156 |
translations = page_translations.get(language, page_translations['English'])
|
157 |
|
158 |
+
# Ustawienie zmiennych motywu
|
159 |
+
set_theme_variables()
|
160 |
+
|
161 |
+
# Wstrzykni臋cie niestandardowego CSS
|
162 |
+
inject_custom_css()
|
163 |
+
|
164 |
st.title(translations['header'])
|
165 |
st.markdown(translations['description'])
|
166 |
|
167 |
# Pobieranie danych z plik贸w JSON
|
168 |
+
try:
|
169 |
+
stats = get_stats()
|
170 |
+
history = get_history()
|
171 |
+
except Exception as e:
|
172 |
+
st.error(f"Wyst膮pi艂 b艂膮d podczas pobierania danych: {e}")
|
173 |
+
st.stop()
|
174 |
|
175 |
# Kluczowe metryki
|
176 |
total_analyses = stats.get("total_analyses", 0)
|
|
|
191 |
|
192 |
st.markdown("---")
|
193 |
|
194 |
+
# Dodanie interaktywnego filtra daty w g艂贸wnym obszarze
|
195 |
+
st.header(translations['select_date_range'])
|
196 |
+
try:
|
197 |
+
start_date = st.date_input(
|
198 |
+
translations['select_date_range'] + " - " + "Start",
|
199 |
+
value=datetime.now().date() - timedelta(days=translations['recent_days']),
|
200 |
+
min_value=datetime.now().date() - timedelta(days=365),
|
201 |
+
max_value=datetime.now().date()
|
202 |
+
)
|
203 |
+
end_date = st.date_input(
|
204 |
+
translations['select_date_range'] + " - " + "End",
|
205 |
+
value=datetime.now().date(),
|
206 |
+
min_value=start_date,
|
207 |
+
max_value=datetime.now().date()
|
208 |
+
)
|
209 |
+
except Exception as e:
|
210 |
+
st.error(f"Wyst膮pi艂 b艂膮d przy wyborze daty: {e}")
|
211 |
+
st.stop()
|
212 |
|
213 |
# Filtracja historii na podstawie daty
|
214 |
if history:
|
215 |
+
try:
|
216 |
+
df_history = pd.DataFrame(history)
|
217 |
+
# Upewnij si臋, 偶e 'timestamp' jest w formacie datetime
|
218 |
+
df_history['timestamp'] = pd.to_datetime(df_history['timestamp'], errors='coerce')
|
219 |
+
# Usuni臋cie wpis贸w z b艂臋dnymi datami
|
220 |
+
df_history = df_history.dropna(subset=['timestamp'])
|
221 |
+
# Filtracja na podstawie daty
|
222 |
+
mask = (df_history['timestamp'].dt.date >= start_date) & (df_history['timestamp'].dt.date <= end_date)
|
223 |
+
df_filtered = df_history.loc[mask]
|
224 |
+
except Exception as e:
|
225 |
+
st.error(f"Wyst膮pi艂 b艂膮d podczas filtrowania danych: {e}")
|
226 |
+
st.stop()
|
227 |
|
228 |
# Wy艣wietlenie tabeli historii analiz
|
229 |
st.markdown(f"### {translations['history_title']}")
|
|
|
267 |
# Trend oszustw w czasie
|
268 |
st.markdown("### " + translations['frauds_over_time'])
|
269 |
fraud_over_time = df_filtered.groupby(df_filtered['timestamp'].dt.date)['phone_number'].count().reset_index()
|
270 |
+
fraud_over_time.rename(columns={'timestamp': 'Date', 'phone_number': 'Frauds Detected'}, inplace=True)
|
271 |
+
fig_trend = px.line(fraud_over_time, x='Date', y='Frauds Detected', title=translations['frauds_over_time'],
|
272 |
+
labels={'Date': 'Date', 'Frauds Detected': 'Frauds Detected'}, markers=True)
|
273 |
fig_trend.update_traces(line=dict(color='firebrick'))
|
274 |
st.plotly_chart(fig_trend, use_container_width=True)
|
275 |
|
|
|
298 |
fraud_countries.columns = ['country', 'counts']
|
299 |
|
300 |
# Dodanie kod贸w kraj贸w
|
301 |
+
fraud_countries['iso_alpha'] = fraud_countries['country'].apply(lambda x: get_iso_alpha3(x) if x != "Unknown" else None)
|
302 |
fraud_countries = fraud_countries.dropna(subset=['iso_alpha'])
|
303 |
|
304 |
if not fraud_countries.empty:
|
|
|
317 |
|
318 |
st.markdown("---")
|
319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# Gauge Chart - Procentowy udzia艂 oszustw
|
321 |
st.markdown("### " + translations['fraud_percentage'])
|
322 |
fig_gauge = go.Figure(go.Indicator(
|
|
|
341 |
}
|
342 |
))
|
343 |
st.plotly_chart(fig_gauge, use_container_width=True)
|
344 |
+
|
345 |
+
else:
|
346 |
+
st.info(translations['no_data'])
|