DocUA's picture
docker_patch.py
bb6217a
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()