Blame view

src/core/pyros_django/scheduler/simulator.py 5.16 KB
1cffbf1c   Etienne Pallier   moved pyros_djang...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from scheduler.Scheduler import Scheduler
from common.models import *
import urllib.request as urllib2
from html.parser import HTMLParser
import math
from decimal import Decimal
import time


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"""
        filtered_list = list(map(lambda s: s.strip(), parser.data))
        page_text = "".join(filtered_list[11:])

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

        """Create the default models linked to the sequences"""
        sp = ScientificProgram()
        country = Country()
05f66454   ALEXIS-PC\alexis   adding and updati...
62
63
        # (AKo) : usr_lvl isn't really needed for this simulator, as we're using the real db, we cannot associate a "dummy" user to a real user_level
        #usr_lvl = UserLevel()
2fdcd8bb   Alexis Koralewski   adapt tests to ne...
64
65
        institute = Institute.objects.create(name="test",quota=50.0)
        py_usr = PyrosUser(username="toto", country=country,institute=institute)
1cffbf1c   Etienne Pallier   moved pyros_djang...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        req = Request(scientific_program=sp, pyros_user=py_usr)

        scheduler = Scheduler()

        """Create the sequences"""
        sequences = []
        for raw_sequence in raw_sequences:
            sequence_array = raw_sequence.split(" ")
            id_seq = sequence_array[0]
            priority = int(sequence_array[2])
            ''' transforms the duration (seconds) in days (86,400s in a day)'''
            duration = Decimal(float(sequence_array[4]) / 86400.0)
            jd1 = Decimal("%.8f" % float(sequence_array[5]))
            jd2 = Decimal("%.8f" % float(sequence_array[6]))

            sequence = Sequence(request=req, status=Sequence.PLANNED,
                                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.simulateSchedule(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=lambda x: x[1].tsp)

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