Carregar ficheiros para "/"
This commit is contained in:
296
backup_manager.py
Normal file
296
backup_manager.py
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QProgressDialog, QFileDialog, QMessageBox)
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from database import get_session
|
||||||
|
from models import Document, DocumentVersion, Folder, Tag
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class BackupManager(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Export button
|
||||||
|
self.export_btn = QPushButton("Exportar Documentos")
|
||||||
|
self.export_btn.clicked.connect(self.export_documents)
|
||||||
|
layout.addWidget(self.export_btn)
|
||||||
|
|
||||||
|
# Import button
|
||||||
|
self.import_btn = QPushButton("Importar Documentos")
|
||||||
|
self.import_btn.clicked.connect(self.import_documents)
|
||||||
|
layout.addWidget(self.import_btn)
|
||||||
|
|
||||||
|
def export_documents(self):
|
||||||
|
export_dir = QFileDialog.getExistingDirectory(
|
||||||
|
self,
|
||||||
|
"Selecionar Pasta para Exportação"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not export_dir:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
|
||||||
|
# Create export directory structure
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
export_path = os.path.join(export_dir, f"doc_export_{timestamp}")
|
||||||
|
os.makedirs(export_path)
|
||||||
|
|
||||||
|
# Create progress dialog
|
||||||
|
documents = session.query(Document).all()
|
||||||
|
progress = QProgressDialog(
|
||||||
|
"Exportando documentos...",
|
||||||
|
"Cancelar",
|
||||||
|
0,
|
||||||
|
len(documents),
|
||||||
|
self
|
||||||
|
)
|
||||||
|
progress.setWindowModality(Qt.WindowModal)
|
||||||
|
|
||||||
|
# Export metadata
|
||||||
|
metadata = {
|
||||||
|
'documents': [],
|
||||||
|
'folders': [],
|
||||||
|
'tags': []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export documents and their versions
|
||||||
|
for i, doc in enumerate(documents):
|
||||||
|
if progress.wasCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
# Create document directory
|
||||||
|
doc_dir = os.path.join(export_path, f"doc_{doc.id}")
|
||||||
|
os.makedirs(doc_dir)
|
||||||
|
|
||||||
|
# Copy main document
|
||||||
|
if os.path.exists(doc.file_path):
|
||||||
|
shutil.copy2(doc.file_path,
|
||||||
|
os.path.join(doc_dir, os.path.basename(doc.file_path)))
|
||||||
|
|
||||||
|
# Copy versions
|
||||||
|
versions_dir = os.path.join(doc_dir, "versions")
|
||||||
|
os.makedirs(versions_dir)
|
||||||
|
for version in doc.versions:
|
||||||
|
if os.path.exists(version.file_path):
|
||||||
|
shutil.copy2(
|
||||||
|
version.file_path,
|
||||||
|
os.path.join(versions_dir, os.path.basename(version.file_path))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add document metadata
|
||||||
|
doc_data = {
|
||||||
|
'id': doc.id,
|
||||||
|
'file_name': doc.file_name,
|
||||||
|
'marca': doc.marca,
|
||||||
|
'modelo': doc.modelo,
|
||||||
|
'ano': doc.ano,
|
||||||
|
'cilindrada': doc.cilindrada,
|
||||||
|
'codigo_motor': doc.codigo_motor,
|
||||||
|
'tipo_documento': doc.tipo_documento,
|
||||||
|
'variante': doc.variante,
|
||||||
|
'observacoes': doc.observacoes,
|
||||||
|
'folder_id': doc.folder_id,
|
||||||
|
'tags': [tag.name for tag in doc.tags],
|
||||||
|
'versions': [{
|
||||||
|
'version_number': v.version_number,
|
||||||
|
'file_name': os.path.basename(v.file_path),
|
||||||
|
'changes': v.changes,
|
||||||
|
'created_at': v.created_at.isoformat()
|
||||||
|
} for v in doc.versions]
|
||||||
|
}
|
||||||
|
metadata['documents'].append(doc_data)
|
||||||
|
|
||||||
|
progress.setValue(i + 1)
|
||||||
|
|
||||||
|
# Export folders
|
||||||
|
folders = session.query(Folder).all()
|
||||||
|
for folder in folders:
|
||||||
|
folder_data = {
|
||||||
|
'id': folder.id,
|
||||||
|
'name': folder.name,
|
||||||
|
'parent_id': folder.parent_id
|
||||||
|
}
|
||||||
|
metadata['folders'].append(folder_data)
|
||||||
|
|
||||||
|
# Export tags
|
||||||
|
tags = session.query(Tag).all()
|
||||||
|
for tag in tags:
|
||||||
|
tag_data = {
|
||||||
|
'name': tag.name,
|
||||||
|
'color': tag.color
|
||||||
|
}
|
||||||
|
metadata['tags'].append(tag_data)
|
||||||
|
|
||||||
|
# Save metadata
|
||||||
|
with open(os.path.join(export_path, 'metadata.json'), 'w',
|
||||||
|
encoding='utf-8') as f:
|
||||||
|
json.dump(metadata, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
# Create ZIP archive
|
||||||
|
zip_path = f"{export_path}.zip"
|
||||||
|
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||||
|
for root, _, files in os.walk(export_path):
|
||||||
|
for file in files:
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
arcname = os.path.relpath(file_path, export_path)
|
||||||
|
zipf.write(file_path, arcname)
|
||||||
|
|
||||||
|
# Clean up temporary directory
|
||||||
|
shutil.rmtree(export_path)
|
||||||
|
|
||||||
|
QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"Sucesso",
|
||||||
|
f"Exportação concluída com sucesso!\nArquivo: {zip_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro durante a exportação: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def import_documents(self):
|
||||||
|
zip_path, _ = QFileDialog.getOpenFileName(
|
||||||
|
self,
|
||||||
|
"Selecionar Arquivo de Importação",
|
||||||
|
"",
|
||||||
|
"Arquivos ZIP (*.zip)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not zip_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
|
||||||
|
# Create temporary directory for extraction
|
||||||
|
temp_dir = os.path.join(os.path.dirname(zip_path), "temp_import")
|
||||||
|
os.makedirs(temp_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Extract ZIP file
|
||||||
|
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||||
|
zipf.extractall(temp_dir)
|
||||||
|
|
||||||
|
# Read metadata
|
||||||
|
with open(os.path.join(temp_dir, 'metadata.json'), 'r',
|
||||||
|
encoding='utf-8') as f:
|
||||||
|
metadata = json.load(f)
|
||||||
|
|
||||||
|
# Import tags
|
||||||
|
for tag_data in metadata['tags']:
|
||||||
|
if not session.query(Tag)\
|
||||||
|
.filter(Tag.name == tag_data['name']).first():
|
||||||
|
tag = Tag(
|
||||||
|
name=tag_data['name'],
|
||||||
|
color=tag_data['color']
|
||||||
|
)
|
||||||
|
session.add(tag)
|
||||||
|
|
||||||
|
# Import folders
|
||||||
|
for folder_data in metadata['folders']:
|
||||||
|
if not session.query(Folder)\
|
||||||
|
.filter(Folder.id == folder_data['id']).first():
|
||||||
|
folder = Folder(
|
||||||
|
id=folder_data['id'],
|
||||||
|
name=folder_data['name'],
|
||||||
|
parent_id=folder_data['parent_id']
|
||||||
|
)
|
||||||
|
session.add(folder)
|
||||||
|
|
||||||
|
# Create progress dialog
|
||||||
|
progress = QProgressDialog(
|
||||||
|
"Importando documentos...",
|
||||||
|
"Cancelar",
|
||||||
|
0,
|
||||||
|
len(metadata['documents']),
|
||||||
|
self
|
||||||
|
)
|
||||||
|
progress.setWindowModality(Qt.WindowModal)
|
||||||
|
|
||||||
|
# Import documents
|
||||||
|
for i, doc_data in enumerate(metadata['documents']):
|
||||||
|
if progress.wasCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check if document already exists
|
||||||
|
existing_doc = session.query(Document)\
|
||||||
|
.filter(Document.file_name == doc_data['file_name'])\
|
||||||
|
.filter(Document.folder_id == doc_data['folder_id'])\
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if not existing_doc:
|
||||||
|
# Copy document file
|
||||||
|
doc_dir = os.path.join(temp_dir, f"doc_{doc_data['id']}")
|
||||||
|
original_file = os.path.join(doc_dir, doc_data['file_name'])
|
||||||
|
|
||||||
|
if os.path.exists(original_file):
|
||||||
|
# Create new document
|
||||||
|
new_doc = Document(
|
||||||
|
file_name=doc_data['file_name'],
|
||||||
|
file_path=original_file,
|
||||||
|
marca=doc_data['marca'],
|
||||||
|
modelo=doc_data['modelo'],
|
||||||
|
ano=doc_data['ano'],
|
||||||
|
cilindrada=doc_data['cilindrada'],
|
||||||
|
codigo_motor=doc_data['codigo_motor'],
|
||||||
|
tipo_documento=doc_data['tipo_documento'],
|
||||||
|
variante=doc_data['variante'],
|
||||||
|
observacoes=doc_data['observacoes'],
|
||||||
|
folder_id=doc_data['folder_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add tags
|
||||||
|
for tag_name in doc_data['tags']:
|
||||||
|
tag = session.query(Tag)\
|
||||||
|
.filter(Tag.name == tag_name).first()
|
||||||
|
if tag:
|
||||||
|
new_doc.tags.append(tag)
|
||||||
|
|
||||||
|
session.add(new_doc)
|
||||||
|
session.flush() # Get new_doc.id
|
||||||
|
|
||||||
|
# Import versions
|
||||||
|
versions_dir = os.path.join(doc_dir, "versions")
|
||||||
|
if os.path.exists(versions_dir):
|
||||||
|
for version_data in doc_data['versions']:
|
||||||
|
version_file = os.path.join(
|
||||||
|
versions_dir,
|
||||||
|
version_data['file_name']
|
||||||
|
)
|
||||||
|
if os.path.exists(version_file):
|
||||||
|
new_version = DocumentVersion(
|
||||||
|
document_id=new_doc.id,
|
||||||
|
version_number=version_data['version_number'],
|
||||||
|
file_path=version_file,
|
||||||
|
changes=version_data['changes'],
|
||||||
|
created_at=datetime.fromisoformat(
|
||||||
|
version_data['created_at'])
|
||||||
|
)
|
||||||
|
session.add(new_version)
|
||||||
|
|
||||||
|
progress.setValue(i + 1)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
shutil.rmtree(temp_dir)
|
||||||
|
|
||||||
|
QMessageBox.information(self, "Sucesso",
|
||||||
|
"Importação concluída com sucesso!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
session.rollback()
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro durante a importação: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
144
batch_upload.py
Normal file
144
batch_upload.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QTableWidget, QTableWidgetItem, QHeaderView,
|
||||||
|
QFileDialog, QMessageBox, QProgressDialog)
|
||||||
|
from PySide6.QtCore import Qt, Signal
|
||||||
|
import os
|
||||||
|
from database import get_session
|
||||||
|
from models import Document
|
||||||
|
from document_form import DocumentForm
|
||||||
|
|
||||||
|
class BatchUploadWidget(QWidget):
|
||||||
|
upload_completed = Signal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.files_to_upload = []
|
||||||
|
self.folder_id = None
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
self.add_files_btn = QPushButton("Adicionar Arquivos")
|
||||||
|
self.add_files_btn.clicked.connect(self.add_files)
|
||||||
|
self.remove_files_btn = QPushButton("Remover Selecionados")
|
||||||
|
self.remove_files_btn.clicked.connect(self.remove_selected_files)
|
||||||
|
self.upload_all_btn = QPushButton("Upload em Lote")
|
||||||
|
self.upload_all_btn.clicked.connect(self.upload_all)
|
||||||
|
|
||||||
|
button_layout.addWidget(self.add_files_btn)
|
||||||
|
button_layout.addWidget(self.remove_files_btn)
|
||||||
|
button_layout.addWidget(self.upload_all_btn)
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
# Files table
|
||||||
|
self.files_table = QTableWidget()
|
||||||
|
self.files_table.setColumnCount(2)
|
||||||
|
self.files_table.setHorizontalHeaderLabels(["Nome do Arquivo", "Caminho"])
|
||||||
|
self.files_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
layout.addWidget(self.files_table)
|
||||||
|
|
||||||
|
# Create document form for metadata
|
||||||
|
self.doc_form = DocumentForm()
|
||||||
|
layout.addWidget(self.doc_form)
|
||||||
|
|
||||||
|
def add_files(self):
|
||||||
|
files, _ = QFileDialog.getOpenFileNames(
|
||||||
|
self,
|
||||||
|
"Selecionar Arquivos",
|
||||||
|
"",
|
||||||
|
"Todos os Arquivos (*);;PDF (*.pdf);;Imagens (*.png *.jpg);;Documentos (*.doc *.docx)"
|
||||||
|
)
|
||||||
|
|
||||||
|
for file_path in files:
|
||||||
|
if file_path not in [self.files_table.item(row, 1).text()
|
||||||
|
for row in range(self.files_table.rowCount())]:
|
||||||
|
row = self.files_table.rowCount()
|
||||||
|
self.files_table.insertRow(row)
|
||||||
|
self.files_table.setItem(row, 0, QTableWidgetItem(os.path.basename(file_path)))
|
||||||
|
self.files_table.setItem(row, 1, QTableWidgetItem(file_path))
|
||||||
|
|
||||||
|
def remove_selected_files(self):
|
||||||
|
rows = set(item.row() for item in self.files_table.selectedItems())
|
||||||
|
for row in sorted(rows, reverse=True):
|
||||||
|
self.files_table.removeRow(row)
|
||||||
|
|
||||||
|
def upload_all(self):
|
||||||
|
if not self.validate_form():
|
||||||
|
return
|
||||||
|
|
||||||
|
progress = QProgressDialog(
|
||||||
|
"Fazendo upload dos arquivos...",
|
||||||
|
"Cancelar",
|
||||||
|
0,
|
||||||
|
self.files_table.rowCount(),
|
||||||
|
self
|
||||||
|
)
|
||||||
|
progress.setWindowModality(Qt.WindowModal)
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
|
||||||
|
for row in range(self.files_table.rowCount()):
|
||||||
|
if progress.wasCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
file_path = self.files_table.item(row, 1).text()
|
||||||
|
|
||||||
|
# Create new document with metadata
|
||||||
|
new_doc = Document(
|
||||||
|
file_path=file_path,
|
||||||
|
file_name=os.path.basename(file_path),
|
||||||
|
marca=self.doc_form.marca_edit.text(),
|
||||||
|
modelo=self.doc_form.modelo_edit.text(),
|
||||||
|
ano=self.doc_form.ano_spin.value(),
|
||||||
|
cilindrada=self.doc_form.cilindrada_edit.text(),
|
||||||
|
codigo_motor=self.doc_form.codigo_motor_edit.text(),
|
||||||
|
tipo_documento=self.doc_form.tipo_combo.currentText(),
|
||||||
|
variante=self.doc_form.variante_combo.currentText(),
|
||||||
|
observacoes=self.doc_form.observacoes_edit.toPlainText(),
|
||||||
|
folder_id=self.folder_id
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(new_doc)
|
||||||
|
progress.setValue(row + 1)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
QMessageBox.information(self, "Sucesso",
|
||||||
|
"Upload em lote concluído com sucesso!")
|
||||||
|
self.clear_form()
|
||||||
|
self.upload_completed.emit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro durante o upload em lote: {str(e)}")
|
||||||
|
session.rollback()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
progress.close()
|
||||||
|
|
||||||
|
def validate_form(self):
|
||||||
|
if self.files_table.rowCount() == 0:
|
||||||
|
QMessageBox.warning(self, "Validação",
|
||||||
|
"Adicione pelo menos um arquivo.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.doc_form.marca_edit.text():
|
||||||
|
QMessageBox.warning(self, "Validação",
|
||||||
|
"Por favor, preencha a marca.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.doc_form.modelo_edit.text():
|
||||||
|
QMessageBox.warning(self, "Validação",
|
||||||
|
"Por favor, preencha o modelo.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def clear_form(self):
|
||||||
|
self.files_table.setRowCount(0)
|
||||||
|
self.doc_form.clear_form()
|
||||||
205
comments.py
Normal file
205
comments.py
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QTextEdit, QListWidget, QListWidgetItem,
|
||||||
|
QLabel, QMenu, QMessageBox, QDialog)
|
||||||
|
from PySide6.QtCore import Qt, Signal, QPoint
|
||||||
|
from PySide6.QtGui import QAction
|
||||||
|
from database import get_session
|
||||||
|
from models import Comment, Document
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class CommentWidget(QWidget):
|
||||||
|
comment_updated = Signal()
|
||||||
|
|
||||||
|
def __init__(self, comment, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.comment = comment
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(5, 5, 5, 5)
|
||||||
|
|
||||||
|
# Header with timestamp and options
|
||||||
|
header_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
timestamp = QLabel(self.comment.created_at.strftime("%d/%m/%Y %H:%M"))
|
||||||
|
timestamp.setStyleSheet("color: gray; font-size: 10px;")
|
||||||
|
header_layout.addWidget(timestamp)
|
||||||
|
|
||||||
|
if self.comment.updated_at and self.comment.updated_at != self.comment.created_at:
|
||||||
|
edited_label = QLabel("(editado)")
|
||||||
|
edited_label.setStyleSheet("color: gray; font-size: 10px;")
|
||||||
|
header_layout.addWidget(edited_label)
|
||||||
|
|
||||||
|
header_layout.addStretch()
|
||||||
|
|
||||||
|
# Options button
|
||||||
|
options_btn = QPushButton("...")
|
||||||
|
options_btn.setFixedWidth(30)
|
||||||
|
options_btn.clicked.connect(self.show_options)
|
||||||
|
header_layout.addWidget(options_btn)
|
||||||
|
|
||||||
|
layout.addLayout(header_layout)
|
||||||
|
|
||||||
|
# Comment text
|
||||||
|
text_label = QLabel(self.comment.text)
|
||||||
|
text_label.setWordWrap(True)
|
||||||
|
layout.addWidget(text_label)
|
||||||
|
|
||||||
|
# Location info if available
|
||||||
|
if self.comment.page_number:
|
||||||
|
location = f"Página {self.comment.page_number}"
|
||||||
|
if self.comment.x_coord and self.comment.y_coord:
|
||||||
|
location += f" (x: {self.comment.x_coord}, y: {self.comment.y_coord})"
|
||||||
|
|
||||||
|
location_label = QLabel(location)
|
||||||
|
location_label.setStyleSheet("color: gray; font-style: italic; font-size: 10px;")
|
||||||
|
layout.addWidget(location_label)
|
||||||
|
|
||||||
|
def show_options(self):
|
||||||
|
menu = QMenu(self)
|
||||||
|
|
||||||
|
edit_action = QAction("Editar", self)
|
||||||
|
edit_action.triggered.connect(self.edit_comment)
|
||||||
|
menu.addAction(edit_action)
|
||||||
|
|
||||||
|
delete_action = QAction("Excluir", self)
|
||||||
|
delete_action.triggered.connect(self.delete_comment)
|
||||||
|
menu.addAction(delete_action)
|
||||||
|
|
||||||
|
# Show menu at button position
|
||||||
|
button = self.sender()
|
||||||
|
pos = button.mapToGlobal(QPoint(0, button.height()))
|
||||||
|
menu.exec_(pos)
|
||||||
|
|
||||||
|
def edit_comment(self):
|
||||||
|
dialog = CommentDialog(self.comment.text, self)
|
||||||
|
if dialog.exec_():
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
comment = session.query(Comment).get(self.comment.id)
|
||||||
|
comment.text = dialog.comment_text()
|
||||||
|
comment.updated_at = datetime.utcnow()
|
||||||
|
session.commit()
|
||||||
|
self.comment_updated.emit()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao editar comentário: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def delete_comment(self):
|
||||||
|
reply = QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"Confirmar Exclusão",
|
||||||
|
"Tem certeza que deseja excluir este comentário?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No
|
||||||
|
)
|
||||||
|
|
||||||
|
if reply == QMessageBox.Yes:
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
comment = session.query(Comment).get(self.comment.id)
|
||||||
|
session.delete(comment)
|
||||||
|
session.commit()
|
||||||
|
self.comment_updated.emit()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao excluir comentário: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
class CommentsList(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.current_document = None
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header_layout = QHBoxLayout()
|
||||||
|
header_layout.addWidget(QLabel("Comentários"))
|
||||||
|
|
||||||
|
self.add_comment_btn = QPushButton("Novo Comentário")
|
||||||
|
self.add_comment_btn.clicked.connect(self.add_comment)
|
||||||
|
header_layout.addWidget(self.add_comment_btn)
|
||||||
|
|
||||||
|
layout.addLayout(header_layout)
|
||||||
|
|
||||||
|
# Comments list
|
||||||
|
self.comments_list = QListWidget()
|
||||||
|
layout.addWidget(self.comments_list)
|
||||||
|
|
||||||
|
def load_document(self, doc_id):
|
||||||
|
self.current_document = doc_id
|
||||||
|
self.refresh_comments()
|
||||||
|
|
||||||
|
def refresh_comments(self):
|
||||||
|
self.comments_list.clear()
|
||||||
|
|
||||||
|
if self.current_document:
|
||||||
|
session = get_session()
|
||||||
|
document = session.query(Document).get(self.current_document)
|
||||||
|
|
||||||
|
for comment in document.comments:
|
||||||
|
item = QListWidgetItem()
|
||||||
|
widget = CommentWidget(comment)
|
||||||
|
widget.comment_updated.connect(self.refresh_comments)
|
||||||
|
|
||||||
|
item.setSizeHint(widget.sizeHint())
|
||||||
|
self.comments_list.addItem(item)
|
||||||
|
self.comments_list.setItemWidget(item, widget)
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def add_comment(self):
|
||||||
|
if not self.current_document:
|
||||||
|
return
|
||||||
|
|
||||||
|
dialog = CommentDialog(parent=self)
|
||||||
|
if dialog.exec_():
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
comment = Comment(
|
||||||
|
document_id=self.current_document,
|
||||||
|
text=dialog.comment_text()
|
||||||
|
)
|
||||||
|
session.add(comment)
|
||||||
|
session.commit()
|
||||||
|
self.refresh_comments()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao adicionar comentário: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
class CommentDialog(QDialog):
|
||||||
|
def __init__(self, initial_text="", parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.initial_text = initial_text
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
self.setWindowTitle("Comentário")
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
self.text_edit = QTextEdit()
|
||||||
|
self.text_edit.setPlainText(self.initial_text)
|
||||||
|
layout.addWidget(self.text_edit)
|
||||||
|
|
||||||
|
buttons_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
cancel_btn = QPushButton("Cancelar")
|
||||||
|
cancel_btn.clicked.connect(self.reject)
|
||||||
|
buttons_layout.addWidget(cancel_btn)
|
||||||
|
|
||||||
|
save_btn = QPushButton("Salvar")
|
||||||
|
save_btn.clicked.connect(self.accept)
|
||||||
|
buttons_layout.addWidget(save_btn)
|
||||||
|
|
||||||
|
layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
|
def comment_text(self):
|
||||||
|
return self.text_edit.toPlainText()
|
||||||
23
database.py
Normal file
23
database.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from models import Base
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Get the absolute path to the database file
|
||||||
|
DATABASE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "automotive_docs.db")
|
||||||
|
DATABASE_URL = f"sqlite:///{DATABASE_FILE}"
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
# Create new engine
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
|
||||||
|
# Create tables if they don't exist
|
||||||
|
if not os.path.exists(DATABASE_FILE):
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
return engine
|
||||||
|
|
||||||
|
def get_session():
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
return Session()
|
||||||
373
document_collections.py
Normal file
373
document_collections.py
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QListWidget, QListWidgetItem, QInputDialog,
|
||||||
|
QMessageBox, QMenu, QDialog, QLabel, QLineEdit,
|
||||||
|
QTextEdit, QComboBox)
|
||||||
|
from PySide6.QtCore import Qt, Signal
|
||||||
|
from PySide6.QtGui import QAction, QColor, QIcon
|
||||||
|
from database import get_session
|
||||||
|
from models import Collection, Document
|
||||||
|
import json
|
||||||
|
|
||||||
|
class CollectionDialog(QDialog):
|
||||||
|
def __init__(self, collection=None, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.collection = collection
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
self.setWindowTitle("Coleção")
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Name
|
||||||
|
name_layout = QHBoxLayout()
|
||||||
|
name_layout.addWidget(QLabel("Nome:"))
|
||||||
|
self.name_edit = QLineEdit()
|
||||||
|
if self.collection:
|
||||||
|
self.name_edit.setText(self.collection.name)
|
||||||
|
name_layout.addWidget(self.name_edit)
|
||||||
|
layout.addLayout(name_layout)
|
||||||
|
|
||||||
|
# Description
|
||||||
|
layout.addWidget(QLabel("Descrição:"))
|
||||||
|
self.desc_edit = QTextEdit()
|
||||||
|
if self.collection:
|
||||||
|
self.desc_edit.setText(self.collection.description)
|
||||||
|
layout.addWidget(self.desc_edit)
|
||||||
|
|
||||||
|
# Color
|
||||||
|
color_layout = QHBoxLayout()
|
||||||
|
color_layout.addWidget(QLabel("Cor:"))
|
||||||
|
self.color_combo = QComboBox()
|
||||||
|
colors = ["#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF"]
|
||||||
|
for color in colors:
|
||||||
|
self.color_combo.addItem("")
|
||||||
|
self.color_combo.setItemData(
|
||||||
|
self.color_combo.count() - 1,
|
||||||
|
QColor(color),
|
||||||
|
Qt.BackgroundRole
|
||||||
|
)
|
||||||
|
if self.collection:
|
||||||
|
index = colors.index(self.collection.color)
|
||||||
|
self.color_combo.setCurrentIndex(index)
|
||||||
|
color_layout.addWidget(self.color_combo)
|
||||||
|
layout.addLayout(color_layout)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
cancel_btn = QPushButton("Cancelar")
|
||||||
|
cancel_btn.clicked.connect(self.reject)
|
||||||
|
button_layout.addWidget(cancel_btn)
|
||||||
|
|
||||||
|
save_btn = QPushButton("Salvar")
|
||||||
|
save_btn.clicked.connect(self.accept)
|
||||||
|
button_layout.addWidget(save_btn)
|
||||||
|
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return {
|
||||||
|
'name': self.name_edit.text(),
|
||||||
|
'description': self.desc_edit.toPlainText(),
|
||||||
|
'color': self.color_combo.itemData(
|
||||||
|
self.color_combo.currentIndex(),
|
||||||
|
Qt.BackgroundRole
|
||||||
|
).name()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CollectionsWidget(QWidget):
|
||||||
|
collection_selected = Signal(int) # collection_id
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Toolbar
|
||||||
|
toolbar = QHBoxLayout()
|
||||||
|
|
||||||
|
new_btn = QPushButton("Nova Coleção")
|
||||||
|
new_btn.clicked.connect(self.add_collection)
|
||||||
|
toolbar.addWidget(new_btn)
|
||||||
|
|
||||||
|
toolbar.addStretch()
|
||||||
|
|
||||||
|
layout.addLayout(toolbar)
|
||||||
|
|
||||||
|
# Collections list
|
||||||
|
self.collections_list = QListWidget()
|
||||||
|
self.collections_list.itemClicked.connect(self.on_collection_clicked)
|
||||||
|
self.collections_list.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.collections_list.customContextMenuRequested.connect(
|
||||||
|
self.show_context_menu)
|
||||||
|
layout.addWidget(self.collections_list)
|
||||||
|
|
||||||
|
self.refresh_collections()
|
||||||
|
|
||||||
|
def refresh_collections(self):
|
||||||
|
self.collections_list.clear()
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
collections = session.query(Collection).all()
|
||||||
|
|
||||||
|
for collection in collections:
|
||||||
|
item = QListWidgetItem(collection.name)
|
||||||
|
item.setData(Qt.UserRole, collection.id)
|
||||||
|
item.setBackground(QColor(collection.color))
|
||||||
|
|
||||||
|
# Add document count
|
||||||
|
doc_count = len(collection.documents)
|
||||||
|
item.setToolTip(
|
||||||
|
f"{collection.description}\n\n"
|
||||||
|
f"Documentos: {doc_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.collections_list.addItem(item)
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao carregar coleções: {str(e)}")
|
||||||
|
|
||||||
|
def add_collection(self):
|
||||||
|
dialog = CollectionDialog(parent=self)
|
||||||
|
if dialog.exec_():
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
|
||||||
|
data = dialog.get_data()
|
||||||
|
collection = Collection(
|
||||||
|
name=data['name'],
|
||||||
|
description=data['description'],
|
||||||
|
color=data['color']
|
||||||
|
)
|
||||||
|
session.add(collection)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.refresh_collections()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao criar coleção: {str(e)}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def edit_collection(self, collection_id):
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
collection = session.query(Collection).get(collection_id)
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
dialog = CollectionDialog(collection, self)
|
||||||
|
if dialog.exec_():
|
||||||
|
data = dialog.get_data()
|
||||||
|
collection.name = data['name']
|
||||||
|
collection.description = data['description']
|
||||||
|
collection.color = data['color']
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.refresh_collections()
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao editar coleção: {str(e)}")
|
||||||
|
|
||||||
|
def delete_collection(self, collection_id):
|
||||||
|
reply = QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"Confirmar Exclusão",
|
||||||
|
"Tem certeza que deseja excluir esta coleção?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No
|
||||||
|
)
|
||||||
|
|
||||||
|
if reply == QMessageBox.Yes:
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
collection = session.query(Collection).get(collection_id)
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
session.delete(collection)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.refresh_collections()
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao excluir coleção: {str(e)}")
|
||||||
|
|
||||||
|
def on_collection_clicked(self, item):
|
||||||
|
collection_id = item.data(Qt.UserRole)
|
||||||
|
self.collection_selected.emit(collection_id)
|
||||||
|
|
||||||
|
def show_context_menu(self, position):
|
||||||
|
item = self.collections_list.itemAt(position)
|
||||||
|
if not item:
|
||||||
|
return
|
||||||
|
|
||||||
|
collection_id = item.data(Qt.UserRole)
|
||||||
|
|
||||||
|
menu = QMenu()
|
||||||
|
edit_action = menu.addAction("Editar")
|
||||||
|
edit_action.triggered.connect(
|
||||||
|
lambda: self.edit_collection(collection_id))
|
||||||
|
|
||||||
|
delete_action = menu.addAction("Excluir")
|
||||||
|
delete_action.triggered.connect(
|
||||||
|
lambda: self.delete_collection(collection_id))
|
||||||
|
|
||||||
|
menu.exec_(self.collections_list.mapToGlobal(position))
|
||||||
|
|
||||||
|
class CollectionDocuments(QWidget):
|
||||||
|
document_selected = Signal(int) # document_id
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.current_collection = None
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.collection_label = QLabel()
|
||||||
|
header_layout.addWidget(self.collection_label)
|
||||||
|
|
||||||
|
self.add_doc_btn = QPushButton("Adicionar Documento")
|
||||||
|
self.add_doc_btn.clicked.connect(self.add_document)
|
||||||
|
header_layout.addWidget(self.add_doc_btn)
|
||||||
|
|
||||||
|
layout.addLayout(header_layout)
|
||||||
|
|
||||||
|
# Documents list
|
||||||
|
self.documents_list = QListWidget()
|
||||||
|
self.documents_list.itemClicked.connect(self.on_document_clicked)
|
||||||
|
self.documents_list.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.documents_list.customContextMenuRequested.connect(
|
||||||
|
self.show_context_menu)
|
||||||
|
layout.addWidget(self.documents_list)
|
||||||
|
|
||||||
|
def load_collection(self, collection_id):
|
||||||
|
self.current_collection = collection_id
|
||||||
|
self.refresh_documents()
|
||||||
|
|
||||||
|
def refresh_documents(self):
|
||||||
|
self.documents_list.clear()
|
||||||
|
|
||||||
|
if not self.current_collection:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
collection = session.query(Collection).get(self.current_collection)
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
self.collection_label.setText(
|
||||||
|
f"Coleção: {collection.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for doc in collection.documents:
|
||||||
|
item = QListWidgetItem(doc.file_name)
|
||||||
|
item.setData(Qt.UserRole, doc.id)
|
||||||
|
item.setToolTip(
|
||||||
|
f"Marca: {doc.marca}\n"
|
||||||
|
f"Modelo: {doc.modelo}\n"
|
||||||
|
f"Ano: {doc.ano}"
|
||||||
|
)
|
||||||
|
self.documents_list.addItem(item)
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao carregar documentos: {str(e)}")
|
||||||
|
|
||||||
|
def add_document(self):
|
||||||
|
if not self.current_collection:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
|
||||||
|
# Get documents not in collection
|
||||||
|
collection = session.query(Collection).get(self.current_collection)
|
||||||
|
all_docs = session.query(Document).all()
|
||||||
|
collection_doc_ids = [doc.id for doc in collection.documents]
|
||||||
|
available_docs = [doc for doc in all_docs
|
||||||
|
if doc.id not in collection_doc_ids]
|
||||||
|
|
||||||
|
if not available_docs:
|
||||||
|
QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"Aviso",
|
||||||
|
"Não há documentos disponíveis para adicionar."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Show document selection dialog
|
||||||
|
doc_names = [f"{doc.file_name} ({doc.marca} {doc.modelo})"
|
||||||
|
for doc in available_docs]
|
||||||
|
name, ok = QInputDialog.getItem(
|
||||||
|
self,
|
||||||
|
"Adicionar Documento",
|
||||||
|
"Selecione um documento:",
|
||||||
|
doc_names,
|
||||||
|
0,
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
if ok and name:
|
||||||
|
index = doc_names.index(name)
|
||||||
|
doc = available_docs[index]
|
||||||
|
collection.documents.append(doc)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.refresh_documents()
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao adicionar documento: {str(e)}")
|
||||||
|
|
||||||
|
def remove_document(self, doc_id):
|
||||||
|
if not self.current_collection:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_session()
|
||||||
|
collection = session.query(Collection).get(self.current_collection)
|
||||||
|
document = session.query(Document).get(doc_id)
|
||||||
|
|
||||||
|
if collection and document:
|
||||||
|
collection.documents.remove(document)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.refresh_documents()
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Erro",
|
||||||
|
f"Erro ao remover documento: {str(e)}")
|
||||||
|
|
||||||
|
def on_document_clicked(self, item):
|
||||||
|
doc_id = item.data(Qt.UserRole)
|
||||||
|
self.document_selected.emit(doc_id)
|
||||||
|
|
||||||
|
def show_context_menu(self, position):
|
||||||
|
item = self.documents_list.itemAt(position)
|
||||||
|
if not item:
|
||||||
|
return
|
||||||
|
|
||||||
|
doc_id = item.data(Qt.UserRole)
|
||||||
|
|
||||||
|
menu = QMenu()
|
||||||
|
remove_action = menu.addAction("Remover da Coleção")
|
||||||
|
remove_action.triggered.connect(
|
||||||
|
lambda: self.remove_document(doc_id))
|
||||||
|
|
||||||
|
menu.exec_(self.documents_list.mapToGlobal(position))
|
||||||
Reference in New Issue
Block a user