simulator.py 5.28 KB
from scheduler.models import Scheduler
from pyrosapp.models import *
from django.contrib.auth.models import User
import urllib.request as urllib2
from html.parser import HTMLParser
import math
import operator
from decimal import Decimal
import time
from operator import attrgetter

class MyHTMLParser(HTMLParser):
    '''
        Subclassing HTMLParser to access the data properly
    '''

    def __init__(self):
        HTMLParser.__init__(self)
        self.data = []

    def handle_data(self, data):
        self.data.append(data)
        
    def handle_comment(self, data):
        pass

class Simulator():
    '''
        Simulates the creation of a planning thanks to an HTML file
    '''
    
    
    def __init__(self):
        pass
        
    def simulate(self, sequences_file):
        '''
           ENTRY POINT
           
           Parse the file with sequences, then call the Scheduler simulation function and give the result to the view
           
           :param sequences_file : HTML file containing sequences description 
           :returns : a tuple (Schedule, list of sequences) after the scheduling process
        '''
        
        file = urllib2.urlopen(sequences_file)
        data = str(file.read())
                
        parser = MyHTMLParser()
        parser.feed(data)

        """filter unwanted \n and other data"""
        filteredList = list(map(lambda s: s.strip(), parser.data))
        pageText = "".join(filteredList[11:])

        rawSequences = pageText.split("\\n")
        del rawSequences[0]
        del rawSequences[len(rawSequences)-1]
        rawSequences = [sequence for sequence in rawSequences if len(sequence) > 0]

        """Create the default models linked to the sequences"""
        sp = ScientificProgram()
        country = Country()
        usr_lvl = UserLevel()
        usr = User(username="toto")
        py_usr = PyrosUser(user=usr, country=country, userlevel=usr_lvl, quota=1000)
        req = Request(scientificprogram=sp, pyros_user=py_usr)
        seq_type = SequenceType()

        scheduler = Scheduler()

        """Create the sequences"""
        sequences = []
        for rawSequence in rawSequences:
            sequenceArray = rawSequence.split(" ")
            id_seq = sequenceArray[0]
            priority = int(sequenceArray[2])
            ''' transforms the duration (seconds) in days (86,400s in a day)'''
            duration = Decimal(float(sequenceArray[4]) / 86400.0)
            jd1 = Decimal("%.8f" % float(sequenceArray[5]))
            jd2 = Decimal("%.8f" % float(sequenceArray[6]))
            
            sequence = Sequence(request=req, sequencetype=seq_type, schedule=scheduler.schedule, status=Sequence.OBSERVABLE,
                                name="sequence", id=id_seq, priority=priority, duration=duration, jd1=jd1, jd2=jd2, t_prefered=-1)
            sequences.append(sequence)

        # 1) trouver le nombre qu'on retrouve le plus souvent en troncature de jd1 (faire un dico)
        days = {}
        for sequence in sequences:
            truncated = math.floor(float(sequence.jd1))
            if truncated in days.keys():
                days[truncated] += 1
            else:
                days[truncated] = 1
        maximum_truncated = max(days.keys(), key=(lambda key: days[key]))
        
        # 2) virer toutes les séquences qui n'ont pas ce jd1 en troncature
        sequences = [sequence for sequence in sequences if math.floor(float(sequence.jd1)) == maximum_truncated]

        # 3) set le plan_start et le plan_end en fonction du plus petit jd1 et du plus grand jd2
        scheduler.schedule.plan_start = -1
        scheduler.schedule.plan_end = -1
        for sequence in sequences:
            if scheduler.schedule.plan_start == -1 or sequence.jd1 < scheduler.schedule.plan_start:
                scheduler.schedule.plan_start = sequence.jd1
            if scheduler.schedule.plan_end == -1 or sequence.jd2 > scheduler.schedule.plan_end:
                scheduler.schedule.plan_end = sequence.jd2

        start = time.time()
        
        schedule, sequences = scheduler.simulate_schedule(sequences)

        end = time.time()
        
        print("Duration : ", end - start)
        
        self.test_sequences_validity(schedule, sequences)
        
        return (schedule, sequences)
    
    
    def test_sequences_validity(self, schedule, sequences):
        '''
            For PLANNED sequences, tests if :
                - all the sequences are in the schedule
                - there is no overlap between sequences
                - [tsp - tep] is in [jd1 - jd2]
                - tep < tsp
        '''
        
        sequences.sort(key=attrgetter('tsp'))

        for index, sequence in enumerate(sequences):
            if sequence.status == Sequence.PLANNED:
                if sequence.tsp > sequence.tep:
                    raise ValueError("tep < tsp ...")
                if sequence.tsp < schedule.plan_start or schedule.plan_end < sequence.tep:
                    raise ValueError("[tsp;tep] not in [plan_start;plan_end]")
                if sequence.tsp < sequence.jd1 or sequence.jd2 < sequence.tep:
                    raise ValueError("[tsp;tep] not in [jd1;jd2]")
                if index > 0 and sequence.tsp < sequences[index - 1].tep:
                    raise ValueError("There is a sequence overlap")