Increasing efficiency and logic for my script that reads text messages and extracts information for action/response. Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
magicinmath
Posts: 162
Joined: 12 Apr 2017, 23:03

Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

28 May 2017, 22:54

My colleagues and I take live scheduling requests from the CSR agent supervisors. The purpose of this script is to take a highlighted messages from a google hangouts work chatroom, email or live google form to set up a scheduling segment that is plotted in peripheral software. These are things like break and lunch updates, training time, system issue time, coaching segments and other things like floor support and team huddles that require shift segments to be plotted in the agent's digital time cards.

At this time the script works and pretty well and when it doesnt pick the right segment or time, fixing it is not hard and we're still way ahead from manually doing the whole process. At the same time, I'm pretty n00b, so I know it's likely to be illogical, inefficient and perhaps not 100% consistent. I'm curious of what the community would change and how much more simple you awesome people can make it.

I've read about RegEx briefly but haven't put together all those pieces exactly. Furthermore, I'm still developing this and I find while to be really intuitive so thats why I use those loops rather than for or loop. I do plan on changing those in the future as it progresses. I'll put some notes in my code so you can follow along and point out functions and snippets of code that might work better, and please let me know about the problems I've recognized and how we can structure or rewrite parts to make them better.

Thank you.

Code: Select all

;The following function is provided with an object array containing words from the message input which matched words from the dictionary array.  
;The function tallies which words belonged to which set, and determines the most common set accessed by summing the total number of times
;words from that set were used.  One issue is when there is a tie between two sets, the winner will always be from the order the sets are arranged 
;in the array.  I've compensated by keeping the order such that the more probable sets have the lower keys so that they will be used when 
;a tie breaker is required.  Struggling to think of a better way and not sure if this is the best that could be done.

most_common_segment(segment){
a:=0, wordrank:=[]
while(a<segment.Length()){
	++a,b:=0
	while(b<segment.Length()){
		++b
		if(segment[a]=segment[b]){
			if(!wordrank[a]){
				wordrank[a]:=0
			}
			wordrank[a]:=wordrank[a]+1
		}
	}
}

a:=0, totalsline:=[]
while(a<wordrank.Length()){
	++a, r:=segment[a], t:=wordrank[a]
	line=%r%@%t%
	totalsline[a]:=line
}

a:=0, outputlines:=[]
outputlines:=totalsline.Clone()
while(a<totalsline.Length()){
	++a
	line:=totalsline[a]
	totalnumber:=strsplit(line,"@")
	if(totalnumber[2]>1){
		b:=0
		k:=0
		while(b<totalsline.Length()){
			++b
			if(k<totalnumber[2]-1){
				if(totalnumber[1]=segment[b]){
					outputlines.Delete(b)
					++k
				}
			}
		}
	}
}

totals:=[], a:=0, c:=0
while(a<outputlines.Length()){
	++a
	if(!outputlines[a]){
	}
	else {
		++c, var:=outputlines[a], totals[c]:=var
	}
}

a:=0
while(a<totals.Length()){
	++a, b:=0, var:=totals[a], newarr:=strsplit(var,"@")
	while(b<totals.Length()){
		++b, var2:=totals[b], newarr2:=strsplit(var2,"@")
		if(newarr[2]>newarr2[2]){
			totals.Delete(b)	
		}
	}	
}

maximum_segment:=[], a:=0
while(a<totals.length()){
	++a
		if(!totals[a]){
	}
	else {
		maximum_segment[1]:=totals[a]
	}
}

v:=maximum_segment[1], breakdown:=strsplit(v,"@"), seg:=breakdown[1]

return seg		; the most common segment is returned as a variable
}



checknumbers(in){
	w:=0
	array:=strsplit(in)
	p:=0
	while(p<array.Length()){
	++p	
	if(array[p]=":"){	
		return w+1
	}
	
	}
	return w
}

;this function determines if the strings from the array are numbers or words and places them in corresponding arrays.  
check_group(groupobj,checkarray,type){

	counter:=0
	i:=0
	while(i<groupobj.Length()){

		++i
		k:=0

		while(k<checkarray.Length()){

			++k
			if(groupobj[i]=checkarray[k]){
				++counter

				if(counter=groupobj.Length()){

					if(type="numbers"){
						out:=""
						for k, v in groupobj
    						out.=v
						return [out,type]
					}

					else if(type="letters"){
						out:=""
						for k, v in groupobj
    						out.=v
						return [out,type]
					}
				}
			}
		}
	}
}

^!#c::
											; Employee id numbers are 4-8 digits
											; Limitations of times are when they don't use a colon or say 3pm, one thing I'm working towards.
String=please plot coaching for 1212343 from 12:20 3:30 	; this is an example of the string that is highlighted in hangouts or email

; other examples:  please put a training recursive for 74569383 from 3:00-4:00pm
; agent 76549832 is going home sick at 1:53
; plot go home early for 10112354 paid at 3:52

t:=0, r:=0, g:=0, n:=0
out:="", outtime:=""
number:=[], word:=[], values:=[], time:=[]
chardictionary:=["1234567890apm.-!@:^numbers","abcdefghijklmnopqrstuvwxyz34579/^letters"]   ;Here is where the [c]RegEx[/c] stuff could be used from what I gather.

; This array contains :  1. segment call term for relating in script 2. title to output for peripheral software 3. dictionary words a supervisor would use to request said segment

outputsegment:= ["coaching segment@COACHING_NB (Coaching NonBillable)@coach@coaching@aux3@3@plot@aux@aux\3@aux/3"
		,"internal system issue@SYSTEM_U (Internal Issue Unplanned)@aux9@9@system@issue@computer@aux@aux\9@aux/9"
		,"ghe unpaid@GHE_U (Go Home Early Unpaid)@unpaid@ghe@go@home@early"
		,"ghe paid@GHE_P (Go Home Early Paid)@paid@ghe@go@home@early"
		,"team meeting segment@MEETING (Team Meeting)@meeting@team@aux5@5@huddle@huddles@huddel@huddels@aux"
		,"personal segment@UNTW_PERSONAL (Personal)@personal@hr@approved"
		,"floor support segment@Floor Support B (Floor Support Billable)@support@floor@fs@sme@4@aux4"
		,"bonus time off@BTO (Bonus Time Off)@bto@bonus@time@off"
		,"pullout segment@HR_PO (HR Pull-out)@pull@pullout@hr@hr-pullout@hrpull@hr-pull@hrpullout@out"
		,"sick segment@ABSENCE Absence Unpaid)@sick@family@emergency@logged@out@log"
		,"extra lunch@EXTRA LUNCH (Extra Lunch)@extra@2@lunch@aux@aux2@aux/2@aux\2"
		,"training segment@TRAINING RECURSIVE (Training Recursive)@training@train@recursive@7@aux7@aux"
		,"could not be determined@COACHING_NB (Coaching NonBillable)"]

Stringobj1:=StrSplit(String," ")

;parse the message via the alphabet and digit arrays which both contain letters and numbers

while(n<Stringobj1.Length()){

	++n, j:=0
	while(j<chardictionary.Length()){

		++j, avar:=chardictionary[j], newarray2:=strsplit(avar,"^")
		avar2:=newarray2[1], newarray:=strsplit(avar2), var:=Stringobj1[n]
		Stringobj2:=StrSplit(var)

		values:=check_group(Stringobj2,newarray,newarray2[2])
		out:=values[1], typeout:=values[2]

		if(typeout="numbers"){
		
			w:=checknumbers(out)

			if(w=0){
				if(StrSplit(out).Length()<9){
					if(StrSplit(out).Length()>3){		;checks for employee id numbers from elements that have been determined to be numbers
						++t, number[t]:=out
					}
				}

				q:=0, chk4time:=strsplit(out)	;checks for time in these numbers which may contain pm or am with no space.
				while(q<strsplit(out).Length()){
					++q, a:=0, offcheck:=["p","a"]
					while(a<offcheck.Length()){
						++a
						if(chk4time[q]=offcheck[a]){
							if(chk4time[q+1]="m"){
								outtime:=""
								for x, v in chk4time
								outtime.=v
							}
						}
					}
				}

				a:=0
				while(a<time.Length()){
					++a
					if(!time[a]){
						time[a]:=outtime
					}
				}
							
			}

			if(w=1){		;for elements that are assumed to be times
				
				removechar:=[".","@","-"] ;removes undesirable characters so they don't upset the array.

				++g, removechararray:=strsplit(out)
				newchararray:=[], x:=0
				while(x<StrSplit(out).Length()){
					++x,c:=0,var:=removechararray[x]
					while(c<removechar.Length()){
						++c
						if(var=removechar[c]){
							newchararray[x]:=" "
						}
						
						else 
						newchararray[x]:=removechararray[x]
						
					}
	
				}
	
				out:=""
				for x, v in newchararray
				out.=v

				timecheckarr:=strsplit(out," ")
				If(timecheckarr.Length()>1){
				time[1]:=timecheckarr[1]
				time[2]:=timecheckarr[2]
			}
			else{
				time[g]:=out

			}
		}
		}

		else if(typeout="letters"){
			++r
			word[r]:=out
		}
	}

}

segment:=[]

a:=0, f:=0	;here the script creates an array of words that matched the dictionary for individual segment call term for relating in script
while(a<outputsegment.Length()){
	++a, b:=0
	crossword:=outputsegment[a]
	crosswordarray:=strsplit(crossword,"@")
	while(b<word.Length()){
		++b, c:=2
		while(c<outputsegment.Length()-2){
		++c
			if(word[b]=crosswordarray[c]){
				++f, segment[f]:=crosswordarray[1]
			}
		}
	}
}

time1:=time[1]
time2:=time[2]
cim:=number[1]

seg:=most_common_segment(segment)  ; here the most common segment is chosen in the first function at the top

if(!cim){
	MsgBox, 0, Please Try Again, No cim, Try Again
	return
}

a:=0
while(a<outputsegment.Length()){
	++a
	var:=outputsegment[a]
	segmentarray:=strsplit(var,"@")

	if(segmentarray[1]=seg){
		seg:=segmentarray[2]
	}
}

MsgBox, %seg% %time1% %time2% %cim%		;from here the script fills out these details in the scheduling software

return
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Location: Denmark
Contact:

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

29 May 2017, 03:08

you have a lot of infomation and i feel that i'd need to know more or be able to test your script to really be of much help

but I did make a regEx Example from your data

Code: Select all

string=
(
please plot coaching for 1212343 from 12:20 3:30
please put a training recursive for 74569383 from 3:00-4:00am
agent 76549832 is going home sick at 1:53
plot go home early for 10112354 paid at 3:52
)


msg_line_times := {}
lines := StrSplit(string, "`n")
for index, line_content in lines
{
    pos:=1, value_count:=""
    while pos
    {
        pos := RegExMatch(line_content, "Om)(?P<time>(?P<hour>\d+):(?P<min>\d+)(?P<12hour_notation>am|pm)*)", match, Match.Len(0)?pos+Match.Len(0):pos) ; time
        
        if Match.count()
        {
            value_count++
            this := msg_line_times[index, value_count] := {}
            this["time"] := match.time
            this["hour"] := match.hour
            this["min"] := match.min
            this["12hour_notation"] := match.12hour_notation
        }
    }
}
for i, this_line in msg_line_times
    for k, object in this_line
        msgbox % "On line " i " the " k " time is " object.time " and its hour is " object.hour " and its minuts is " object.min "`n" ((object.12hour_notation!="")?("And the 12 hour notation is " object.12hour_notation ):("And it has no 12 hour notation!"))
return
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
magicinmath
Posts: 162
Joined: 12 Apr 2017, 23:03

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

29 May 2017, 12:36

Blackholyman wrote:you have a lot of infomation and i feel that i'd need to know more or be able to test your script to really be of much help
Thank you, reviewing your code and trying to get perspective on it.

In the meantime I've done work on this function which counts the number of instances that an element is found within a set to return the most common one.
Is there a more simple way to count which element is most common in an array set?

Code: Select all

seT:=["a","a","a","b","b","a"]
most_common_element(Array){

	a:=0, c:=0,Rank:=[],totalsline:=[],totals:=[] 
	while(a<Array.Length()){

		++a,b:=0,
		while(b<Array.Length()){
			++b
			if(Array[a]=Array[b]){
				if(!Rank[a]){
					Rank[a]:=0
				}
				Rank[a]:=Rank[a]+1
			}
		
			r:=Array[a], t:=Rank[a]
			line=%r%@%t%
			totalsline[a]:=line, outputlines:=totalsline.Clone()
			line:=totalsline[a],totalnumber:=strsplit(line,"@")
			if(totalnumber[2]>1){
				k:=0
				if(k<totalnumber[2]-1){
					if(totalnumber[1]=Array[b]){
						outputlines.Delete(b)
						++k
					}
				}
			}
		}

		++c, totals[c]:=outputlines[a]
		
	}
	
	a:=0,maximum_segment:=[]
	while(a<totals.Length()){
		++a, b:=0, var:=totals[a], newarr:=strsplit(var,"@")
		while(b<totals.Length()){
			++b, var2:=totals[b], newarr2:=strsplit(var2,"@")

			if(newarr[2]>newarr2[2]){
				totals.Delete(b)	
			}
		}

	
		maximum_segment[1]:=totals[a]
			
	}

	v:=maximum_segment[1], breakdown:=strsplit(v,"@"), seg:=breakdown[1]
	return seg
}

var:=most_common_element(set)
msgbox, %var% 			; the result is: a
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Location: Denmark
Contact:

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.  Topic is solved

30 May 2017, 03:19

oki

Can't say if this is better but here you go

Code: Select all

seT:=["a","a","a","b","b","a"]

var:=most_common_element(seT)
msgbox, %var% 			; the result is: a
return


most_common_element(array)
{
    testArray := {}
    for index, value in array
    {
        if counter := testArray[value]
            counter.count++
        else
            testArray[value] := {count: 1}
    }
    for k, v in testArray
    {
        if (v.count>testArray[highest].count)
        {
            highest:=k
        }
    }
    return highest
}
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

30 May 2017, 03:30

Blackholyman wrote:oki

Can't say if this is better but here you go
@ Blackholyman
That's cool! Out of curiosity, I've replaced one "a" with a "b" (creating a draw) ... :angel: :silent:
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Location: Denmark
Contact:

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

30 May 2017, 03:36

thanks! In this case 'a' will win as that is the first item to be set as the highest so any following draw will fail the if check :)
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

30 May 2017, 03:57

Blackholyman wrote:thanks! In this case 'a' will win as that is the first item to be set as the highest so any following draw will fail the if check :)
Make sense! As I'm (still) not used to that "new" syntax of AHK - and only if not to complicated - would you mind to provide (most preferable in the "Scripts and Function"-section) that sample with an output like "1st: a with 8| 2nd: c with 8| 3rd: f with 5 | .." ??

Tak for at lytte :)

@ magicinmath
sorry to have hijacked your thread. Feel free to shout out and I will delete my postings in here.
magicinmath
Posts: 162
Joined: 12 Apr 2017, 23:03

Re: Increasing efficiency and logic for my script that reads text messages and extracts information for action/response.

30 May 2017, 19:43

BoBo wrote:
Blackholyman wrote: sorry to have hijacked your thread. Feel free to shout out and I will delete my postings in here.
Not at all, it's just a discussion you're free to exchange and I appreciate the input.
Blackholyman wrote:In this case 'a' will win as that is the first item to be set as the highest so any following draw will fail the if check :)
That is the same result I get except it goes from the bottom up. great code and thanks for sharing, less variable objects and loops :)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: doodles333 and 277 guests