quinta-feira, 17 de abril de 2025

Como Exportar Dados do Trello para Excel em Português (PT-BR)

Tenho utilizado o Trello para organizar as tarefas do meu trabalho, mas precisei exportar os dados para o Excel, e percebi que não havia uma opção nativa para isso. Após uma pesquisa, encontrei um script VBA desenvolvido por Kevin Harper, que estava disponível na internet. O script é totalmente funcional, mas não fazia a conversão adequada para o português (PT-BR).

Diante disso, decidi atualizar o código mantendo os direitos autorais de Kevin Harper e realizar as devidas adaptações para que ele funcione corretamente em português. Agora, o script está pronto para ser utilizado e pode ajudar a facilitar o processo de exportação de dados do Trello para o Excel, respeitando o idioma português.

Abaixo, compartilho o script atualizado para que vocês possam utilizar e aprimorar ainda mais a organização das suas tarefas.





'    Tool for importing Trello JSON files into Excel.
'    Written by Kevin Harper, 16-Mar-17.
'    Updated by Joaquim GVF, 17-Apr-25
'
'    This spreadsheet uses the JSON export from Trello to import a rich content into Excel as a list of cards or a list of actions.
'    No Chrome or other browser extensions are required for the export/import.
'    The import scripts use the JSON parser capability developed by Tim Hall, available at:
'    https://github.com/VBA-tools/VBA-JSON
'    The VBA-JSON parser is already included in the spreadsheet file
'
'    From within the spreadsheet, there are two import options
'    1. The "ImportedCards" sheet runs vbscript ImportMyTrello - this creates a list of all the cards on your Trello board,
'    excluding those that have been archived.
'    2. The "ImportedActions" sheet runs vbscript ImportActionsFromTrello - this creates a list of
'    all the actions that have been carried out on your Trello board -
'    by applying Excel filters, you can create a list of specific actions;  eg, such as a record of who and when moved cards into a particular column.
'
'    To export a board from Trello into Excel using the spreadsheet, carry out the following steps:
'
'    (1) In Trello, for your chosen board and using the right-hand side menu options, select:
'           More / Print and Export / Export to JSON
'
'    o  In Chrome, this will display the JSON code in the open tab.  Save this as a local file on your computer,
'    by right clicking and selecting "Save as..."
'    o  In Internet Explorer, this will download the JSON export as a local file on your computer.
'
'    (2) Using the spreadsheet, click the "Import" button on either worksheet and select your downloaded JSON file
'    ....and the import should proceed.
'
' Copyright (c) 2017, 2018, Kevin Harper
'
'   version 1.0, 16-Mar-17 - first version published in GitHub
'   version 1.1, 17-May-18 - added error handling for lookup of idMemberAdded and idMember where the added person is no
'   longer a member of the board and therefore not found in the array of Members.
'   version 1.2, 17-Apr-25 - updated by Joaquim GVF to add support for import in Portuguese (PT-BR) language with UTF-8.
'
' All rights reserved.
'
' Redistribution and use in source and binary forms, with or without
' modification, are permitted provided that the following conditions are met:
'     * Redistributions of source code must retain the above copyright
'       notice, this list of conditions and the following disclaimer.
'     * Redistributions in binary form must reproduce the above copyright
'       notice, this list of conditions and the following disclaimer in the
'       documentation and/or other materials provided with the distribution.
'     * Neither the name of the  nor the
'       names of its contributors may be used to endorse or promote products
'       derived from this software without specific prior written permission.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
' DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Option Explicit
Public myFile As String
Public cardMovedColumn As Boolean

Public Sub ImportMyTrello()

    Dim JsonText As String
    Dim Parsed As New Dictionary
    
    Const StartRow = 4    'Row to contain column headers, should be greater than 3
    
    ' Fetch the JSON string and then parse it to Dictionary, or Exit if file selection is cancelled:
    JsonText = GetJsonText()
    If JsonText = "" Then Exit Sub
    
    Set Parsed = JsonConverter.ParseJson(JsonText)
    
'    Clear the current content from the spreadsheet:
'    Sheets("ImportedCards").Cells.ClearContents
    Range(Sheets("ImportedCards").Cells(1, 1), Sheets("ImportedCards").Cells(1, 1).SpecialCells(xlLastCell)).ClearContents

'    and insert the Board name and URL
    Sheets("ImportedCards").Cells(1, 3) = Parsed("name")
    Sheets("ImportedCards").Cells(2, 1) = "URL for imported Trello board:"
    Sheets("ImportedCards").Hyperlinks.Add Anchor:=Cells(3, 1), Address:=Parsed("url"), TextToDisplay:=Parsed("url")
        
'   =======================================================================================

'    Create an array called Columns, of content (id, name);  this is used later to lookup
'    column names based on their id

    Dim Columns As Variant
    Dim NumberOfColumns As Integer
    Dim NumberOfArchivedColumns As Integer
    Call FetchColumns(Parsed, Columns, NumberOfColumns, NumberOfArchivedColumns)

'   =======================================================================================

'    Create an array called Members, of content (id, fullName);  this is used later to lookup
'    people's names based on their id

    Dim Members As Variant
    Dim NumberOfMembers As Integer
    Call FetchMembers(Parsed, Members, NumberOfMembers)

'   =======================================================================================
    
'    Create an array called Cards, of content aligned to the following headers:
   
    Const CollectedColumns = 10
    Dim LastColumnIndex As Integer
    LastColumnIndex = CollectedColumns - 1
    Dim Header(CollectedColumns) As String
    Header(0) = "Column"
    Header(1) = "Card title"
    Header(2) = "Description"
    Header(3) = "Date of last update"
    Header(4) = "Due date"
    Header(5) = "Card Members"
    Header(6) = "Labels"
    Header(7) = "Card ID"
    Header(8) = "Attachments"
    Header(9) = "URL"

    Dim myObject As String
    myObject = "cards"
    
    Dim Card As Dictionary
    Dim Label As Dictionary
    Dim CardMember As Variant
    Dim Cards As Variant
    Dim Filename As Variant
    Dim NumberOfCards As Integer
    Dim NumberOfArchivedCards As Integer
    Dim i As Integer
    Dim FirstPass As Boolean

    
'    Redim the upper array bounds, where lowest bounds are 0
    ReDim Cards(Parsed(myObject).Count - 1, LastColumnIndex)
    
'   Loop through each card in the list, skipping it if it has been closed in Trello (ie archived):
    i = 0
    For Each Card In Parsed(myObject)
        If (Card("closed") <> "True") Then
            
'           First do all the card fields that are single values:
            Cards(i, 0) = Application.VLookup(Card("idList"), Columns, 2, False)        ' Lookup the list id to extract it's name, stored in the list created earlier
            Cards(i, 1) = Card("name")
            Cards(i, 2) = Card("desc")
            Cards(i, 3) = Left(Card("dateLastActivity"), 10)
            Cards(i, 4) = Left(Card("due"), 10)
            Cards(i, 7) = Card("idShort")
            Cards(i, 9) = Card("shortUrl")
            
'           The next three fields may have more than one value for the card; where multiple values exist, a comma separated list is created
            FirstPass = True
            For Each CardMember In Card("idMembers")
     
                If FirstPass Then
                    Cards(i, 5) = Application.VLookup(CardMember, Members, 2, False)    ' Lookup the person's id to extract their name, stored in the list created earlier
                    FirstPass = False
                Else
                    Cards(i, 5) = Cards(i, 5) & ", " & Application.VLookup(CardMember, Members, 2, False)
                End If
            Next CardMember
           
            FirstPass = True
            For Each Label In Card("labels")
                If FirstPass Then
                    Cards(i, 6) = Label("name")
                    FirstPass = False
                Else
                    Cards(i, 6) = Cards(i, 6) & ", " & Label("name")
                End If
            Next Label
    
            FirstPass = True
            For Each Filename In Card("attachments")
                If FirstPass Then
                    Cards(i, 8) = Filename("name")
                    FirstPass = False
                Else
                    Cards(i, 8) = Cards(i, 8) & ", " & Filename("name")
                End If
            Next Filename
                      
            i = i + 1
        Else
            NumberOfArchivedCards = NumberOfArchivedCards + 1
            
        End If
    Next Card
    
    NumberOfCards = i
'   =======================================================================================

'    Put the Cards array into the spreadsheet:
    Sheets("ImportedCards").Range(Cells(StartRow, 1), Cells(StartRow, CollectedColumns)) = Header
    Sheets("ImportedCards").Range(Cells(StartRow + 1, 1), Cells(StartRow + 1 + NumberOfCards - 1, CollectedColumns)) = Cards
    
'   Add some stats in the header:
    
    Sheets("ImportedCards").Cells(2, 4) = NumberOfColumns
    Sheets("ImportedCards").Cells(2, 5) = "columns (lists) imported from Trello"
    Sheets("ImportedCards").Cells(3, 4) = NumberOfCards
    Sheets("ImportedCards").Cells(3, 5) = "cards"
    
    Sheets("ImportedCards").Cells(2, 7) = NumberOfArchivedColumns
    Sheets("ImportedCards").Cells(2, 8) = "archived column(s) NOT imported"
    Sheets("ImportedCards").Cells(3, 7) = NumberOfArchivedCards
    Sheets("ImportedCards").Cells(3, 8) = "archived card(s) NOT imported"
    
    Call FormatSheet("ImportedCards", StartRow)

End Sub

Public Sub ImportActionsFromTrello()

    Dim JsonText As String
    Dim Parsed As New Dictionary

    Const StartRow = 4    'Row to contain column headers, should be greater than 3
    
    ' Fetch the JSON string and then parse it to Dictionary, or Exit if file selection is cancelled:
    JsonText = GetJsonText()
    If JsonText = "" Then Exit Sub
    
    Set Parsed = JsonConverter.ParseJson(JsonText)
    
    
'    Clear the current content from the spreadsheet:
'    Sheets("ImportedActions").Cells.ClearContents
    Range(Sheets("ImportedActions").Cells(1, 1), Sheets("ImportedActions").Cells(1, 1).SpecialCells(xlLastCell)).ClearContents
    
'    and insert the Board name and URL
    Sheets("ImportedActions").Cells(1, 3) = Parsed("name")
    Sheets("ImportedActions").Cells(2, 1) = "URL for imported Trello board:"
    Sheets("ImportedActions").Hyperlinks.Add Anchor:=Cells(3, 1), Address:=Parsed("url"), TextToDisplay:=Parsed("url")
        
'   =======================================================================================

'    Create an array called Members, of content (id, fullName);  this is used later to lookup
'    people's names based on their id

    Dim Members As Variant
    Dim NumberOfMembers As Integer
    Call FetchMembers(Parsed, Members, NumberOfMembers)
    
'   =======================================================================================
    
'    Create an array called Cards, of content aligned to the following headers:

    Const CollectedColumns = 6
    Dim LastColumnIndex As Integer
    LastColumnIndex = CollectedColumns - 1
    Dim Header(CollectedColumns) As String
    Header(0) = "Column"
    Header(1) = "Card title"
    Header(2) = "Member"
    Header(3) = "Action"
    Header(4) = "Date"
    Header(5) = "Details"
    
    Dim myObject As String
    myObject = "actions"
    Dim Action As Dictionary
    Dim Card As Variant
    Dim Actions As Variant
    Dim NumberOfActions As Integer
    Dim i As Integer
    Dim FirstPass As Boolean
    Dim lookup As Variant
    
'    Redim the upper array bounds, where lowest bounds are 0
    ReDim Actions(Parsed(myObject).Count - 1, LastColumnIndex)
    
'   Loop through each card in the list, skipping it if it has been closed in Trello (ie archived):
    i = 0
    For Each Action In Parsed(myObject)
            
            Actions(i, 0) = FindActionListName(Action)
            Actions(i, 1) = FindActionCardName(Action)

            Actions(i, 2) = Application.VLookup(Action("idMemberCreator"), Members, 2, False)        ' Lookup the person's id to extract their name, stored in the list created earlier
            Actions(i, 3) = Action("type")
            Actions(i, 4) = Left(Action("date"), 10)
            
            If Action("type") = "addAttachmentToCard" Then
                Actions(i, 5) = "Attached file: " & Action("data")("attachment")("name")
                
            ElseIf Action("type") = "deleteAttachmentFromCard" Then
                Actions(i, 5) = "Removed file: " & Action("data")("attachment")("name")
'                Actions(i, 0) =
                
            ElseIf Action("type") = "commentCard" Then
                Actions(i, 5) = "Added comment: " & Action("data")("text")
                
            ElseIf Action("type") = "createCard" Then
                Actions(i, 5) = "New card added"
                
            ElseIf Action("type") = "copyCard" Then
                Actions(i, 5) = "Copied from: " & Action("data")("cardSource")("name")
                
            ElseIf Action("type") = "addChecklistToCard" Then
                Actions(i, 5) = "Added checklist: " & Action("data")("checklist")("name")
                
            ElseIf Action("type") = "removeChecklistFromCard" Then
                Actions(i, 5) = "Removed checklist: " & Action("data")("checklist")("name")
                
            ElseIf Action("type") = "updateChecklist" Then
                Actions(i, 5) = "Changed from [" & Action("data")("old")("name") _
                    & "] to [" & Action("data")("checklist")("name") & "]"
                    
            ElseIf Action("type") = "updateCheckItemStateOnCard" Then
                Actions(i, 5) = "Checklist item [" & Action("data")("checkItem")("name") & "] set to <" & _
                    Action("data")("checkItem")("state") & ">"
                
            ElseIf Action("type") = "createList" Then
                Actions(i, 5) = "Added new column: " & Action("data")("list")("name")
                
            ElseIf Action("type") = "updateList" Then
                If Action("data")("old")("name") <> "" Then
                    Actions(i, 5) = "Column changed from: " & Action("data")("old")("name")
                    
                ElseIf Action("data")("old")("pos") > Action("data")("list")("pos") Then
                    Actions(i, 5) = "Column moved left"
                    
                ElseIf Action("data")("old")("pos") < Action("data")("list")("pos") Then
                    Actions(i, 5) = "Column moved right"
                    
                End If
                
            ElseIf Action("type") = "createBoard" Then
                Actions(i, 5) = "Added new board: " & Action("data")("board")("name")
                
            ElseIf Action("type") = "updateBoard" Then
                Actions(i, 5) = "Background changed from [" & Action("data")("old")("prefs")("background") _
                    & "] to [" & Action("data")("board")("prefs")("background") & "]"
                
            ElseIf Action("type") = "updateCard" Then
                If Action("data")("old")("due") <> "" Then
                    Actions(i, 5) = "Due changed from: " & Left(Action("data")("old")("due"), 10)
                    
                ElseIf Action("data")("old")("name") <> "" Then
                    Actions(i, 5) = "Name changed from: " & Action("data")("old")("name")
                    
                ElseIf Action("data")("old")("pos") > Action("data")("card")("pos") Then
                    Actions(i, 5) = "Card moved up in column"
                    
                ElseIf Action("data")("old")("pos") < Action("data")("card")("pos") Then
                    Actions(i, 5) = "Card moved down in column"
                    
                ElseIf cardMovedColumn Then
                    Actions(i, 5) = "Moved from column: " & Action("data")("listBefore")("name")
                    
                ElseIf Len(Action("data")("old")("desc")) > 0 Then
                    Actions(i, 5) = "Card description amended"
                    
                ElseIf Action("data")("old")("desc") = "" Then
                    Actions(i, 5) = "Card description added"
                    
                End If

            ElseIf Action("type") = "addMemberToBoard" Then
'                Actions(i, 5) = "Added new member: " & Application.VLookup(Action("data")("idMemberAdded"), Members, 2, False)
'               [10-May-18]replaced with, handles error if
                lookup = Application.VLookup(Action("data")("idMemberAdded"), Members, 2, False)
                If Not IsError(lookup) Then
                    Actions(i, 5) = "Added new member: " & lookup
                Else
                    Actions(i, 5) = "Added new member: ERROR: " & Action("data")("idMemberAdded") & " not found"
                End If

                          
            ElseIf Action("type") = "addMemberToCard" Then
'                Actions(i, 5) = "Added to card: " & Application.VLookup(Action("data")("idMember"), Members, 2, False)
'               [10-May-18]replaced with
                lookup = Application.VLookup(Action("data")("idMember"), Members, 2, False)
                If Not IsError(lookup) Then
                    Actions(i, 5) = "Added to card: " & lookup
                Else
                    Actions(i, 5) = "Added to card: ERROR: " & Action("data")("idMember") & " not found"
                End If
                
            ElseIf Action("type") = "removeMemberFromCard" Then
'                Actions(i, 5) = "Removed: " & Application.VLookup(Action("data")("idMember"), Members, 2, False)
'               [10-May-18]replaced with
                lookup = Application.VLookup(Action("data")("idMember"), Members, 2, False)
                If Not IsError(lookup) Then
                    Actions(i, 5) = "Removed: " & lookup
                Else
                    Actions(i, 5) = "Removed: ERROR: " & Action("data")("idMember") & " not found"
                End If
                
            ElseIf Action("type") = "addToOrganizationBoard" Then
                Actions(i, 5) = "Added to org: " & Action("data")("organization")("name")
                               
        End If
       
        i = i + 1
        
    Next Action
    
    NumberOfActions = i
'   =======================================================================================

'    Put the Cards array into the spreadsheet:
    Sheets("ImportedActions").Range(Cells(StartRow, 1), Cells(StartRow, CollectedColumns)) = Header
    Sheets("ImportedActions").Range(Cells(StartRow + 1, 1), Cells(StartRow + 1 + NumberOfActions - 1, CollectedColumns)) = Actions
    
'   Add some stats in the header:
    
    Sheets("ImportedActions").Cells(3, 4) = NumberOfActions
    Sheets("ImportedActions").Cells(3, 5) = "actions"

    Call FormatSheet("ImportedActions", StartRow)

End Sub


Private Function GetJsonText_old()
'   Open the filechooser, select file and read .json file
    Dim FSO As New FileSystemObject
    Dim JsonTS As TextStream
    
    myFile = Application.GetOpenFilename("JSON Files (*.json),*.json", 1, "Open JSON file for import")
    If myFile <> "False" Then
        Set JsonTS = FSO.OpenTextFile(myFile, ForReading)
        GetJsonText = JsonTS.ReadAll
        JsonTS.Close
    Else
        GetJsonText = ""
    End If
    
End Function


Function GetJsonText() As String
    Dim fileDialog As fileDialog
    Dim filePath As String
    Dim fileStream As Object
    Dim inputStream As Object

    Set fileDialog = Application.fileDialog(msoFileDialogFilePicker)
    
    With fileDialog
        .AllowMultiSelect = False
        .Title = "Selecione o arquivo JSON exportado do Trello"
        .Filters.Clear
        .Filters.Add "Arquivos JSON", "*.json"
        
        If .Show <> -1 Then
            GetJsonText = ""
            Exit Function
        End If
        
        filePath = .SelectedItems(1)
    End With

    ' Criar objetos para leitura com codificação UTF-8
    Set inputStream = CreateObject("ADODB.Stream")
    inputStream.Charset = "utf-8"
    inputStream.Open
    inputStream.LoadFromFile filePath
    GetJsonText = inputStream.ReadText
    inputStream.Close
    Set inputStream = Nothing
End Function


Private Sub FetchColumns(Parsed As Dictionary, Columns As Variant, NumberOfColumns As Integer, NumberOfArchivedColumns As Integer)

    Dim Column As Dictionary
    Dim myObject As String
    Dim i As Integer
    myObject = "lists"
'    Dim Columns As Variant
'    Dim NumberOfColumns As Integer
'    Dim NumberOfArchivedColumns As Integer
'    Redim the upper array bounds, where lowest bounds are 0
    ReDim Columns(Parsed(myObject).Count - 1, 1)
    
    i = 0
    For Each Column In Parsed(myObject)
        If (Column("closed") <> "True") Then
            Columns(i, 0) = Column("id")
            Columns(i, 1) = Column("name")
    
            i = i + 1
        Else
            NumberOfArchivedColumns = NumberOfArchivedColumns + 1
        End If
    Next Column
    NumberOfColumns = i

End Sub

Private Sub FetchMembers(Parsed As Dictionary, Members As Variant, NumberOfMembers As Integer)


    Dim Member As Dictionary
    Dim myObject As String
    Dim i As Integer
    myObject = "members"
    
'    Dim Members As Variant
'    Dim NumberOfMembers As Integer
'    Redim the upper array bounds, where lowest bounds are 0
    ReDim Members(Parsed(myObject).Count - 1, 1)
      
    i = 0
    For Each Member In Parsed(myObject)
        Members(i, 0) = Member("id")
        Members(i, 1) = Member("fullName")

        i = i + 1
    Next Member
    NumberOfMembers = i
    
End Sub





Private Function FindActionListName(Action As Dictionary)
    cardMovedColumn = False
    On Error GoTo ErrHandler1
    FindActionListName = Action("data")("list")("name")
    Exit Function
    
TryNext1:
    On Error GoTo ErrHandler2
    FindActionListName = Action("data")("listAfter")("name")
    cardMovedColumn = True
    Exit Function
    
TryNext2:
    FindActionListName = "not found"
    Exit Function
    
ErrHandler1:
    Resume TryNext1
ErrHandler2:
    Resume TryNext2
        
End Function


Private Function FindActionCardName(Action As Dictionary)
    On Error GoTo ErrHandler1
    FindActionCardName = Action("data")("card")("name")
    Exit Function
    
TryNext1:
    FindActionCardName = "not found"
    Exit Function
        
ErrHandler1:
    Resume TryNext1
    
End Function

Private Sub FormatSheet(Sheet As String, StartRow As Integer)
'   Apply formatting to the header:
    Sheets(Sheet).Rows("1:3").Style = "Ênfase1"
    
    With Sheets(Sheet).Rows("2:3")
        .HorizontalAlignment = xlLeft
        .VerticalAlignment = xlTop
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    
    With Sheets(Sheet).Range("C1")
        .HorizontalAlignment = xlLeft
        .VerticalAlignment = xlCenter
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
        .RowHeight = 50
        .Font.Bold = True
        .Font.Size = 26
    End With
    
    With Sheets(Sheet).Range("D2:D3")
        .HorizontalAlignment = xlRight
        .VerticalAlignment = xlTop
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    
    With Sheets(Sheet).Range("G2:G3")
        .HorizontalAlignment = xlRight
        .VerticalAlignment = xlTop
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    
    With Sheets(Sheet).Rows(StartRow)
        .Style = "Ênfase1"
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlCenter
        .WrapText = True
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
        .RowHeight = 40
        .Font.Bold = True
    End With
    
    With Sheets(Sheet).Range("A3")
        .Style = "Hyperlink"
    End With
    
End Sub


segunda-feira, 24 de março de 2025

Técnica dos Dois Passos no ChatGPT

 

Técnica dos Dois Passos no ChatGPT

Introdução

A técnica dos dois passos é uma forma otimizada de utilizar o ChatGPT para obter respostas mais precisas e elaboradas. Em vez de fazer uma pergunta direta, você primeiro estabelece os critérios ideais para a resposta desejada e só então solicita a análise ou geração de conteúdo.

Relação com a Engenharia de Prompt

A técnica dos dois passos está diretamente ligada à Engenharia de Prompt (Prompt Engineering), que é a arte de estruturar perguntas e comandos para obter respostas mais eficazes da IA. Ela se baseia em dois princípios fundamentais:

  1. Contextualização Progressiva: Criar um contexto detalhado antes da solicitação principal melhora a precisão das respostas geradas pelo ChatGPT.

  2. Divisão de Tarefas: Separar uma solicitação em dois passos permite que a IA compreenda melhor a necessidade do usuário antes de produzir um resultado final.

Ao aplicar essa técnica, você maximiza a qualidade das respostas e torna sua interação com a IA mais eficiente.

Como Funciona a Técnica dos Dois Passos

Exemplo de Uso

Imagine que você deseja que o ChatGPT analise um contrato de aluguel. Em vez de apenas pedir "Analise este contrato", você pode seguir os dois passos abaixo:

  1. Definir os Critérios Ideais

    • Pergunte: "O que faz um contrato de aluguel ser excelente?"

    • O ChatGPT listará os princípios e boas práticas que tornam um contrato bem elaborado.

  2. Aplicar os Critérios na Análise

    • Com base na resposta anterior, você solicita: "Agora use esses princípios para analisar o meu contrato."

Dessa forma, o ChatGPT tem um contexto mais rico para fornecer uma análise mais precisa e estruturada.


Aplicabilidade da Técnica

Essa metodologia pode ser aplicada em diversas situações, como:

  • Redação de textos: Pergunte "O que torna um artigo científico excelente?" antes de solicitar a redação.

  • Criação de modelos financeiros: Pergunte "Quais elementos um modelo financeiro precisa conter?" antes de pedir para gerar um modelo.

  • Análise de documentos: Pergunte "Quais são os principais critérios para avaliar um relatório financeiro?" antes de solicitar uma avaliação.

Benefícios da Técnica

  • Respostas mais precisas e completas

  • Maior controle sobre a qualidade do resultado

  • Possibilidade de aprendizado durante o processo

  • Melhoria na interação com o ChatGPT

Outros Exemplos de Aplicação

Aqui estão algumas situações onde a técnica dos dois passos pode ser utilizada:

  • Criação de um plano de negócios:

    1. Pergunte: "Quais são os elementos essenciais de um plano de negócios eficaz?"

    2. Depois, solicite: "Agora elabore um plano de negócios para minha empresa com base nesses elementos."

  • Melhoria na escrita de e-mails profissionais:

    1. Pergunte: "Quais são as melhores práticas para escrever um e-mail profissional persuasivo?"

    2. Depois, solicite: "Agora me ajude a escrever um e-mail seguindo essas práticas."

  • Desenvolvimento de um roteiro de apresentação:

    1. Pergunte: "O que faz um roteiro de apresentação ser impactante?"

    2. Depois, solicite: "Agora me ajude a criar um roteiro para minha apresentação com base nessas diretrizes."

  • Análise de um currículo:

    1. Pergunte: "O que faz um currículo se destacar no mercado de trabalho atual?"

    2. Depois, solicite: "Agora analise meu currículo com base nesses princípios."

  • Aprendizado de um novo idioma:

    1. Pergunte: "Quais são as melhores estratégias para aprender um novo idioma rapidamente?"

    2. Depois, solicite: "Agora crie um plano de estudos baseado nessas estratégias."

  • Criação de posts para redes sociais:

    1. Pergunte: "Quais elementos tornam um post para redes sociais mais engajador?"

    2. Depois, solicite: "Agora gere um post para minha marca seguindo essas diretrizes."

  • Estruturação de um código de programação:

    1. Pergunte: "Quais são as melhores práticas para escrever código limpo e eficiente em Python?"

    2. Depois, solicite: "Agora me ajude a refatorar esse código com base nessas práticas."

  • Preparação para entrevistas de emprego:

    1. Pergunte: "Quais são as perguntas mais comuns em entrevistas para minha área de atuação?"

    2. Depois, solicite: "Agora me ajude a elaborar respostas eficazes para essas perguntas."

Considerações Finais

A técnica dos dois passos é uma estratégia simples, mas extremamente eficaz para obter melhores resultados ao interagir com o ChatGPT. Sempre que precisar de uma resposta detalhada, comece estabelecendo os critérios de excelência antes de solicitar a tarefa principal.

segunda-feira, 13 de janeiro de 2025

Trazer em uma célula do Google Sheets,a Data Máxima de um conjunto de células.

Trazer em uma célula do Google Sheets,
a Data Máxima de um conjunto de células.

Vinha utilizando-se =MAX(X1:Y1).

Mas de um tempo para cá começou a retornar erros.

Passei a utilizar o SORTN. Coloquei para retornar o primeiro item de um conjunto de dados após a classificar decrescente. Caso deseje o mínimo, coloque para classificar em ordem crescente.

=SORTN(I3:I;1;0;1;FALSO)

Script para Registro de Edições no Google Sheets

 

Script para Registro de Edições no Google Sheets


Objetivo do Script

Este script tem como função registrar automaticamente a data, hora e a coluna editada na aba "Tratativa" de uma planilha do Google Sheets. O registro é feito em uma coluna específica (coluna 51) para facilitar a identificação das alterações realizadas.

Embora seja possível acessar essas informações clicando com o botão direito sobre a célula editada e verificando o histórico de edições, o script oferece uma forma prática e consolidada de acompanhar diretamente, em uma coluna separada, o que foi alterado nas outras colunas. Serve para um cenário com várias pessoas alterando a planilha.


Configuração Inicial

1. Inserção do Script

  1. Abra a planilha Google Sheets.
  2. Acesse Extensões > Apps Script.
  3. Substitua qualquer conteúdo existente pelo código abaixo:

javascript

function onEdit(e) {

    const sheet = e.source.getActiveSheet(); // Obtém a aba onde a edição ocorreu
    const sheetName = "Tratativa"; // Nome da planilha específica
    const range = e.range; // Local da edição
    const col = range.getColumn(); // Coluna editada
    const row = range.getRow(); // Linha editada
    const timestampCol = 51; // Coluna onde será registrado o horário e a coluna usada

    // Verifica se a edição ocorreu na planilha "Tratativa", entre as colunas 1 e 50, exceto na linha 1
    if (sheet.getName() === sheetName && col >= 1 && col <= 50 && row > 1) {
    const currentDate = new Date();
    const editedColumn = `Coluna ${col}`;
    const timestamp = currentDate.toLocaleString(); // Horário formatado

    // Concatena o horário e a coluna editada
    const logMessage = `${timestamp} - ${editedColumn}`;
    sheet.getRange(row, timestampCol).setValue(logMessage); // Escreve na coluna 51
    }
}

  1. Salve o projeto (ex.: "LogEdit").
  2. O script está pronto para uso.

Funcionamento do Script

  1. Monitoramento de Edições
    • O script é acionado automaticamente sempre que uma célula é editada na aba "Tratativa".
    • Ele monitora edições nas colunas 1 a 50 (A até AX) e ignora alterações na linha 1.
  2. Registro do Log
    • Para cada edição válida:
      • O script captura a data e hora atuais.
      • Identifica a coluna editada.
      • Registra uma mensagem no formato DD/MM/AAAA HH:MM - Coluna X na coluna 51 (coluna AY) da mesma linha.

Personalizações

1. Alterar o Nome da Aba

Caso deseje usar o script em outra aba, altere a linha:

javascript

const sheetName = "NovoNomeDaAba";

2. Modificar o Alcance de Colunas Monitoradas

Para ajustar as colunas que serão monitoradas, edite a condição:

javascript

if (col >= X && col <= Y)

Substitua X e Y pelos números das colunas desejadas.

3. Alterar a Coluna de Registro

Para que o log seja registrado em outra coluna, modifique o valor da variável timestampCol:

javascript

const timestampCol = NovaColuna;


Restrições

  • Aba específica: Apenas monitora a aba chamada "Tratativa".
  • Edições manuais: Não registra alterações feitas por fórmulas ou scripts automáticos.

Exemplo de Uso

  1. Um usuário edita a célula B3 da aba "Tratativa".
  2. O script registra na célula AY3:

13/01/2025 14:35 - Coluna 2


Resolução de Problemas

1. O script não está funcionando.

  • Certifique-se de que a aba tem o nome "Tratativa".
  • Verifique se as edições estão ocorrendo nas colunas 1 a 50 e abaixo da linha 1.

2. O log aparece na coluna errada.

  • Confirme o valor da variável timestampCol.


terça-feira, 13 de fevereiro de 2024

Dust always finds a way to get into your CPAP. (A poeira sempre acha um modo de entrar dentro do seu CPAP.)

A poeira sempre acha um modo de entrar dentro do seu CPAP. 

Veja o meu CPAP Resmed S10 Autoset, no qual fiz limpeza há 3 anos em setembro de 2020.

As fotos mostram a quantidade de poeira acumulada antes da limpeza. É assustador pensar que respiramos isso durante 6 a 8 horas todas as noites. 








Agora veja o resultado final.







.

Acredito que a frequência recomendada para essa limpeza seja de pelo menos a cada dois anos, apesar de trocar os filtros mensalmente. No entanto, mesmo com essa precaução, a poeira sempre parece encontrar um caminho para entrar.

Esse problema não é exclusivo da Resmed, meu Philips Respironics também apresentava essa questão, onde a poeira encontrava sempre uma brecha para se infiltrar.

Portanto, é essencial não negligenciar a limpeza interna do aparelho. Existem empresas especializadas que oferecem esse serviço. Garantir a limpeza regular do CPAP é fundamental para uma terapia mais segura e eficaz, prevenindo sintomas como coriza e outras enfermidades causadas pela presença de poeira.



sexta-feira, 26 de junho de 2020

Comandos para Executar no CMD em modo admin

"Neste tópico trataremos de abordar os comandos do menu executar. Com alguma pratica muito mais rápido do que acessar determinada tarefa.
A tecla de atalho é Windows (nos teclados modernos uma janela voadora, entre as teclas CTRL e ALT) + R, ou Iniciar e clicar no Ícone ao lado de Todos os programas."

São 117 comandos para executar no cmd em modo admin ou no menu executar.

Adicionar/Remover Programas
Digite: appwiz.cpl

Ferramentas Administrativas
Digite: control admintools

Atualizações Automáticas
Digite: wuaucpl.cpl

Assistente para Transferência de Arquivos Bluetooth
Digite: fsquirt
 
Compartilhamentos DDE
Digite: ddeshare

Gerenciador de Dispositivos
Digite: devmgmt.msc

Painel de Controle do Direct X (se instalado)
Digite: directx.cpl

Ferramenta de Diagnóstico do Direct X
Digite: dxdiag

Limpeza de Disco
Digite: cleanmgr
 
Verificação de Assinatura de Arquivo
Digite: sigverif

Findfast
Digite: findfast.cpl

Opções de Pasta
Digite: control folders

Fontes
Digite: control fonts

Pasta Fontes
Digite: fonts
 
Conexões de Rede
Digite: control netconnections
Digite: ncpa.cpl

Assistente para Configuração de Rede
Digite: netsetup.cpl
Propriedades de Internet
Digite: inetcpl.cpl

Configuração do IP (Exibe informações completas da Configuração da Conexão)
Digite: ipconfig /all

Configuração do IP (Exibe o conteúdo da Cache DNS Resolver)
Digite: ipconfig /displaydns

Configuração do IP (Depura a Cache DNS Resolver)
Digite: ipconfig /flushdns

Configuração do IP (Libera o endereço IP para o adaptador especificado)
Digite: ipconfig /release

Configuração do IP (Renova o endereço IP para o adaptador especificado)
Digite: ipconfig /renew

Configuração do IP (Atualiza todas as concessões DHCP e registra novamente nomes DNS)
Digite: ipconfig /registerdns

Configuração do IP (Exibe todas as identificações de classe DHCP permitidas para o adaptador)
Digite: ipconfig /showclassid

Configuração do IP (Modifica a identificação de classe DHCP)
Digite: ipconfig /setclassid
 
Usuários e Grupos Locais
Digite: lusrmgr.msc

Logoff do Windows
Digite: logoff

Bate-Papo
Digite: winchat

Propriedades de Mouse
Digite: control mouse
Digite: main.cpl
 
Impressoras e Aparelhos de Fax
Digite: control printers

Pasta Impressoras
Digite: printers

Editor de Caracteres Particulares
Digite: eudcedit
 
Editor do Registro
Digite: regedit
Digite: regedit32

Conexão de Área de Trabalho Remota
Digite: mstsc

Armazenamento Removível
Digite: ntmsmgr.msc

Tarefas Agendadas
Digite: control schedtasks

Central de Segurança do Windows
Digite: wscui.cpl

Serviços
Digite: services.msc

Pastas Compartilhadas
Digite: fsmgmt.msc

Propriedades de Som e Dispositivos de Áudio
Digite: mmsys.cpl

Utilitário de Rede para Clientes do SQL Server
Digite: cliconfg

Editor de Configuração do Sistema
Digite: sysedit

Utilitário de Configuração do Sistema
Digite: msconfig

Propriedades do Sistema
Digite: sysdm.cpl

Gerenciador de Tarefas
Digite: taskmgr

Cliente de Telnet
Digite: telnet
 
Desligar o Windows
Digite: shutdown

System File Checker (Pesquisa imediatamente todos os APS)
Digite: sfc /scannow

System File Checker (Pesquisa imediatamente todos os APS a cada inicialização)
Digite: sfc /scanboot

Gerenciador de Utilitários
Digite: utilman

Firewall do Windows
Digite: firewall.cpl

System File Checker (Restaura a pesquisa à configuração padrão)
Digite: sfc /revert
Lente de Aumento
Digite: magnify

Janela das Ligações de Rede
Digite: ncpa.cpl

Windows Management Infrastructure
Digite: wmimgmt.msc

System File Checker (Pesquisa imediatamente todos os APS uma vez na próxima inicialização)

Gerenciamento de Disco
Digite: diskmgmt.msc

Gerenciador de Partição
Digite: diskpart

Propriedades de Vídeo
Digite: control desktop
Digite: desk.cpl
 
Propriedades de Vídeo (com a aba Aparência já selecionada)
Digite: control color

Dr. Watson
Digite: drwtsn32

Gerenciador de Verificação de Driver
Digite: verifier

Visualizador de Eventos
Digite: eventvwr.msc

Opções de Acessibilidade
Digite: access.cpl

Assistente para Adicionar Hardware
Digite: hdwwiz.cpl

Certificados
Digite: certmgr.msc

Mapa de Caracteres
Digite: charmap

Visualizador da Área de Transferência
Digite: clipbrd

Prompt de Comando
Digite: cmd

Serviços de Componentes
Digite: dcomcnfg

Propriedades de Teclado
Digite: control keyboard

Configurações Locais de Segurança
Digite: secpol.msc
 
Gerenciador de Objetos - Pacote
Digite: packager

Administrador de Fonte de Dados ODBC
Digite: odbccp32.cpl
 
Opções de Telefone e Modem
Digite: telephon.cpl

Propriedades de Opções de Energia
Digite: powercfg.cpl

Solicitações do Operador de Armazenamento Removível
Digite: ntmsoprq.msc

Conjunto de Diretivas Resultantes (XP Prof)
Digite: rsop.msc

Scanners e Câmeras
Digite: sticpl.cpl

Serviços de Componentes
Digite: comexp.msc

System File Checker (Limpa o cache do arquivo)
Digite: sfc /purgecache

Definições locais de segurança
Digite: secpol.msc

Performance Monitor
Digite: perfmon.msc

 
Resultant Set of Policies
Digite: rsop.msc
 
Serviços
Digite: services.msc
 
Contas de Usuário
Digite: control userpasswords2

Gerenciador de Usuários do Wint Server (somente windows2003 server)
Digite: usrmgr

Instalador do Active Directory (somente windows server)
Digite: dcpromo

Gerenciador de Tarefas
Digite: taskmgr

Pastas Partilhadas
Digite: fsmgmt.msc

Politicas de Grupo
Digite: gpedit.msc

Utilizadores Locais e Grupos
Digite: lusrmgr.msc

Desfragmentador de Disco
Digite: dfrg.msc
 
Visualizador de Eventos
Digite: eventvwr.msc

Protegendo Banco de Dados de Contas do Windows XP
Digite: syskey

Conectar-se  ao Site do Windows Update
Digite: wupdmgr

Introdução ao Windows XP
Digite: tourstart
 
Gestão do Computador
Digite: compmgmt.msc

Gestão de Discos
Digite: diskmgmt.msc
 
System File Checker (Define o tamanho de cache)
Digite: sfc /cachesize=x

Gestor de Dispositivos
Digite: devmgmt.msc
 
Password Properties
Digite: password.cpl

Desempenho
Digite: perfmon.msc
Digite: perfmon

Opções Regionais e de Idioma
Digite: intl.cpl

Contas de Usuário
Digite: nusrmgr.cpl

Controladores de Jogo
Digite: joy.cpl

Group Policy Editor (XP Prof)
Digite: gpedit.msc

Iexpress Wizard
Digite: iexpress

Serviço de Indexação
Digite: ciadv.msc

Check Disk
Digite: chkdsk

Gerenciamento do Computador
Digite: compmgmt.msc

Propriedades de Data e Hora
Digite: timedate.cpl

Desfragmentador de Disco
Digite: dfrg.msc




quinta-feira, 13 de setembro de 2018

Erro ao emitir Comprovante de Inscrição de 2ª via CPF

Erro ao emitir Comprovante de Inscrição de 2ª via CPF

Esses erros abaixo ocorrem sempre nos sites da receita caso não tenha a cadeia de certificados instalada ou atualizada! 
"Invasores podem estar tentando roubar suas informações de www.receita.fazenda.gov.br (por exemplo, senhas, mensagens ou cartões de crédito). Saiba maisNET::ERR_CERTIFICATE_TRANSPARENCY_REQUIREDO servidor apresentou um certificado que não foi divulgado publicamente usando a política de Transparência dos certificados. Esse é um requisito para alguns certificados, a fim de garantir que eles sejam confiáveis e protejam você contra invasores.Ir para www.receita.fazenda.gov.br (não seguro)"
"O servidor www.receita.fazenda.gov.br usa um certificado de segurança inválido.O certificado não é considerado confiável porque o certificado do expedidor é desconhecido.(Código do erro: sec_error_unknown_issuer)"

No site do ITI (http://www.iti.gov.br/icp-brasil/) podemos encontrar os links e instruções para atualizar os certificados.

Veja o passo a passo para atualizar o Firefox e acessar normalmente a segunda via de CPF e outros sites da Receita Federal. No Chrome ainda não consegui resolver.

Passo 1: Acesse http://www.receita.fazenda.gov.br/publico/Certificados/icpbrasilv2.cer para baixar a cadeia "Autoridade Certificadora Raiz Brasileira v2". O browser disponibilizará duas opções, selecione todas para proceder a instalação, clique no botão 'Ok';

Passo 2: Acesse http://www.receita.fazenda.gov.br/publico/Certificados/acrfbv3.cer para baixar a cadeia "Autoridade Certificadora Secretaria da Receita Federal do Brasil v3". O browser disponibilizará duas opções, selecione todas para proceder a instalação, clique no botão 'Ok';

Passo 3: Acesse http://www.receita.fazenda.gov.br/publico/Certificados/acserprorfbv3.cer para baixar a cadeia "Autoridade Certificadora SERPRORFB v3". O browser disponibilizará duas opções, selecione todas para proceder a instalação, clique no botão 'Ok';


Feche e abra o seu Mozilla Firefox, e acesse normalmente o site do CPF (Aqui funcionou tanto no windows quanto no Linux).
O Portal do CPF e de boa parte da Receita funcionará normalmente.

Referências: