You are viewing the Portuguese (Brazil) site, but your language preference is set to English. Switch to English site →

Menu

Expand
Classifique esta página:

Lembretes de agendamentos com Python e Django

Ahoy! Agora recomendamos que você crie seus lembretes de agendamento por SMS com a funcionalidade de programação de mensagens integrada da Twilio. Acesse a documentação de recursos de mensagens para saber mais sobre como programar mensagens SMS!

Pronto para implementar lembretes de agendamentos de SMS no seu aplicativo da web do Django? Usaremos a Biblioteca auxiliar Python da Twilio e a API Twilio SMS para enviar por push lembretes aos nossos clientes quando os agendamentos estiverem próximos. Veja como funciona em um nível alto:

  1. Um administrador (nosso usuário) cria um agendamento para uma data e hora futuras e armazena o número de telefone de um cliente no banco de dados para esse agendamento
  2. Quando esse agendamento é salvo, uma tarefa em segundo plano é programada para enviar um lembrete para esse cliente antes do início do agendamento
  3. Em um horário configurado antes do agendamento, a tarefa em segundo plano envia um lembrete SMS ao cliente para lembrá‐lo de seu agendamento

Confira como a Yelp usa o SMS para confirmar reservas de restaurantes para clientes.

Blocos de construção do lembrete de agendamento

Aqui estão as tecnologias que usaremos:

  • Django para criar um aplicativo da web orientado por banco de dados
  • O recurso de mensagens da API REST da Twilio para enviar mensagens de texto
  • Dramatiq para nos ajudar a programar e executar tarefas em segundo plano de forma recorrente

Como ler este tutorial

Para implementar lembretes de agendamentos, trabalharemos em uma série de histórias de usuários que descrevem como implementar totalmente lembretes de agendamentos em um aplicativo da web.

Veremos o código necessário para satisfazer cada história e exploraremos o que precisávamos adicionar em cada etapa.

Tudo isso pode ser feito com a ajuda da Twilio em menos de meia hora.

Vamos começar!

Conheça nossa pilha de lembretes de agendamento do Django

Estamos criando este app para Django 2.1 no Python 3.6. Somos grandes fãs do livro Two Scoops of Django e usaremos muitas das práticas recomendadas descritas nele.

Além do Dramatiq, usaremos algumas outras bibliotecas Python para facilitar nossa tarefa:

Também usaremos o PostgreSQL para nosso banco de dados e o Redis como nosso agente de mensagens do Dramatiq.

Loading Code Sample...
        
        
        requirements.txt

        Dependências do projeto

        requirements.txt

        Agora que temos todas as nossas dependências definidas, podemos começar com a nossa primeira história de utilizador: criar um novo agendamento.

        Criar um agendamento

        Criar um agendamento

        Como usuário, quero criar um agendamento com um nome, número de telefone de convidado e um horário no futuro.

        Para criar um app de lembrete de agendamento automatizado, provavelmente devemos começar com um agendamento. Esta história requer que criemos um objeto de modelo e um pouco da interface do usuário para criar e salvar um novo Appointment em nosso sistema.

        Em um nível alto, vamos adicionar o que precisamos:

        • Um modelo Appointment para armazenar as informações necessárias para enviar o lembrete
        • Uma exibição para renderizar nosso formulário e aceitar dados POST dele
        • Um formulário HTML para inserir detalhes sobre o agendamento

        Muito bem, então sabemos o que precisamos para criar um novo agendamento. Agora, vamos começar analisando o modelo, onde decidimos quais informações queremos armazenar com o agendamento.

        Visite nosso modelo de agendamento

        O modelo de agendamento

        Só precisamos armazenar quatro dados sobre cada agendamento para enviar um lembrete:

        • O nome do cliente
        • Seu número de telefone
        • A data e a hora do agendamento
        • O fuso horário do agendamento

        Também incluímos dois campos adicionais: task_id e created. O campo task_id nos ajudará a acompanhar a tarefa de lembrete correspondente para este agendamento. O campo created é apenas um carimbo de data/hora preenchido quando um agendamento é criado.

        Finalmente, definimos um método __str__ para dizer ao Django como representar instâncias de nosso modelo como texto. Esse método usa a chave primária e o nome do cliente para criar uma representação legível de um agendamento.

        Loading Code Sample...
              
              
              reminders/models.py

              Campos do modelo do agendamento

              reminders/models.py

              Nosso modelo de agendamento agora está configurado, o passo seguinte é escrever uma exibição para ele.

              Confira a nova exibição do agendamento

              Nova exibição do agendamento

              O Django permite que os desenvolvedores escrevam exibições como funções ou classes.

              As visualizações baseadas em classe são excelentes é necessário oferecer suporte a recursos simples, do tipo CRUD, perfeitos para o nosso projeto de agendamentos.

              Para fazer com que uma exibição crie novos objetos Appointment, usaremos a classe genérica CreateView do Django.

              Tudo o que precisamos especificar é o modelo que ele deve usar e quais campos ele deve incluir. Nem sequer precisamos declarar um formulário, o Django usará um ModelForm para nós nos bastidores.

              Mensagens de sucesso

              Nossa exibição está pronta para ser usada apenas com as três primeiras linhas de código, mas vamos torná‐la um pouco melhor adicionando o SuccessMessageMixin.

              Este mixin diz a nossa exibição para passar a propriedade success_message de nossa classe para o framework de mensagens do Django após uma criação bem‐sucedida. Exibiremos essas mensagens para o usuário em nossos modelos.

              Loading Code Sample...
                    
                    
                    reminders/views.py

                    Criar novo agendamento

                    reminders/views.py

                    Agora que temos uma exibição para criar novos agendamentos, precisamos adicionar um novo URL a nosso despachante de URL para que os usuários possam chegar até ele.

                    Conectando o URL com a exibição que acabamos de criar

                    Conectar os URLs

                    Para satisfazer a história do usuário de criação de agendamento, criaremos um novo URL em /new e apontamos para nosso AppointmentCreateView.

                    Como estamos usando uma exibição baseada em classe, passamos nossa exibição para nosso URL com o método .as_view() em vez de apenas usar o nome da exibição.

                    Loading Code Sample...
                          
                          
                          reminders/urls.py

                          Conectar o URL com exibição de agendamento

                          reminders/urls.py

                          Com uma exibição e um modelo em vigor, a última grande parte que precisamos para permitir que nossos usuários criem novos agendamentos é o formulário HTML.

                          Confira o novo formulário de agendamento

                          Novo formulário de agendamento

                          Nosso modelo de formulário herda de nosso modelo básico, que você pode conferir em templates/base.html.

                          Estamos usando o Bootstrap para o front‐end de nosso app, e usamos a biblioteca django-forms-bootstrap para nos ajudar a processar nosso formulário com o filtro de modelo |as_bootstrap_horizontal.

                          Ao nomear este arquivo appointment_form.html, nosso AppointmentCreateView usará automaticamente esse modelo ao renderizar sua resposta. Se quiser nomear seu modelo como algo mais, você pode especificar seu nome adicionando uma propriedade template_name em nossa classe de exibição.

                          Loading Code Sample...
                                
                                
                                templates/reminders/appointment_form.html

                                Novo formulário de agendamento

                                templates/reminders/appointment_form.html

                                Ainda não estamos saindo deste formulário. Em vez disso, vamos dar uma olhada em um de seus widgets: o datepicker.

                                Dê uma olhada no widget do datepicker

                                Datepicker do formulário de agendamento

                                Para facilitar a nossos usuários a inserção de data e hora de um agendamento, usaremos um widget de datepicker do JavaScript.

                                Nesse caso, bootstrap-datetimepicker é uma boa opção. Incluímos os arquivos CSS e JS necessários das redes de entrega de conteúdo e, em seguida, adicionamos um pouco de JavaScript personalizado para inicializar o widget na entrada do formulário para nosso campo de tempo.

                                Loading Code Sample...
                                      
                                      
                                      templates/reminders/appointment_form.html

                                      Widget Datepicker

                                      templates/reminders/appointment_form.html

                                      Agora, vamos voltar ao nosso modelo Appointment para ver o que acontece depois de publicarmos este formulário com sucesso.

                                      Adicionar um método get_absolute_url

                                      Adicionar um método get_absolute_url()

                                      Quando um usuário clica em "Submit" em nosso novo formulário de agendamento, sua entrada será recebida pela nossa AppointmentCreateView e validada nos campos especificados em nosso modelo Appointment.

                                      Se tudo estiver certo, o Django salvará o novo agendamento no banco de dados. Precisamos dizer ao AppointmentCreateView para onde enviar nosso usuário em seguida.

                                      Poderíamos especificar uma propriedade success_url em nossa AppointmentCreateView, mas por padrão a classe CreateView do Django usará o método get_absolute_url do objeto recém‐criado para descobrir aonde ir em seguida.

                                      Assim, definiremos um método get_absolute_url em nosso modelo Appointment, que usa a função de utilidade reversa do Django para criar um URL para a página de detalhes deste agendamento. Você pode ver esse modelo em templates/reminders/appointment_detail.html.

                                      E agora nossos usuários estão todos configurados para criar novos agendamentos.

                                      Loading Code Sample...
                                            
                                            
                                            reminders/models.py

                                            Método get_absolute_url

                                            reminders/models.py

                                            Agora, podemos criar novos agendamentos. Em seguida, vamos implementar rapidamente alguns outros recursos básicos: listar, atualizar e excluir agendamentos.

                                            Veja o que precisamos para interagir com os agendamentos

                                            Interação com agendamentos

                                            Como usuário, quero exibir uma lista de todos os agendamentos futuros e poder editar e excluir esses agendamentos.

                                            Se você é uma organização que lida com muitos agendamentos, provavelmente deseja poder visualizá‐los e gerenciá‐los em uma única interface. É isso que vamos abordar nesta história do usuário. Criaremos uma IU para:

                                            • Mostrar todos os agendamentos
                                            • Editar agendamentos individuais
                                            • Excluir agendamentos individuais

                                            Como essas são operações do tipo CRUD básicas, vamos continuar usando as exibições genéricas de Django baseadas em classes para facilitar muito nosso trabalho.

                                            Loading Code Sample...
                                                  
                                                  
                                                  reminders/views.py

                                                  Interação com agendamentos

                                                  reminders/views.py

                                                  Temos a visão de alto nível da tarefa, então vamos começar listando todos os agendamentos futuros.

                                                  Confira a exibição para listar os agendamentos

                                                  Mostrar uma lista de agendamentos

                                                  A classe ListView do Django nasceu para isso.

                                                  Tudo o que precisamos fazer é apontá‐la em nosso modelo Appointment e ele lidará com a construção de um QuerySet de todos os agendamentos para nós.

                                                  E conectar esta exibição em nosso módulo reminders/urls.py é tão fácil quanto nosso AppointmentCreateView:

                                                  from .views import AppointmentListView
                                                  
                                                  re_path(r'^$', AppointmentListView.as_view(), name='list_appointments'),
                                                  
                                                  Loading Code Sample...
                                                        
                                                        
                                                        reminders/views.py

                                                        Listar agendamentos

                                                        reminders/views.py

                                                        Nossa exibição está pronta, agora vamos conferir o modelo para exibir esta lista de agendamentos.

                                                        Consulte nosso modelo da lista de agendamentos

                                                        Modelo da lista de agendamentos

                                                        Nossa AppointmentListView passa sua lista de objetos de agendamento para nosso modelo na variável object_list.

                                                        Se essa variável estiver vazia, incluímos uma tag <p> dizendo que não há agendamentos futuros.

                                                        Caso contrário, preenchemos uma tabela com uma linha para cada agendamento em nossa lista. Podemos usar nosso método prático get_absolute_url novamente para incluir um link para a página de detalhes de cada agendamento.

                                                        Também usamos a tag do modelo {% url %} para incluir links para as nossas exibições de edição e exclusão.

                                                        Loading Code Sample...
                                                              
                                                              
                                                              templates/reminders/appointment_list.html

                                                              Renderizar lista de agendamentos

                                                              templates/reminders/appointment_list.html

                                                              E agora que nosso requisito de listagem de agendamentos está completo, vamos ver como podemos usar o novo formulário de Agendamento para atualizar agendamentos existentes.

                                                              Veja como o novo formulário de agendamento pode ser usado para atualizar os agendamentos

                                                              Ajustando nosso modelo de formulário

                                                              A UpdateView do Django facilita a adição de uma exibição para a atualização de agendamentos. No entanto, nosso modelo de formulário precisa de alguns ajustes para lidar com dados pré‐preenchidos de um agendamento existente.

                                                              O Django armazenará nossos datetimes com precisão, até o segundo, mas não queremos incomodar nossos usuários forçando‐os a escolher o segundo exato um agendamento começa.

                                                              Para corrigir esse problema, usamos a opção de configuração extraFormats do bootstrap-datetimepicker.

                                                              Ao configurar nosso datetimepicker com um valor de format que não pede aos usuários os segundos e um valor extraFormat que aceita datetimes com segundos, nosso formulário será preenchido corretamente quando o Django fornecer uma data e hora completa para nosso modelo.

                                                              Loading Code Sample...
                                                                    
                                                                    
                                                                    templates/reminders/appointment_form.html

                                                                    Ajustar o novo formulário de agendamento

                                                                    templates/reminders/appointment_form.html

                                                                    Agora temos tudo para usar as classesList, Create e Update de um Appointment. Agora, só falta o tratamento do Delete.

                                                                    DeleteView para o resgate

                                                                    Exibição do Delete

                                                                    O DeleteView é uma classe de exibição especialmente útil. Ela mostra aos usuários uma página de confirmação antes de excluir o objeto especificado.

                                                                    Como UpdateView, DeleteView localiza o objeto a ser excluído usando o parâmetro pk em seu URL, declarado no reminders/urls.py:

                                                                    from .views import AppointmentDeleteView
                                                                    
                                                                    re_path(r'^/(?P[0-9]+)/delete$', AppointmentDeleteView.as_view(), name='delete_appointment'),
                                                                    

                                                                    Também precisamos especificar uma propriedade success_url em nossa classe de exibição. Esta propriedade informa ao Django onde enviar usuários após uma exclusão bem‐sucedida. Em nosso caso, vamos enviá‐los de volta para a lista de agendamentos no URL chamado list_appointments.

                                                                    Quando um projeto do Django começa a funcionar, ele avalia as exibições antes das URLs, então precisamos usar a função de utilitário reverse_lazy para obter o URL da lista de agendamentos em vez reverse.

                                                                    Por padrão, vamos procurar um modelo AppointmentDeleteView chamado appointment_confirm_delete.html. Você pode fazer o check‐out em nosso diretório templates/reminders.

                                                                    E isso encerra esta história de usuário.

                                                                    Loading Code Sample...
                                                                          
                                                                          
                                                                          reminders/views.py

                                                                          Importar exibições genéricas

                                                                          reminders/views.py

                                                                          Nossos usuários agora têm tudo o que precisam para gerenciar agendamentos, tudo o que resta para implementar está enviando os lembretes.

                                                                          Confira os requisitos para enviar um lembrete

                                                                          Enviar o lembrete

                                                                          Como um sistema de agendamentos, desejo notificar um cliente via SMS sobre um intervalo arbitrário antes de um agendamento futuro.

                                                                          Para satisfazer essa história de usuário, precisamos fazer com que nosso aplicativo funcione de forma assíncrona, independentemente de qualquer interação individual do usuário.

                                                                          Uma das bibliotecas Python mais populares para tarefas assíncronas é o Dramatiq. Para integrar o Dramatiq com nosso aplicativo, precisamos fazer algumas alterações:

                                                                          • Crie uma nova função que envie uma mensagem SMS usando informações de um objeto Appointment
                                                                          • Registre essa função como uma tarefa com o Dramatiq para que ela possa ser executada de forma assíncrona
                                                                          • Execute um processo de trabalhador do Dramatiq separado junto com nosso aplicativo do Django para chamar nossa função de lembrete de SMS no momento certo para cada agendamento

                                                                          Se você é novo no Dramatiq, pode querer dar uma olhada na página Introdução ao Dramatiq antes de continuar.

                                                                          Loading Code Sample...
                                                                                
                                                                                
                                                                                reminders/tasks.py

                                                                                Enviar tarefa de lembrete de SMS

                                                                                reminders/tasks.py

                                                                                Em seguida, configuraremos o Dramatiq para trabalhar com nosso projeto.

                                                                                Configuração do Dramatiq

                                                                                Configuração do Dramatiq

                                                                                Dramatiq e Django são ambos grandes projetos Python, mas eles podem trabalhar juntos facilmente.

                                                                                Seguindo as instruções nos documentos do Dramatiq, podemos incluir nossas configurações do Dramatiq em nossos módulos de configuração do Django. Também podemos escrever nossas tarefas do Dramatiq nos módulos tasks.py que vivem dentro de nossos aplicativos do Django, o que mantém nosso layout de projeto consistente e simples.

                                                                                Para usar o Dramatiq, você também precisa de um serviço separado para ser seu agente de mensagens. Usamos o Redis para este projeto.

                                                                                As configurações específicas do Dramatiq em nosso módulo de configurações common.py sãoDRAMATIQ_BROKER.

                                                                                Se quiser ver todas as etapas para fazer com que o Django, Dramatiq, Redis e Postgres trabalhem em sua máquina Confira o README do projeto no GitHub.

                                                                                Loading Code Sample...
                                                                                      
                                                                                      
                                                                                      appointments/settings/common.py

                                                                                      Configurações do Dramatiq

                                                                                      appointments/settings/common.py

                                                                                      Agora que o Dramatiq está trabalhando com nosso projeto, é hora de escrever uma nova tarefa para enviar uma mensagem SMS a um cliente sobre seu agendamento.

                                                                                      Veja como criar uma nova tarefa do Dramatiq

                                                                                      Criando uma tarefa do Dramatiq

                                                                                      Nossa tarefa toma o ID de um agendamento, é a chave principal, como seu único argumento. Poderíamos passar o próprio objeto Appointment como argumento, mas essa prática recomendada garante que nosso SMS usará a versão mais atualizada dos dados de nosso agendamento.

                                                                                      Ela também nos dá a oportunidade de verificar se o agendamento foi excluído antes do envio do lembrete, o que fazemos no topo de nossa função. Dessa forma, não enviaremos lembretes de SMS para agendamentos que não existem mais.

                                                                                      Loading Code Sample...
                                                                                            
                                                                                            
                                                                                            reminders/tasks.py

                                                                                            Buscar agendamentos na tarefa do Dramatiq

                                                                                            reminders/tasks.py

                                                                                            Vamos ficar um pouco mais em nossa tarefa, porque o próximo passo é compor o texto da nossa mensagem SMS.

                                                                                            Veja como enviar um SMS com o cliente Python da Twilio

                                                                                            Como enviar uma mensagem SMS

                                                                                            Usamos a prática biblioteca arrow para formatar o horário do nosso agendamento. Depois disso, usamos a biblioteca twilio-python para enviar nossa mensagem.

                                                                                            Instanciamos um cliente Twilio REST na parte superior do módulo, que procura as variáveis de ambiente TWILIO_ACCOUNT_SID para TWILIO_AUTH_TOKEN se autenticar. Você pode encontrar os valores corretos para você no dashboard de sua conta.

                                                                                            Enviar a mensagem SMS em si é tão fácil quanto chamar client.messages.create(), passar argumentos para o corpo da mensagem SMS, o número de telefone do destinatário e o número de telefone da Twilio do qual você deseja enviar esta mensagem. A Twilio enviará a mensagem SMS imediatamente.

                                                                                            Loading Code Sample...
                                                                                                  
                                                                                                  
                                                                                                  reminders/tasks.py

                                                                                                  Enviar SMS na tarefa do Dramatiq

                                                                                                  reminders/tasks.py

                                                                                                  Com nossa tarefa send_sms_reminder concluída, vamos ver como chamá‐la quando nossos agendamentos forem criados ou atualizados.

                                                                                                  Como chamamos essa tarefa?

                                                                                                  Chamar nossa tarefa de lembrete

                                                                                                  Adicionamos um novo método em nosso modelo Appointment para ajudar a agendar um lembrete para um agendamento individual.

                                                                                                  Nosso método começa usando arrow novamente para criar uma nova datetime com time e time_zone do agendamento.

                                                                                                  Retroceder no tempo pode ser complicado no Python normal, mas o método .replace() do arrow permite subtrair facilmente minutos de nosso appointment_time. A configuração padrão REMINDER_TIME é 30 minutos.

                                                                                                  Terminamos invocando nossa tarefa do Dramatiq, usando o parâmetro delay (atrasar) para dizer ao Dramatiq quando esta tarefa deve ser executada.

                                                                                                  Não é possível importar a tarefa send_sms_reminder na parte superior de nosso módulo models.py porque o módulo tasks.py importa o modelo Appointment. A importação em nosso método schedule_reminder evita uma dependência circular.

                                                                                                  Loading Code Sample...
                                                                                                        
                                                                                                        
                                                                                                        reminders/models.py

                                                                                                        Agendar uma nova tarefa do Dramatiq

                                                                                                        reminders/models.py

                                                                                                        A última coisa que precisamos fazer é garantir que o Django chame nosso método schedule_reminder sempre que um objeto Appointment é criado ou atualizado.

                                                                                                        Substituir o método Save do agendamento

                                                                                                        Substituir o método Save do agendamento

                                                                                                        A melhor maneira de fazer isso é substituir o método de salvamento do nosso modelo, incluindo uma chamada extra para schedule_reminder depois que a chave primária do objeto tiver sido atribuída.

                                                                                                        Evitar lembretes duplicados ou com tempo incorreto

                                                                                                        Agendar uma tarefa do Dramatiq sempre que um agendamento for salvo tem um efeito colateral lamentável, nossos clientes receberão lembretes duplicados se um agendamento for salvo mais de uma vez. E esses lembretes poderão ser enviados no momento errado se o campo time de um agendamento tiver sido alterado após sua criação.

                                                                                                        Para corrigir isso, acompanhamos a tarefa de lembrete de cada agendamento no campo task_id, que armazena o identificador exclusivo do Dramatiq para cada tarefa.

                                                                                                        Em seguida, procuramos uma tarefa previamente agendada na parte superior do nosso método save personalizado e cancelamos essa tarefa, se houver.

                                                                                                        Isso garante que um e exatamente um lembrete será enviado para cada agendamento em nosso banco de dados, e que ele será enviado no time mais recente fornecido para esse agendamento.

                                                                                                        Loading Code Sample...
                                                                                                              
                                                                                                              
                                                                                                              reminders/models.py

                                                                                                              Método save() substituído para chamar schedule_reminder()

                                                                                                              reminders/models.py

                                                                                                              Tutorial divertido, certo? Onde podemos levá‐lo a partir daqui?

                                                                                                              Para onde ir em seguida?

                                                                                                              Concluir a implementação do lembrete de agendamento do Django

                                                                                                              Usamos as exibições baseadas em classe do Django para nos ajudar a desenvolver rapidamente os recursos para dar suporte a operações CRUD simples em nosso modelo Appointment.

                                                                                                              Em seguida, integramos o Dramatiq em nosso projeto e usamos a biblioteca auxiliar twilio-python para enviar lembretes SMS sobre nossos agendamentos de forma assíncrona.

                                                                                                              Você encontrará instruções para executar este projeto localmente no README do GitHub.

                                                                                                              Loading Code Sample...
                                                                                                                    
                                                                                                                    
                                                                                                                    reminders/models.py

                                                                                                                    O modelo de agendamento

                                                                                                                    reminders/models.py

                                                                                                                    Para onde ir em seguida?

                                                                                                                    E com um pequeno código e um pouco de configuração, estamos prontos para receber lembretes automatizados de agendamentos disparados em nosso aplicativo. Bom trabalho!

                                                                                                                    Se você for um desenvolvedor Python que trabalha com a Twilio, talvez queira conferir outros tutoriais em Python:

                                                                                                                    Chamada do navegador

                                                                                                                    Coloque um botão em sua página da web que conecta os visitantes ao suporte ao vivo ou à equipe de vendas por telefone.

                                                                                                                    Autenticação de 2 fatores

                                                                                                                    Melhore a segurança da funcionalidade de login do seu app Python adicionando autenticação de 2 fatores por mensagem de texto.

                                                                                                                    Isso ajudou?

                                                                                                                    Obrigado por conferir este tutorial! Se você tiver algum feedback para compartilhar, entre em contato pelo Twitter... adoraríamos ouvir suas ideias e saber o que você está desenvolvendo!

                                                                                                                    Andrew Baker Kevin Segovia Daniel Erazo
                                                                                                                    Classifique esta página:

                                                                                                                    Precisa de ajuda?

                                                                                                                    Às vezes, todos nós precisamos; a programação é difícil. Receba ajuda agora da nossa equipe de suporte, ou confie na sabedoria da multidão navegando pelo Stack Overflow Collective da Twilio ou buscando a tag Twilio no Stack Overflow.

                                                                                                                    Loading Code Sample...
                                                                                                                          
                                                                                                                          
                                                                                                                          

                                                                                                                          Obrigado pelo seu feedback!

                                                                                                                          Selecione o(s) motivo(s) para seu feedback. As informações adicionais que você fornece nos ajudam a melhorar nossa documentação:

                                                                                                                          Enviando seu feedback...
                                                                                                                          🎉 Obrigado pelo seu feedback!
                                                                                                                          Algo deu errado. Tente novamente.

                                                                                                                          Obrigado pelo seu feedback!

                                                                                                                          thanks-feedback-gif