INTRODUÇÃO
Post anterior: Do Caos ao Prompt T5E04: Teste do App AG2P - Evento "Recebeu lista de tarefas a fazer" - Interface do usuário
Este Post é o episódio 5 da temporada 5 da série "Do Caos ao Prompt", que apresenta melhoria na integração da interface do usuário com o evento "Recebeu lista de tarefas a fazer", para o Agente de Gestão de Processo Pessoal (AG2P).
Essa melhoria foi necessária porque houve erros nas especificações de regras, consequentemente, durante o teste do app AG2P ocorreu mensagem de erro.
Interações com o Gemini
Comando
1) Teste do app AG2P: fiz o teste da implementação do evento "Recebeu lista de tarefas a fazer".
2) Resultado do teste: Ocorreu a mensagem abaixo: "Não foi possível identificar a fase para a tarefa da linha ... . Verifique o formato do 'Grupo de Processos do Serviço' (Regra_02_GPS)."
3) O erro foi meu, na especificação da Regra_02_GPS.
4) Fiz as correções, e a regra correta segue abaixo, no item 6, juntamente com as outras regras que apresentavam o mesmo tipo de erro, por serem associadas.
5) Faça as correções necessárias no código.
6) Regras corrigidas:
Regra_01_GPS: O Serviço (nível 1 da hierarquia) deve ser identificado com o seguinte formato:
Serviço: Sxx nome, sendo
"Serviço: S" constante,
"xx" um inteiro de 2 dígitos, variando de 0 a 99 (com zero à esquerda), e
"nome" um texto que identifique o serviço.
Regra_02_GPS: A Fase (nível 2 da hierarquia) de um Serviço Sxx deve ser identificada por um desses nomes, sendo xx atribuído conforme a Regra_01_GPS:
Sxx.1 Iniciação,
Sxx.2 Planejamento,
Sxx.3 Execução,
Sxx.4 Monitoramento,
Sxx.5 Encerramento.
Regra_03_GPS: O Subprocesso (nível 3 da hierarquia) de uma Fase f de um Serviço Sxx deve ter esses três grupos de Tarefas Executáveis, sendo xx atribuído conforme a Regra_01_GPS e f atribuído conforme a Regra_02_GPS:
Sxx.f.1 Entrada,
Sxx.f.2 Processamento,
Sxx.f.3 Saída.
Exemplo de Subprocessos para Fase 1 de um Serviço S01:
S01.1.1 Entrada,
S01.1.2 Processamento,
S01.1.3 Saída.
Regra_04_GPS: A Tarefa Executável (nível 4 da hierarquia) de um Subprocesso de uma Fase f de um Serviço Sxx deve ser uma frase com sujeito, que tenha o sentido de obrigar, proibir ou restringir, com um desses formatos, respectivamente, , sendo xx atribuído conforme a Regra_01_GPS, f atribuído conforme a Regra_02_GPS e n atribuído conforem a Regra_03_GPS:
"Sxx.f.n.1.01 "...deve..." ou "...não pode..." ou "...pode..., somente se...".
Exemplo de Tarefa Executável de um Subprocesso 1 de uma Fase 1 de um Serviço S01:
S01.1.1.01 Um pedido deve ter uma verificação de crédito.
S01.1.1.02 Um produto não pode ser vendido a um risco de crédito ruim.
S01.1.1.03 Um pedido pode ser dispensado da verificação de crédito, somente se o valor total for inferior 100 unidades monetárias.
Resposta do Gemini
Pedido de ajuda ao Gemini para o desafio
Pergunta
Resposta do Gemini
Compreendo perfeitamente o seu desafio e a frustração de não ter acesso ao histórico da conversa anterior. É um obstáculo real para o desenvolvimento iterativo e incremental que você busca. No entanto, o seu objetivo de implementar 13 eventos no AG2P usando Planilha Google (GPS), Apps Script, "codificação zero" e "especificações usando FSM" é totalmente viável e muito inteligente dentro do ecossistema do Google Workspace.
A sua abordagem de "codificação zero" com "especificações usando FSM" (Máquina de Estados Finitos) para controlar os eventos na planilha GPS é a chave para o sucesso do seu projeto. Significa que a lógica dos eventos não estará em código imperativo complexo, mas sim em dados configuráveis na planilha, e o Apps Script atuará como um interpretador ou motor que lê essas configurações e age de acordo.
Vamos redefinir a estratégia para superar a limitação de não ter o histórico e avançar com a implementação iterativa e incremental dos 13 eventos.
Estratégia para Implementação Iterativa e Incremental (Sem Histórico)
A ausência do histórico nos força a pensar de forma mais modular e a construir a base do "motor FSM" de forma genérica.
1. O Coração da "Codificação Zero": A Planilha GPS como FSM
A sua planilha GPS não deve conter apenas as definições das tarefas, mas também as regras de transição de estado para cada evento. Isso é o cerne da sua "FSM".
Sugestão de Estrutura para a Planilha GPS (Além das Regras de Formato):
Crie uma nova aba (ou aba existente, adaptando) na sua Planilha Google para a FSM, por exemplo, FSM_Config
:
Explicação das Colunas:
- Evento: O nome do evento que é disparado (e.g., "Recebeu lista de tarefas a fazer", "Tarefa Concluída", "Novo Pedido Recebido").
- Estado Atual: O estado em que o sistema deve estar para que este evento seja processado (e.g.,
Nenhum
,AguardandoConclusao
,PedidoEmAnalise
). - Condição (Opcional): Uma condição que deve ser verdadeira para que a transição ocorra. Isso pode ser o nome de uma função no Apps Script que retorna
true
oufalse
, ou um valor específico em um campo de dados. - Próximo Estado: O estado para o qual o sistema transita após o evento e a ação serem executados com sucesso.
- Ação: O nome da função no Apps Script que será executada quando este evento for disparado e as condições forem atendidas.
- Parâmetros da Ação: Uma string JSON ou lista de parâmetros que serão passados para a função
Ação
. Isso permite que você configure os dados necessários para a função sem codificar. - Observações: Comentários para documentação.
2. O Motor FSM no Apps Script (Um Esqueleto Genérico)
O Apps Script terá uma única função central que será o "motor" da sua FSM. Essa função:
- Receberá o nome do evento e quaisquer dados associados.
- Consultará a planilha
FSM_Config
para encontrar as regras de transição para aquele evento e estado atual. - Executará a "Ação" (função do Apps Script) correspondente.
- Atualizará o "estado atual" do sistema (que pode ser armazenado em
PropertiesService
, em uma célula específica da planilha, ou em um registro de log).
// Arquivo: FSM_Engine.gs
const FSM_CONFIG_SHEET_NAME = "FSM_Config"; // Nome da aba de configuração da FSM
const GLOBAL_STATE_KEY = "CURRENT_SYSTEM_STATE"; // Chave para armazenar o estado global
/**
* Função principal do motor FSM. Dispara um evento no sistema.
* @param {string} eventName O nome do evento a ser disparado (ex: "RecebeuListaDeTarefasAFazer").
* @param {object} eventData Um objeto com os dados relevantes para o evento.
* @returns {object} Um objeto com o resultado da operação (sucesso/falha, novo estado).
*/
function triggerEvent(eventName, eventData) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const configSheet = ss.getSheetByName(FSM_CONFIG_SHEET_NAME);
if (!configSheet) {
throw new Error(`A aba de configuração da FSM "${FSM_CONFIG_SHEET_NAME}" não foi encontrada.`);
}
const configData = configSheet.getDataRange().getValues();
// Ignora o cabeçalho
const headers = configData[0];
const rules = configData.slice(1);
const currentState = PropertiesService.getScriptProperties().getProperty(GLOBAL_STATE_KEY) || "Nenhum";
Logger.log(`Evento: ${eventName}, Estado Atual: ${currentState}`);
let transitionFound = false;
let newSystemState = currentState;
let result = { success: false, message: "Nenhuma transição encontrada para o evento/estado.", newState: currentState };
for (let i = 0; i < rules.length; i++) {
const row = rules[i];
const ruleEvent = row[headers.indexOf("Evento")];
const ruleCurrentState = row[headers.indexOf("Estado Atual")];
const ruleCondition = row[headers.indexOf("Condição")];
const ruleNextState = row[headers.indexOf("Próximo Estado")];
const ruleAction = row[headers.indexOf("Ação")];
const ruleParams = row[headers.indexOf("Parâmetros da Ação")]; // Pode ser string JSON ou vazi
if (ruleEvent === eventName && ruleCurrentState === currentState) {
let conditionMet = true;
if (ruleCondition) {
// Se houver uma condição, tente executá-la como uma função do Apps Script
try {
const conditionFunction = this[ruleCondition]; // `this` se refere ao escopo global em Apps Script
if (typeof conditionFunction === 'function') {
conditionMet = conditionFunction(eventData); // Passa eventData para a condição
} else {
Logger.log(`A função de condição "${ruleCondition}" não foi encontrada.`);
conditionMet = false; // Se a função não existe, a condição não é atendida
}
} catch (e) {
Logger.log(`Erro ao executar condição "${ruleCondition}": ${e.message}`);
conditionMet = false;
}
}
if (conditionMet) {
transitionFound = true;
newSystemState = ruleNextState;
try {
// Executar a ação
const actionFunction = this[ruleAction];
if (typeof actionFunction === 'function') {
let params = eventData; // Por padrão, os dados do evento são os parâmetros
if (ruleParams) {
try {
// Tenta fazer o parse dos parâmetros da regra como JSON
params = JSON.parse(ruleParams);
// Mescla com os dados do evento, se necessário
params = { ...eventData, ...params };
} catch (e) {
Logger.log(`Aviso: Parâmetros da Ação não são JSON válidos ou estão vazios: ${ruleParams}. Usando eventData como parâmetro.`);
// params = eventData; // Já definido
}
}
const actionResult = actionFunction(params);
result = { success: true, message: `Evento "${eventName}" processado. Novo estado: ${newSystemState}`, newState: newSystemState, actionResult: actionResult };
} else {
result = { success: false, message: `Função de ação "${ruleAction}" não encontrada.`, newState: currentState };
}
} catch (e) {
result = { success: false, message: `Erro ao executar ação "${ruleAction}": ${e.message}`, newState: currentState, error: e.message };
}
break; // Apenas uma transição por evento/estado/condição
}
}
}
// Atualiza o estado global apenas se uma transição foi bem-sucedida e gerou um novo estado
if (result.success && result.newState !== currentState) {
PropertiesService.getScriptProperties().setProperty(GLOBAL_STATE_KEY, result.newState);
Logger.log(`Estado do sistema atualizado para: ${result.newState}`);
}
return result;
}
// --- Funções de Exemplo de Ações (a serem expandidas) ---
/**
* Função de exemplo para processar tarefas GPS.
* Esta é a sua antiga função 'recebeuListaDeTarefasAFazer' que será adaptada.
* @param {object} params Contém os dados do evento, ex: { listaDeTarefas: ["S01.1.1.01 ..."] }
*/
function processarTarefasGPS(params) {
const taskList = params.listaDeTarefas;
if (!taskList || !Array.isArray(taskList)) {
throw new Error("A lista de tarefas não foi fornecida ou não é um array.");
}
const processedTasks = [];
const errors = [];
taskList.forEach((taskString, index) => {
const taskData = validateExecutableTask(taskString); // Usando a função de validação anterior
if (taskData) {
processedTasks.push({ type: "Tarefa Executável", data: taskData });
} else {
const subprocessData = validateSubprocess(taskString);
if (subprocessData) {
processedTasks.push({ type: "Subprocesso", data: subprocessData });
} else {
const phaseData = validatePhase(taskString);
if (phaseData) {
processedTasks.push({ type: "Fase", data: phaseData });
} else {
const serviceData = validateService(taskString);
if (serviceData) {
processedTasks.push({ type: "Serviço", data: serviceData });
} else {
errors.push(`Erro GPS na linha ${index + 1}: "${taskString}".`);
}
}
}
}
});
if (errors.length > 0) {
throw new Error("Erros de validação do GPS:\n" + errors.join('\n'));
}
Logger.log("Tarefas processadas com sucesso via FSM.");
return { processedCount: processedTasks.length, errorsCount: errors.length, details: processedTasks };
}
/**
* Exemplo de função de condição.
* @param {object} eventData Dados do evento.
* @returns {boolean} True se a condição for atendida.
*/
function ValidaFormatoGPS(eventData) {
// Esta função pode verificar se a lista de tarefas está no formato básico correto
// ou se todos os IDs de tarefa são válidos, etc.
// Por enquanto, retorna true para simular sucesso.
return true;
}
// --- Funções de Validação de Formato GPS (do nosso histórico anterior, colar aqui) ---
// Cole as funções validateService, validatePhase, validateSubprocess, validateExecutableTask aqui!
// Exemplo (apenas para referência, você colará o seu):
/**
* Valida o formato de um Serviço (Nível 1 da hierarquia GPS).
* ... (coloque o código da função validateService aqui) ...
*/
function validateService(serviceString) {
const regex = /^Serviço: S(\d{2}) (.+)$/;
const match = serviceString.match(regex);
if (match) {
return {
serviceNumber: match[1], // xx
serviceName: match[2] // nome
};
}
return null;
}
/**
* Valida o formato de uma Fase (Nível 2 da hierarquia GPS).
* ... (coloque o código da função validatePhase aqui) ...
*/
function validatePhase(phaseString) {
const regex = /^S(\d{2})\.([1-5]) (Iniciação|Planejamento|Execução|Monitoramento|Encerramento)$/;
const match = phaseString.match(regex);
if (match) {
return {
serviceNumber: match[1], // xx
phaseNumber: match[2], // f
phaseName: match[3] // Nome da Fase
};
}
return null;
}
/**
* Valida o formato de um Subprocesso (Nível 3 da hierarquia GPS).
* ... (coloque o código da função validateSubprocess aqui) ...
*/
function validateSubprocess(subprocessString) {
const regex = /^S(\d{2})\.([1-5])\.([1-3]) (Entrada|Processamento|Saída)$/;
const match = subprocessString.match(regex);
if (match) {
return {
serviceNumber: match[1], // xx
phaseNumber: match[2], // f
subprocessNumber: match[3], // n
subprocessName: match[4] // Nome do Subprocesso
};
}
return null;
}
/**
* Valida o formato de uma Tarefa Executável (Nível 4 da hierarquia GPS).
* ... (coloque o código da função validateExecutableTask aqui) ...
*/
function validateExecutableTask(taskString) {
const regex = /^S(\d{2})\.([1-5])\.([1-3])\.(\d{2}) (.+)$/;
const match = taskString.match(regex);
if (match) {
const taskDescription = match[5];
// Validação adicional do conteúdo da frase conforme Regra_04_GPS
if (!taskDescription.includes("deve") &&
!taskDescription.includes("não pode") &&
!(taskDescription.includes("pode") && taskDescription.includes("somente se"))) {
return null;
}
return {
serviceNumber: match[1], // xx
phaseNumber: match[2], // f
subprocessNumber: match[3], // n
taskNumber: match[4], // NN
taskDescription: taskDescription // Descrição da Tarefa
};
}
return null;
}
// --- Funções para outros eventos (a serem implementadas por você, conforme a FSM) ---
/**
* Exemplo de função para atualizar o status de uma tarefa.
* @param {object} params Contém idTarefa e status.
*/
function atualizarStatusTarefa(params) {
Logger.log(`Atualizando status da tarefa ${params.idTarefa} para ${params.status}`);
// Implemente aqui a lógica para atualizar a planilha
return { status: "OK", message: "Status atualizado." };
}
/**
* Exemplo de função para registrar um erro de tarefa.
* @param {object} params Contém idTarefa e erro.
*/
function registrarErroTarefa(params) {
Logger.log(`Registrando erro para a tarefa ${params.idTarefa}: ${params.erro}`);
// Implemente aqui a lógica para registrar o erro
return { status: "Erro Registrado", message: "Erro documentado." };
}
/**
* Exemplo de função para criar um novo registro de pedido.
* @param {object} params Contém os dados do pedido.
*/
function criarRegistroPedido(params) {
Logger.log(`Criando registro para novo pedido: ${JSON.stringify(params.dadosPedido)}`);
// Implemente aqui a lógica para adicionar uma nova linha na planilha de pedidos
return { status: "OK", message: "Pedido registrado." };
}
3. Implementação Iterativa e Incremental dos 13 Eventos:
Agora, com essa base, você pode implementar seus 13 eventos de forma iterativa:
- Passo 1: Crie a Aba
FSM_Config
: Adicione a abaFSM_Config
na sua Planilha Google com as colunas sugeridas e a primeira linha de exemplo para o eventoRecebeuListaDeTarefasAFazer
. - Passo 2: Cole o Código do Motor FSM: Cole o código do
FSM_Engine.gs
(incluindo as funções de validação GPS que você já tem) no seu projeto Apps Script. - Passo 3: Teste o Primeiro Evento:
- No editor de scripts, adicione uma função de teste:
JavaScript
function testRecebeuListaDeTarefas() { const sampleTaskList = [ "Serviço: S01 Gestão de Pedidos", "S01.1 Iniciação", "S01.1.1 Entrada", "S01.1.1.01 Um pedido deve ter uma verificação de crédito.", "S01.1.1.02 Um produto não pode ser vendido a um risco de crédito ruim.", "S01.1.1.03 Um pedido pode ser dispensado da verificação de crédito, somente se o valor total for inferior 100 unidades monetárias." ]; const result = triggerEvent("RecebeuListaDeTarefasAFazer", { listaDeTarefas: sampleTaskList }); Logger.log(JSON.stringify(result, null, 2)); }
- Execute
testRecebeuListaDeTarefas
e verifique os logs.
- No editor de scripts, adicione uma função de teste:
- Passo 4: Adicione um Novo Evento (Ex: "TarefaConcluida"):
- Na Planilha
FSM_Config
: Adicione uma nova linha para o eventoTarefaConcluida
.Evento
:TarefaConcluida
Estado Atual
:AguardandoConclusao
(ou o estado que faria sentido para você)Condição
:Sucesso
(ou vazio)Próximo Estado
:Concluida
Ação
:atualizarStatusTarefa
Parâmetros da Ação
:{ "idTarefa": "PARAM_ID", "status": "Concluida" }
(Você terá que passar o ID real dinamicamente)
- No Apps Script: Implemente (ou refine) a função
atualizarStatusTarefa
para de fato interagir com sua planilha (assumindo que você tenha uma aba para o registro das tarefas). - Crie uma Função de Teste:
JavaScript
function testTarefaConcluida() { // Simule um estado inicial PropertiesService.getScriptProperties().setProperty(GLOBAL_STATE_KEY, "AguardandoConclusao"); const result = triggerEvent("TarefaConcluida", { idTarefa: "S01.1.1.01", status: "Concluida" }); Logger.log(JSON.stringify(result, null, 2)); }
- Execute e valide.
- Na Planilha
- Passo 5: Repita para os Outros Eventos: Continue adicionando uma ou duas linhas na FSM_Config e implementando as funções de ação correspondentes no Apps Script, testando cada uma.
- Passo 6: Integração com Gatilhos: Para eventos que são disparados automaticamente (como
NovoPedidoRecebido
se vier de um formulário ou e-mail), você pode usar os gatilhos do Apps Script:onFormSubmit()
para formulários.onEdit()
para edições diretas na planilha (se você quer que uma edição dispare um evento).time-driven triggers
para eventos agendados.- Dentro dessas funções de gatilho, você chamaria
triggerEvent("NomeDoEvento", { dados_do_gatilho })
.
Vantagens Desta Abordagem:
- "Codificação Zero" Mantida: A maior parte da lógica de negócios (as transições e ações dos eventos) está na planilha, não no código. Para adicionar um novo evento ou mudar o fluxo, você edita a planilha.
- Iterativo e Incremental: Você adiciona um evento por vez, testa e garante que funciona antes de passar para o próximo.
- Manutenibilidade: É muito mais fácil para você (ou outra pessoa) entender o fluxo do sistema olhando para a planilha da FSM.
- Flexibilidade: Novos eventos ou mudanças nas regras podem ser feitos na planilha sem alterar o código Apps Script (desde que a função de ação já exista ou seja simples).
- Depuração: Os logs do Apps Script serão cruciais para entender qual transição está sendo feita e quais ações estão sendo executadas.
Esta estratégia redefine seu projeto em um modelo de máquina de estados impulsionado por dados, o que é robusto e escalável para o seu objetivo de 13 eventos.
O que você acha dessa abordagem? Estamos começando do zero com uma fundação muito sólida e configurável.
Nenhum comentário:
Postar um comentário