Spaces:
Running
Running
Commit ·
8a9c71f
1
Parent(s): 8f0749d
3.68
Browse files
app.py
CHANGED
|
@@ -32,6 +32,9 @@ from queue import Queue
|
|
| 32 |
|
| 33 |
from deep_translator import GoogleTranslator
|
| 34 |
from googletrans import Translator as LegacyTranslator
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
class ProcessControl:
|
|
@@ -431,54 +434,272 @@ class ProcessingUI:
|
|
| 431 |
def __init__(self):
|
| 432 |
if 'control' not in st.session_state:
|
| 433 |
st.session_state.control = ProcessControl()
|
| 434 |
-
if 'negative_container' not in st.session_state:
|
| 435 |
-
st.session_state.negative_container = st.empty()
|
| 436 |
-
if 'events_container' not in st.session_state:
|
| 437 |
-
st.session_state.events_container = st.empty()
|
| 438 |
|
| 439 |
-
#
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
self.progress_bar = st.progress(0)
|
| 453 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
|
| 455 |
def update_progress(self, current, total):
|
|
|
|
| 456 |
progress = current / total
|
| 457 |
self.progress_bar.progress(progress)
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
<div style='background-color: #ffebee; padding: 10px; border-radius: 5px; margin: 5px 0;'>
|
| 464 |
-
<strong style='color: #d32f2f;'>⚠️ Внимание: негатив!:</strong><br>
|
| 465 |
-
<strong>Entity:</strong> {entity}<br>
|
| 466 |
-
<strong>News:</strong> {headline}<br>
|
| 467 |
-
<strong>Analysis:</strong> {analysis}<br>
|
| 468 |
-
{f"<strong>Impact:</strong> {impact}<br>" if impact else ""}
|
| 469 |
-
</div>
|
| 470 |
-
""", unsafe_allow_html=True)
|
| 471 |
-
|
| 472 |
-
def show_event(self, entity, event_type, headline):
|
| 473 |
-
with st.session_state.events_container:
|
| 474 |
-
st.markdown(f"""
|
| 475 |
-
<div style='background-color: #e3f2fd; padding: 10px; border-radius: 5px; margin: 5px 0;'>
|
| 476 |
-
<strong style='color: #1976d2;'>🔔 Возможно, речь о важном факте:</strong><br>
|
| 477 |
-
<strong>Entity:</strong> {entity}<br>
|
| 478 |
-
<strong>Type:</strong> {event_type}<br>
|
| 479 |
-
<strong>News:</strong> {headline}
|
| 480 |
-
</div>
|
| 481 |
-
""", unsafe_allow_html=True)
|
| 482 |
|
| 483 |
class EventDetectionSystem:
|
| 484 |
def __init__(self):
|
|
@@ -656,6 +877,8 @@ class TranslationSystem:
|
|
| 656 |
|
| 657 |
def process_file(uploaded_file, model_choice, translation_method=None):
|
| 658 |
df = None
|
|
|
|
|
|
|
| 659 |
try:
|
| 660 |
# Initialize UI and control systems
|
| 661 |
ui = ProcessingUI()
|
|
@@ -699,11 +922,32 @@ def process_file(uploaded_file, model_choice, translation_method=None):
|
|
| 699 |
# Check for stop/pause
|
| 700 |
if st.session_state.control.is_stopped():
|
| 701 |
st.warning("Обработку остановили")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
break
|
| 703 |
|
| 704 |
st.session_state.control.wait_if_paused()
|
| 705 |
if st.session_state.control.is_paused():
|
| 706 |
st.info("Обработка на паузе. Можно возобновить.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 707 |
continue
|
| 708 |
|
| 709 |
try:
|
|
@@ -723,6 +967,9 @@ def process_file(uploaded_file, model_choice, translation_method=None):
|
|
| 723 |
df.at[idx, 'Event_Type'] = event_type
|
| 724 |
df.at[idx, 'Event_Summary'] = event_summary
|
| 725 |
|
|
|
|
|
|
|
|
|
|
| 726 |
# Show events in real-time
|
| 727 |
if event_type != "Нет":
|
| 728 |
ui.show_event(
|
|
@@ -756,6 +1003,8 @@ def process_file(uploaded_file, model_choice, translation_method=None):
|
|
| 756 |
impact
|
| 757 |
)
|
| 758 |
|
|
|
|
|
|
|
| 759 |
# Update progress
|
| 760 |
processed_rows += 1
|
| 761 |
ui.update_progress(processed_rows, total_rows)
|
|
@@ -766,20 +1015,8 @@ def process_file(uploaded_file, model_choice, translation_method=None):
|
|
| 766 |
|
| 767 |
time.sleep(0.1)
|
| 768 |
|
| 769 |
-
|
| 770 |
-
if st.session_state.control.is_stopped()
|
| 771 |
-
st.warning("Обработку остановили. Показываю частичные результаты.")
|
| 772 |
-
output = create_output_file(df, uploaded_file, llm)
|
| 773 |
-
if output is not None:
|
| 774 |
-
st.download_button(
|
| 775 |
-
label="📊 Скачать частичный результат",
|
| 776 |
-
data=output,
|
| 777 |
-
file_name="partial_analysis.xlsx",
|
| 778 |
-
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
| 779 |
-
key="partial_download"
|
| 780 |
-
)
|
| 781 |
-
|
| 782 |
-
return df
|
| 783 |
|
| 784 |
except Exception as e:
|
| 785 |
st.error(f"Ошибка в обработке файла: {str(e)}")
|
|
@@ -1165,7 +1402,7 @@ def main():
|
|
| 1165 |
st.set_page_config(layout="wide")
|
| 1166 |
|
| 1167 |
with st.sidebar:
|
| 1168 |
-
st.title("::: AI-анализ мониторинга новостей (v.3.
|
| 1169 |
st.subheader("по материалам СКАН-ИНТЕРФАКС")
|
| 1170 |
|
| 1171 |
model_choice = st.radio(
|
|
|
|
| 32 |
|
| 33 |
from deep_translator import GoogleTranslator
|
| 34 |
from googletrans import Translator as LegacyTranslator
|
| 35 |
+
import plotly.graph_objects as go
|
| 36 |
+
from datetime import datetime
|
| 37 |
+
import plotly.express as px
|
| 38 |
|
| 39 |
|
| 40 |
class ProcessControl:
|
|
|
|
| 434 |
def __init__(self):
|
| 435 |
if 'control' not in st.session_state:
|
| 436 |
st.session_state.control = ProcessControl()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
|
| 438 |
+
# Initialize processing stats in session state if not exists
|
| 439 |
+
if 'processing_stats' not in st.session_state:
|
| 440 |
+
st.session_state.processing_stats = {
|
| 441 |
+
'start_time': time.time(),
|
| 442 |
+
'entities': {},
|
| 443 |
+
'events_timeline': [],
|
| 444 |
+
'negative_alerts': [],
|
| 445 |
+
'processing_speed': []
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
# Create main layout
|
| 449 |
+
self.setup_layout()
|
| 450 |
+
|
| 451 |
+
def setup_layout(self):
|
| 452 |
+
"""Setup the main UI layout with tabs and sections"""
|
| 453 |
+
# Control Panel
|
| 454 |
+
with st.container():
|
| 455 |
+
col1, col2, col3 = st.columns([2,2,1])
|
| 456 |
+
with col1:
|
| 457 |
+
if st.button(
|
| 458 |
+
"⏸️ Пауза" if not st.session_state.control.is_paused() else "▶️ Продолжить",
|
| 459 |
+
use_container_width=True
|
| 460 |
+
):
|
| 461 |
+
if st.session_state.control.is_paused():
|
| 462 |
+
st.session_state.control.resume()
|
| 463 |
+
else:
|
| 464 |
+
st.session_state.control.pause()
|
| 465 |
+
with col2:
|
| 466 |
+
if st.button("⏹️ Остановить", use_container_width=True):
|
| 467 |
+
st.session_state.control.stop()
|
| 468 |
+
with col3:
|
| 469 |
+
self.timer_display = st.empty()
|
| 470 |
+
|
| 471 |
+
# Progress Bar with custom styling
|
| 472 |
+
st.markdown("""
|
| 473 |
+
<style>
|
| 474 |
+
.stProgress > div > div > div > div {
|
| 475 |
+
background-image: linear-gradient(to right, #FF6B6B, #4ECDC4);
|
| 476 |
+
}
|
| 477 |
+
</style>""",
|
| 478 |
+
unsafe_allow_html=True
|
| 479 |
+
)
|
| 480 |
self.progress_bar = st.progress(0)
|
| 481 |
+
|
| 482 |
+
# Create tabs for different views
|
| 483 |
+
tab1, tab2, tab3, tab4 = st.tabs([
|
| 484 |
+
"📊 Основные метрики",
|
| 485 |
+
"🏢 По организациям",
|
| 486 |
+
"⚠️ Важные события",
|
| 487 |
+
"📈 Аналитика"
|
| 488 |
+
])
|
| 489 |
+
|
| 490 |
+
with tab1:
|
| 491 |
+
self.setup_main_metrics_tab()
|
| 492 |
+
|
| 493 |
+
with tab2:
|
| 494 |
+
self.setup_entity_tab()
|
| 495 |
+
|
| 496 |
+
with tab3:
|
| 497 |
+
self.setup_events_tab()
|
| 498 |
+
|
| 499 |
+
with tab4:
|
| 500 |
+
self.setup_analytics_tab()
|
| 501 |
+
|
| 502 |
+
def setup_main_metrics_tab(self):
|
| 503 |
+
"""Setup the main metrics display"""
|
| 504 |
+
# Create metrics containers
|
| 505 |
+
metrics_cols = st.columns(4)
|
| 506 |
+
self.total_processed = metrics_cols[0].empty()
|
| 507 |
+
self.negative_count = metrics_cols[1].empty()
|
| 508 |
+
self.events_count = metrics_cols[2].empty()
|
| 509 |
+
self.speed_metric = metrics_cols[3].empty()
|
| 510 |
+
|
| 511 |
+
# Recent items container with custom styling
|
| 512 |
+
st.markdown("""
|
| 513 |
+
<style>
|
| 514 |
+
.recent-item {
|
| 515 |
+
padding: 10px;
|
| 516 |
+
margin: 5px 0;
|
| 517 |
+
border-radius: 5px;
|
| 518 |
+
background-color: #f0f2f6;
|
| 519 |
+
}
|
| 520 |
+
.negative-item {
|
| 521 |
+
border-left: 4px solid #FF6B6B;
|
| 522 |
+
}
|
| 523 |
+
.positive-item {
|
| 524 |
+
border-left: 4px solid #4ECDC4;
|
| 525 |
+
}
|
| 526 |
+
.event-item {
|
| 527 |
+
border-left: 4px solid #FFE66D;
|
| 528 |
+
}
|
| 529 |
+
</style>
|
| 530 |
+
""", unsafe_allow_html=True)
|
| 531 |
+
|
| 532 |
+
self.recent_items_container = st.empty()
|
| 533 |
+
|
| 534 |
+
def setup_entity_tab(self):
|
| 535 |
+
"""Setup the entity-wise analysis display"""
|
| 536 |
+
# Entity filter
|
| 537 |
+
self.entity_filter = st.multiselect(
|
| 538 |
+
"Фильтр по организациям:",
|
| 539 |
+
options=[], # Will be populated as entities are processed
|
| 540 |
+
default=None
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
# Entity metrics
|
| 544 |
+
self.entity_cols = st.columns([2,1,1,1])
|
| 545 |
+
self.entity_chart = st.empty()
|
| 546 |
+
self.entity_table = st.empty()
|
| 547 |
+
|
| 548 |
+
def setup_events_tab(self):
|
| 549 |
+
"""Setup the events timeline display"""
|
| 550 |
+
# Event type filter
|
| 551 |
+
self.event_filter = st.multiselect(
|
| 552 |
+
"Тип события:",
|
| 553 |
+
options=["Отчетность", "РЦБ", "Суд"],
|
| 554 |
+
default=None
|
| 555 |
+
)
|
| 556 |
+
|
| 557 |
+
# Timeline container
|
| 558 |
+
self.timeline_container = st.container()
|
| 559 |
+
|
| 560 |
+
def setup_analytics_tab(self):
|
| 561 |
+
"""Setup the analytics display"""
|
| 562 |
+
# Processing speed chart
|
| 563 |
+
self.speed_chart = st.empty()
|
| 564 |
+
|
| 565 |
+
# Sentiment distribution pie chart
|
| 566 |
+
self.sentiment_chart = st.empty()
|
| 567 |
+
|
| 568 |
+
# Entity correlation matrix
|
| 569 |
+
self.correlation_chart = st.empty()
|
| 570 |
+
|
| 571 |
+
def update_stats(self, row, sentiment, event_type, processing_speed):
|
| 572 |
+
"""Update all statistics and displays"""
|
| 573 |
+
# Update session state stats
|
| 574 |
+
stats = st.session_state.processing_stats
|
| 575 |
+
entity = row['Объект']
|
| 576 |
+
|
| 577 |
+
# Update entity stats
|
| 578 |
+
if entity not in stats['entities']:
|
| 579 |
+
stats['entities'][entity] = {
|
| 580 |
+
'total': 0,
|
| 581 |
+
'negative': 0,
|
| 582 |
+
'events': 0,
|
| 583 |
+
'timeline': []
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
stats['entities'][entity]['total'] += 1
|
| 587 |
+
if sentiment == 'Negative':
|
| 588 |
+
stats['entities'][entity]['negative'] += 1
|
| 589 |
+
if event_type != 'Нет':
|
| 590 |
+
stats['entities'][entity]['events'] += 1
|
| 591 |
+
|
| 592 |
+
# Update processing speed
|
| 593 |
+
stats['processing_speed'].append(processing_speed)
|
| 594 |
+
|
| 595 |
+
# Update UI components
|
| 596 |
+
self._update_main_metrics(row, sentiment, event_type, processing_speed)
|
| 597 |
+
self._update_entity_view()
|
| 598 |
+
self._update_events_view(row, event_type)
|
| 599 |
+
self._update_analytics()
|
| 600 |
+
|
| 601 |
+
def _update_main_metrics(self, row, sentiment, event_type, speed):
|
| 602 |
+
"""Update main metrics tab"""
|
| 603 |
+
total = sum(e['total'] for e in st.session_state.processing_stats['entities'].values())
|
| 604 |
+
total_negative = sum(e['negative'] for e in st.session_state.processing_stats['entities'].values())
|
| 605 |
+
total_events = sum(e['events'] for e in st.session_state.processing_stats['entities'].values())
|
| 606 |
+
|
| 607 |
+
# Update metrics
|
| 608 |
+
self.total_processed.metric("Обработано", total)
|
| 609 |
+
self.negative_count.metric("Негативных", total_negative)
|
| 610 |
+
self.events_count.metric("Событий", total_events)
|
| 611 |
+
self.speed_metric.metric("Скорость", f"{speed:.1f} сообщ/сек")
|
| 612 |
+
|
| 613 |
+
# Update recent items
|
| 614 |
+
self._update_recent_items(row, sentiment, event_type)
|
| 615 |
+
|
| 616 |
+
def _update_recent_items(self, row, sentiment, event_type):
|
| 617 |
+
"""Update recent items display with custom styling"""
|
| 618 |
+
items_html = "<div style='max-height: 300px; overflow-y: auto;'>"
|
| 619 |
+
|
| 620 |
+
# Add new item to the beginning of the list
|
| 621 |
+
item_class = 'recent-item '
|
| 622 |
+
if sentiment == 'Negative':
|
| 623 |
+
item_class += 'negative-item'
|
| 624 |
+
elif sentiment == 'Positive':
|
| 625 |
+
item_class += 'positive-item'
|
| 626 |
+
if event_type != 'Нет':
|
| 627 |
+
item_class += ' event-item'
|
| 628 |
+
|
| 629 |
+
items_html += f"""
|
| 630 |
+
<div class='{item_class}'>
|
| 631 |
+
<strong>{row['Объект']}</strong><br>
|
| 632 |
+
{row['Заголовок']}<br>
|
| 633 |
+
<small>
|
| 634 |
+
Тональность: {sentiment}
|
| 635 |
+
{f" | Событие: {event_type}" if event_type != 'Нет' else ""}
|
| 636 |
+
</small>
|
| 637 |
+
</div>
|
| 638 |
+
"""
|
| 639 |
+
|
| 640 |
+
items_html += "</div>"
|
| 641 |
+
self.recent_items_container.markdown(items_html, unsafe_allow_html=True)
|
| 642 |
+
|
| 643 |
+
def _update_entity_view(self):
|
| 644 |
+
"""Update entity tab visualizations"""
|
| 645 |
+
stats = st.session_state.processing_stats['entities']
|
| 646 |
+
if not stats:
|
| 647 |
+
return
|
| 648 |
+
|
| 649 |
+
# Update entity filter options
|
| 650 |
+
current_options = set(self.entity_filter)
|
| 651 |
+
all_entities = set(stats.keys())
|
| 652 |
+
if current_options != all_entities:
|
| 653 |
+
self.entity_filter = list(all_entities)
|
| 654 |
+
|
| 655 |
+
# Create entity comparison chart using Plotly
|
| 656 |
+
df_entities = pd.DataFrame.from_dict(stats, orient='index')
|
| 657 |
+
fig = go.Figure(data=[
|
| 658 |
+
go.Bar(name='Total', x=df_entities.index, y=df_entities['total']),
|
| 659 |
+
go.Bar(name='Negative', x=df_entities.index, y=df_entities['negative']),
|
| 660 |
+
go.Bar(name='Events', x=df_entities.index, y=df_entities['events'])
|
| 661 |
+
])
|
| 662 |
+
fig.update_layout(barmode='group', title='Entity Statistics')
|
| 663 |
+
self.entity_chart.plotly_chart(fig, use_container_width=True)
|
| 664 |
+
|
| 665 |
+
def _update_events_view(self, row, event_type):
|
| 666 |
+
"""Update events timeline"""
|
| 667 |
+
if event_type != 'Нет':
|
| 668 |
+
event_html = f"""
|
| 669 |
+
<div class='timeline-item'>
|
| 670 |
+
<div class='timeline-marker'></div>
|
| 671 |
+
<div class='timeline-content'>
|
| 672 |
+
<h3>{event_type}</h3>
|
| 673 |
+
<p><strong>{row['Объект']}</strong></p>
|
| 674 |
+
<p>{row['Заголовок']}</p>
|
| 675 |
+
<small>{datetime.now().strftime('%H:%M:%S')}</small>
|
| 676 |
+
</div>
|
| 677 |
+
</div>
|
| 678 |
+
"""
|
| 679 |
+
with self.timeline_container:
|
| 680 |
+
st.markdown(event_html, unsafe_allow_html=True)
|
| 681 |
+
|
| 682 |
+
def _update_analytics(self):
|
| 683 |
+
"""Update analytics tab visualizations"""
|
| 684 |
+
stats = st.session_state.processing_stats
|
| 685 |
+
|
| 686 |
+
# Processing speed chart
|
| 687 |
+
speeds = stats['processing_speed'][-50:] # Last 50 measurements
|
| 688 |
+
fig_speed = go.Figure(data=go.Scatter(y=speeds, mode='lines'))
|
| 689 |
+
fig_speed.update_layout(title='Processing Speed Over Time')
|
| 690 |
+
self.speed_chart.plotly_chart(fig_speed, use_container_width=True)
|
| 691 |
+
|
| 692 |
+
# Add other analytics visualizations as needed
|
| 693 |
|
| 694 |
def update_progress(self, current, total):
|
| 695 |
+
"""Update progress bar and elapsed time"""
|
| 696 |
progress = current / total
|
| 697 |
self.progress_bar.progress(progress)
|
| 698 |
+
|
| 699 |
+
# Update elapsed time
|
| 700 |
+
elapsed = time.time() - st.session_state.processing_stats['start_time']
|
| 701 |
+
self.timer_display.markdown(f"⏱️ {format_elapsed_time(elapsed)}")
|
| 702 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 703 |
|
| 704 |
class EventDetectionSystem:
|
| 705 |
def __init__(self):
|
|
|
|
| 877 |
|
| 878 |
def process_file(uploaded_file, model_choice, translation_method=None):
|
| 879 |
df = None
|
| 880 |
+
processed_rows_df = pd.DataFrame()
|
| 881 |
+
|
| 882 |
try:
|
| 883 |
# Initialize UI and control systems
|
| 884 |
ui = ProcessingUI()
|
|
|
|
| 922 |
# Check for stop/pause
|
| 923 |
if st.session_state.control.is_stopped():
|
| 924 |
st.warning("Обработку остановили")
|
| 925 |
+
if not processed_rows_df.empty: # Only offer download if we have processed rows
|
| 926 |
+
output = create_output_file(processed_rows_df, uploaded_file, llm)
|
| 927 |
+
if output is not None:
|
| 928 |
+
st.download_button(
|
| 929 |
+
label=f"📊 Скачать результат ({processed_rows} из {total_rows} строк)",
|
| 930 |
+
data=output,
|
| 931 |
+
file_name="partial_analysis.xlsx",
|
| 932 |
+
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
| 933 |
+
key="partial_download"
|
| 934 |
+
)
|
| 935 |
break
|
| 936 |
|
| 937 |
st.session_state.control.wait_if_paused()
|
| 938 |
if st.session_state.control.is_paused():
|
| 939 |
st.info("Обработка на паузе. Можно возобновить.")
|
| 940 |
+
if not processed_rows_df.empty: # Only offer download if we have processed rows
|
| 941 |
+
output = create_output_file(processed_rows_df, uploaded_file, llm)
|
| 942 |
+
if output is not None:
|
| 943 |
+
st.download_button(
|
| 944 |
+
label=f"📊 Скачать результат ({processed_rows} из {total_rows} строк)",
|
| 945 |
+
data=output,
|
| 946 |
+
file_name="partial_analysis.xlsx",
|
| 947 |
+
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
| 948 |
+
key="partial_download"
|
| 949 |
+
)
|
| 950 |
+
break
|
| 951 |
continue
|
| 952 |
|
| 953 |
try:
|
|
|
|
| 967 |
df.at[idx, 'Event_Type'] = event_type
|
| 968 |
df.at[idx, 'Event_Summary'] = event_summary
|
| 969 |
|
| 970 |
+
# Update live statistics
|
| 971 |
+
ui.update_stats(row, sentiment, event_type)
|
| 972 |
+
|
| 973 |
# Show events in real-time
|
| 974 |
if event_type != "Нет":
|
| 975 |
ui.show_event(
|
|
|
|
| 1003 |
impact
|
| 1004 |
)
|
| 1005 |
|
| 1006 |
+
processed_rows_df = pd.concat([processed_rows_df, df.iloc[[idx]]], ignore_index=True)
|
| 1007 |
+
|
| 1008 |
# Update progress
|
| 1009 |
processed_rows += 1
|
| 1010 |
ui.update_progress(processed_rows, total_rows)
|
|
|
|
| 1015 |
|
| 1016 |
time.sleep(0.1)
|
| 1017 |
|
| 1018 |
+
|
| 1019 |
+
return processed_rows_df if st.session_state.control.is_stopped() else df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
|
| 1021 |
except Exception as e:
|
| 1022 |
st.error(f"Ошибка в обработке файла: {str(e)}")
|
|
|
|
| 1402 |
st.set_page_config(layout="wide")
|
| 1403 |
|
| 1404 |
with st.sidebar:
|
| 1405 |
+
st.title("::: AI-анализ мониторинга новостей (v.3.68!):::")
|
| 1406 |
st.subheader("по материалам СКАН-ИНТЕРФАКС")
|
| 1407 |
|
| 1408 |
model_choice = st.radio(
|