Projetos /

Luno

Aplicação desktop (PyQt5) para criação e gestão automatizada de escalas de trabalho semanais para farmácias.

Exemplo de Escala Gerada pelo Luno

O Desafio

A criação manual de escalas de rotinas semanais em farmácias é uma tarefa complexa e demorada. É preciso conciliar as rotinas específicas de cada turno e grupo de trabalho com as folgas individuais dos funcionários, garantindo a cobertura de todas as tarefas e uma distribuição justa.

Fazer isso manualmente, muitas vezes em planilhas, é propenso a erros, difícil de manter e consome um tempo valioso que poderia ser dedicado a outras atividades da farmácia.

A Solução: Aplicação Desktop Luno

Desenvolvi o "Luno", uma aplicação desktop em Python com interface gráfica PyQt5, projetada para simplificar e automatizar a geração de escalas de rotinas em farmácias:

  • Gestão Centralizada: Permite cadastrar funcionários (manually ou via importação Excel), definir seus grupos de trabalho (cores) e gerenciar rotinas específicas para cada turno e grupo (salvas em JSON).
  • Gerenciador de Folgas: Interface dedicada para registrar os dias de folga fixos de cada funcionário, salvando os dados em formato JSON para persistência.
  • Dois Modos de Geração:
    • Grupos Fixos (Manual): Aloca funcionários às rotinas baseando-se estritamente no grupo de cor definido, rotacionando dentro do grupo para evitar sobrecarga e respeitando folgas.
    • Rotação Diária (Automático): Distribui aleatoriamente os funcionários disponíveis (sem folga no dia) entre os grupos de rotinas ativos, garantindo cobertura e variação.
  • Visualização Clara da Escala: Apresenta a escala semanal gerada numa tabela (QTableWidget) colorida de acordo com os grupos, facilitando a visualização.
  • Fixação Manual (Pinning): Permite ao usuário clicar com o botão direito numa célula da escala e "fixar" um funcionário específico para aquela rotina/dia, sobrepondo a geração automática.
  • Importação/Exportação Excel: Facilita a entrada de dados (funcionários, rotinas via Excel) e a partilha da escala final gerada (formato Excel).
  • Interface Personalizada: Utiliza um stylesheet customizado (`ui_design.py`) e ícones SVG para um visual moderno e agradável, focado na usabilidade.

Interface da Aplicação Luno

Visualização das principais telas: configuração inicial, gerenciador de rotinas e gerenciador de folgas.

Tela inicial do Luno
Tela principal da aplicação, com painel de controle à esquerda.
Gerenciador de Rotinas
Janela modal para adicionar, editar, remover e importar rotinas por turno e grupo.
Gerenciador de Folgas
Janela modal para definir os dias de folga fixos de cada funcionário.

Código em Destaque

Trecho do `main_app.py` que implementa a lógica de geração da escala no modo "Grupos Fixos". Ele utiliza `deque` para rotacionar os funcionários dentro de cada grupo e verifica as folgas (`days_off_data`) antes de alocar um funcionário a uma célula da escala.

main_app.py (Geração - Grupos Fixos)

from collections import deque

class PharmacyScheduler(QMainWindow):

    def generate_with_fixed_groups(self):
        group_deques = {group: deque(sorted([name for name, g in self.employees.items() if g == group])) 
                        for group in GROUP_COLORS}
        
        for day_idx, day in enumerate(DAYS_OF_WEEK):
            col_idx = day_idx + 1
            
            for row_idx in range(self.schedule_table.rowCount()):
                routine_group = self.routine_map[row_idx] 
                found_employee = None
                
                if group_deques[routine_group]:
                    for _ in range(len(group_deques[routine_group])):
                        candidate = group_deques[routine_group][0]
                        if day not in self.days_off_data.get(candidate, []):
                            found_employee = candidate
                            break
                        
                        group_deques[routine_group].rotate(-1) 
                        
                employee_name = found_employee
                
                if (row_idx, col_idx) in self.pinned_assignments:
                    employee_name = self.pinned_assignments[(row_idx, col_idx)]
                    
                if employee_name:
                    self.set_table_cell(row_idx, col_idx, employee_name, routine_group)
            
            for group in group_deques:
                if group_deques[group]: 
                    group_deques[group].rotate(-1)