import tkinter as tk from tkinter import messagebox import threading from analyzer import analyze_repository from storage import register_user, check_user, get_repositories, add_repository, delete_repository class GitAnalyzerApp: def __init__(self): self.root = tk.Tk() self.root.title('Universal Git Analyzer') self.root.geometry('1200x750') self.root.minsize(1000, 650) self.root.resizable(True, True) self.current_user = None self.bg = '#eef4ff' self.panel = '#ffffff' self.button = '#6aa6ff' self.root.configure(bg=self.bg) self.show_splash() self.root.mainloop() def clear(self): for widget in self.root.winfo_children(): widget.destroy() def make_button(self, parent, text, command, width=24): return tk.Button( parent, text=text, command=command, width=width, bg=self.button, fg='white', activebackground='#4d8ee8', font=('Arial', 12, 'bold'), relief='flat', cursor='hand2', pady=6, ) def show_splash(self): self.clear() frame = tk.Frame(self.root, bg=self.bg) frame.pack(expand=True) tk.Label( frame, text='Universal Git Analyzer', font=('Arial', 30, 'bold'), bg=self.bg, fg='#1d3557', ).pack(pady=20) tk.Label( frame, text='Анализ Git-репозиториев: GitHub, Gitea, GitLab и другие публичные git-ссылки', font=('Arial', 14), bg=self.bg, fg='#333333', ).pack(pady=10) tk.Label(frame, text='Загрузка...', font=('Arial', 12), bg=self.bg, fg='#555555').pack(pady=30) self.root.after(1200, self.show_login) def show_login(self): self.clear() frame = tk.Frame(self.root, bg=self.panel, padx=35, pady=35) frame.place(relx=0.5, rely=0.5, anchor='center') tk.Label(frame, text='Вход в аккаунт', font=('Arial', 22, 'bold'), bg=self.panel).pack(pady=10) tk.Label(frame, text='Логин', bg=self.panel, font=('Arial', 11)).pack(anchor='w') login_entry = tk.Entry(frame, width=35, font=('Arial', 12)) login_entry.pack(pady=5) tk.Label(frame, text='Пароль', bg=self.panel, font=('Arial', 11)).pack(anchor='w') password_entry = tk.Entry(frame, width=35, font=('Arial', 12), show='*') password_entry.pack(pady=5) def login(): login_value = login_entry.get().strip() password_value = password_entry.get().strip() if not login_value or not password_value: messagebox.showerror('Ошибка', 'Введите логин и пароль') return if check_user(login_value, password_value): self.current_user = login_value self.show_main_screen() else: messagebox.showerror('Ошибка', 'Неверный логин или пароль') self.make_button(frame, 'Войти', login).pack(pady=12) self.make_button(frame, 'Создать профиль', self.show_register).pack(pady=5) def show_register(self): self.clear() frame = tk.Frame(self.root, bg=self.panel, padx=35, pady=35) frame.place(relx=0.5, rely=0.5, anchor='center') tk.Label(frame, text='Создание профиля', font=('Arial', 22, 'bold'), bg=self.panel).pack(pady=10) tk.Label(frame, text='Логин', bg=self.panel, font=('Arial', 11)).pack(anchor='w') login_entry = tk.Entry(frame, width=35, font=('Arial', 12)) login_entry.pack(pady=5) tk.Label(frame, text='Пароль', bg=self.panel, font=('Arial', 11)).pack(anchor='w') password_entry = tk.Entry(frame, width=35, font=('Arial', 12), show='*') password_entry.pack(pady=5) def register(): login_value = login_entry.get().strip() password_value = password_entry.get().strip() if not login_value or not password_value: messagebox.showerror('Ошибка', 'Введите логин и пароль') return if len(password_value) < 4: messagebox.showerror('Ошибка', 'Пароль должен быть минимум 4 символа') return ok, text = register_user(login_value, password_value) if ok: messagebox.showinfo('Готово', text) self.show_login() else: messagebox.showerror('Ошибка', text) self.make_button(frame, 'Зарегистрироваться', register).pack(pady=12) self.make_button(frame, 'Назад', self.show_login).pack(pady=5) def show_main_screen(self): self.clear() top = tk.Frame(self.root, bg='#1d3557', height=60) top.pack(fill='x') tk.Label( top, text=f'Universal Git Analyzer | Пользователь: {self.current_user}', bg='#1d3557', fg='white', font=('Arial', 18, 'bold'), ).pack(side='left', padx=20, pady=15) tk.Button(top, text='Выйти', command=self.show_login, bg='#e63946', fg='white', relief='flat', font=('Arial', 12)).pack(side='right', padx=20) content = tk.Frame(self.root, bg=self.bg) content.pack(fill='both', expand=True, padx=15, pady=15) left = tk.Frame(content, bg=self.panel, padx=15, pady=15) left.pack(side='left', fill='y') right = tk.Frame(content, bg=self.panel, padx=15, pady=15) right.pack(side='right', fill='both', expand=True, padx=(15, 0)) tk.Label(left, text='Мои репозитории', bg=self.panel, font=('Arial', 22, 'bold')).pack(anchor='w') repo_listbox = tk.Listbox(left, width=48, height=20, font=('Arial', 12)) repo_listbox.pack(pady=10) tk.Label(left, text='Новая ссылка', bg=self.panel, font=('Arial', 13)).pack(anchor='w') repo_entry = tk.Entry(left, width=48, font=('Arial', 12)) repo_entry.pack(pady=5) result_text = tk.Text(right, width=65, height=28, font=('Consolas', 13), wrap='word') result_text.pack(fill='both', expand=True) def refresh_list(): repo_listbox.delete(0, tk.END) for repo in get_repositories(self.current_user): repo_listbox.insert(tk.END, repo) def get_selected_repo(): selection = repo_listbox.curselection() if not selection: return None return repo_listbox.get(selection[0]) def save_repo(): link = repo_entry.get().strip() if not link: messagebox.showerror('Ошибка', 'Введите ссылку') return if not (link.startswith('https://') or link.startswith('http://') or link.startswith('git@')): messagebox.showerror('Ошибка', 'Введите нормальную git-ссылку') return added = add_repository(self.current_user, link) if added: repo_entry.delete(0, tk.END) refresh_list() messagebox.showinfo('Готово', 'Ссылка сохранена') else: messagebox.showinfo('Информация', 'Эта ссылка уже сохранена') def remove_repo(): link = get_selected_repo() if not link: messagebox.showerror('Ошибка', 'Выберите репозиторий') return delete_repository(self.current_user, link) refresh_list() result_text.delete('1.0', tk.END) def analyze_selected(): link = get_selected_repo() if not link: messagebox.showerror('Ошибка', 'Выберите репозиторий') return result_text.delete('1.0', tk.END) result_text.insert(tk.END, 'Загрузка данных...\n') def worker(): try: info = analyze_repository(link) self.root.after(0, lambda: show_result(info)) except Exception as e: self.root.after(0, lambda: show_error(str(e))) def show_result(info): result_text.delete('1.0', tk.END) result_text.insert(tk.END, info) def show_error(error): result_text.delete('1.0', tk.END) messagebox.showerror('Ошибка', f'Не удалось проанализировать репозиторий:\n{error}') threading.Thread(target=worker, daemon=True).start() self.make_button(left, 'Сохранить ссылку', save_repo, width=28).pack(pady=5) self.make_button(left, 'Обновить информацию', analyze_selected, width=28).pack(pady=5) self.make_button(left, 'Удалить ссылку', remove_repo, width=28).pack(pady=5) tk.Label( left, text='Поддерживаются публичные git-ссылки:\nGitea, GitHub, GitLab, Forgejo и другие.', bg=self.panel, fg='#444444', font=('Arial', 10), justify='left', ).pack(anchor='w', pady=15) refresh_list() if __name__ == '__main__': GitAnalyzerApp()