Spaces:
Running
Running
Commit
·
a1bce75
1
Parent(s):
480a85a
3.71 fix viz
Browse files
app.py
CHANGED
@@ -548,26 +548,38 @@ class ProcessingUI:
|
|
548 |
|
549 |
def setup_events_tab(self):
|
550 |
"""Setup the events timeline display"""
|
551 |
-
# Event type filter
|
552 |
-
|
|
|
|
|
|
|
553 |
"Тип события:",
|
554 |
options=["Отчетность", "РЦБ", "Суд"],
|
555 |
-
default=None
|
|
|
556 |
)
|
557 |
|
558 |
# Timeline container
|
559 |
-
|
|
|
560 |
|
561 |
def setup_analytics_tab(self):
|
562 |
"""Setup the analytics display"""
|
563 |
-
#
|
564 |
-
self.
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
571 |
|
572 |
def update_stats(self, row, sentiment, event_type, processing_speed):
|
573 |
"""Update all statistics and displays"""
|
@@ -616,27 +628,45 @@ class ProcessingUI:
|
|
616 |
|
617 |
def _update_recent_items(self, row, sentiment, event_type):
|
618 |
"""Update recent items display with custom styling"""
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
item_class = 'recent-item '
|
623 |
-
if sentiment == 'Negative':
|
624 |
-
item_class += 'negative-item'
|
625 |
-
elif sentiment == 'Positive':
|
626 |
-
item_class += 'positive-item'
|
627 |
-
if event_type != 'Нет':
|
628 |
-
item_class += ' event-item'
|
629 |
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
640 |
|
641 |
items_html += "</div>"
|
642 |
self.recent_items_container.markdown(items_html, unsafe_allow_html=True)
|
@@ -647,60 +677,101 @@ class ProcessingUI:
|
|
647 |
if not stats:
|
648 |
return
|
649 |
|
650 |
-
#
|
651 |
-
|
652 |
-
|
653 |
-
if current_options != all_entities:
|
654 |
-
self.entity_filter = list(all_entities)
|
655 |
-
|
656 |
# Create entity comparison chart using Plotly
|
657 |
df_entities = pd.DataFrame.from_dict(stats, orient='index')
|
|
|
|
|
658 |
fig = go.Figure(data=[
|
659 |
-
go.Bar(
|
660 |
-
|
661 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
662 |
])
|
663 |
-
fig.update_layout(barmode='group', title='Entity Statistics')
|
664 |
-
self.entity_chart.plotly_chart(fig, use_container_width=True)
|
665 |
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
<p>{row['Заголовок']}</p>
|
676 |
-
<small>{datetime.now().strftime('%H:%M:%S')}</small>
|
677 |
-
</div>
|
678 |
-
</div>
|
679 |
-
"""
|
680 |
-
with self.timeline_container:
|
681 |
-
st.markdown(event_html, unsafe_allow_html=True)
|
682 |
|
683 |
def _update_analytics(self):
|
684 |
"""Update analytics tab visualizations"""
|
685 |
stats = st.session_state.processing_stats
|
686 |
|
687 |
-
# Processing speed chart
|
688 |
-
speeds = stats['processing_speed'][-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
694 |
|
695 |
def update_progress(self, current, total):
|
696 |
-
"""Update progress bar and
|
697 |
progress = current / total
|
698 |
self.progress_bar.progress(progress)
|
699 |
self.status.text(f"Обрабатываем {current} из {total} сообщений...")
|
700 |
|
701 |
-
#
|
702 |
-
|
703 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
704 |
|
705 |
|
706 |
class EventDetectionSystem:
|
@@ -1477,7 +1548,7 @@ def main():
|
|
1477 |
st.set_page_config(layout="wide")
|
1478 |
|
1479 |
with st.sidebar:
|
1480 |
-
st.title("::: AI-анализ мониторинга новостей (v.3.
|
1481 |
st.subheader("по материалам СКАН-ИНТЕРФАКС")
|
1482 |
|
1483 |
model_choice = st.radio(
|
|
|
548 |
|
549 |
def setup_events_tab(self):
|
550 |
"""Setup the events timeline display"""
|
551 |
+
# Event type filter - store in session state
|
552 |
+
if 'event_filter' not in st.session_state:
|
553 |
+
st.session_state.event_filter = []
|
554 |
+
|
555 |
+
st.session_state.event_filter = st.multiselect(
|
556 |
"Тип события:",
|
557 |
options=["Отчетность", "РЦБ", "Суд"],
|
558 |
+
default=None,
|
559 |
+
key="event_filter_key"
|
560 |
)
|
561 |
|
562 |
# Timeline container
|
563 |
+
if 'timeline_container' not in st.session_state:
|
564 |
+
st.session_state.timeline_container = st.container()
|
565 |
|
566 |
def setup_analytics_tab(self):
|
567 |
"""Setup the analytics display"""
|
568 |
+
# Create containers for analytics
|
569 |
+
self.speed_container = st.container()
|
570 |
+
with self.speed_container:
|
571 |
+
st.subheader("Скорость обработки")
|
572 |
+
self.speed_chart = st.empty()
|
573 |
+
|
574 |
+
self.sentiment_container = st.container()
|
575 |
+
with self.sentiment_container:
|
576 |
+
st.subheader("Распределение тональности")
|
577 |
+
self.sentiment_chart = st.empty()
|
578 |
+
|
579 |
+
self.correlation_container = st.container()
|
580 |
+
with self.correlation_container:
|
581 |
+
st.subheader("Корреляция между метриками")
|
582 |
+
self.correlation_chart = st.empty()
|
583 |
|
584 |
def update_stats(self, row, sentiment, event_type, processing_speed):
|
585 |
"""Update all statistics and displays"""
|
|
|
628 |
|
629 |
def _update_recent_items(self, row, sentiment, event_type):
|
630 |
"""Update recent items display with custom styling"""
|
631 |
+
# Maintain a list of recent items in session state
|
632 |
+
if 'recent_items' not in st.session_state:
|
633 |
+
st.session_state.recent_items = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
|
635 |
+
# Add new item to the beginning of the list
|
636 |
+
new_item = {
|
637 |
+
'entity': row['Объект'],
|
638 |
+
'headline': row['Заголовок'],
|
639 |
+
'sentiment': sentiment,
|
640 |
+
'event_type': event_type,
|
641 |
+
'time': datetime.now().strftime('%H:%M:%S')
|
642 |
+
}
|
643 |
+
|
644 |
+
st.session_state.recent_items.insert(0, new_item)
|
645 |
+
# Keep only last 10 items
|
646 |
+
st.session_state.recent_items = st.session_state.recent_items[:10]
|
647 |
+
|
648 |
+
# Create HTML for all recent items
|
649 |
+
items_html = "<div style='max-height: 400px; overflow-y: auto;'>"
|
650 |
+
|
651 |
+
for item in st.session_state.recent_items:
|
652 |
+
if item['sentiment'] in ['Positive', 'Negative']: # Only show Positive and Negative items
|
653 |
+
item_class = 'recent-item '
|
654 |
+
if item['sentiment'] == 'Negative':
|
655 |
+
item_class += 'negative-item'
|
656 |
+
elif item['sentiment'] == 'Positive':
|
657 |
+
item_class += 'positive-item'
|
658 |
+
|
659 |
+
items_html += f"""
|
660 |
+
<div class='{item_class}'>
|
661 |
+
<strong>{item['entity']}</strong><br>
|
662 |
+
{item['headline']}<br>
|
663 |
+
<small>
|
664 |
+
Тональность: {item['sentiment']}
|
665 |
+
{f" | Событие: {item['event_type']}" if item['event_type'] != 'Нет' else ""}
|
666 |
+
| {item['time']}
|
667 |
+
</small>
|
668 |
+
</div>
|
669 |
+
"""
|
670 |
|
671 |
items_html += "</div>"
|
672 |
self.recent_items_container.markdown(items_html, unsafe_allow_html=True)
|
|
|
677 |
if not stats:
|
678 |
return
|
679 |
|
680 |
+
# Get filtered entities
|
681 |
+
filtered_entities = self.entity_filter or stats.keys()
|
682 |
+
|
|
|
|
|
|
|
683 |
# Create entity comparison chart using Plotly
|
684 |
df_entities = pd.DataFrame.from_dict(stats, orient='index')
|
685 |
+
df_entities = df_entities.loc[filtered_entities] # Apply filter
|
686 |
+
|
687 |
fig = go.Figure(data=[
|
688 |
+
go.Bar(
|
689 |
+
name='Всего',
|
690 |
+
x=df_entities.index,
|
691 |
+
y=df_entities['total'],
|
692 |
+
marker_color='#E0E0E0' # Light gray
|
693 |
+
),
|
694 |
+
go.Bar(
|
695 |
+
name='Негативные',
|
696 |
+
x=df_entities.index,
|
697 |
+
y=df_entities['negative'],
|
698 |
+
marker_color='#FF6B6B' # Red
|
699 |
+
),
|
700 |
+
go.Bar(
|
701 |
+
name='События',
|
702 |
+
x=df_entities.index,
|
703 |
+
y=df_entities['events'],
|
704 |
+
marker_color='#2196F3' # Blue
|
705 |
+
)
|
706 |
])
|
|
|
|
|
707 |
|
708 |
+
fig.update_layout(
|
709 |
+
barmode='group',
|
710 |
+
title='Статистика по организациям',
|
711 |
+
xaxis_title='Организация',
|
712 |
+
yaxis_title='Количество',
|
713 |
+
showlegend=True
|
714 |
+
)
|
715 |
+
|
716 |
+
self.entity_chart.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
717 |
|
718 |
def _update_analytics(self):
|
719 |
"""Update analytics tab visualizations"""
|
720 |
stats = st.session_state.processing_stats
|
721 |
|
722 |
+
# Processing speed chart - showing last 20 measurements
|
723 |
+
speeds = stats['processing_speed'][-20:]
|
724 |
+
if speeds:
|
725 |
+
fig_speed = go.Figure(data=go.Scatter(
|
726 |
+
y=speeds,
|
727 |
+
mode='lines+markers',
|
728 |
+
name='Скорость',
|
729 |
+
line=dict(color='#4CAF50')
|
730 |
+
))
|
731 |
+
fig_speed.update_layout(
|
732 |
+
title='Скорость обработки',
|
733 |
+
yaxis_title='Сообщений в секунду',
|
734 |
+
showlegend=True
|
735 |
+
)
|
736 |
+
self.speed_chart.plotly_chart(fig_speed, use_container_width=True)
|
737 |
+
|
738 |
+
# Sentiment distribution pie chart
|
739 |
+
if stats['entities']:
|
740 |
+
total_negative = sum(e['negative'] for e in stats['entities'].values())
|
741 |
+
total_positive = sum(e['events'] for e in stats['entities'].values())
|
742 |
+
total_neutral = sum(e['total'] for e in stats['entities'].values()) - total_negative - total_positive
|
743 |
+
|
744 |
+
fig_sentiment = go.Figure(data=[go.Pie(
|
745 |
+
labels=['Негативные', 'Позитивные', 'Нейтральные'],
|
746 |
+
values=[total_negative, total_positive, total_neutral],
|
747 |
+
marker_colors=['#FF6B6B', '#4ECDC4', '#95A5A6']
|
748 |
+
)])
|
749 |
+
self.sentiment_chart.plotly_chart(fig_sentiment, use_container_width=True)
|
750 |
|
751 |
def update_progress(self, current, total):
|
752 |
+
"""Update progress bar, elapsed time and estimated time remaining"""
|
753 |
progress = current / total
|
754 |
self.progress_bar.progress(progress)
|
755 |
self.status.text(f"Обрабатываем {current} из {total} сообщений...")
|
756 |
|
757 |
+
# Calculate times
|
758 |
+
current_time = time.time()
|
759 |
+
elapsed = current_time - st.session_state.processing_stats['start_time']
|
760 |
+
|
761 |
+
# Calculate processing speed and estimated time remaining
|
762 |
+
if current > 0:
|
763 |
+
speed = current / elapsed # items per second
|
764 |
+
remaining_items = total - current
|
765 |
+
estimated_remaining = remaining_items / speed if speed > 0 else 0
|
766 |
+
|
767 |
+
time_display = (
|
768 |
+
f"⏱️ Прошло: {format_elapsed_time(elapsed)} | "
|
769 |
+
f"Осталось: {format_elapsed_time(estimated_remaining)}"
|
770 |
+
)
|
771 |
+
else:
|
772 |
+
time_display = f"⏱️ Прошло: {format_elapsed_time(elapsed)}"
|
773 |
+
|
774 |
+
self.timer_display.markdown(time_display)
|
775 |
|
776 |
|
777 |
class EventDetectionSystem:
|
|
|
1548 |
st.set_page_config(layout="wide")
|
1549 |
|
1550 |
with st.sidebar:
|
1551 |
+
st.title("::: AI-анализ мониторинга новостей (v.3.71+):::")
|
1552 |
st.subheader("по материалам СКАН-ИНТЕРФАКС")
|
1553 |
|
1554 |
model_choice = st.radio(
|