Commit 2bedc6aa6df2d45cf54cb72ebdc54a7a8bc863e0
Exists in
master
and in
1 other branch
Merge branch 'dev'
Showing
20 changed files
with
194 additions
and
34 deletions
Show diff stats
.gitignore
install/REQUIREMENTS.txt
install/REQUIREMENTS_DEV.txt
install/REQUIREMENTS_SQLITE.txt
install/REQUIREMENTS_WINDOWS.txt
... | ... | @@ -4,7 +4,7 @@ anyjson==0.3.3 |
4 | 4 | billiard==3.3.0.23 |
5 | 5 | celery==3.1.23 |
6 | 6 | DateTime==4.1.1 |
7 | -Django==1.9.4 | |
7 | +Django==1.10.2 | |
8 | 8 | django-admin-tools==0.7.2 |
9 | 9 | django-bootstrap3==7.0.1 |
10 | 10 | django-celery==3.1.17 |
... | ... | @@ -23,4 +23,4 @@ six==1.10.0 |
23 | 23 | sqlparse==0.1.19 |
24 | 24 | Twisted==16.0.0 |
25 | 25 | virtualenv==15.0.1 |
26 | -zope.interface==4.1.1 | |
27 | 26 | \ No newline at end of file |
27 | +zope.interface==4.1.1 | ... | ... |
... | ... | @@ -0,0 +1,28 @@ |
1 | +File Logs are organized by module | |
2 | + | |
3 | +every file is reseted at pyros launch except pyros.log | |
4 | +You must clean this directory before pushing. | |
5 | +You can use the pyrosrun.sh clear_logs command. | |
6 | + | |
7 | +For example, logs for the module MONITORING must be in the file : | |
8 | + | |
9 | +> monitoring.logs | |
10 | + | |
11 | +The logs are formated like this : | |
12 | + | |
13 | +> '%(filename)s : %(lineno)s -> %(message)s' | |
14 | +> "filename : line -> message". | |
15 | + | |
16 | +To use the logger you must import logger.config | |
17 | + | |
18 | +> import logger.config as l | |
19 | +> log = l.setupLogger("name", "file_name") | |
20 | + | |
21 | +Basic log : | |
22 | + | |
23 | +> log.info('Your message') | |
24 | + | |
25 | +if you want to log in the file pyros.log you must use logging | |
26 | + | |
27 | +> import logger.config as l | |
28 | +> l.logging.info('Your message') | ... | ... |
pyrosrun.sh
... | ... | @@ -10,7 +10,8 @@ COMMANDS="\n |
10 | 10 | \t'simul_on' : Starts the simulators\n |
11 | 11 | \t'simul_off' : Stops the simulators\n |
12 | 12 | \t'start' : Starts the simulators then the celery workers, then the web server\n |
13 | -\t'stop' : Stops the celery workers then the simulators" | |
13 | +\t'stop' : Stops the celery workers then the simulators\n | |
14 | +\t'clean_logs' : Clear all pyros .log files from /logs" | |
14 | 15 | |
15 | 16 | NEEDED_COMMAND="One command is needed. Possible commands : $COMMANDS" |
16 | 17 | INVALID_COMMAND="Invalid command. Possible commands : $COMMANDS" |
... | ... | @@ -74,6 +75,11 @@ case "$1" in |
74 | 75 | ./kill_all.sh |
75 | 76 | cd - |
76 | 77 | ;; |
78 | + "clean_logs") | |
79 | + cd ../logs | |
80 | + rm -f *.log | |
81 | + cd - | |
82 | + ;; | |
77 | 83 | *) |
78 | 84 | echo -e $INVALID_COMMAND |
79 | 85 | ;; | ... | ... |
src/dashboard/templates/dashboard/system.html
... | ... | @@ -6,24 +6,24 @@ |
6 | 6 | background-color: #383838; |
7 | 7 | height: 300px; |
8 | 8 | } |
9 | - | |
9 | + | |
10 | 10 | hr { |
11 | 11 | height: 1px; |
12 | 12 | border-style: solid; |
13 | 13 | border-width: 1px 0 0 0; |
14 | 14 | border-color: white; |
15 | 15 | } |
16 | - | |
16 | + | |
17 | 17 | p, h1, h4, h5, hr { |
18 | 18 | color: white; |
19 | 19 | } |
20 | - | |
20 | + | |
21 | 21 | .fixed { |
22 | 22 | height: 340px; |
23 | 23 | overflow: hidden; |
24 | 24 | background-color: black; |
25 | 25 | } |
26 | - | |
26 | + | |
27 | 27 | li { |
28 | 28 | list-style-type: none; |
29 | 29 | } |
... | ... | @@ -31,17 +31,17 @@ |
31 | 31 | |
32 | 32 | <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> |
33 | 33 | <script> |
34 | - | |
35 | - function retrieve_logs() { | |
36 | - $('#logs').load("{% url "dashboard.views.system_retrieve_logs" %}" ); | |
37 | - | |
34 | + | |
35 | + function retrieve_logs() { | |
36 | + $('#logs').load("{% url 'system_retrieve_logs' %}"); | |
37 | + | |
38 | 38 | } |
39 | - | |
39 | + | |
40 | 40 | $(document).ready(function() { |
41 | 41 | retrieve_logs(); |
42 | 42 | setInterval(retrieve_logs, 1000); |
43 | 43 | }); |
44 | - | |
44 | + | |
45 | 45 | </script> |
46 | 46 | |
47 | 47 | {% endblock %} | ... | ... |
src/dashboard/views.py
... | ... | @@ -3,6 +3,9 @@ from django.core import urlresolvers |
3 | 3 | from django.contrib.auth.decorators import login_required |
4 | 4 | from common.models import Log |
5 | 5 | |
6 | +import logger.config as l | |
7 | +log = l.setupLogger("dashboard", "dashboard"); | |
8 | + | |
6 | 9 | @login_required |
7 | 10 | def users(request): |
8 | 11 | url_ = urlresolvers.reverse('admin:auth_user_changelist') |
... | ... | @@ -36,11 +39,10 @@ def devices(request): |
36 | 39 | def system(request): |
37 | 40 | return render(request, 'dashboard/system.html') |
38 | 41 | |
39 | - | |
40 | 42 | @login_required |
41 | 43 | def system_retrieve_logs(request): |
42 | 44 | ''' |
43 | - Called by the dashboard system page with ajax request every 1s, to get the logs and print them | |
45 | + Called by the dashboard system page with ajax request every seconds, to get the logs and print them | |
44 | 46 | ''' |
45 | 47 | if request.is_ajax(): |
46 | 48 | alert_logs = Log.objects.filter(agent='Alert manager') | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +from django.conf import settings | |
2 | +import logging | |
3 | +import sys | |
4 | + | |
5 | +# maybe reset files with a variable from settings for the logging | |
6 | +# if (settings.RESETLOGS): | |
7 | + | |
8 | + | |
9 | +logging.basicConfig(filename='%s/../logs/pyros.log'%(settings.BASE_DIR), format='-> At : [%(asctime)s]\n\t By module [%(module)s] logger : [%(name)s] : "%(message)s"', level=logging.DEBUG) | |
10 | + | |
11 | +if (settings.DEBUG): | |
12 | + logging.info('Logger instantiated') | |
13 | + | |
14 | +def setupLogger(logger_name, log_file, level=logging.INFO): | |
15 | + l = logging.getLogger(logger_name) | |
16 | + formatter = logging.Formatter('[%(message)s] at %(filename)s : %(lineno)s') | |
17 | + fileHandler = logging.FileHandler('%s/../logs/%s.log'%(settings.BASE_DIR, log_file), mode='w') | |
18 | + fileHandler.setFormatter(formatter) | |
19 | + # streamHandler = logging.StreamHandler() | |
20 | + # streamHandler.setFormatter(formatter) | |
21 | + | |
22 | + l.setLevel(level) | |
23 | + l.addHandler(fileHandler) | |
24 | + # l.addHandler(streamHandler) | |
25 | + return (logging.getLogger(logger_name)) | ... | ... |
31.4 KB
8.64 KB
2.27 KB
src/pyros/settings.py
... | ... | @@ -183,8 +183,12 @@ USE_L10N = True |
183 | 183 | USE_TZ = True |
184 | 184 | |
185 | 185 | |
186 | +# To find the media files {{ MEDIA_URL }} | |
187 | +MEDIA_URL = '/public/static/media/' | |
188 | + | |
189 | + | |
186 | 190 | # To find the static files in the app/static/ap/... folders |
187 | -STATIC_URL = '/static/' | |
191 | +STATIC_URL = '/public/static/' | |
188 | 192 | |
189 | 193 | # To find the static files in src/static/. Any local directory can be added to this list. |
190 | 194 | STATICFILES_DIRS = ( | ... | ... |
src/routine_manager/forms.py
1 | +from django.conf import settings | |
1 | 2 | from django import forms |
2 | 3 | from common.models import * |
3 | 4 | from routine_manager.validators import check_album_validity |
4 | 5 | |
6 | +import logging | |
7 | +log = logging.getLogger("routine_manager-views") | |
8 | + | |
5 | 9 | class RequestForm(forms.ModelForm): |
6 | 10 | """ |
7 | 11 | Form for Request edition |
... | ... | @@ -13,6 +17,8 @@ class RequestForm(forms.ModelForm): |
13 | 17 | |
14 | 18 | def __init__(self, *args, readonly=False, **kwargs): |
15 | 19 | super(RequestForm, self).__init__(*args, **kwargs) |
20 | + if (settings.DEBUG): | |
21 | + log.info("From __init__ of RequestForm") | |
16 | 22 | for field in self.fields.values(): |
17 | 23 | if readonly == True: |
18 | 24 | field.widget.attrs['readonly'] = True |
... | ... | @@ -40,6 +46,8 @@ class SequenceForm(forms.ModelForm): |
40 | 46 | |
41 | 47 | def __init__(self, *args, readonly=False, **kwargs): |
42 | 48 | super(SequenceForm, self).__init__(*args, **kwargs) |
49 | + if (settings.DEBUG): | |
50 | + log.info("From __init__ of SequenceForm") | |
43 | 51 | for field in self.fields.values(): |
44 | 52 | if readonly == True: |
45 | 53 | field.widget.attrs['readonly'] = True |
... | ... | @@ -55,6 +63,8 @@ class SequenceForm(forms.ModelForm): |
55 | 63 | |
56 | 64 | def save(self): |
57 | 65 | seq = super(SequenceForm, self).save() |
66 | + if (settings.DEBUG): | |
67 | + log.info("From save function of SequenceForm") | |
58 | 68 | if self.cleaned_data["start_expo_pref"] == "IM": |
59 | 69 | seq.t_prefered = -1; |
60 | 70 | elif self.cleaned_data["start_expo_pref"] == "BE": |
... | ... | @@ -78,6 +88,8 @@ class AlbumForm(forms.ModelForm): |
78 | 88 | |
79 | 89 | def __init__(self, *args, readonly=False, **kwargs): |
80 | 90 | super(AlbumForm, self).__init__(*args, **kwargs) |
91 | + if (settings.DEBUG): | |
92 | + log.info("From __init__ of AlbumForm") | |
81 | 93 | for field in self.fields.values(): |
82 | 94 | if readonly == True: |
83 | 95 | field.widget.attrs['readonly'] = True |
... | ... | @@ -98,6 +110,8 @@ class PlanForm(forms.ModelForm): |
98 | 110 | |
99 | 111 | def __init__(self, *args, readonly=False, **kwargs): |
100 | 112 | super(PlanForm, self).__init__(*args, **kwargs) |
113 | + if (settings.DEBUG): | |
114 | + log.info("From __init__ of PlanForm") | |
101 | 115 | plan = kwargs["instance"] |
102 | 116 | |
103 | 117 | self.fields["filter"].queryset = plan.album.detector.filter_wheel.filters |
... | ... | @@ -133,6 +147,8 @@ class PlanForm(forms.ModelForm): |
133 | 147 | |
134 | 148 | def save(self): |
135 | 149 | plan = super(PlanForm, self).save() |
150 | + if (settings.DEBUG): | |
151 | + log.info("From save of PlanForm") | |
136 | 152 | plan.complete = True |
137 | 153 | plan.duration = self.cleaned_data["duration"] / 86400 |
138 | 154 | plan.save() | ... | ... |
src/routine_manager/views.py
... | ... | @@ -7,6 +7,11 @@ from .validators import check_plan_validity, check_album_validity, check_sequenc |
7 | 7 | from .RequestSerializer import RequestSerializer |
8 | 8 | import scheduler |
9 | 9 | |
10 | +""" logger """ | |
11 | +from django.conf import settings | |
12 | +import logger.config as l | |
13 | +log = l.setupLogger("routine_manager-views", "routine_manager-views") | |
14 | + | |
10 | 15 | """ XML Export / Import utils """ |
11 | 16 | from wsgiref.util import FileWrapper |
12 | 17 | from django.utils.encoding import smart_str |
... | ... | @@ -24,6 +29,9 @@ def requests_list(request, status=0, message=""): |
24 | 29 | Retrieves and display the routines list (routine manager main page) |
25 | 30 | """ |
26 | 31 | |
32 | + if settings.DEBUG: | |
33 | + log.info("From requests_list") | |
34 | + | |
27 | 35 | if status == "-1": |
28 | 36 | error = True |
29 | 37 | elif status == "1": |
... | ... | @@ -54,6 +62,9 @@ def action_request(request, req_id, action, status=0, message=""): |
54 | 62 | req = Request.objects.get(id=req_id) |
55 | 63 | depth_level = 1 |
56 | 64 | |
65 | + if settings.DEBUG: | |
66 | + log.info("From action_request") | |
67 | + | |
57 | 68 | if status == "-1": |
58 | 69 | error = True |
59 | 70 | elif status == "1": |
... | ... | @@ -91,6 +102,9 @@ def request_validate(request, req_id): |
91 | 102 | req = Request.objects.get(id=req_id) |
92 | 103 | form = RequestForm(instance=req, data=request.POST) |
93 | 104 | |
105 | + if (settings.DEBUG): | |
106 | + log.info("From request_validate") | |
107 | + | |
94 | 108 | if action == "cancel": |
95 | 109 | if req.name == "New request": |
96 | 110 | req.delete() |
... | ... | @@ -123,6 +137,9 @@ def action_sequence(request, seq_id, action, status=0, message=""): |
123 | 137 | req_id = req.id |
124 | 138 | depth_level = 2 |
125 | 139 | |
140 | + if (settings.DEBUG): | |
141 | + log.info("From action_sequence") | |
142 | + | |
126 | 143 | if status == "-1": |
127 | 144 | error = True |
128 | 145 | elif status == "1": |
... | ... | @@ -158,6 +175,9 @@ def sequence_validate(request, seq_id): |
158 | 175 | Possible actions : Cancel, Save, Save and add album, Delete |
159 | 176 | """ |
160 | 177 | |
178 | + if (settings.DEBUG): | |
179 | + log.info("From sequence_validate") | |
180 | + | |
161 | 181 | seq_id = int(seq_id) |
162 | 182 | action = request.POST.get("action") |
163 | 183 | seq = Sequence.objects.get(id=seq_id) |
... | ... | @@ -197,6 +217,9 @@ def action_album(request, alb_id, action, status=0, message=""): |
197 | 217 | seq_id = alb.sequence.id |
198 | 218 | depth_level = 3 |
199 | 219 | |
220 | + if (settings.DEBUG): | |
221 | + log.info("From action_album") | |
222 | + | |
200 | 223 | if status == "-1": |
201 | 224 | error = True |
202 | 225 | elif status == "1": |
... | ... | @@ -235,6 +258,10 @@ def album_validate(request, alb_id): |
235 | 258 | alb = Album.objects.get(id=alb_id) |
236 | 259 | form = AlbumForm(instance=alb, data=request.POST) |
237 | 260 | |
261 | + if (settings.DEBUG): | |
262 | + log.info("From album_validate") | |
263 | + | |
264 | + | |
238 | 265 | if action == "cancel": |
239 | 266 | if alb.name == "New album": |
240 | 267 | alb.delete() |
... | ... | @@ -272,6 +299,9 @@ def action_plan(request, plan_id, action, status=0, message=""): |
272 | 299 | alb_id = plan.album.id |
273 | 300 | depth_level = 4 |
274 | 301 | |
302 | + if (settings.DEBUG): | |
303 | + log.info("From action_plan") | |
304 | + | |
275 | 305 | if status == "-1": |
276 | 306 | error = True |
277 | 307 | elif status == "1": |
... | ... | @@ -310,6 +340,10 @@ def plan_validate(request, plan_id): |
310 | 340 | plan = Plan.objects.get(id=plan_id) |
311 | 341 | form = PlanForm(instance=plan, data=request.POST) |
312 | 342 | |
343 | + if (settings.DEBUG): | |
344 | + log.info("From plan_validate") | |
345 | + | |
346 | + | |
313 | 347 | if action == "cancel": |
314 | 348 | if plan.name == "New plan": |
315 | 349 | plan.delete() |
... | ... | @@ -376,6 +410,9 @@ def submit_request(request, req_id, redir): |
376 | 410 | Submits a request and its sequences for scheduling |
377 | 411 | """ |
378 | 412 | |
413 | + if (settings.DEBUG): | |
414 | + log.info("From submit_request") | |
415 | + | |
379 | 416 | req = Request.objects.get(id=req_id) |
380 | 417 | error = False |
381 | 418 | if req.complete == False: |
... | ... | @@ -409,6 +446,9 @@ def unsubmit_request(request, req_id): |
409 | 446 | Unsubmits a request and remove its sequences from scheduling |
410 | 447 | """ |
411 | 448 | |
449 | + if (settings.DEBUG): | |
450 | + log.info("From unsubmit_request") | |
451 | + | |
412 | 452 | req = Request.objects.get(id=req_id) |
413 | 453 | |
414 | 454 | # TODO: uncomment pour la production |
... | ... | @@ -438,6 +478,9 @@ def export_request(request, req_id): |
438 | 478 | Create an XML file with the given request, and send a download request to the user |
439 | 479 | """ |
440 | 480 | |
481 | + if (settings.DEBUG): | |
482 | + log.info("From export_request") | |
483 | + | |
441 | 484 | req = Request.objects.get(id=req_id) |
442 | 485 | if req.complete == False: |
443 | 486 | message = "Request must be completely valid to be serialized" |
... | ... | @@ -463,6 +506,9 @@ def import_request(request): |
463 | 506 | Don't do anything if there is a single error into the file |
464 | 507 | """ |
465 | 508 | |
509 | + if (settings.DEBUG): | |
510 | + log.info("From import_request") | |
511 | + | |
466 | 512 | if request.method == "POST": |
467 | 513 | file = request.FILES.get("request_file") |
468 | 514 | if file is None: | ... | ... |
src/scheduler/templates/scheduler/current_schedule.html
... | ... | @@ -25,16 +25,22 @@ |
25 | 25 | |
26 | 26 | <div class="row"> |
27 | 27 | |
28 | - <h3>Schedule info</h3> | |
28 | + <h2>Schedule info</h2> | |
29 | + | |
30 | + {% if schedule == None %} | |
31 | + | |
32 | + <h3>There is no schedule in the database</h3> | |
33 | + | |
34 | + {% else %} | |
29 | 35 | |
30 | 36 | <div class="well"> |
31 | 37 | Schedule Night Start: {{schedule.plan_night_start|jdtodate}} |
32 | 38 | </div> |
33 | - | |
39 | + | |
34 | 40 | <div class="well"> |
35 | 41 | Schedule Start: {{schedule.plan_start|jdtodate}} |
36 | 42 | </div> |
37 | - | |
43 | + | |
38 | 44 | <div class="well"> |
39 | 45 | Schedule End: {{schedule.plan_end|jdtodate}} |
40 | 46 | </div> |
... | ... | @@ -53,15 +59,15 @@ |
53 | 59 | |
54 | 60 | </div> |
55 | 61 | |
56 | - | |
57 | 62 | <div class="row"> |
58 | 63 | <h3>List of Sequences - <span class="label label-primary">{{sequences|length}}</span></h3> |
59 | - | |
64 | + | |
60 | 65 | <div class="table-responsive"> |
61 | 66 | <table |
62 | 67 | class="table table-bordered table-hover table-striped tablesorter"> |
63 | 68 | <thead> |
64 | 69 | <tr> |
70 | + <th>State <i class="fa fa-sort"></i></th> | |
65 | 71 | <th>ID <i class="fa fa-sort"></i></th> |
66 | 72 | <th>Owner <i class="fa fa-sort"></i></th> |
67 | 73 | <th>Start <i class="fa fa-sort"></i></th> |
... | ... | @@ -75,8 +81,18 @@ |
75 | 81 | </thead> |
76 | 82 | <tbody> |
77 | 83 | <!-- sequence is a tuple (sequence, shs) --> |
84 | + {% load static %} | |
78 | 85 | {% for sequence in sequences %} |
79 | 86 | <tr> |
87 | + <td> | |
88 | + {% if sequence.processing == 1 %} | |
89 | + <img src="{% static "media/arrow_green.png" %}" alt="html5" height="30" width="30" style="margin-left:25%"/> | |
90 | + {% elif sequence.processing == 0 and sequence.status == "EXECUTED" %} | |
91 | + <img src="{% static "media/validate.jpg" %}" alt="html5" height="30" width="30" style="margin-left:25%"/> | |
92 | + {% else %} | |
93 | + <img src="{% static "media/error.png" %}" alt="{{sequence.processing}}" height="30" width="30" style="margin-left:25%"/> | |
94 | + {% endif %} | |
95 | + </td> | |
80 | 96 | <td>{{ sequence.0.id }}</td> |
81 | 97 | <td>{{ sequence.0.request.pyros_user.user.username }}</td> |
82 | 98 | <td>{{ sequence.1.tsp|jdtodate }}</td> |
... | ... | @@ -92,8 +108,8 @@ |
92 | 108 | </table> |
93 | 109 | </div> |
94 | 110 | </div> |
111 | + {% endif %} | |
95 | 112 | |
96 | 113 | </div><!-- /#page-wrapper --> |
97 | 114 | |
98 | 115 | {% endblock %} |
99 | - | |
100 | 116 | \ No newline at end of file | ... | ... |
src/scheduler/views.py
... | ... | @@ -8,13 +8,19 @@ SIMULATION_FILE = 'file:./scheduler/sequences_cador.html' |
8 | 8 | |
9 | 9 | @login_required |
10 | 10 | def current_schedule(request): |
11 | - schedule = Schedule.objects.order_by("-created")[0] | |
12 | - shs_list = schedule.shs.all() | |
13 | - sequences = [(shs.sequence, shs) for shs in shs_list] | |
14 | - | |
15 | - nb_scheduled_sequences = len(shs_list) | |
16 | - executed_sequences = len([shs for shs in shs_list if shs.status == Sequence.EXECUTED]) | |
17 | - | |
11 | + if (len(Schedule.objects.all()) > 0): # checking if the schedule is empty | |
12 | + schedule = Schedule.objects.order_by("-created")[0] # Sorting Schedule | |
13 | + shs_list = schedule.shs.all() # getting all the schedule has sequences references | |
14 | + sequences = [(shs.sequence, shs) for shs in shs_list] # getting all sequences | |
15 | + | |
16 | + nb_scheduled_sequences = len(shs_list) | |
17 | + executed_sequences = len([shs for shs in shs_list if shs.status == Sequence.EXECUTED]) | |
18 | + else: # if empty set everything to 0 / None (variables are checked in src/templates/scheduler/current_schedule.html) | |
19 | + schedule = None | |
20 | + sequences = None | |
21 | + shs_list = None | |
22 | + nb_scheduled_sequences = 0 | |
23 | + executed_sequences = 0 | |
18 | 24 | |
19 | 25 | return render(request, 'scheduler/current_schedule.html', {'sequences' : sequences, 'schedule' : schedule, 'nb_scheduled_sequences' : nb_scheduled_sequences, 'executed_sequences' : executed_sequences}) |
20 | 26 | |
... | ... | @@ -31,4 +37,3 @@ def schedule_simulation(request): |
31 | 37 | |
32 | 38 | sequences.sort(key=lambda seq: seq[1].tsp) |
33 | 39 | return render(request, 'scheduler/current_schedule.html', {'sequences' : sequences, 'schedule' : schedule, 'nb_scheduled_sequences' : nb_scheduled_sequences, 'executed_sequences' : executed_sequences}) |
34 | - | ... | ... |