Home > Blockchain >  VBA Import data and then find matching entry (Concurrent Licence data file)
VBA Import data and then find matching entry (Concurrent Licence data file)

Time:06-10

I have written the following code to import data from a Concurrent licence file on a server. The next part I would like to then go through the entries and find the OUT: entry and then look down the sheet and find the corresponding IN: so that I can see how long the person had the licence out for. I just don't know how to do it. I think if I use an Array it will get ugly as some of these logs are months long:

Option Explicit
Sub ImportAppData(importTable, importFile)
Dim WB2 As Workbook
Dim WS2 As Worksheet
Dim lRow, lCol, x As Long
Dim preDate, postDate As Date
Dim strAPPStats() As String
Dim strTimeStamp

lCol = 3
WB1.Sheets.Add
Set WS2 = WB1.ActiveSheet
WS2.Name = "App_Logs_Import"
Set WB2 = Workbooks.Open(importFile)
WB2.Sheets(1).Cells.Copy WS2.Cells
WB2.Close False

lRow = 1
lCol = 1

Do While WS2.Cells(lRow, lCol) <> ""
    If InStr(WS2.Cells(lRow, lCol), "IN:") = 0 _
        And InStr(WS2.Cells(lRow, lCol), "OUT:") = 0 _
        And InStr(WS2.Cells(lRow, lCol), "TIMESTAMP") = 0 _
        And InStr(WS2.Cells(lRow, lCol), "UNSUPPORTED:") = 0 _
        And InStr(WS2.Cells(lRow, lCol), "DENIED:") = 0 Then
        WS2.Cells(lRow, lCol).EntireRow.Delete
        lRow = lRow - 1
    Else
        If Left(WS2.Cells(lRow, lCol), 1) = " " Then
            WS2.Cells(lRow, lCol) = LTrim(WS2.Cells(lRow, lCol))
        End If
    End If
    lRow = lRow   1
Loop

lRow = 1
lCol = 1

Do While strTimeStamp = 0
    If InStr(WS2.Cells(lRow, lCol), "TIMESTAMP") <> 0 Then
        strTimeStamp = us2ukDate(WS2.Cells(lRow, lCol))
        strTimeStamp = Format$(strTimeStamp, "dd/mmm/yyyy")
        WS2.Cells(lRow, lCol).EntireRow.Delete
        lRow = lRow - 1
    Else
        WS2.Cells(lRow, lCol).EntireRow.Delete
        lRow = lRow - 1
        
    End If
    lRow = lRow   1
Loop

Do While WS2.Cells(lRow, lCol) <> ""
    If InStr(WS2.Cells(lRow, lCol), "TIMESTAMP") <> 0 Then
        strTimeStamp = us2ukDate(WS2.Cells(lRow, lCol))
        strTimeStamp = Format(strTimeStamp, "dd/mmm/yyyy")
        WS2.Cells(lRow, lCol).EntireRow.Delete
        lRow = lRow - 1

    ElseIf InStr(WS2.Cells(lRow, lCol), "UNSUPPORTED:") <> 0 Then
        strAPPStats = Split(WS2.Cells(lRow, lCol), " ")
        For x = LBound(strAPPStats) To UBound(strAPPStats)
            If x = 0 Then
                WS2.Cells(lRow, lCol) = Format$(strTimeStamp & " " & strAPPStats(x), "dd/mmm/yyyy HH:MM")
                lCol = lCol   1
            ElseIf x = 2 Or x = 3 Or x = 8 Then
                WS2.Cells(lRow, lCol) = strAPPStats(x)
                lCol = lCol   1
            End If
        Next
    ElseIf InStr(WS2.Cells(lRow, lCol), "OUT:") <> 0 Then
        strAPPStats = Split(WS2.Cells(lRow, lCol), " ")
        For x = LBound(strAPPStats) To UBound(strAPPStats)
            If x = 0 Then
                WS2.Cells(lRow, lCol) = Format$(strTimeStamp & " " & strAPPStats(x), "dd/mmm/yyyy HH:MM")
                lCol = lCol   1
            ElseIf x = 2 Or x = 3 Or x = 4 Then
                WS2.Cells(lRow, lCol) = strAPPStats(x)
                lCol = lCol   1
            End If
        Next
    ElseIf InStr(WS2.Cells(lRow, lCol), "IN:") <> 0 Then
        strAPPStats = Split(WS2.Cells(lRow, lCol), " ")
        For x = LBound(strAPPStats) To UBound(strAPPStats)
            If x = 0 Then
                WS2.Cells(lRow, lCol) = Format$(strTimeStamp & " " & strAPPStats(x), "dd/mmm/yyyy HH:MM")
                lCol = lCol   1
            ElseIf x = 2 Or x = 3 Or x = 4 Then
                WS2.Cells(lRow, lCol) = strAPPStats(x)
                lCol = lCol   1
            End If
        Next
    ElseIf InStr(WS2.Cells(lRow, lCol), "DENIED:") <> 0 Then
        strAPPStats = Split(WS2.Cells(lRow, lCol), " ")
        For x = LBound(strAPPStats) To UBound(strAPPStats)
            If x = 0 Then
                WS2.Cells(lRow, lCol) = Format$(strTimeStamp & " " & strAPPStats(x), "dd/mmm/yyyy HH:MM")
                lCol = lCol   1
            ElseIf x = 2 Or x = 3 Or x = 4 Or x = 5 Then
                WS2.Cells(lRow, lCol) = strAPPStats(x)
                lCol = lCol   1
            End If
        Next
    End If
    lCol = 1
    lRow = lRow   1
Loop
End Sub

What I want to do next is add a column and then work through the sheet looking for the first entry (OUT:) and then match it to the IN: (the same date, ignore the time) entry for the same user and then move them both to the same line.

Data I'm importing is:

 4:39:18 (*****) TIMESTAMP 3/14/2022
 8:24:12 (*****) OUT: "Application1" User1@Win10PC1
 8:24:12 (*****) OUT: "Application2" User1@Win10PC1
 8:49:23 (*****) OUT: "Application1" User2@Win10PC2
 8:49:23 (*****) OUT: "Application2" User2@Win10PC2
 8:59:40 (*****) OUT: "Application1" User3@Win10PC3
 8:59:40 (*****) OUT: "Application3" User3@Win10PC3
13:39:22 (*****) IN: "Application1" User1@Win10PC1
13:39:22 (*****) IN: "Application2" User1@Win10PC1
13:53:43 (*****) OUT: "Application1" User1@Win10PC1
13:53:44 (*****) OUT: "Application2" User1@Win10PC1
14:41:53 (*****) OUT: "Application1" User3@Win10PC3
14:55:24 (*****) IN: "Application1" User3@Win10PC3
17:48:59 (*****) IN: "Application1" User2@Win10PC2
17:48:59 (*****) IN: "Application2" User2@Win10PC2
18:07:09 (*****) IN: "Application1" User3@Win10PC3
18:07:09 (*****) IN: "Application3" User3@Win10PC3
18:32:53 (*****) IN: "Application1" User1@Win10PC1
18:32:54 (*****) IN: "Application2" User1@Win10PC1
20:49:46 (*****) OUT: "Application1" User1@Win10PC1
20:49:47 (*****) OUT: "Application2" User1@Win10PC1
23:29:03 (*****) IN: "Application2" User1@Win10PC1
23:29:03 (*****) IN: "Application1" User1@Win10PC1

The code imports the data into the sheet like this :

-------------------------------------------------------------
| 14/Mar/2022 8:24 | OUT: | "Application1" | User1@Win10PC1 |
| 14/Mar/2022 8:24 | OUT: | "Application2" | User1@Win10PC1 |
| 14/Mar/2022 8:49 | OUT: | "Application1" | User2@Win10PC2 |
| 14/Mar/2022 8:49 | OUT: | "Application2" | User2@Win10PC2 |
-------------------------------------------------------------

and what I trying to get to is:

------------------------------------------------------------------------
|       Out        |        In         |  Application |     User       |
------------------------------------------------------------------------
| 14/Mar/2022 8:24 | 14/Mar/2022 13:39 | Application1 | User1@Win10PC1 |
| 14/Mar/2022 8:49 | 14/Mar/2022 17:48 | Application1 | User2@Win10PC2 |
------------------------------------------------------------------------

------------------------------------------------------------------------
|       Out        |        In         |  Application |     User       |
------------------------------------------------------------------------
| 14/Mar/2022 8:24 | 14/Mar/2022 13:39 | Application2 | User1@Win10PC1 |
| 14/Mar/2022 8:49 | 14/Mar/2022 17:48 | Application2 | User2@Win10PC2 |
------------------------------------------------------------------------

------------------------------------------------------------------------
|       Out        |        In         |  Application |     User       |
------------------------------------------------------------------------
| 14/Mar/2022 8:59 | 14/Mar/2022 18:07 | Application3 | User3@Win10PC3 |
------------------------------------------------------------------------

TYIA

EDIT:

Now found this situation:

 8:59:40 (*****) OUT: "Application1" user2@Win10PC2
 8:59:40 (*****) OUT: "Application2" user2@Win10PC2
14:41:53 (*****) OUT: "Application1" user2@Win10PC2
14:55:24 (*****) IN: "Application1" user2@Win10PC2
18:07:09 (*****) IN: "Application1" user2@Win10PC2
18:07:09 (*****) IN: "Application2" user2@Win10PC2

CodePudding user response:

I've done this with similar log files before and it's usually easier to read the file directly in one pass instead of parsing it after opening in Excel.

For example (just dealing with OUT and IN to simplify and because I wasn't sure what you're doing with the other flags)

EDIT - handle multiple checkouts of the same license per user.

Sub ImportLog()
    
    Const ForReading = 1
    Dim f As Object, wsResults As Worksheet, dt As String, l As String, arr
    Dim dict As Object, rw As Long, k, ts, app, usr, action, col As Collection
    
    Set dict = CreateObject("scripting.dictionary") 'for tracking sheet row numbers
    
    Set wsResults = ThisWorkbook.Sheets("Logs")
    
    wsResults.UsedRange.Offset(1, 0).ClearContents 'leave the headers
    
    rw = 1
    dt = "{no date}" 'until we hit a TIMESTAMP line...
    
    Set f = CreateObject("scripting.filesystemobject"). _
                   opentextfile("C:\Temp\log.txt", ForReading)
                   
    Do While Not f.AtEndOfStream
        l = Trim(f.readline())      'read a line
        If Len(l) > 0 Then          'any data?
            arr = Split(l, " ")     'split to array
            
            If l Like "*TIMESTAMP*" Then
                dt = GetDate(arr(UBound(arr))) 'pick up the date?
            Else
                ts = dt & " " & Trim(arr(0))
                action = arr(2) 'OUT/IN/etc
                app = Replace(arr(3), """", "")
                usr = arr(4)
                
                k = app & "~~" & usr 'user app combination
                Select Case action
                    Case "OUT:"      'checking out: record info and row number
                        rw = rw   1
                        'collection allows for multiple "OUT" rows per key
                        If Not dict.exists(k) Then Set dict(k) = New Collection
                        dict(k).Add rw 'add row number for this check-out to the collection
                        
                        With wsResults.Rows(rw)
                            .Cells(1).Value = ts  'add checkout info...
                            .Cells(3).Value = app
                            .Cells(4).Value = usr
                        End With
                    Case "IN:"
                        If dict.exists(k) Then 'is there a check-out for this app user?
                            Set col = dict(k)
                            wsResults.Cells(col(1), 2).Value = ts 'add the "IN" time
                            col.Remove 1  'remove the row we just closed from the collection
                            If col.Count = 0 Then dict.Remove k 'all rows closed for this key
                        Else
                            'no corresponding "out" record...
                            Debug.Print "No match: " & l
                        End If
                End Select
            End If 'TIMESTAMP?
        End If
    Loop
    f.Close
    
    'any licenses checked out but not checked in?
    If dict.Count > 0 Then
        Debug.Print "Unclosed checkouts"
        For Each k In dict
            Debug.Print , k
        Next k
    End If
    
End Sub

Function GetDate(txt)
    Dim arr
    arr = Split(Trim(txt), "/")
    GetDate = arr(1) & "/" & arr(0) & "/" & arr(2) 'swap day/month
End Function

Output from your sample data: you can tidy it up with a bit of sorting/grouping
enter image description here

  • Related