Spaces:
Sleeping
Sleeping
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(
|