Exemple #1
0
	def add(self, timeObject, timeString, rule = None):
		'''
		timeObject - the id found in the <time> tag
		timeString - the text of the <time> tag
		rule - this parameter is purely for recursion. Because the rule has
		already been created, it can be passed in as a parameter to avoid
		creating an unnecessary copy. Recursion is only employed if the rule is
		a constant (no symbols); otherwise, a modified timeObject will
		invalidate the rule.
		'''
		log('Adding rule %s: ' % str(timeObject) + timeString.encode('utf-8'))
		
		# Use 1-based time for rule resolution
		timeObject.hours = (timeObject.hours - 1) % (24 if self.use24 else 12) + 1
		
		# Create the rule
		if not rule:
			rule = [self.createToken(word, timeObject) for word in timeString.split(' ')]
		
		# If rule contains no symbols, only constants, then only add rule for its hour
		isConst = self.isConstant(rule)
		
		# Now that the rule is created, we can apply the default duration if
		# the rule is all constant
		if isConst and not timeObject.duration:
			timeObject.duration = Time.fromSeconds(self.defaultDuration)
		
		for i in range(len(self.rules)):
			hour = (i - 1) % (24 if self.use24 else 12) + 1 # make hour 1-based
			
			# Constant times are only for a single hour
			if isConst and hour != timeObject.hours:
				continue
			
			self.rules[i] = self.insert(self.rules[i], rule, timeObject, hour)
			
			if isConst:
				# However, if the duration pushes the rule into the next hour,
				# clone the rule (with an appropriately shorter duration) and
				# recursively add to the next hour as well.
				hourBoundary = (timeObject.hours + 1) * 3600
				if timeObject.end() > hourBoundary:
					# Start at the beginning of the next hour
					nextTime = Time(timeObject.hours + 1, 0, 0)
					nextTime.duration = Time.fromSeconds(timeObject.end() - hourBoundary)
					nextTime.useSeconds = timeObject.useSeconds # For cosmetics
					self.add(nextTime, timeString, rule)
				break
Exemple #2
0
    def __init__(self, file):
        log('Using layout: ' + os.path.basename(file))
        try:
            root = ElementTree.parse(file).getroot()
        except:
            log('Error parsing layout file!')
            raise  # Let the user see the error
        try:
            background = root.find('background')
            self.height = int(background.attrib['height'])
            self.width = int(background.attrib['width'])
        except:
            log('Error: <background> tag missing/incorrect!')
            sys.exit()

        self.matrix = []
        entities = [
            char.strip().upper() for char in background.text.split(',')
        ]
        if (self.height * self.width > len(entities)):
            log('Error: Too many characters in background (expecting %d, found %d)' % \
             (self.height * self.width, len(entities)))
            sys.exit()
        elif (self.height * self.width < len(entities)):
            log('Error: Too few characters in background (expecting %d, found %d)' % \
             (self.height * self.width, len(entities)))
            sys.exit()
        for i in range(self.height):
            self.matrix.append(entities[i * self.width:(i + 1) * self.width])

        timesNode = root.find('times')
        if timesNode == None:
            log('Error: <times> node not found!')
            sys.exit()
        if 'use24' in timesNode.attrib and timesNode.attrib['use24'] == 'true':
            self.use24 = True
        else:
            self.use24 = False

        self.times = {}
        for time in timesNode.findall('time'):
            if 'id' not in time.attrib:
                log('Warning: found <time> tag with no "id" attribute')
                continue
            key = Time(time.attrib['id'])
            # If set, store the duration with the key
            if 'duration' in time.attrib:
                key.duration = Time(time.attrib['duration'])
            self.times[key] = time.text.lower()
        if len(self.times) == 0:
            log('Error: no <time> tags found!')
            sys.exit()

        self.strings = {}
        stringsNode = root.find('strings')
        if stringsNode == None:
            log('Error: <strings> node not found!')
            sys.exit()
        for string in stringsNode.findall('string'):
            if 'id' not in string.attrib:
                log('Warning: found <string> tag with no "id" attribute')
                continue
            if string.text == None or string.text.strip() == '':
                log('Warning: empty <string> tag for id=%s' %
                    string.attrib['id'])
                continue
            self.strings[int(
                string.attrib['id'])] = string.text.strip().lower()
Exemple #3
0
	def insert(self, node, rule, time, ruleChainHour):
		'''
		Recursive functions can get really tricky really quickly sticky.
		
		Ideally, this docstring would contain some helpful hints. Here's some
		terminology:
		"this node" - the parameter called node
		"this rule" - the parameter called rule, not this node's rule
		
		Once upon a time, the code for this function was short and concise.
		Then the dawning of the "duration" attribute appeared. With this
		attribute, a constant and a rule of variants can overlap and overwrite
		other rules. The code is now much more complex, because each hour has
		to be properly partitioned by rules defined in all hours; and not only
		this, but this partitioned time has to be represented by linked lists
		and everything has to occur recursively.
		
		Keep in mind: Constants ALWAYS have a duration (if it isn't explicitly
		declared, then it defaults to the GCD). Rule with no duration are
		considered to be rules with INFINITE duration. Variant rules CAN have a
		duration; this acts to give them a lower priority in times past the end
		of the duration and a higher priority in times during the duration.
		
		The return value of this function is a RuleNode which (in the first
		level deep) becomes the new root node for that hour in the RuleChain.
		Therefore, it is helpful to think of the return value as a way to
		modify the parent node's next node.
		'''
		# If true, rule will override node during conflicts
		precedence = ruleChainHour >= time.hours
		
		if node == None:
			# Base case: insert a new node into the rule chain by creating it
			# and setting its child to this node.
			return RuleNode(rule, time, node)
		
		# Assume the same hour as the node so comparisons will work
		tempTime = Time(node.time.hours, time.minutes, time.seconds)
		tempTime.duration = time.duration
		
		# At the highest level, we consider three cases: the time attached to
		# this rule is either before, equal to, or after the time of this node.
		
		if tempTime.toSeconds() < node.time.toSeconds():
			# Time occurs before this node. In all cases, the rule is prepended
			# to the chain. Keep in mind, a time with no duration is basically
			# a time with infinite duration. Also keep in mind, Constants
			# ALWAYS have a duration.
			if not time.duration:
				return RuleNode(rule, time, node)
			
			# Three cases: rules don't overlap, rules overlap partially, rules overlap fully
			# Case 1: rules don't overlap
			if tempTime.end() <= node.time.toSeconds():
				return RuleNode(rule, time, node)
			
			# Case 2: rules overlap partially
			if tempTime.end() < node.time.end():
				if precedence:
					# Move node into the furture and shorten its duration
					newBeginning = Time.fromSeconds(tempTime.end())
					newDuration = node.time.duration.toSeconds() - (node.time.end() - tempTime.end())
					newBeginning.duration = Time.fromSeconds(newDuration)
					node.time = newBeginning
					return RuleNode(rule, time, node)
				else:
					# Shorten time
					time.duration = Time.fromSeconds(node.time.toSeconds() - tempTime.toSeconds())
					return RuleNode(rule, time, node)
			
			# Case 3: node is fully overlapped by rule
			# time.end() >= node.time.end()
			if precedence:
				# Not including this node in the return statement effectively
				# eliminates it. However, things aren't this simple. We need to
				# check if the next node is partially/fully consumed via
				# recursion.
				return self.insert(node.next, rule, time, ruleChainHour)
			else:
				# Split the rule into two nodes that fall on either side of
				# this node. We create the following chain:
				# parent node -> node1 -> node -> node2 -> node.next
				time1 = time.copy()
				time1.duration = Time.fromSeconds(node.time.toSeconds() - tempTime.toSeconds())
				node1 = RuleNode(rule, time1, node)
				time2 = Time.fromSeconds(node.time.end())
				time2.hours = time.hours # Use original hours to maintain precedence
				time2.duration = Time.fromSeconds(tempTime.end() - node.time.end())
				# Use recursion, because time2 might extend past node.next
				node2 = self.insert(node.next, rule, time2, ruleChainHour)
				node.next = node2
				return node1
		
		# The case where rule occured before node was relatively straightforward.
		# Now, rule and node occur at the same time, which means that most
		# likely either rule or node is omitted.
		if tempTime.toSeconds() == node.time.toSeconds():
			if not precedence:
				# Ignore the rule
				return node
			
			# We've established that the rule has precedence. Now it's just a
			# matter of finding out how much of node (and its children) to
			# delete.
			
			# Three cases
			# Case 1: No rule duration
			if not tempTime.duration:
				# Replace the node
				return RuleNode(rule, time, node.next)
			
			# Case 2: Rule duration, but no node duration
			if not node.time.duration:
				# Peek ahead at the future node
				if node.next:
					tempTime2 = Time(node.next.time.hours, time.minutes, time.seconds)
					tempTime2.duration = time.duration
					if tempTime2.end() > node.next.time.toSeconds():
						# This node is fully engulfed. Replace the node, and
						# make sure that we replace any other overlapped nodes
						# (recursively, of course)
						# parent node -> node1 -> node.next
						node1 = self.insert(node.next, rule, time, ruleChainHour)
						return node1
				# Make this node start at the end of this rule
				node.time = Time.fromSeconds(tempTime.end())
				return RuleNode(rule, time, node)
			
			# Case 3: Rule duration AND node duration. Let the battle begin!
			if tempTime.end() >= node.time.end():
				# Replace the node and any following nodes if necessary
				node1 = self.insert(node.next, rule, time, ruleChainHour)
				return node1
			else:
				# Chop off and preserve the dangling part
				end = node.time.end()
				node.time = Time.fromSeconds(tempTime.end())
				# node.time.hours is already set because tempTime.hours was copied earlier
				node.time.duration = Time.fromSeconds(end - tempTime.end())
				return RuleNode(rule, time, node)
		
		# Rule occurs in the future: tempTime.toSeconds() > node.time.toSeconds()
		
		# Three cases: rules don't overlap, rules overlap partially, rules overlap fully
		# Case 1
		if node.time.end() <= tempTime.toSeconds():
			# If node.rule is a constant, it doesn't need to persist after its duration
			if not tempTime.duration or self.isConstant(node.rule):
				# Regular rule or node is constant, recurse deeper
				node.next = self.insert(node.next, rule, time, ruleChainHour)
				return node
			else:
				# Peek ahead at the future node
				if node.next:
					tempTime2 = Time(node.next.time.hours, time.minutes, time.seconds)
					tempTime2.duration = time.duration
					if tempTime2.toSeconds() > node.next.time.toSeconds():
						# Next node is in the future too, recurse deeper
						node.next = self.insert(node.next, rule, time, ruleChainHour)
						return node
				
				# tempTime has a duration so node should persist after rule's completion
				# To do so, we dupe node and let recursion do the rest
				timeCopy = Time.fromSeconds(tempTime.toSeconds())
				if node.time.duration:
					# Need to modify duration of both old and new time
					timeCopy.duration = Time.fromSeconds(node.time.end() - tempTime.toSeconds())
					node.time = node.time.copy()
					node.time.duration = Time.fromSeconds(tempTime.toSeconds() - node.time.toSeconds())
				nodeCopy = RuleNode(node.rule, timeCopy, node.next)
				node.next = self.insert(nodeCopy, rule, time, ruleChainHour)
				return node
		
		# Implicit that node.time.duration exists
		# Case 2
		if tempTime.end() > node.time.end():
			# Implicit that tempTime.duration exists
			if precedence:
				# Shorten node
				node.time = node.time.copy()
				node.time.duration = Time.fromSeconds(tempTime.toSeconds() - node.time.toSeconds())
				# Link in the new node after this one
				node.next = RuleNode(rule, time, node.next)
				return node
			else:
				# Shorten rule by giving it a later starting time
				time2 = Time.fromSeconds(node.time.end())
				time2.hours = time.hours # Use original hours to maintain precedence
				time2.duration = Time.fromSeconds(tempTime.end() - node.time.end())
				node.next = RuleNode(rule, time2, node.next)
				return node
		
		# Case 3: node.time has a duration and tempTime occurs in the middle of it
		if not precedence:
			if tempTime.duration:
				# tempTime is fully engulfed by node
				return node
			else:
				# tempTime is a rule, so it continues past node
				time2 = Time.fromSeconds(node.time.end())
				node.next = self.insert(node.next, rule, time2, ruleChainHour)
				return node
		if not tempTime.duration:
			# tempTime is a regular rule, so chop node in half and occupy the
			# second half with this rule
			node.time = node.time.copy()
			node.time.duration = Time.fromSeconds(tempTime.toSeconds() - node.time.toSeconds())
			# Link in the new node after this one
			node.next = RuleNode(rule, time, node.next)
			return node
		if tempTime.end() == node.time.end():
			# Ends coincide. Things are easy, just chop and link
			node.time = node.time.copy()
			node.time.duration = Time.fromSeconds(tempTime.toSeconds() - node.time.toSeconds())
			# Link in the new node after this one
			node.next = RuleNode(rule, time, node.next)
			return node
		# Things are messy. Bisect node:
		# parent node -> node(1) -> rule -> node(2) -> node.next
		# Go from last to first. Start with second node:
		time2 = Time.fromSeconds(tempTime.end())
		time2.duration = Time.fromSeconds(node.time.end() - tempTime.end())
		node2 = RuleNode(node.rule, time2, node.next)
		newNode = RuleNode(rule, time, node2)
		time1 = node.time.copy()
		time1.duration = Time.fromSeconds(tempTime.toSeconds() - node.time.toSeconds())
		node.time = time1
		node.next = newNode
		return node
Exemple #4
0
	def __init__(self, file):
		log('Using layout: ' + os.path.basename(file))
		try:
			root = ElementTree.parse(file).getroot()
		except:
			log('Error parsing layout file!')
			raise # Let the user see the error
		try:
			background = root.find('background')
			self.height = int(background.attrib['height'])
			self.width = int(background.attrib['width'])
		except:
			log('Error: <background> tag missing/incorrect!')
			sys.exit()
		
		self.matrix = []
		entities = [char.strip().upper() for char in background.text.split(',')]
		if (self.height * self.width > len(entities)):
			log('Error: Too many characters in background (expecting %d, found %d)' % \
				(self.height * self.width, len(entities)))
			sys.exit()
		elif (self.height * self.width < len(entities)):
			log('Error: Too few characters in background (expecting %d, found %d)' % \
				(self.height * self.width, len(entities)))
			sys.exit()
		for i in range(self.height):
			self.matrix.append(entities[i * self.width : (i + 1) * self.width])
		
		timesNode = root.find('times')
		if timesNode == None:
			log('Error: <times> node not found!')
			sys.exit()
		if 'use24' in timesNode.attrib and timesNode.attrib['use24'] == 'true':
			self.use24 = True
		else:
			self.use24 = False
		
		self.times = {}
		for time in timesNode.findall('time'):
			if 'id' not in time.attrib:
				log('Warning: found <time> tag with no "id" attribute')
				continue
			key = Time(time.attrib['id'])
			# If set, store the duration with the key
			if 'duration' in time.attrib:
				key.duration = Time(time.attrib['duration'])
			self.times[key] = time.text.lower()
		if len(self.times) == 0:
			log('Error: no <time> tags found!')
			sys.exit()
		
		self.strings = {}
		stringsNode = root.find('strings')
		if stringsNode == None:
			log('Error: <strings> node not found!')
			sys.exit()
		for string in stringsNode.findall('string'):
			if 'id' not in string.attrib:
				log('Warning: found <string> tag with no "id" attribute')
				continue
			if string.text == None or string.text.strip() == '':
				log('Warning: empty <string> tag for id=%s' % string.attrib['id'])
				continue
			self.strings[int(string.attrib['id'])] = string.text.strip().lower()