Excel macros multiple columns to a single column and delete a repetitive range of columns.

When I’m not fighting the war on bad call centers I try to assist my better half with some of her work.  Lately this has involved a lot of spreadsheets and data manipulation, which is something I enjoy as it’s a bit different than what I do day-to-day. The latest issue was turning a dataset from rows to columns. Transpose, you say? I wouldn’t be posting this if it would have been that easy.  The dataset looked like this:

A 7 2 9
B 3 4 5
A 9 9 2
C 3 2 3
C 1 3 9
B 1 2 2

The good thing is that the data had the same number or rows for every entity, in my case 10 rows belonged to each entity.  The bad news is that transposing got me from 10 rows to 10 columns.  The first thing I did was sort the rows by entity to group all entity rows together and did a transpose. 

AABBCC
793131
224223
925239

Next, I went to Google for some help and came up with the following macro:

Sub ManyColumnsTo1()
Dim LR As Long, index1 As Long, index2 As Long, x As Long, y As Long, z As Long, maxCol As Long
x = 2
y = 10
z = 1
maxCol = 50
‘ First entry
    For index1 = x To y
        LR = Cells(Rows.Count, index1).End(xlUp).Row
        Range(Cells(1, index1), Cells(LR, index1)).Copy
        Cells(Rows.Count, z).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
    Next index1
‘ All others
For index2 = x To maxCol
    x = x + 10
    y = y + 10
    z = z + 10
    For index1 = x To y
        LR = Cells(Rows.Count, index1).End(xlUp).Row
        Range(Cells(1, index1), Cells(LR, index1)).Copy
        Cells(Rows.Count, z).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
    Next index1
Next index2
On Error Resume Next
Columns("A").SpecialCells(xlCellTypeBlanks).Delete shift:=xlShiftUp
On Error GoTo 0
End Sub

This macro will copy a range of columns into one column.  The code will copy y columns, starting with column x into column z.  The for loop will then cycle through maxCol to ensure you get every column in your dataset. Which then gives us the following.

AABBCC
793131
224223
925239
A  B  C
9  1  1
2  2  3
2  2  9

Ultimately, this is what I wanted, but I had a lot of unnecessary columns in the middle which we needed to get rid of.  Enter a column delete macro:

Sub DeleteColumn()
    Dim startCol As Long, endCol As Long, maxCol As Long
    startCol = 1
    endCol = 10
    maxCol = 50
    For i = startCol To maxCol
        ActiveSheet.Range(Cells(1, startCol + 1), Cells(10, endCol)).EntireColumn.Delete
        startCol = startCol + 1
        endCol = endCol + 1
    Next i
End Sub

This macro will delete a range of columns.  The deletion will start with startCol through endCol for maxCol.

Which gives us the following.

ABC
733
242
953
ABC
911
223
229

One more transpose and some cleanup and we’re good to go.  Hope this helps others.

david

Find the total number of attributes and skillgroups in an agent team.

With the new 9.x release and the popularity of Finesse I’m seeing a lot more cases of latency/timeouts from the Finesse desktop.  A lot of these have been attributed to network issues, but I still feel that Finesse has been made way too sensitive to the network.  Makes me yearn for the good old days of CTIOS.  One thing which came up recently is to find out how many skillgroups and attributes/PQs an agent team has.  As the SRND alludes that having too many SG/PQs (>50) in a team will cause performance issues.  The following two queries will give you the total count of unique attributes and skillgroups in an agent team, all you need to do is know your agent team ID.

Attributes

SELECT count(distinct(awdb.dbo.t_Agent_Attribute.AttributeID)), COUNT(awdb.dbo.t_Agent_Attribute.AttributeID) FROM awdb.dbo.t_Agent_Attribute INNER JOIN t_Agent_Team_Member ON (t_Agent_Attribute.SkillTargetID = t_Agent_Team_Member.SkillTargetID)
WHERE t_Agent_Team_Member.AgentTeamID = ‘XYZ’

Skillgroups

SELECT count(distinct(awdb.dbo.t_Agent_Attribute.AttributeID)), COUNT(awdb.dbo.t_Agent_Attribute.AttributeID) FROM awdb.dbo.t_Agent_Attribute INNER JOIN t_Agent_Team_Member ON (t_Agent_Attribute.SkillTargetID = t_Agent_Team_Member.SkillTargetID)
WHERE t_Agent_Team_Member.AgentTeamID = ‘XYZ’

david

Uniform Server (WAMP) and Laravel

I’ve been playing around with Laravel a bit just to try it out and figured I would bundle this with a light weight WAMP solution.  Went through the install and everything worked fine exept when it came time to migrate my database:

C:\UniServer\laravel1>php artisan migrate
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Call to undefined function Symfony\\Component\\Console\\mb_detect_encoding()","file":"C:\\UniServer\\laravel1\\vendor\\symfony\\console\\Symfony\\Component\\Console\\Application.php","line":721}}

Took some googling around, but the solution was pretty easy.  Assuming you’re running a stock and fairly recent version of Uniform, all you have to do is modify your php-cli.ini file and add:

extension=php_mbstring.dll

Restart Apache and presto.

~david

Determine Busy Hour From UCCE TCD

The busy call hour is a valuable piece of information in order to forecast how many phone lines you’re going to need as well as how many agents, however getting this information can be difficult and then ensuring that it’s accurate is a whole other matter.  Recently I ran into an issue and I wanted to get the busy hour for myself.  I don’t have any access to the provider records, so all I have is the TCD table… when all you have is a hammer the whole world looks like a nail.

First, I turned to Google, which pointed me to how to get the calls per second.  This is good information as you would imagine that the highest CPS would fall during the busiest hour.  The query mentioned is:

   1:  SELECT Time=CONVERT(char,DateTime,108), CPS=CONVERT(decimal(5,2), RouteCallDetailTo5/300.0)
   2:  FROM t_Logger_Meters
   3:  WHERE DateTime BETWEEN '01/01/2013 00:05' AND '01/02/2013 23:59'
   4:  ORDER BY Time

Second, I opened up SQL Studio and began running some queries and came up with the following:

   1:  SELECT DATEPART(HOUR, DateTime) AS [Hour of Day], COUNT(distinct RouterCallKey) 
   2:  FROM t_Termination_Call_Detail
   3:  WHERE DateTime > '01/01/2013 06:00' and DateTime < '01/01/2013 20:00'
   4:  GROUP BY DATEPART(HOUR, DateTime)
   5:  ORDER BY DATEPART(HOUR, DateTime)
 
This query assumes that every new call will receive its own RCK, so we can use this as our counter.  Next, the query breaks up the select statement by hour.  Finally, put the output in Excel and make a pretty graph.  Some caveats, this is very generic and over counts.  It doesn’t care about transfers, outbound calls, etc.  Next, the query doesn’t look at the routing client, so if you have a lot of UCM generated calls this might not help you very much.
~david

Download Huge Cisco Finesse Logs

You might have seen my previous post on how to use wget to download Finesse logs.  This works like a champ, however wget has an issue with large files (> 1GB), so I had to come up with a new solution as some of our logs were in the 2-3 GB range.  This is where mulk comes in.  This little tool runs great on Windows and has had no problems with any file size.  Download it and using the command prompt do the following:

mulk-0.6.0>mulk -v –user=<user> –password=<password> -d=0 http://10.89.66.102/finesse/logs/

Updated 01/13/2014:

mulk.exe http://10.0.0.1/finesse/logs/ –v –-user <user> –-password <password> –d 0

The only major drawback from mulk is that it doesn’t have a setting to just download the latest files, but if you think about it, you could probably use wget and mulk in conjunction to create the perfect too to save you some time.

~david

Faster Way to Download Cisco Finesse Logs Using Wget

You should check out my other post on dealing with large Finesse logs.

If you’ve had the ‘pleasure’ of troubleshooting Cisco’s newest desktop offering Finesse you know that getting logs from it is a bit of a pain.  While sitting on a TAC call I came up with this approach which has made the process a bit easier.  This solution is not perfect, but it’s better than having to click, right click, download as…

  1. First ensure you have access to the Finesse logs directory /finesse/logs/">/finesse/logs/">/finesse/logs/">http://<finessehost>/finesse/logs/, you’ll need your administrator username and password.
  2. Once you can confirm you can login, download GNU Wget for you operating system.
  3. Open a command prompt and run the following command:

wget –-http-user=<user> –-http-password=<password> –r –np http://<finessehost>/finesse/logs/ \

This command will download every log file, it might take a while depending on your setup, but ultimately this first download is the most painful and afterwards you can use a slightly different command to just download the newest logs.

wget –-http-user=<user> –-http-password=<password> –r –np –N http://<finessehost>/finesse/logs/ \

This command will only download the newest logs which you can then quickly grab.  If you want to be a badass you could then come up with a way to automatically only zip the new ones.

Hope this helps.

~david

Check Windows 2003 Service Status Remotely

At my current project there are times where I have to restart a bunch of servers at a time.  On these servers there is a particular service which I want to make sure it has started and is running.  There’s the sc.exe command which allows your stop, start, query service statuses from the command prompt.

Here’s a small batch script I wrote which runs through a list of server names and prints out the status.  You’ll need two files.

First file includes the server names and is called Servers.txt:

Server1
Server2
Server3
Server4

The second file just needs to be saved as a .bat file.

   1:  REM ***Start Here***
   2:  Echo Off
   3:  Setlocal EnableDelayedExpansion
   4:  FOR /F "Tokens=*" %%L IN (Servers.txt) DO (
   5:     SET ServerName=
   6:     SET ServerName=%%L
   7:     sc.exe \\!ServerName! query <Your Service Name> > Results.txt
   8:     ECHO !ServerName!
   9:     Find /i "RUNNING" < Result.txt
  10:     Find /i "STOPPED" < Result.txt
  11:  )
  12:  IF EXIST Results.txt DEL Result.txt
  13:  REM ***END HERE***

This script will print out the server name and if your particular service is RUNNING or STOPPED.  You can expand this as you would like.

~david

Cisco CVP 7.x No Session Error

Ran into this small issue while trying to release a new CVP application for one of my customers.  Everything was working on the test CVP server, but when it was time to put it on the production CVP servers I received the following error:

24769204: 10.10.90.115: Jul 19 2012 04:51:33.406 -0500: %CVP_7_0_IVR-3-CALL_ERROR: CALLGUID=9EA62E12100001383796F8800A0A5A73 DNIS=8111111111122013 CVP VXML Server encountered a No Session error – URL: http://CVP03:7000/CVP/en-us/../Server?DNIS=18775555555&InsuranceOpen=0&CallType=InsuranceCall&ANI=+1555555555

&PrimaryLocation=Birm&callid=9EA62E12100001383796F8800A0A5A73&

application=Insurance&RoutingClient=CVP03 (Client: 10.10.90.123) [id:3023]
24769205: 10.10.90.115: Jul 19 2012 04:51:33.406 -0500: %CVP_7_0_IVR-3-CALL_ERROR: RunScript Error from 10.10.90.123 [CVP_NO_SESSION_ERROR(44)] CALLGUID: 9EA62E12100001383796F8800A0A5A73 DNIS=8111111111122013 {VRUScriptName: ‘GS,Server,V’ ConfigParam: ”} [id:3023]

Now, this stumped me for a bit as it made not sense as to why this wouldn’t work as all the servers are exactly the same.  After a bit more digging I noticed that the application wasn’t deployed in CVP03.  Then I noticed that the application wasn’t deployed on any of the CVP servers.  Back in business.

When you don’t write CVP applications enough you forget the little things.

~david

Dialed Number, Call Type, and Script Association

Found this gem on the Cisco Support Forums today and I just had to blog about it because it’s awesome!  The OP is looking for a way an easy way to find which ICM dialed numbers are associated with which call type and which call type is associated with which script.  The end result is this work of art:

SELECT dn.DialedNumberString, dn.EnterpriseName AS Dialed_Number_Name, ct.EnterpriseName AS Call_Type_Name, ms.EnterpriseName AS Script_Name
FROM Dialed_Number dn
LEFT OUTER JOIN Dialed_Number_Map dnm ON dn.DialedNumberID = dnm.DialedNumberID
LEFT OUTER JOIN Call_Type ct ON dnm.CallTypeID = ct.CallTypeID
LEFT OUTER JOIN Call_Type_Map ctm ON ct.CallTypeID = ctm.CallTypeID
LEFT OUTER JOIN Master_Script ms ON ctm.MasterScriptID = ms.MasterScriptID

Put this in a CUIC report and you can give the users the power to confirm where things are supposed to route without having to ask you. :-)

~david