Commit c1a58d54e38d58fba3f23c100934bb3ea6e5d03c

Authored by Alexis Koralewski
1 parent e186733f
Exists in dev

Adding views to view agents status, agent log and a view to change software mode.

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(&quot;../../..&quot;)
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">
... ...