Runtime Schedule

Hi,

I am trying to build a schedule UI with three text fields/pop up shift selectors. Underneath the UI I would like to have a string of the format start1Time AM - end1Time PM , start2Time etc…
Right now I have a good amount of code on the text fields and the popups to accomplish this without any overlapping for shifts. Has anyone built a complex scheduler UI in the past, and how did you overcome the scripting challenges, and maintain ease of user for the user when they are determined to use shift definitions instead of regular time ranges?

Have you seen this discussion:

Let them use shift definitions. No UI for it at the moment, but I think you can handle it. :grin:

Thanks for the link, I did look at the post but I’m not sure if I can take that approach here. I have restrictions where the time ranges have to look like the large strings I have circled.

Here is working code from one of the text fields where monday_setup and tuesday_setup are my long string properties that are copied from the monday and tuesday string values from the db:

if event.propertyName == 'text':
	
	def timeRangeBreakup(rangeStr, delimiter):
		componentList = []
		lastBreak = 0
		for i in range(len(rangeStr)):
			if rangeStr[i] == delimiter:
				componentList.append(rangeStr[lastBreak: i])
				lastBreak = i + 1
			if i == len(rangeStr)-1:
				componentList.append(rangeStr[lastBreak:len(rangeStr)].strip())
		return componentList
	
	event.source.parent.getComponent('select_shift 1').visible = 0
	rangesStrSetup = event.source.parent.monday_setup
	nextDayRangesStrSetup = event.source.parent.tuesday_setup
	
	
	
	try:
		rangeStr = event.source.text
		time = timeRangeBreakup(rangeStr, '-')
		start = time[0]
		startComponents = timeRangeBreakup(start,':')
		startRightSideTime = timeRangeBreakup(startComponents[1],' ')
		startMinute = int(startRightSideTime[0])
		startAMPM = startRightSideTime[1]
		if startAMPM.startswith('A'):
			startHour = int(startComponents[0])
		else:
			startHour = int(startComponents[0]) + 12
		
		end = time[1]
		endComponents = timeRangeBreakup(end,':')
		endRightSideTime = timeRangeBreakup(endComponents[1],' ')
		endMinute = int(endRightSideTime[0])
		endAMPM = endRightSideTime[1]
		if endAMPM.startswith('A'):
			endHour = int(endComponents[0])
		else:
			endHour = int(endComponents[0]) + 12

	except:
		startHour = 23
		startMinute = 58
		endHour = 23
		endMinute = 59

	nextDayActive = 0
	nextDayStartHour = 0
	nextDayStartMinute = 0
	nextDayEndHour = 0
	nextDayEndMinute = 0
	if startHour >= endHour:
		if startMinute > endMinute:
			#Range goes past midnight
			nextDayActive = 1
			nextDayEndHour = endHour
			nextDayEndMinute = endMinute
			endHour = 23
			endMinute = 59
	
	now = system.date.now()		
	startDate = system.date.setTime(now,startHour,startMinute,0)
	endDate = system.date.setTime(now,endHour,endMinute,0)
	
	tomorrow = system.date.addDays(now,1)
	nextDayStartDate = system.date.setTime(tomorrow,nextDayStartHour,nextDayStartMinute,0)
	nextDayEndDate = system.date.setTime(tomorrow,nextDayEndHour,nextDayEndMinute,0)
	
	overlap = False
	nextDayOverlap = False
	if len(rangesStrSetup) > 0:
		ranges = timeRangeBreakup(rangesStrSetup,',')

		for i in ranges:
			time = timeRangeBreakup(i,'-')
			start = time[0]
			startComponents = timeRangeBreakup(start,':')
			startHourSetup = int(startComponents[0])
			startMinuteSetup = int(startComponents[1])
			startDateSetup = system.date.setTime(system.date.now(),startHourSetup,startMinuteSetup,0)
		
			end = time[1]
			endComponents = timeRangeBreakup(end,':')
			endHourSetup = int(endComponents[0])
			endMinuteSetup = int(endComponents[1])
			endDateSetup = system.date.setTime(system.date.now(),endHourSetup,endMinuteSetup,0)

			if (system.date.isBetween(startDate,startDateSetup,endDateSetup) or
				 system.date.isBetween(endDate,startDateSetup,endDateSetup) or
				 system.date.isBetween(startDateSetup,startDate,endDate) or
				 system.date.isBetween(endDateSetup,startDate,endDate)):
				overlap = True					
				break
	
	if nextDayActive:
		if len(nextDayRangesStrSetup) > 0:
			nextDayRanges = timeRangeBreakup(nextDayRangesStrSetup,',')
			for i in nextDayRanges:
				time = timeRangeBreakup(i,'-')
				start = time[0]
				startComponents = timeRangeBreakup(start,':')
				nextDayStartHourSetup = int(startComponents[0])
				nextDayStartMinuteSetup = int(startComponents[1])
				nextDayStartDateSetup = system.date.setTime(tomorrow,nextDayStartHourSetup,nextDayStartMinuteSetup,0)
			
				end = time[1]
				endComponents = timeRangeBreakup(end,':')
				nextDayEndHourSetup = int(endComponents[0])
				nextDayEndMinuteSetup = int(endComponents[1])
				nextDayEndDateSetup = system.date.setTime(tomorrow,nextDayEndHourSetup,nextDayEndMinuteSetup,0)
				
				if (system.date.isBetween(nextDayStartDate,nextDayStartDateSetup,nextDayEndDateSetup) or
					 system.date.isBetween(nextDayEndDate,nextDayStartDateSetup,nextDayEndDateSetup) or
					 system.date.isBetween(nextDayStartDateSetup,nextDayStartDate,nextDayEndDate) or
					 system.date.isBetween(nextDayEndDateSetup,nextDayStartDate,nextDayEndDate)):
					nextDayOverlap = True					
					break

	formatStartTimeHH = system.date.format(startDate,"HH:mm")
	formatEndTimeHH = system.date.format(endDate,"HH:mm")
	formatStartTimehh = system.date.format(startDate,"hh:mm a")
	formatEndTimehh = system.date.format(endDate,"hh:mm a")

	timeRangeHH = formatStartTimeHH + ' - ' + formatEndTimeHH
	timeRangehh = formatStartTimehh + ' - ' + formatEndTimehh
	
	nextDayFormatStartTimeHH = system.date.format(nextDayStartDate,"HH:mm")
	nextDayFormatEndTimeHH = system.date.format(nextDayEndDate,"HH:mm")
	nextDayFormatStartTimehh = system.date.format(nextDayStartDate,"hh:mm a")
	nextDayFormatEndTimehh = system.date.format(nextDayEndDate,"hh:mm a")
	
	nextDayTimeRangeHH = nextDayFormatStartTimeHH + ' - ' + nextDayFormatEndTimeHH
	nextDayTimeRangehh = nextDayFormatStartTimehh + ' - ' + nextDayFormatEndTimehh
	
	if rangeStr is not None and len(rangeStr) > 0:
	
		if overlap == False and nextDayOverlap == False:
	
			if len(rangesStrSetup) > 0:
				rangesStr = rangesStrSetup + ' , ' + timeRangeHH
			else:
				rangesStr = timeRangeHH
			
			event.source.parent.monday_setup = rangesStr
			
			if nextDayActive:
				if len(nextDayRangesStrSetup) > 0:
					nextDayRangesStr = nextDayRangesStrSetup + ' , ' + nextDayTimeRangeHH
				else:
					nextDayRangesStr = nextDayTimeRangeHH
				
				event.source.parent.tuesday_setup = nextDayRangesStr
			event.source.parent.refresh = True
		else:
			if overlap:
				event.source.parent.getComponent('tf 1').nonEditableBackground = system.gui.color(255,0,0)
			if nextDayOverlap:
				1
				#system.gui.messageBox('This entry does not fit the schedule','Overlaps next day')
	else:
		daySetup = event.source.parent.monday_setup
		ranges = timeRangeBreakup(daySetup, ',')
		print ranges
		loopCount = 0
		newList = ''
		for i in ranges:
			if loopCount == 0:
				newList = i
			elif loopCount == 1:
				newList = newList + ' , ' + i
			elif loopCount == 2:
				newList = newList + ' , ' + i
			loopCount += 1
		
		event.source.parent.monday_setup = newList	
		event.source.parent.refresh = True
		#How to remove the corresponding item from the next day for shift 3?	

I think this is a terrible mistake. A sequence of time ranges should always be specified by start time where the end time is implied by the following entry. When used in SQL for joins, it should look something like this:

SELECT ....
FROM some_production_data pd INNER JOIN production_shifts_for_range(....) s
  ON pd.t_stamp >= s.beginat AND pd.t_stamp < s.endat

Note the equals only for the start point of the shift. This is called a "half-open interval". Using half-open intervals is crucial to avoiding inefficient date conversions to/from strings to perform grouping and bounds checking.

My 2¢. Ignore as you wish.

Or present it your way in the UI and transform it into an efficient representation for actual use.

Thanks for the feedback Phil.
I am happy to have your diagnosis.

I am familiar with the sql properties to simplify the ui and time math equations in the way you describe. I will give your suggestion a try and reply with my progress. This isn’t the only use for my schedule system which has an mes component that needs to run transaction groups on remote tag servers.