Spaces:
Runtime error
Runtime error
File size: 14,967 Bytes
a7174ff 26e822e a7174ff 26e822e a7174ff bb6217a a7174ff ece9efb a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e a7174ff 26e822e fb9ceee 26e822e a7174ff 26e822e a7174ff 26e822e b4da720 9876f34 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
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() |