Commit c1a58d54e38d58fba3f23c100934bb3ea6e5d03c
1 parent
e186733f
Exists in
dev
Adding views to view agents status, agent log and a view to change software mode.
Showing
9 changed files
with
213 additions
and
10 deletions
Show diff stats
src/core/pyros_django/dashboard/forms.py
1 | 1 | from django import forms |
2 | -from common.models import Config, PyrosUser, UserLevel | |
2 | +from common.models import Config, PyrosUser, UserLevel, Majordome | |
3 | 3 | |
4 | 4 | class ConfigForm(forms.ModelForm): |
5 | 5 | class Meta: |
... | ... | @@ -29,6 +29,15 @@ class ConfigForm(forms.ModelForm): |
29 | 29 | 'plc_ip_address': ('Ip Address :'), |
30 | 30 | } |
31 | 31 | |
32 | +class MajordomeForm(forms.ModelForm): | |
33 | + class Meta: | |
34 | + model = Majordome | |
35 | + fields = ( | |
36 | + "soft_mode", | |
37 | + ) | |
38 | + labels = { | |
39 | + "soft_mode": ("Software mode :") | |
40 | + } | |
32 | 41 | class UserForm(forms.ModelForm): |
33 | 42 | class Meta: |
34 | 43 | model = PyrosUser | ... | ... |
src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html
0 → 100644
... | ... | @@ -0,0 +1,45 @@ |
1 | +{% extends "base.html" %} | |
2 | + | |
3 | +{% load static %} | |
4 | + | |
5 | +{% block title %} | |
6 | + Agent {{ agent_name }} detail : | |
7 | +{% endblock %} | |
8 | + | |
9 | +{% block content %} | |
10 | +<style> | |
11 | + #log_content{ | |
12 | + width: 80vw; | |
13 | + height: 80vh; | |
14 | + overflow-y: scroll | |
15 | + | |
16 | + } | |
17 | +</style> | |
18 | +{% if not error_message %} | |
19 | +<h1> Last logs of {{ agent_name }} : </h1> | |
20 | +<pre id="log_content"> | |
21 | +{% for line in log_last_30_lines %} | |
22 | +{{ line }} | |
23 | + | |
24 | +{% endfor %} | |
25 | +</pre> | |
26 | +{% else %} | |
27 | +<h1> {{ error_message }} </h1> | |
28 | +{% endif %} | |
29 | +<a class="btn btn-info" href={% url 'agents_state' %}> Go back to agents state </a> | |
30 | +{% csrf_token %} | |
31 | +<script> | |
32 | + function get_last_log(){ | |
33 | + | |
34 | + $.post('{% url 'retrieve_log_content' %}',{"agent_name":'{{agent_name}}',csrfmiddlewaretoken: '{{ csrf_token }}'},function(data){ | |
35 | + var new_logs = ""; | |
36 | + for(var line of data){ | |
37 | + new_logs += line; | |
38 | + } | |
39 | + $("#log_content").text(new_logs); | |
40 | + }); | |
41 | + } | |
42 | + | |
43 | + setInterval(get_last_log,2000); | |
44 | +</script> | |
45 | +{% endblock %} | |
0 | 46 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/templates/dashboard/agents_state.html
0 → 100644
... | ... | @@ -0,0 +1,33 @@ |
1 | +{% extends "base.html" %} | |
2 | + | |
3 | +{% load static %} | |
4 | + | |
5 | +{% block title %} | |
6 | + Agents status : | |
7 | +{% endblock %} | |
8 | + | |
9 | +{% block content %} | |
10 | +<div class="table-responsive"> | |
11 | + <table | |
12 | + class="table table-bordered table-hover table-striped tablesorter"> | |
13 | + <thead> | |
14 | + <tr> | |
15 | + <th> Agent </th> | |
16 | + <th> Status </th> | |
17 | + <th> Mode </th> | |
18 | + <th> Udpated </th> | |
19 | + </tr> | |
20 | + </thead> | |
21 | + <tbody> | |
22 | + {% for agent in agents %} | |
23 | + <tr> | |
24 | + <td> <a href={% url 'agent_detail' agent.name %}>{{ agent.name }}</a></td> | |
25 | + <td> {{ agent.status }}</td> | |
26 | + <td> {{ agent.mode }}</td> | |
27 | + <td> {{ agent.updated }} </td> | |
28 | + </tr> | |
29 | + {% endfor %} | |
30 | + </tbody> | |
31 | + </table> | |
32 | +</div> | |
33 | +{% endblock %} | |
0 | 34 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/templates/dashboard/settings.html
... | ... | @@ -125,6 +125,28 @@ |
125 | 125 | </div> |
126 | 126 | </li> |
127 | 127 | {% endif %} |
128 | + {% if CAN_CHANGE_SOFT_MODE %} | |
129 | + <li> | |
130 | + <div class="setting-box setting-img-configuration"> | |
131 | + <a href="{% url "soft_mode" %}"> | |
132 | + <div class="setting-info"> | |
133 | + <h3>Software mode</h3> | |
134 | + <img src="{% static "media/configuration.png" %}" alt="html5"/> | |
135 | + </div></a> | |
136 | + </div> | |
137 | + </li> | |
138 | + {% endif %} | |
139 | + {% if CAN_VIEW_AGENTS_STATE %} | |
140 | + <li> | |
141 | + <div class="setting-box setting-img-configuration"> | |
142 | + <a href="{% url "agents_state" %}"> | |
143 | + <div class="setting-info"> | |
144 | + <h3>Agents state</h3> | |
145 | + <img src="{% static "media/configuration.png" %}" alt="html5"/> | |
146 | + </div></a> | |
147 | + </div> | |
148 | + </li> | |
149 | + {% endif %} | |
128 | 150 | <li> |
129 | 151 | <div class="setting-box setting-img-configuration"> |
130 | 152 | <a href="{% url "obs_global_config" %}"> | ... | ... |
src/core/pyros_django/dashboard/templates/dashboard/soft_mode.html
0 → 100644
... | ... | @@ -0,0 +1,20 @@ |
1 | +{% extends "base.html" %} | |
2 | + | |
3 | +{% load static %} | |
4 | + | |
5 | +{% block title %} | |
6 | + Observation System Status : | |
7 | +{% endblock %} | |
8 | + | |
9 | +{% block content %} | |
10 | + | |
11 | +<form id="soft_mode_form" action="" method="post"> | |
12 | +{% csrf_token %} | |
13 | +<ul> | |
14 | + <li>{{ form.as_p }}</li> | |
15 | + | |
16 | +</ul> | |
17 | +<input type="submit" value="Submit"> | |
18 | +</form> | |
19 | + | |
20 | +{% endblock %} | |
0 | 21 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/templatetags/tags.py
1 | 1 | from django import template |
2 | 2 | from django.contrib.auth.models import User |
3 | 3 | #from common.models import Config, PyrosUser, PyrosState |
4 | -from common.models import Config, PyrosUser, UserLevel | |
4 | +from common.models import Config, PyrosUser, UserLevel, Majordome | |
5 | 5 | from datetime import date |
6 | 6 | from django.conf import settings |
7 | 7 | |
... | ... | @@ -133,4 +133,14 @@ def to_int(value): |
133 | 133 | |
134 | 134 | @register.filter |
135 | 135 | def index(indexable, i): |
136 | - return indexable[i] | |
137 | 136 | \ No newline at end of file |
137 | + return indexable[i] | |
138 | + | |
139 | +@register.simple_tag | |
140 | +def soft_mode(request): | |
141 | + try: | |
142 | + soft_mode = Majordome.objects.get(id=1).soft_mode | |
143 | + if soft_mode: | |
144 | + return soft_mode | |
145 | + raise Majordome.DoesNotExist | |
146 | + except Majordome.DoesNotExist: | |
147 | + return ("No Majordome instance in DB") | |
138 | 148 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/urls.py
... | ... | @@ -47,5 +47,9 @@ urlpatterns = [ |
47 | 47 | path('simulator_lock', views.simulator_switch_lock, name="simulator_switch_lock"), |
48 | 48 | path('simulator_majordome_restart', views.simulator_majordome_restart, name="simulator_majordome_restart"), |
49 | 49 | path('simulator_majordome_shutdown', views.simulator_majordome_shutdown, name="simulator_majordome_shutdown"), |
50 | - path('simulator_switch_globalmode', views.simulator_switch_globalMode, name="simulator_switch_globalMode") | |
50 | + path('simulator_switch_globalmode', views.simulator_switch_globalMode, name="simulator_switch_globalMode"), | |
51 | + path('soft_mode', views.soft_mode, name="soft_mode"), | |
52 | + path('agents_state', views.agents_state, name="agents_state"), | |
53 | + path('agent_detail/<str:agent_name>', views.agent_detail, name="agent_detail"), | |
54 | + path('retrieve_log_content', views.retrieve_log_content, name="retrieve_log_content"), | |
51 | 55 | ] | ... | ... |
src/core/pyros_django/dashboard/views.py
... | ... | @@ -5,12 +5,12 @@ from django.shortcuts import render, redirect |
5 | 5 | |
6 | 6 | from django.contrib.auth.decorators import login_required |
7 | 7 | import datetime |
8 | -from common.models import Log, SP_Period_Guest, WeatherWatch, SiteWatch, ScientificProgram, Config, PyrosUser, PlcDeviceStatus, Telescope, TelescopeCommand, UserLevel, WeatherWatchHistory | |
8 | +from common.models import Log, SP_Period_Guest, WeatherWatch, SiteWatch, ScientificProgram, Config, PyrosUser, PlcDeviceStatus, Telescope, TelescopeCommand, UserLevel, WeatherWatchHistory, Majordome, AgentSurvey | |
9 | 9 | from django.core import serializers |
10 | 10 | import utils.Logger as l |
11 | 11 | from django.forms import modelformset_factory |
12 | 12 | from django.http import HttpResponseRedirect |
13 | -from dashboard.forms import ConfigForm, UserForm | |
13 | +from dashboard.forms import ConfigForm, UserForm, MajordomeForm | |
14 | 14 | from dashboard.decorator import level_required |
15 | 15 | from django.views.generic.edit import UpdateView |
16 | 16 | from django.shortcuts import get_object_or_404 |
... | ... | @@ -32,6 +32,7 @@ sys.path.append("../../..") |
32 | 32 | from config.pyros.config_pyros import ConfigPyros |
33 | 33 | #import utils.celme as celme |
34 | 34 | import vendor.guitastro as guitastro |
35 | +from django.contrib import messages | |
35 | 36 | |
36 | 37 | |
37 | 38 | from collections import OrderedDict |
... | ... | @@ -177,8 +178,66 @@ def settings(request): |
177 | 178 | View called to see the settings (for the software, observatory, users...) page |
178 | 179 | ''' |
179 | 180 | CAN_VIEW_PERIOD = request.session.get("role") in ("Admin", "Unit-PI", "Observer", "Unit-board") |
181 | + CAN_CHANGE_SOFT_MODE = request.session.get("role") in ("Admin", "Unit-PI", "Operator") | |
182 | + CAN_VIEW_AGENTS_STATE = request.session.get("role") in ("Admin", "Unit-PI", "Operator") | |
180 | 183 | return(render(request, "dashboard/settings.html", locals())) |
181 | 184 | |
185 | +def agent_detail(request, agent_name): | |
186 | + agent_log_path = os.path.join(os.environ.get("PROJECT_ROOT_PATH"),f"logs/{agent_name}/",f"{agent_name}.log") | |
187 | + try: | |
188 | + log = open(agent_log_path,"r") | |
189 | + log_content = log.readlines() | |
190 | + | |
191 | + if len(log_content) > 30: | |
192 | + log_last_30_lines = log_content[-30:] | |
193 | + | |
194 | + else: | |
195 | + log_last_30_lines = log_content | |
196 | + log.close() | |
197 | + except FileNotFoundError: | |
198 | + error_message = f"Cannot find agent logs. (Agent log path tried : {agent_log_path})" | |
199 | + return render(request, "dashboard/agent_detail.html", locals()) | |
200 | + | |
201 | +def retrieve_log_content(request): | |
202 | + if request.POST: | |
203 | + agent_name = request.POST.get("agent_name") | |
204 | + agent_log_path = os.path.join(os.environ.get("PROJECT_ROOT_PATH"),f"logs/{agent_name}/",f"{agent_name}.log") | |
205 | + try: | |
206 | + log = open(agent_log_path,"r") | |
207 | + log_content = log.readlines() | |
208 | + | |
209 | + if len(log_content) > 30: | |
210 | + log_last_30_lines = log_content[-30:] | |
211 | + | |
212 | + else: | |
213 | + log_last_30_lines = log_content | |
214 | + log.close() | |
215 | + return HttpResponse(log_last_30_lines) | |
216 | + except FileNotFoundError: | |
217 | + error_message = f"Cannot find agent logs. (Agent log path tried : {agent_log_path})" | |
218 | + return HttpResponse(error_message) | |
219 | + | |
220 | + | |
221 | +@login_required | |
222 | +@level_required("Admin", "Unit-PI", "Operator") | |
223 | +def soft_mode(request): | |
224 | + if request.POST: | |
225 | + form = MajordomeForm(request.POST) | |
226 | + if form.is_valid(): | |
227 | + obj, created = Majordome.objects.get_or_create(id=1) | |
228 | + obj.soft_mode = form.cleaned_data["soft_mode"] | |
229 | + obj.save() | |
230 | + new_soft_mode = form.instance.soft_mode | |
231 | + messages.add_message(request, messages.INFO, f"Changed observatory mode to {new_soft_mode}") | |
232 | + obj, created = Majordome.objects.get_or_create(id=1) | |
233 | + form = MajordomeForm(instance=obj) | |
234 | + soft_mode = obj.soft_mode | |
235 | + is_auto = soft_mode == Majordome.AUTO_MODE | |
236 | + return render(request, "dashboard/soft_mode.html", locals()) | |
237 | + | |
238 | +def agents_state(request): | |
239 | + agents = AgentSurvey.objects.all() | |
240 | + return render(request, "dashboard/agents_state.html", locals()) | |
182 | 241 | def retrieve_main_icon(request): |
183 | 242 | if request.is_ajax(): |
184 | 243 | try: | ... | ... |
src/core/pyros_django/misc/templates/base.html
... | ... | @@ -339,12 +339,13 @@ footer{ |
339 | 339 | <li><a class="nav-brand" id="observatory_state" href="{% url "site" %}"><img id="obs_state_img"></a></li> |
340 | 340 | <li><a class="nav-brand" id="wind" href="{% url "weather" %}"><img id="wind_img"></a></li> |
341 | 341 | <li><a class="nav-brand" id="weather" href="{% url "weather" %}"><img id="weather_img"></a></li> |
342 | - <li><a class="nav-brand" id="globalmode" style="color: black;"> <p id="text_globalmode" >{% global_mode request %} </p></a> | |
343 | - {% endcomment %} | |
344 | - <ul class="navbar-nav ml-auto"> | |
342 | + <li><a class="nav-brand" id="globalmode" style="colr: black;"> <p id="text_globalmode" >{% global_mode request %} </p></a> | |
343 | + {% endcomment %} | |
344 | + <ul class="navbar-nav ml-auto"> | |
345 | + <li>Software mode : <a class="nav-brand" id="soft_mode" style="color: black;"> <p id="text_globalmode" >{% soft_mode request %} </p></a> | |
345 | 346 | |
346 | 347 | {% if request.user.is_authenticated %} |
347 | - <li class="nav-item dropdown no-arrow"> | |
348 | + <li class="nav-item dropdown no-arrow" style="border-left: 1px dotted black;margin-left: 0.5vw"> | |
348 | 349 | {# <a class="nav-brand" href="{% url "user_detail" user.id %}" style="color: black;"> #} |
349 | 350 | |
350 | 351 | <a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ... | ... |