def getSolutionEndDate(solution,restrictions):
	try:
			startDate=restrictions.startDate
	except AttributeError:
		startDate=datetime.datetime.today()	#If no start date specified, assume today
	date=startDate
	try:
		date=addTime(date, secs=waypoint_durations[frozenset([restrictions.startLoc,getItinFromItinList(solution.solutionList[0]).trailhead])],restrictions=restrictions)	#Add in driving time to first location
	except AttributeError:
		pass	#If no start location, don't add any extra time
	prevItin=None
	for itin in solution.solutionList:
		date = date + datetime.timedelta(days=itinList[itin].los) #Add LOS from current location to the date to be used for temp measurement
		if prevItin is not None: date = addTime(date,secs=waypoint_durations[frozenset([prevItin,itin])],restrictions=restrictions)	#Add travel time to most recent date
	return date
def checkSolutionAgainstEndDate(solution,restrictions):
	if restrictions.endDate is not None:
		try:
			startDate=restrictions.startDate
		except AttributeError:
			startDate=datetime.datetime.today()	#If no start date specified, assume today
		date=startDate
		try:
			date=addTime(date, secs=waypoint_durations[frozenset([restrictions.startLoc,getItinFromItinList(solution.solutionList[0]).trailhead])],restrictions=restrictions)	#Add in driving time to first location
		except AttributeError:
			pass	#If no start location, don't add any extra time
		prevItin=None
		listLength=0
		for itin in solution.solutionList:
			date = date + datetime.timedelta(days=itinList[itin].los) #Add LOS from current location to the date to be used for temp measurement
			if prevItin is not None: date = addTime(date,secs=waypoint_durations[frozenset([prevItin,itin])],restrictions=restrictions)	#Add travel time to most recent date
			if (date-restrictions.endDate).days<0: 
				listLength+=1
			else: break	#Don't bother continuing if we hit an end point
			prevItin=itin
		return max(listLength-1,1)	#Need a minimum of 1, because if you truncate to the 0th element, it's an empty list
def makeReplenishedCopy(agent_genome,restrictions=None):
	new_agent=deepcopy(agent_genome)
	solEndDate=getSolutionEndDate(new_agent, restrictions)
	waypoints=list(getWaypointSet(all_waypoints, restrictions, agent_genome))	#Create list of waypoints that should be used
	random.shuffle(waypoints)	#Then randomize so we add them in random order
	randomCount=0	
	if restrictions.endDate is not None and randomCount<=len(waypoints)-1:
		while solEndDate<restrictions.endDate:
			newRandomLoc=getItinFromItinList(waypoints[randomCount])
			solEndDate=addTime(solEndDate, days=newRandomLoc.los, restrictions=restrictions)
			if (restrictions.endDate-solEndDate).days>0:	#If we haven't hit the end date yet, append and continue
				new_agent.solutionList.append(newRandomLoc.id)
				randomCount+=1
			else:	#Otherwise, break the loop
				break
	new_agent.updateSet()
	return new_agent
 def createXML(self,
               db,
               solution,
               itinList,
               restrictions,
               waypoint_distances={},
               waypoint_durations={}):
     try:
         startDate = restrictions.startDate
     except AttributeError:
         startDate = datetime.datetime.today()
     try:
         endDate = restrictions.endDate
     except AttributeError:
         endDate = None
     try:
         startLoc = restrictions.startLoc
     except AttributeError:
         startLoc = None
     try:
         endLoc = restrictions.endLoc
     except AttributeError:
         endLoc = None
     date = startDate
     root = etree.Element("solution")
     etree.SubElement(root, "itinList").text = str(solution.solutionList)
     fitness = str(solution.fitness)
     fitnessNode = etree.SubElement(root, "fitness")
     fitnessNode.text = fitness
     try:
         etree.SubElement(root, "end_date").text = str(
             solution.endDate.isoformat())
     except AttributeError:
         etree.SubElement(root, "end_date").text = "No end date recorded"
     prevLocation = None
     prevNode = None
     if startLoc is not None:
         startNode = etree.SubElement(root, "start_location")
         startNode.text = startLoc
         etree.SubElement(startNode,
                          "departureDate").text = startDate.isoformat()
         date = addTime(date,
                        secs=getGMapsVal(
                            startLoc,
                            itinList[solution.solutionList[0]].trailhead,
                            "dur", waypoint_durations),
                        restrictions=restrictions)
         etree.SubElement(startNode, "distance").text = str(
             getGMapsVal(startLoc,
                         itinList[solution.solutionList[0]].trailhead,
                         "dist", waypoint_distances))
     for location in solution.solutionList:
         itin = itinList[location]
         itinNode = etree.SubElement(root, "location")
         if prevLocation is not None:
             date = addTime(
                 date,
                 secs=getGMapsVal(prevLocation, location, "dur",
                                  waypoint_durations),
                 restrictions=restrictions
             )  #Add travel time to most recent date if not first location
         if prevNode is not None:
             distNode = etree.SubElement(prevNode, "distance")
             distNode.text = str(
                 getGMapsVal(prevLocation, location, "dist",
                             waypoint_distances))
         arrivalNode = etree.SubElement(itinNode, "arrivalDate")
         arrivalNode.text = str(date.isoformat())
         #Get all static data in itin item
         for att in itin.__dict__.keys():
             if not (att == "tempArr" or att == "conn"
                     or att == "db"):  #skip any non-base types for now
                 attNode = etree.SubElement(itinNode, att)
                 attNode.text = str(getattr(itin, att))
         #Get weather data
         try:
             expWeather = itin.tempArr[int(date.strftime("%j"))]
         except KeyError:
             expWeather = "No weather data for " + str(date.isoformat())
         weatherNode = etree.SubElement(itinNode, "expectedWeather")
         weatherNode.text = str(expWeather)
         #Get departure date data
         date = date + datetime.timedelta(days=itinList[location].los)
         etree.SubElement(itinNode,
                          "departureDate").text = str(date.isoformat())
         #Track data for loc+1
         prevLocation = location
         prevNode = itinNode
     if endLoc is not None:
         etree.SubElement(prevNode, "distance").text = str(
             getGMapsVal(itinList[prevLocation].trailhead, endLoc, "dist",
                         waypoint_durations))
         endNode = etree.SubElement(root, "end_location")
         endNode.text = str(endLoc)
         endDate = addTime(date,
                           secs=getGMapsVal(
                               itinList[prevLocation].trailhead, endLoc,
                               "dur", waypoint_durations),
                           restrictions=restrictions)
         etree.SubElement(endNode,
                          "arrivalDate").text = str(endDate.isoformat())
     #print(etree.tostringlist(root, pretty_print=True))
     open("secondLeg/route" + fitness + ".xml",
          "wb").write(etree.tostring(root, pretty_print=True))
     xslt = etree.XSLT(etree.parse("xslTrans.xsl"))
     html = xslt(root)
     html.write("route.html")
     webbrowser.open_new_tab("route.html")
def createXML(db,solution,itinList,restrictions,outputDirectory=None):
	try:
		startDate=restrictions.startDate
	except AttributeError:
		startDate=datetime.datetime.today()
	try:
		endDate=restrictions.endDate
	except AttributeError:
		endDate=None
	try:
		startLoc=restrictions.startLoc
	except AttributeError:
		startLoc=None
	try:
		endLoc=restrictions.endLoc
	except AttributeError:
		endLoc=None
	date=startDate
	root=etree.Element("solution")
	etree.SubElement(root,"itinList").text=str(solution.solutionList)
	fitness=str(solution.fitness)
	fitnessNode=etree.SubElement(root,"fitness")
	fitnessNode.text=fitness
	try:
		etree.SubElement(root,"end_date").text=str(solution.endDate.isoformat())
	except AttributeError:
		etree.SubElement(root,"end_date").text="No end date recorded"
	prevLocation=None
	prevNode=None
	if startLoc is not None:
		startNode=etree.SubElement(root,"start_location")
		startNode.text=startLoc
		etree.SubElement(startNode,"departureDate").text=startDate.isoformat()
		date=addTime(date,secs=waypoint_durations[frozenset([startLoc,getItinFromItinList(solution.solutionList[0]).trailhead])],restrictions=restrictions)
		etree.SubElement(startNode,"distance").text=str(waypoint_distances[frozenset([startLoc,getItinFromItinList(solution.solutionList[0]).trailhead])])
	for location in solution.solutionList:
		itin=getItinFromItinList(location)
		itinNode=etree.SubElement(root,"location")
		if prevLocation is not None:
				date = addTime(date,secs=waypoint_durations[frozenset([prevLocation,location])],restrictions=restrictions)	#Add travel time to most recent date if not first location
		if prevNode is not None:
			distNode=etree.SubElement(prevNode,"distance")
			distNode.text=str(waypoint_distances[frozenset([prevLocation,location])])
		arrivalNode=etree.SubElement(itinNode,"arrivalDate")
		arrivalNode.text=str(date.isoformat())
		#Get all static data in itin item
		for att in itin.__dict__.keys():
			if not (att=="tempArr" or att=="conn" or att=="db"): 	#skip any non-base types for now
				attNode=etree.SubElement(itinNode,att)
				attNode.text=str(getattr(itin,att))
		#Get weather data
		try:
			expWeather=itin.tempArr[int(date.strftime("%j"))]
		except KeyError:
			expWeather="No weather data for "+str(date.isoformat())
		weatherNode=etree.SubElement(itinNode,"expectedWeather")
		weatherNode.text=str(expWeather)
		#Get departure date data
		date=date + datetime.timedelta(days=itinList[location].los)
		etree.SubElement(itinNode,"departureDate").text=str(date.isoformat())
		#Track data for loc+1
		prevLocation=location
		prevNode=itinNode
	if endLoc is not None:
		etree.SubElement(prevNode,"distance").text=str(waypoint_distances[frozenset([getItinFromItinList(prevLocation).trailhead,endLoc])])
		endNode=etree.SubElement(root,"end_location")
		endNode.text=str(endLoc)
		endDate=addTime(date, secs=waypoint_durations[frozenset([getItinFromItinList(prevLocation).trailhead,endLoc])],restrictions=restrictions)
		etree.SubElement(endNode,"arrivalDate").text=str(endDate.isoformat())
	if outputDirectory is not None:
		fPath=outputDirectory+"/route"+fitness+".xml"
	else:
		fPath="route"+fitness+".xml"
	open(fPath,"wb").write(etree.tostring(root, pretty_print=True))
	xslt=etree.XSLT(etree.parse("xslTrans.xsl"))
	html=xslt(root)
	html.write("route.html")
def compute_fitness_alt(solution,itinList,restrictions=None):
	solution_fitness = 0.0
	solution_dist = 0
	solution_tempOffset = 0.0
	openDays=0
	tempCount = 0
	tempOffsets=[]
	startDriveTime=0
	endDriveTime=0
	startDate=None
	endDate=None
	
	if restrictions is not None:
		startDate=restrictions.startDate
		endDate=restrictions.endDate
		startLoc=restrictions.startLoc
		endLoc=restrictions.endLoc
		if startLoc is not None:
			firstLoc=itinList[solution.solutionList[0]]		
			try:
				solution_dist+=waypoint_distances[frozenset([startLoc,firstLoc.trailhead])]
				startDriveTime=waypoint_durations[frozenset([startLoc,firstLoc.trailhead])]
			except KeyError:
				getStartEndData(1, solution, startLoc)
				solution_dist+=waypoint_distances[frozenset([startLoc,firstLoc.trailhead])]
				startDriveTime=waypoint_durations[frozenset([startLoc,firstLoc.trailhead])]
		if endLoc is not None:
			lastLoc=itinList[solution.solutionList[len(solution.solutionList)-1]]
			try:
				solution_dist+=waypoint_distances[frozenset([lastLoc.trailhead,endLoc])]
				endDriveTime=waypoint_durations[frozenset([lastLoc.trailhead,endLoc])]
			except KeyError:
				getStartEndData(2, solution, endLoc)
				solution_dist+=waypoint_distances[frozenset([lastLoc.trailhead,endLoc])]
				endDriveTime=waypoint_durations[frozenset([lastLoc.trailhead,endLoc])]
	
	if startDate is None:
		date=datetime.datetime.today()
	else:
		date=startDate
	if startDriveTime is not None:
		date=addTime(startDate, secs=startDriveTime,restrictions=restrictions)	#Add drive time to first location to start date
	for index in range(len(solution.solutionList)):
		if index>0:
			startWaypoint = solution.solutionList[index-1]
			destWaypoint = solution.solutionList[index]
			destItin=itinList[destWaypoint]	#Get the itinerary object for the destination, so that the temperature can be retrieved from it
		else:
			destItin=itinList[solution.solutionList[index]]	#If first location, set destination appropriately
		#Get temperature data
		temps=[]
		tempHigh=None
		tempLow=None
		idealTemp=80-(3.5*(destItin.weatherLocElevation/1000))
		temps=destItin.getTemp(date)	#[maxTemp,minTemp] format
		try:
			tempHigh=temps[0]
			tempLow=temps[1]
			if tempHigh is not None and ((tempHigh<(idealTemp-10)) or (tempHigh>(idealTemp+10))):
				tempCount+=1
				tempOffsets.append(abs(idealTemp-tempHigh))
		except TypeError:
			doy=int(date.strftime('%j'))
			print("No temperature data for {}-{} on day {}".format(destItin.id,destItin.parkName,doy))
				#Get distance data
		date = addTime(date, days=destItin.los, restrictions=restrictions)
		if endDate is not None and date>endDate: return float("inf")	#If the itinerary passes end date, it's invalid
		if index>0:
			try:
				solution_dist += waypoint_distances[frozenset([startWaypoint,destWaypoint])]
			except:
				print("Unable to retrieve distance data for %s to %s" % (startWaypoint,destWaypoint))
			date = addTime(date,secs=waypoint_durations[frozenset([startWaypoint,destWaypoint])],restrictions=restrictions)	#Add travel time to most recent date
			if endDate is not None and date>endDate: return float("inf")	#If the itinerary passes end date, it's invalid
	#Check return date
	if endDate is not None:
		date = addTime(date, secs=endDriveTime,restrictions=restrictions)
		if date>endDate: return float("inf")
		openDays=(endDate-date).days
	else:
		openDays=0	
	solution_fitness +=solution_dist	#firstly, add distance
	for index in range(0,tempCount):
		solution_tempOffset += tempOffsets[index]
	solution_fitness += (9654 * solution_tempOffset)		#and now add in the temp offsets
	solution_fitness=(solution_fitness/len(solution.solutionList))	#Now adjust by # of locations in list, to ensure we compare solutions evenly even with different #s of dests
	if openDays>3: solution_fitness+=(9654*(openDays**2))
	solution.endDate=date	#Record end date calculated here, to avoid recalculating it if not necessary
	return solution_fitness
		
	"""
		This function returns the total distance traveled on the current road trip.
		
		The genetic algorithm will favor road trips that have shorter
		total distances traveled.
	"""
	
	solution_fitness = 0.0
	
	for index in range(len(solution)):
		waypoint1 = solution[index - 1]
		waypoint2 = solution[index]
		#try:
		solution_fitness += waypoint_distances[frozenset([waypoint1, waypoint2])]
		#except Exception as e:	#if cannot compute distance for some reason, disregard this solution
			#solution_fitness = 9999999999999.00
			#break
		
	return solution_fitness