Commit 2bedc6aa6df2d45cf54cb72ebdc54a7a8bc863e0

Authored by Jeremy
2 parents 2ec87e0f 4891bfbd
Exists in master and in 1 other branch dev

Merge branch 'dev'

.gitignore
... ... @@ -7,4 +7,5 @@ __pycache__
7 7 src/images/*_*
8 8 src/alert_manager/twistd*
9 9 src/db.sqlite3
10   -src/common/migrations/0*
11 10 \ No newline at end of file
  11 +src/common/migrations/0*
  12 +logs/*.log
12 13 \ No newline at end of file
... ...
install/REQUIREMENTS.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
... ...
install/REQUIREMENTS_DEV.txt
... ... @@ -4,7 +4,7 @@ billiard==3.3.0.23
4 4 celery==3.1.23
5 5 astroid==1.4.5
6 6 colorama==0.3.7
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-debug-toolbar==1.4
... ...
install/REQUIREMENTS_SQLITE.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
... ...
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
... ...
logs/Readme.md 0 → 100644
... ... @@ -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')
... ...
src/logger/__init__.py 0 → 100644
src/logger/config.py 0 → 100644
... ... @@ -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))
... ...
src/misc/static/media/arrow_green.png 0 → 100644

31.4 KB

src/misc/static/media/error.png 0 → 100644

8.64 KB

src/misc/static/media/validate.jpg 0 → 100644

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/tests_routine_simulator.py 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +from django.test import TestCase
  2 +from common.models import *
  3 +import os
  4 +
  5 +class SimulatorRoutine(TestCase):
  6 + def setUp(self):
  7 + pass
  8 +
  9 + def test_creation(self):
  10 + # self.assertEqual(1, 0, "Standard error")
  11 + pass
... ...
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=&quot;&quot;):
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=&quot;&quot;):
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=&quot;&quot;):
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=&quot;&quot;):
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=&quot;&quot;):
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 = &#39;file:./scheduler/sequences_cador.html&#39;
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   -
... ...