Spaces:
Runtime error
Runtime error
import os | |
import logging | |
from pathlib import Path | |
from datetime import datetime | |
import traceback | |
import docker_patch | |
# Налаштування логування | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
handlers=[ | |
# logging.FileHandler("jira_assistant.log"), | |
logging.FileHandler("/tmp/jira_assistant.log"), | |
logging.StreamHandler() | |
] | |
) | |
logger = logging.getLogger("jira_assistant") | |
# Створення необхідних директорій | |
for directory in ["data", "reports", "temp", "logs"]: | |
Path(directory).mkdir(exist_ok=True, parents=True) | |
# Імпорт необхідних модулів | |
from modules.data_import.csv_importer import JiraCsvImporter | |
from modules.data_analysis.statistics import JiraDataAnalyzer | |
from modules.data_analysis.visualizations import JiraVisualizer | |
from modules.reporting.report_generator import ReportGenerator | |
from modules.core.app_manager import AppManager | |
class JiraAssistantApp: | |
""" | |
Головний клас додатку, який координує роботу всіх компонентів | |
""" | |
def __init__(self): | |
self.app_manager = AppManager() | |
self.current_data = None | |
self.current_analysis = None | |
self.visualizations = None | |
def analyze_csv_file(self, file_path, inactive_days=14, include_ai=False, api_key=None, model_type="openai"): | |
""" | |
Аналіз CSV-файлу Jira | |
Args: | |
file_path (str): Шлях до CSV-файлу | |
inactive_days (int): Кількість днів для визначення неактивних тікетів | |
include_ai (bool): Чи використовувати AI-аналіз | |
api_key (str): API ключ для LLM (якщо include_ai=True) | |
model_type (str): Тип моделі LLM ("openai" або "gemini") | |
Returns: | |
dict: Результати аналізу | |
""" | |
try: | |
logger.info(f"Аналіз файлу: {file_path}") | |
# Завантаження даних | |
csv_importer = JiraCsvImporter(file_path) | |
self.current_data = csv_importer.load_data() | |
if self.current_data is None: | |
return {"error": "Не вдалося завантажити дані з CSV-файлу"} | |
# Аналіз даних | |
analyzer = JiraDataAnalyzer(self.current_data) | |
# Базова статистика | |
stats = analyzer.generate_basic_statistics() | |
# Аналіз неактивних тікетів | |
inactive_issues = analyzer.analyze_inactive_issues(days=inactive_days) | |
# Створення візуалізацій | |
visualizer = JiraVisualizer(self.current_data) | |
self.visualizations = { | |
"status": visualizer.plot_status_counts(), | |
"priority": visualizer.plot_priority_counts(), | |
"type": visualizer.plot_type_counts(), | |
"created_timeline": visualizer.plot_created_timeline(), | |
"inactive": visualizer.plot_inactive_issues(days=inactive_days) | |
} | |
# AI аналіз, якщо потрібен | |
ai_analysis = None | |
if include_ai and api_key: | |
from modules.ai_analysis.llm_connector import LLMConnector | |
llm = LLMConnector(api_key=api_key, model_type=model_type) | |
ai_analysis = llm.analyze_jira_data(stats, inactive_issues) | |
# Генерація звіту | |
report_generator = ReportGenerator(self.current_data, stats, inactive_issues, ai_analysis) | |
report = report_generator.create_markdown_report(inactive_days=inactive_days) | |
# Зберігаємо поточний аналіз | |
self.current_analysis = { | |
"stats": stats, | |
"inactive_issues": inactive_issues, | |
"report": report, | |
"ai_analysis": ai_analysis | |
} | |
return { | |
"report": report, | |
"visualizations": self.visualizations, | |
"ai_analysis": ai_analysis, | |
"error": None | |
} | |
except Exception as e: | |
error_msg = f"Помилка аналізу: {str(e)}\n\n{traceback.format_exc()}" | |
logger.error(error_msg) | |
return {"error": error_msg} | |
def save_report(self, format_type="markdown", include_visualizations=True, filepath=None): | |
""" | |
Збереження звіту у файл | |
Args: | |
format_type (str): Формат звіту ("markdown", "html", "pdf") | |
include_visualizations (bool): Чи включати візуалізації у звіт | |
filepath (str): Шлях для збереження файлу | |
Returns: | |
str: Шлях до збереженого файлу або повідомлення про помилку | |
""" | |
try: | |
if not self.current_analysis or "report" not in self.current_analysis: | |
return "Помилка: спочатку виконайте аналіз даних" | |
# Створення імені файлу, якщо не вказано | |
if not filepath: | |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') | |
report_filename = f"jira_report_{timestamp}" | |
reports_dir = Path("reports") | |
if format_type == "markdown": | |
filepath = reports_dir / f"{report_filename}.md" | |
elif format_type == "html": | |
filepath = reports_dir / f"{report_filename}.html" | |
elif format_type == "pdf": | |
filepath = reports_dir / f"{report_filename}.pdf" | |
# Створення генератора звітів | |
report_generator = ReportGenerator( | |
self.current_data, | |
self.current_analysis.get("stats"), | |
self.current_analysis.get("inactive_issues"), | |
self.current_analysis.get("ai_analysis") | |
) | |
# Збереження звіту | |
saved_path = report_generator.save_report( | |
filepath=filepath, | |
format=format_type, | |
include_visualizations=include_visualizations, | |
visualization_data=self.visualizations if include_visualizations else None | |
) | |
if saved_path: | |
return f"Звіт успішно збережено: {saved_path}" | |
else: | |
return "Не вдалося зберегти звіт" | |
except Exception as e: | |
error_msg = f"Помилка при збереженні звіту: {str(e)}\n\n{traceback.format_exc()}" | |
logger.error(error_msg) | |
return error_msg | |
def test_jira_connection(self, jira_url, username, api_token): | |
""" | |
Тестування підключення до Jira | |
Args: | |
jira_url (str): URL сервера Jira | |
username (str): Ім'я користувача | |
api_token (str): API токен | |
Returns: | |
bool: True якщо підключення успішне, False інакше | |
""" | |
from modules.data_import.jira_api import JiraConnector | |
return JiraConnector.test_connection(jira_url, username, api_token) | |
def generate_visualization(self, viz_type, limit=10, groupby="day"): | |
""" | |
Генерація конкретної візуалізації | |
Args: | |
viz_type (str): Тип візуалізації | |
limit (int): Ліміт для топ-N елементів | |
groupby (str): Групування для часових діаграм ('day', 'week', 'month') | |
Returns: | |
matplotlib.figure.Figure: Об'єкт figure | |
""" | |
if self.current_data is None: | |
logger.error("Немає даних для візуалізації") | |
return None | |
# Створюємо візуалізатор | |
visualizer = JiraVisualizer(self.current_data) | |
# Вибір типу візуалізації | |
if viz_type == "Статуси": | |
return visualizer.plot_status_counts() | |
elif viz_type == "Пріоритети": | |
return visualizer.plot_priority_counts() | |
elif viz_type == "Типи тікетів": | |
return visualizer.plot_type_counts() | |
elif viz_type == "Призначені користувачі": | |
return visualizer.plot_assignee_counts(limit=limit) | |
elif viz_type == "Активність створення": | |
return visualizer.plot_timeline(date_column='Created', groupby=groupby, cumulative=False) | |
elif viz_type == "Активність оновлення": | |
return visualizer.plot_timeline(date_column='Updated', groupby=groupby, cumulative=False) | |
elif viz_type == "Кумулятивне створення": | |
return visualizer.plot_timeline(date_column='Created', groupby=groupby, cumulative=True) | |
elif viz_type == "Неактивні тікети": | |
return visualizer.plot_inactive_issues() | |
elif viz_type == "Теплова карта: Типи/Статуси": | |
return visualizer.plot_heatmap(row_col='Issue Type', column_col='Status') | |
elif viz_type == "Часова шкала проекту": | |
timeline_plots = visualizer.plot_project_timeline() | |
return timeline_plots[0] if timeline_plots[0] is not None else None | |
elif viz_type == "Склад статусів з часом": | |
timeline_plots = visualizer.plot_project_timeline() | |
return timeline_plots[1] if timeline_plots[1] is not None else None | |
else: | |
logger.error(f"Невідомий тип візуалізації: {viz_type}") | |
return None | |
def generate_infographic(self): | |
""" | |
Генерація комплексної інфографіки з ключовими показниками | |
Returns: | |
matplotlib.figure.Figure: Об'єкт figure з інфографікою | |
""" | |
try: | |
if self.current_data is None: | |
logger.error("Немає даних для інфографіки") | |
return None | |
# Створюємо візуалізатор | |
visualizer = JiraVisualizer(self.current_data) | |
# Генеруємо інфографіку | |
return visualizer.generate_infographic() | |
except Exception as e: | |
logger.error(f"Помилка при генерації інфографіки: {e}") | |
return None | |
def save_visualization(self, viz_type, filepath, limit=10, groupby="day"): | |
""" | |
Збереження конкретної візуалізації у файл | |
Args: | |
viz_type (str): Тип візуалізації | |
filepath (str): Шлях для збереження файлу | |
limit (int): Ліміт для топ-N елементів | |
groupby (str): Групування для часових діаграм | |
Returns: | |
str: Шлях до збереженого файлу або повідомлення про помилку | |
""" | |
try: | |
fig = self.generate_visualization(viz_type, limit, groupby) | |
if fig is None: | |
return f"Помилка: не вдалося створити візуалізацію типу '{viz_type}'" | |
# Перевірка наявності розширення | |
if not any(filepath.lower().endswith(ext) for ext in ['.png', '.jpg', '.svg', '.pdf']): | |
filepath += '.png' | |
# Створення директорії, якщо не існує | |
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True) | |
# Збереження візуалізації | |
fig.savefig(filepath, dpi=300, bbox_inches='tight') | |
return f"Візуалізацію успішно збережено: {filepath}" | |
except Exception as e: | |
error_msg = f"Помилка при збереженні візуалізації: {str(e)}\n\n{traceback.format_exc()}" | |
logger.error(error_msg) | |
return error_msg | |
def save_infographic(self, filepath): | |
""" | |
Збереження інфографіки у файл | |
Args: | |
filepath (str): Шлях для збереження файлу | |
Returns: | |
str: Шлях до збереженого файлу або повідомлення про помилку | |
""" | |
try: | |
infographic = self.generate_infographic() | |
if infographic is None: | |
return "Помилка: не вдалося створити інфографіку" | |
# Перевірка наявності розширення | |
if not any(filepath.lower().endswith(ext) for ext in ['.png', '.jpg', '.svg', '.pdf']): | |
filepath += '.png' | |
# Створення директорії, якщо не існує | |
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True) | |
# Збереження інфографіки | |
infographic.savefig(filepath, dpi=300, bbox_inches='tight') | |
return f"Інфографіку успішно збережено: {filepath}" | |
except Exception as e: | |
error_msg = f"Помилка при збереженні інфографіки: {str(e)}\n\n{traceback.format_exc()}" | |
logger.error(error_msg) | |
return error_msg | |
# Створення екземпляру додатку | |
app = JiraAssistantApp() | |
# Точка входу для запуску з командного рядка | |
if __name__ == "__main__": | |
from interface import launch_interface | |
interface = launch_interface(app) | |
interface.launch() |