diff options
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | sbhs/forms.py | 19 | ||||
-rw-r--r-- | sbhs/management/commands/update_boards.py | 38 | ||||
-rw-r--r-- | sbhs/models.py | 49 | ||||
-rw-r--r-- | sbhs/templates/dashboard/dashboard_index.html | 10 | ||||
-rw-r--r-- | sbhs/templates/dashboard/profile.html | 45 | ||||
-rw-r--r-- | sbhs/templates/dashboard/test_boards.html | 143 | ||||
-rw-r--r-- | sbhs/templates/dashboard/update_mid.html | 12 | ||||
-rw-r--r-- | sbhs/templates/nav.html | 0 | ||||
-rw-r--r-- | sbhs/templates/slot/create_slot.html (renamed from sbhs/templates/slot/new.html) | 26 | ||||
-rw-r--r-- | sbhs/templatetags/custom_filter.py | 14 | ||||
-rw-r--r-- | sbhs/urls.py | 13 | ||||
-rw-r--r-- | sbhs/views.py | 473 | ||||
-rw-r--r-- | sbhs_server/settings.py | 25 |
14 files changed, 489 insertions, 379 deletions
diff --git a/requirements.txt b/requirements.txt index 09ac99a..7b8fa7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ django==1.11.15 django-crispy-forms==1.7.2 - diff --git a/sbhs/forms.py b/sbhs/forms.py index 7efadb5..70fa189 100644 --- a/sbhs/forms.py +++ b/sbhs/forms.py @@ -5,12 +5,12 @@ except ImportError: from string import digits, punctuation from django import forms -from django.conf import settings +from sbhs_server import settings from django.utils import timezone from django.contrib.auth.models import User -from .models import Profile, Slot +from .models import Profile, Slot, UserBoard from .send_emails import generate_activation_key UNAME_CHARS = letters + "._" + digits @@ -134,4 +134,17 @@ class FilterLogsForm(forms.ModelForm): 'name':'end_date', 'readonly':'readonly' }), - }
\ No newline at end of file + } + +class UserBoardForm(forms.ModelForm): + def save(self): + user = self.cleaned_data["user"] + board = self.cleaned_data["board"] + user_board = UserBoard.objects.get(user=user) + user_board.board = board + user_board.save() + + + class Meta: + model = UserBoard + fields = ["user", "board"] diff --git a/sbhs/management/commands/update_boards.py b/sbhs/management/commands/update_boards.py new file mode 100644 index 0000000..238646c --- /dev/null +++ b/sbhs/management/commands/update_boards.py @@ -0,0 +1,38 @@ +''' + This command creates a moderator group and adds users to the moderator group + with permissions to add, change and delete + the objects in the exam app. +''' + +# django imports +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User, Group, Permission + +# local imports +from sbhs.models import Board +from sbhs.views import map_sbhs_to_rpi + + +class Command(BaseCommand): + help = 'Ping all boards and update status of boards' + + def add_arguments(self, parser): + # Positional arguments + parser.add_argument('host_ip', type=str) + + def handle(self, *args, **options): + app_label = 'sbhs' + try: + board_check, dead_servers = map_sbhs_to_rpi(options["host_ip"]) + board = Board() + all_mac_ids = [] + for machines in board_check: + all_mac_ids.extend(machines["mac_ids"]) + board.switch_off_inactive_boards(all_mac_ids) + self.stdout.write('Updated Board Status') + if dead_servers: + self.stdout.write('Servers {0} are not responding.'\ + .format(", ".join(dead_servers)) + ) + except Exception as e: + self.stdout.write('Failed updating Board because {0}'.format(e))
\ No newline at end of file diff --git a/sbhs/models.py b/sbhs/models.py index d3cc24c..95d5b62 100644 --- a/sbhs/models.py +++ b/sbhs/models.py @@ -43,7 +43,7 @@ class Board(models.Model): board = self board.mid = device["sbhs_mac_id"] board.raspi_path = raspi_ip - board.usb_path = int(device["usb_id"]) + board.usb_id = int(device["usb_id"]) board.online = True board.save() @@ -60,7 +60,11 @@ class Board(models.Model): ordering = ['mid',] def __str__(self): - return '{}: {}'.format(self.mid, self.online) + return 'MID: {0} Online: {1}: User Count: {2}'.format( + self.mid, + self.online, + self.userboard_set.all().count() + ) class Profile(models.Model): """ @@ -97,15 +101,37 @@ class Profile(models.Model): class SlotManager(models.Manager): def get_user_slots(self, user): - now = timezone.now() + now = timezone.localtime() slots = self.filter(user=user, start_time__lte=now, end_time__gt=now) return slots def get_all_active_slots(self): - now = timezone.now() + now = timezone.localtime() slots = self.filter(start_time__lte=now, end_time__gt=now) return slots + def check_booked_slots(self, start_time, users): + return self.filter(start_time__lte=start_time, + end_time__gt = start_time, + user__in=users + ).count() == 0 + def get_active_slot_for_board(self, mid): + now = timezone.localtime() + users = [userboard.user.id for userboard in + UserBoard.objects.filter(board__mid=mid) + ] + slots = self.filter(user__in=users, start_time__lte=now, + end_time__gt=now) + return slots + + def board_all_booked_slots(self,mid): + now = timezone.localtime() + users = [userboard.user.id for userboard in UserBoard.objects.filter( + board__mid=mid) + ] + slots = self.filter(user__in=users,start_time__gte=now) + return slots + class Slot(models.Model): user = models.ForeignKey(User) start_time = models.DateTimeField("Start time of a slot", @@ -133,5 +159,20 @@ class UserBoard(models.Model): user = models.ForeignKey(User) board = models.ForeignKey(Board) + def get_all_users_for_board(self): + now = timezone.now() + users = UserBoard.objects.filter(board=self.board) + return users + def __str__(self): return '{0}: {1}'.format(self.user.username, self.board.mid) + +class Webcam(): + + def __init__(self): + pass + + def load_image(className,mid): + command = "timeout 2s streamer -q -f jpeg -c /dev/video" + '0'+str(mid) + command += " -o " + settings.WEBCAM_DIR + "image" + '0'+str(mid) + ".jpeg" + os.system(command)
\ No newline at end of file diff --git a/sbhs/templates/dashboard/dashboard_index.html b/sbhs/templates/dashboard/dashboard_index.html index ccbba77..9155781 100644 --- a/sbhs/templates/dashboard/dashboard_index.html +++ b/sbhs/templates/dashboard/dashboard_index.html @@ -8,7 +8,6 @@ <script src="{% static 'js/jquery-ui.js' %}"></script> <link rel="stylesheet" type="text/css" href="{% static 'css/jquery.datetimepicker.css' %}"/> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"/> - <script type="text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script> {% endblock %} {% block content %} <br> @@ -36,15 +35,6 @@ <li> <a href="{% url 'logs_folder_index' %}">Download Experiment Directory</a> </li> - <li> - <a href="{% url 'turn_on_all_boards' %}">Turn On all Boards</a> - </li> - <li> - <a href="{% url 'turn_off_all_boards' %}">Turn Off all Boards</a> - </li> - <li> - <a href="{% url 'book_all_suser_slots' %}">Book all Slots</a> - </li> </ul> </div> diff --git a/sbhs/templates/dashboard/profile.html b/sbhs/templates/dashboard/profile.html index 8a055df..fe999f6 100644 --- a/sbhs/templates/dashboard/profile.html +++ b/sbhs/templates/dashboard/profile.html @@ -9,8 +9,7 @@ <script type="{% static 'js/jquery-ui.js' %}"></script> <link rel="stylesheet" type="text/css" href="{% static 'css/jquery.datetimepicker.css' %}"/> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"/> - <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.min.css" /> - <script src="//cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.min.js"></script> + <script type="text/javascript" src = "{% static 'js/dygraph-combined.js' %}"></script> {% endblock %} {% block main %} <div class = "row"> @@ -25,25 +24,25 @@ </div> </div> <script> - (function(){ - var h = new Dygraph( - document.getElementById("heat"), - "{{ heat }}", - { - valueRange: [0,100] - } - ); - var f = new Dygraph( - document.getElementById("fan"), - "{{ fan }}", - { - valueRange: [0,100] - } - ); - var t = new Dygraph( - document.getElementById("temp"), - "{{ temp }}" - ); - })(); -</script> +(function(){ + var h = new Dygraph( + document.getElementById("heat"), + "{{ heat }}", + { + valueRange: [0,100] + } + ); + var f = new Dygraph( + document.getElementById("fan"), + "{{ fan }}", + { + valueRange: [0,100] + } + ); + var t = new Dygraph( + document.getElementById("temp"), + "{{ temp }}" + ); +})(); + </script> {% endblock %}
\ No newline at end of file diff --git a/sbhs/templates/dashboard/test_boards.html b/sbhs/templates/dashboard/test_boards.html index 11362df..c9a6330 100644 --- a/sbhs/templates/dashboard/test_boards.html +++ b/sbhs/templates/dashboard/test_boards.html @@ -17,129 +17,50 @@ <div class = "container"> <div class = "row"> <div class = "span7"> + {% if dead_servers %} + <div class="alert alert-danger"> + These SBHS servers did not respond - <br/> + <strong>{{dead_servers|join:", "}}</strong> + </div> + + {% endif %} <table class = "table table-striped" id = "tableId"> <thead> <tr> <th>Board MID</th> <th>Status</th> <th>Occupied</th> + <th>Temperature</th> + <th>Set Heat</th> + <th>Set Fan</th> + <th>Action</th> </tr> </thead> <tbody> - {% for board in boards %} - <tr style="cursor:pointer;" title="Test/Monitor board" data-toggle="modal" data-target="#myModal"> - <td>{{board.mid}}</td> - <td> - <span class = "label label-{% if board.online %}success{% else %}danger{% endif %}">{% if board.online %}Online{% else %}Offline{% endif %}</span> - </td> - <td> - {% vacant_slot slot_history.start_time slot_history.end_time now as slot_status %} - {% if slot_status == "vacant" %} - <p class="label label-{% if board.online %}success{% else %}danger{% endif %}">{% if board.online %}Vacant{% else %}Not available{% endif %}</p> - {% elif slot_status == "occupied" %} - <p class = "label label-danger">Occupied</p> - {% endif %} - - </td> - </tr> + {% for device in all_devices %} + <form class = "form-horizontal" action = "{% url 'update_board_values' device.board.mid %}" method="POST" name="form_{{device.board.mid}}" autocomplete="off"> + {% csrf_token %} + <tr> + <td>{{device.board.mid}}</td> + <td><span style="cursor: pointer;" class = "label label-{% if device.board.online %}success{% else %}danger{% endif %}"> + {% if device.board.online %}Online{% else %}Offline{% endif %}</span></td> + {% check_board_occupancy device.board.mid as slot_status %} + <td><span style="cursor: pointer;" class = "label label-{% if slot_status %}danger{% else %}success{% endif %}"> + {% if slot_status %}Occupied{% else %}Vacant{% endif %}</span></td> + <td>{{device.temp.temp}}</td> + <td><input type="text" name="set_heat" id="set_heat_{{device.board.mid}}"/></td> + <td><input type="text" name="set_fan" id="set_fan_{{device.board.mid}}"/></td> + <td><button class="btn btn-primary"> Submit </button></td> + </tr> + </form> {% endfor %} </tbody> </table> + <br/><br/> + <form class = "form-horizontal" action = "" method="POST" autocomplete="off"> + {% csrf_token %} + <center><button class = "btn btn-primary" name="reset_all" value="reset_all">Reset all boards</button></center> + </form> </div> - - <!-- Modal code starts here --> - - <div id="myModal" class="modal fade test-modal" id="" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button> - <h3>Test Board</h3> - </div> - <div id="orderDetails" class="modal-body"> - <div class = "input-group"> - <input type="text" class = "form-control" id = temp" disabled = "true"name=""> - <span class = "input-group-btn"> - <button class = "btn btn-primary" onclick="getTemp()">Get Temperature</button> - </span> - </div> - <br> - <div class = "input-group"> - Heater Input: <input type="number" size="3" min="0" max="250" id="heater-val"> - Fan Input: <input type="number" size="3" min="0" max="250" id="fan-val" name=""> - <span class = "input-group-btn"> - <button class = "btn btn-primary" onclick="setParams()">Set Parameters</button> - </span> - </div> - <br> - <div class = "input-group"> - <span class = "input-group-btn"> - <button class = "btn btn-primary" onclick="resetParams()">Reset Parameters</button> - </span> - </div> - </div> - <div class="modal-footer"> - <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> - </div> - </div> - </div> - </div> - - <!-- Modal code ends here --> - - <!-- Modal code starts here --> - - <div id="myModal" class="modal fade monitor-modal" id="" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button> - <h3>Monitor Experiment</h3> - </div> - <div id="orderDetails" class="modal-body"> - <div class = "input-group"> - <strong><p id = "username"></p></strong> - <p id = "log-data" style="word-spacing:4em"></p> - <span class = "input-group-btn"> - <button class = "btn btn-primary" onclick="getLogs()">Refresh Logs</button> - </span> - </div> - </div> - <div class="modal-footer"> - <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> - </div> - </div> - </div> - </div> - - <!-- Modal code ends here --> - </div> -</div> -<script type="text/javascript"> - $("#tableId").on("click", "tr", function(e) { - $("#tableId").find("tr.highlight").removeClass("highlight"); - $(this).addClass("highlight"); - - label = e.currentTarget.getElementsByClassName("label")[1]; - console.log(label) - isSelectedMachineVacant = label.className.indexOf("label-success") > -1; - console.log(isSelectedMachineVacant); - - if (isSelectedMachineVacant) { - $("#temp").val(""); - $("#fan-val").val(""); - $("#heater-val").val(""); - $(".test-modal").modal('show'); - } - else { - $(".test-modal").modal('hide'); - $(".montor-modal").modal('show'); - getLogs(); - } - }); - function getLogs(){ - console.log('hello from getLogs'); - } -</script> {% endblock %}
\ No newline at end of file diff --git a/sbhs/templates/dashboard/update_mid.html b/sbhs/templates/dashboard/update_mid.html index ea2eb4d..1fa3435 100644 --- a/sbhs/templates/dashboard/update_mid.html +++ b/sbhs/templates/dashboard/update_mid.html @@ -1,9 +1,17 @@ {% extends 'dashboard/dashboard_index.html' %} +{% load crispy_forms_tags %} {% block title %} Update MID {% endblock %} {% block main %} - <div> - <h4>Update MID</h4> + <div class = "container"> + <div class = "row"> + <form class = "form-horizontal" action = "" method="POST" autocomplete="off"> + {% csrf_token %} + {{form | crispy}} + <br> + <button class="btn btn-primary" type="submit" name='update_mid' value='update_mid'>Update Mid</button><br/> + </form> + </div> </div> {% endblock %}
\ No newline at end of file diff --git a/sbhs/templates/nav.html b/sbhs/templates/nav.html deleted file mode 100644 index e69de29..0000000 --- a/sbhs/templates/nav.html +++ /dev/null diff --git a/sbhs/templates/slot/new.html b/sbhs/templates/slot/create_slot.html index 2713396..dc76382 100644 --- a/sbhs/templates/slot/new.html +++ b/sbhs/templates/slot/create_slot.html @@ -45,8 +45,30 @@ </form> + <div class = "tab-content col-md-10"> + <table class = "table table-striped"> + <thead> + <tr> + <th> + Sr.No + </th> + <th> + Already Booked Slots + </th> + </tr> + </thead> + <tbody> + {% for slot in board_all_booked_slots %} + <tr> + <td>{{forloop.counter}}</td> + <td>{{slot.start_time}} : {{slot.end_time}} </td> + </tr> + {% endfor %} + </tbody> + </table> + </div> </div> - <div class = "tab-pane" id="tab_b"> + <div class = "tab-pane col-md-10" id="tab_b"> <form action="" method="POST"> {%csrf_token %} <table class="table table-striped"> @@ -59,6 +81,7 @@ <b><th>Action</th></b> </tr> </thead> + <tbody> {% for h in history %} <tr> {%if h.start_time >= now %} @@ -79,6 +102,7 @@ {% endif %} </tr> {% endfor %} + </tbody> </table> <button class="btn btn-danger" type="submit" name="delete" value="delete">Delete</button> </form> diff --git a/sbhs/templatetags/custom_filter.py b/sbhs/templatetags/custom_filter.py index 77f7cba..1e37336 100644 --- a/sbhs/templatetags/custom_filter.py +++ b/sbhs/templatetags/custom_filter.py @@ -1,5 +1,6 @@ from django import template from django.template.defaultfilters import stringfilter +from sbhs.models import Slot register = template.Library() @@ -16,10 +17,9 @@ def compare_slot_time(start_time, end_time, now): @register.simple_tag -def vacant_slot(start_time, end_time, now): - slot = "vacant" - if start_time <= now <= end_time: - slot ="occupied" - elif start_time >= now: - slot ="vacant" - return slot +def check_board_occupancy(mid): + slot = Slot.objects.get_active_slot_for_board(mid) + if slot: + return True + else: + return False diff --git a/sbhs/urls.py b/sbhs/urls.py index cfba27f..001572e 100644 --- a/sbhs/urls.py +++ b/sbhs/urls.py @@ -74,8 +74,6 @@ urlpatterns = [ name='moderator_dashboard'), url(r'^moderator/all-bookings/$',views.all_bookings, name='all_bookings'), - url(r'^moderator/all-boards/$',views.all_boards, - name='all_boards'), url(r'^moderator/profile/(?P<mid>\d+)/$',views.profile, name='profile'), url(r'^moderator/log/(?P<mid>\d+)/$',views.download_log, @@ -83,16 +81,15 @@ urlpatterns = [ url(r'^moderator/logs_folder_index/?$',views.logs_folder_index, name='logs_folder_index'), url(r'^moderator/all-images/$',views.all_images,name='all_images'), + url(r'^moderator/update_board_values/(?P<mid>\d+)/$', + views.update_board_values, + name='update_board_values' + ), url(r'^moderator/test-boards/$',views.test_boards,name='test_boards'), url(r'^moderator/update-mid/$',views.update_mid,name='update_mid'), url(r'^moderator/fetch-logs/$',views.fetch_logs,name='fetch_logs'), url(r'^moderator/fetch-logs/(?P<experiment_id>\d+)/$',views.download_file, name='download_file'), - url(r'^moderator/turn-on-all-boards/$',views.turn_on_all_boards, - name='turn_on_all_boards'), - url(r'^moderator/turn-off-all-boards/$',views.turn_off_all_boards, - name='turn_off_all_boards'), - url(r'^moderator/book-all-suser-slots/$',views.book_all_suser_slots, - name='book_all_suser_slots'), + url(r'^moderator/updatemid/$', views.update_mid, name='update_mid'), ] diff --git a/sbhs/views.py b/sbhs/views.py index d36469c..b83d488 100644 --- a/sbhs/views.py +++ b/sbhs/views.py @@ -6,14 +6,13 @@ import random import zipfile import inspect import pytz -import datetime import requests import subprocess, zipfile # import serial from textwrap import dedent from time import gmtime, strftime -import time -from datetime import datetime, timedelta, date +import time as tm +from datetime import datetime as dt, timedelta, date, time from django.urls import reverse from django.conf import settings @@ -25,6 +24,7 @@ from django.contrib.auth.models import User from django.contrib import messages from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt +from django.utils.six import python_2_unicode_compatible from django.contrib.auth.decorators import login_required from django.contrib.auth import login, logout, authenticate from django.shortcuts import render, redirect, get_object_or_404 @@ -34,16 +34,22 @@ from django.http import HttpResponse, HttpResponseRedirect,\ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from .models import Board, Experiment, Profile, Slot, UserBoard#, Webcam +from .models import Board, Experiment, Profile, Slot, UserBoard, Webcam from .forms import ( - UserLoginForm, UserRegistrationForm, SlotCreationForm, FilterLogsForm + UserLoginForm, UserRegistrationForm, SlotCreationForm, FilterLogsForm, + UserBoardForm ) from .send_emails import send_user_mail from sbhs_server import credentials as credentials from sbhs.decorators import email_verified + + ################# pages views ####################### def index(request, next_url=None): + """ + Index page of Website. + """ if request.user.is_authenticated(): return render(request,'account/home.html') return render(request,"pages/pages_index.html") @@ -79,11 +85,22 @@ def feedback(req): #########Account Views ########### @email_verified def account_index(request): + """ + Checks if the user is authenticated + Assign the board to user when he first logins in a random order. + If not authenticated redirect back to LoginForm. + """ user = request.user if user.is_authenticated(): if not UserBoard.objects.filter(user=user).exists(): - random_board = Board.objects.order_by('?').last() - UserBoard.objects.create(user=user, board=random_board) + if Board.objects.all().exists(): + user_board = random.choice(Board.objects.filter( + online=True + ) + ) + UserBoard.objects.create(user=user, board=user_board) + else: + raise Http404("Could not find any SBHS devices connected.") return render(request,'account/home.html') return render(request,'account/account_index.html',{ @@ -92,6 +109,12 @@ def account_index(request): }) def user_login(request): + """ + Logs in existing user + Generates alerts if: + Either username or password do not match. + If the account is disabled or not activated yet. + """ user = request.user context = {} if user.is_authenticated(): @@ -126,10 +149,17 @@ def user_login(request): return redirect('account_enter') def user_logout(request): + """ + Logs out a logged-in user + """ logout(request) return redirect('account_enter') def user_register(request): + """ + Create new user: + Generates activation key and sends it to users registered email id. + """ user = request.user if user.is_authenticated(): return render(request,'account/home.html') @@ -157,13 +187,17 @@ def user_register(request): @login_required def activate_user(request, key): + """ + Verify user account from the generated activation key user received + in his mail. + """ profile = get_object_or_404(Profile, activation_key=key) context = {} context['success'] = False if profile.is_email_verified: context['activation_msg'] = "Your account is already verified" return render(request,'account/activation_status.html',context) - if timezone.now() > profile.key_expiry_time: + if timezone.localtime() > profile.key_expiry_time: content['msg'] = dedent( """ Your activation time expired. Please try again @@ -177,6 +211,10 @@ def activate_user(request, key): return render(request,'account/activation_status.html',context) def new_activation(request, email=None): + """ + User requests for new_activation key incase if the first activation + key expires + """ context = {} if request.method == 'POST': email = request.POST.get('email') @@ -194,7 +232,7 @@ def new_activation(request, email=None): if not user.profile.is_email_verified: user.profile.activation_key = generate_activation_key(user.username) - user.profile.key_expiry_time = timezone.now() \ + user.profile.key_expiry_time = timezone.localtime() \ + timezone.timedelta(minutes=20) user.profile.save() new_user_data = User.objects.get(email=email) @@ -211,6 +249,9 @@ def new_activation(request, email=None): @login_required def update_email(request): + """ + Updates user email_id + """ context = {} if request.method == 'POST': email = request.POST.get('email') @@ -226,51 +267,112 @@ def update_email(request): @login_required @email_verified def slot_new(request): + """ + Books a new slot for the user: + + Shows all booked slots + Shows users alerts if: + + Slot is successfully booked + User exceeds the limit of number of slots that can be + booked in advance for a day. + + Requested slot is already booked by another user. + + Deletes a previouslt booked slot: + Booked slot is deleted successfully. + Slot cannot be deleted if it expires. + """ user = request.user + board = UserBoard.objects.filter(user=user).order_by("id").last() + if board: + all_board_users = board.get_all_users_for_board() + all_board_users_id = [i.user.id for i in all_board_users] + else: + all_board_users_id = [] slot_history = Slot.objects.filter(user=user).order_by("-start_time") context = {} - now = timezone.now() - today = now.today() - todays_slots = slot_history.filter(start_time__date=today) + now = timezone.localtime() current_slot = slot_history.filter(start_time__lt=now, end_time__gte=now) + board_all_booked_slots = Slot.objects.board_all_booked_slots( + board.board.mid + ) if not request.user.is_authenticated(): return redirect('account_enter') if request.method == 'POST': - if len(todays_slots) >= settings.LIMIT: - messages.warning(request,'Cannot Book more than {0} slots in advance in a day'\ - .format(settings.LIMIT)) - else: - if request.POST.get('delete') == "delete": - slots = request.POST.getlist("slots") - Slot.objects.filter(id__in=slots).delete() - if request.POST.get("book_date") == "book_date": - form = SlotCreationForm(request.POST) - if form.is_valid(): - new_slot = form.save(commit=False) - if new_slot.start_time >= now: - new_slot.end_time = new_slot.start_time + timedelta( - minutes=settings.SLOT_DURATION + if request.POST.get('delete') == "delete": + slots = request.POST.getlist("slots") + Slot.objects.filter(id__in=slots).delete() + + if request.POST.get("book_date") == "book_date": + form = SlotCreationForm(request.POST) + if form.is_valid(): + new_slot = form.save(commit=False) + + new_slot_date = new_slot.start_time.date() + new_slot_time = new_slot.start_time.time() + new_slot_date_slots = slot_history.filter( + start_time__date=new_slot_date + ) + + if len(new_slot_date_slots) >= settings.LIMIT: + messages.warning(request,'Cannot Book more than {0} \ + slots in advance in a day'.format(settings.LIMIT)) + else: + if Slot.objects.check_booked_slots( + new_slot.start_time, all_board_users_id): + + if new_slot.start_time >= now: + new_slot.start_time = dt.combine( + new_slot.start_time.date(), + time(new_slot.start_time.hour,00) + ) + new_slot.end_time = dt.combine( + new_slot.start_time.date(), + time(new_slot.start_time.hour, + settings.SLOT_DURATION + ) + ) + new_slot.user = user + new_slot.save() + messages.success(request, + 'Slot created successfully.' ) - new_slot.user = user - new_slot.save() - messages.success(request,'Slot created successfully.') + else: + messages.error(request, + 'Start time selected' + + ' is before today.' + + 'Please choose again.' + ) else: messages.error(request, - 'Start time selected' - + ' is before today.' - + 'Please choose again.' - ) - if request.POST.get("book_now") == "book_now": - if not current_slot: + 'Slot is already booked.' + + ' Try the next slot.' + ) + + if request.POST.get("book_now") == "book_now": + if not current_slot: + if Slot.objects.check_booked_slots( + now, all_board_users_id): slot_now = Slot.objects.create( - user=user, start_time=now, - end_time=now+timedelta(minutes=55) + user=user, + start_time=dt.combine(now.date(), + time(now.hour,00)), + end_time=dt.combine(now.date(), + time(now.hour, settings.SLOT_DURATION) + ) ) messages.success(request,'Slot created successfully.') else: - messages.warning(request,'Slot is already booked for \ - current time. Please select future slot.') + messages.error(request, + 'Slot is booked by someone else.' + + ' Try the next slot.' + ) + else: + messages.error(request,'Slot is already booked for \ + current time. Please select a future slot.' + ) return redirect("slot_new") else: @@ -278,34 +380,44 @@ def slot_new(request): context['history']=slot_history context['form']=form context['now'] = now - return render(request,'slot/new.html',context) + context['board_all_booked_slots'] = board_all_booked_slots + return render(request,'slot/create_slot.html',context) ###################Experiment Views ###################### def check_connection(request): + """ + Check connection if it exists or not with the Client App . + """ return HttpResponse("TESTOK") def client_version(request): + """ + Returns client version + """ return HttpResponse(str(settings.CLIENT_VERSION)) @csrf_exempt def initiation(request): + """ + + """ username = request.POST.get("username") password = request.POST.get("password") user = authenticate(username=username, password=password) if user: if user.is_active: - now = timezone.now() + now = timezone.localtime() slots = Slot.objects.get_user_slots(user).order_by("id") slot = slots.last() board = UserBoard.objects.get(user=user).board check_status_path = "reset/{0}".format(board.usb_id) check_status = connect_sbhs(board.raspi_path, check_status_path) if check_status["status"] and slot: - filename = datetime.strftime(now, "%Y%b%d_%H_%M_%S.txt") + filename = dt.strftime(now, "%Y%b%d_%H_%M_%S.txt") logdir = os.path.join(settings.EXPERIMENT_LOGS_DIR, user.username ) @@ -352,36 +464,43 @@ def initiation(request): } return JsonResponse(message, safe=True, status=200) -def map_sbhs_to_rpi(client_ip): +def map_sbhs_to_rpi(client_name): """ + Scans if the machine are connected to the rpis. + If the machines are connected map them with their specific rpis. """ r_pis = settings.RASP_PI_IPS map_machines = [] + dead_machines = [] + rpi_map = {} if r_pis: for r_pi in r_pis: - rpi_map = {} rpi_map["rpi_ip"] = r_pi - mac_ids = connect_sbhs(r_pi, "get_machine_ids") - for devices in mac_ids: - board = Board() - board.save_board_details(r_pi, devices) - rpi_map["mac_ids"] = [i['sbhs_mac_id'] for i in mac_ids] - map_machines.append(rpi_map) - else: - rpi_map = {} - rpi_map["rpi_ip"] = client_name + try: + mac_ids = connect_sbhs(r_pi, "get_machine_ids") + for devices in mac_ids: + board = Board() + board.save_board_details(r_pi, devices) + rpi_map["mac_ids"] = [i['sbhs_mac_id'] for i in mac_ids] + map_machines.append(rpi_map) + except: + dead_machines.append(r_pi) + rpi_map["rpi_ip"] = client_name + try: mac_ids = connect_sbhs(client_name, "get_machine_ids") board = Board() board.save_board_details(client_name, mac_ids) rpi_map["mac_ids"] = [i['sbhs_mac_id'] for i in mac_ids] map_machines.append(rpi_map) - return map_machines + except: + dead_machines.append(client_name) + return map_machines, dead_machines def connect_sbhs(rpi_ip, experiment_url): connect_rpi = requests.get("http://{0}/experiment/{1}".format( - rpi_ip, experiment_url - ) - ) + rpi_ip, experiment_url + ) + ) data = json.loads(connect_rpi.text) return data @@ -389,7 +508,7 @@ def connect_sbhs(rpi_ip, experiment_url): def experiment(request): try: username = request.POST.get("username") - server_start_ts = int(time.time() * 1000) + server_start_ts = int(tm.time() * 1000) user = User.objects.get(username=username) slot = Slot.objects.get_user_slots(user)\ .order_by("start_time").last() @@ -412,7 +531,7 @@ def experiment(request): temp = get_temp["temp"] log_data(board.mid, heat, fan, temp) - server_end_ts = int(time.time() * 1000) + server_end_ts = int(tm.time() * 1000) STATUS = 1 MESSAGE = "%s %d %d %2.2f" % (request.POST.get("iteration"), @@ -432,7 +551,6 @@ def experiment(request): f = open(experiment_log_path, "a") f.write(" ".join(MESSAGE.split(",")[:2]) + "\n") f.close() - # Experiment.objects.create(slot=slot, log=experiment_log_path) else: STATUS = 0 MESSAGE = "Slot has ended. Please book the next slot to \ @@ -451,8 +569,10 @@ def experiment(request): ) def log_data(mid, heat, fan, temp): - - data = "{0} {1} {2} {3}\n".format(int(time.time()),str(heat), + """ + Update the experimental log file. + """ + data = "{0} {1} {2} {3}\n".format(int(tm.time()),str(heat), str(fan), str(temp) ) global_logfile = settings.SBHS_GLOBAL_LOG_DIR + "/" + str(mid) + ".log" @@ -465,6 +585,9 @@ def log_data(mid, heat, fan, temp): @login_required def logs(request): + """ + Renders experimental log files to the user interface. + """ user = request.user context = {} all_bookings = Slot.objects.filter(user__username=user) @@ -479,72 +602,17 @@ def logs(request): @login_required def download_user_log(request, experiment_id): + """ + download logs related to the user + """ user = request.user experiment_data = Experiment.objects.get(slot__id=experiment_id) f = open(os.path.join(settings.EXPERIMENT_LOGS_DIR, experiment_data.log),"r") data = f.read() - print(data) f.close() return HttpResponse(data, content_type="text/text") -# @csrf_exempt -# def reset(req): -# try: -# from pi_server.settings import boards -# user = req.user -# if user.is_authenticated(): -# key = str(user.board.mid) -# experiment = Experiment.objects.select_related().filter(id=boards[key]["experiment_id"]) - -# if len(experiment) == 1 and user == experiment[0].booking.account: -# experiment = experiment[0] -# now = datetime.datetime.now() -# endtime = experiment.booking.end_time() - -# boards[key]["board"].setHeat(0) -# boards[key]["board"].setFan(100) - -# log_data(boards[key]["board"], key, experiment.id, 0, 100) -# if endtime < now: -# boards[key]["experiment_id"] = None -# except: -# pass - -# return HttpResponse("") - -# def log_data(sbhs, mid, experiment_id, heat=None, fan=None, temp=None): -# if heat is None: -# heat = sbhs.getHeat() -# if fan is None: -# fan = sbhs.getFan() -# if temp is None: -# temp = sbhs.getTemp() - -# data = "%d %s %s %s\n" % (int(time.time()), str(heat), str(fan), str(temp)) -# global_logfile = settings.SBHS_GLOBAL_LOG_DIR + "/" + str(mid) + ".log" -# with open(global_logfile, "a") as global_loghandler: -# global_loghandler.write(data) - -# def validate_log_file(req): -# import hashlib -# data = req.POST.get("data") -# data = data.strip().split("\n") -# clean_data = "" -# for line in data: -# columns = line.split(" ") -# if len(columns) >= 6: -# clean_data += (" ".join(columns[0:6]) + "\n") - -# checksum = hashlib.sha1(clean_data).hexdigest() - -# try: -# e = Experiment.objects.get(checksum=checksum) -# return HttpResponse("TRUE") -# except: -# return HttpResponse("FALSE") - - ################## Moderator Views ########################## def is_moderator(user): @@ -559,15 +627,10 @@ def moderator_dashboard(request): if not is_moderator(user): raise Http404("You are not allowed to see this page!") else: - board_check = map_sbhs_to_rpi(request.META["SERVER_NAME"]) - board = Board() - all_mac_ids = [] - for machines in board_check: - all_mac_ids.extend(machines["mac_ids"]) - board.switch_off_inactive_boards(all_mac_ids) context["all_boards"] = Board.objects.all() return render(request, 'dashboard/show_all_boards.html', context) + @login_required def profile(request, mid): user = request.user @@ -576,7 +639,9 @@ def profile(request, mid): raise Http404("You are not allowed to see this page.") else: try: - filename = settings.SBHS_GLOBAL_LOG_DIR + "/" + str(mid) + ".log" + filename = settings.SBHS_GLOBAL_LOG_DIR + "/" + mid + ".log" + f = open(filename, "r") + f.close() except: raise Http404("Log does not exist for this profile.") @@ -585,22 +650,23 @@ def profile(request, mid): delta_T, filename ), shell=True) + data = data.split("\n".encode()) + plot = [] heatcsv = "" fancsv = "" tempcsv = "" - plot = [] for t in range(len(data)): line = data[t].decode("utf-8") entry = line.strip().split(" ") try: plot.append([int(float(i)) for i in entry[0:-1] \ + [float(entry[-1])]]) - heatcsv += "{0},{1}".format(t+1, entry[1]) - fancsv += "{0},{1}".format(t+1,entry[2]) - tempcsv += "{0},{1}".format(t+1, entry[3]) + heatcsv += "{0},{1}\\n".format(t+1, entry[1]) + fancsv += "{0},{1}\\n".format(t+1,entry[2]) + tempcsv += "{0},{1}\\n".format(t+1, entry[3]) except: continue @@ -615,6 +681,9 @@ def profile(request, mid): @login_required def download_log(request, mid): + """ + Download logs related to the user + """ user = request.user if not is_moderator(user): raise Http404("You are not allowed to see this page.") @@ -636,6 +705,9 @@ def zipdir(path,ziph): @login_required def logs_folder_index(request): + """ + Compress the experiments directory and download. + """ user = request.user if not is_moderator(user): raise Http404("You are not allowed to see this page.") @@ -643,7 +715,8 @@ def logs_folder_index(request): if os.path.exists('Experiments.zip'): os.remove('Experiments.zip') - with zipfile.ZipFile('Experiments.zip','w',zipfile.ZIP_DEFLATED) as zipf: + with zipfile.ZipFile('Experiments.zip','w',zipfile.ZIP_DEFLATED) \ + as zipf: path = settings.BASE_DIR + '/experiments/' zipdir(path,zipf) @@ -656,6 +729,9 @@ def logs_folder_index(request): @login_required def all_bookings(request): + """ + Show all the bookings by all the users + """ user = request.user context = {} if not is_moderator(user): @@ -674,16 +750,6 @@ def all_bookings(request): return render(request,'dashboard/all_bookings.html', context) - -@login_required -def all_boards(request): - user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to see this page.") - else: - return render(request,'dashboard/all_boards.html') - - @login_required def all_images(request): user = request.user @@ -692,19 +758,65 @@ def all_images(request): else: return render(request,'dashboard/all_images.html') + +@login_required +def update_board_values(request, mid): + if request.method == 'POST': + heat = request.POST.get('set_heat', None) + fan = request.POST.get('set_fan', None) + device = Board.objects.get(mid=mid) + if heat and fan: + set_heat = connect_sbhs(device.raspi_path, + "set_heat/{0}/{1}".format(device.usb_id, heat) + ) + set_fan = connect_sbhs(device.raspi_path, + "set_fan/{0}/{1}".format(device.usb_id, fan) + ) + if not (set_fan["status"] or set_heat["status"]): + messages.error(request, "Could not set heat and for board {}"\ + .format(board.mid)) + + + return redirect("test_boards") + @login_required def test_boards(request): + """ + Test boards from the Web interface. + """ user = request.user now = timezone.now() context = {} if not is_moderator(user): raise Http404("You are not allowed to see this page.") else: + board_check, dead_servers = map_sbhs_to_rpi( + request.META["SERVER_NAME"] + ) + board = Board() + all_mac_ids = [] + for machines in board_check: + all_mac_ids.extend(machines["mac_ids"]) + board.switch_off_inactive_boards(all_mac_ids) boards = Board.objects.filter(online=True) - slot_history = Slot.objects.all().order_by("-start_time") - context["boards"] = boards - context["slot_history"] = slot_history[0] - context["now"] = now + all_devices = [] + if request.POST.get("reset_all") == "reset_all": + for board in boards: + resp = connect_sbhs(board.raspi_path,"reset/{0}".format( + board.mid + ) + ) + + for device in boards: + devices = {} + temp = connect_sbhs(device.raspi_path, + "get_temp/{0}".format(device.usb_id) + ) + devices["board"] = device + devices["temp"] = temp + all_devices.append(devices) + context["all_devices"] = all_devices + context["dead_servers"] = dead_servers return render(request,'dashboard/test_boards.html',context) def user_exists(username): @@ -715,42 +827,31 @@ def user_exists(username): @login_required def update_mid(request): - user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to see this page.") - else: - try: - username = request.POST.get("username") - board_id = request.POST.get("board_id") - except: - raise Http404("Invalid Parameters") - user = user_exists(username) - if user is not None: - # board = user.userboard_set.all() - # board.mid = board_id - # board.save() - - return messages.success("Mid updated successfully") - else: - raise Http404("Username: {} does not exists".format(username)) - - return redirect(reverse('get_allocated_mids')) - -@login_required -def get_allocated_mids(request): + """ + Update mid given to user. + """ user = request.user context = {} if not is_moderator(user): raise Http404("You are not allowed to see this page.") else: - pass - return render(request, 'dashboard/update_mid.html',context) + if request.method == 'POST': + if request.POST.get("update_mid") == "update_mid": + form = UserBoardForm(request.POST) + if form.is_valid(): + form.save() + + context["form"]= UserBoardForm() + return render(request, 'dashboard/update_mid.html',context) @login_required def fetch_logs(request): + """ + fetch logs in between the given dates. + """ user = request.user context = {} - now = datetime.now() + now = dt.now() if not is_moderator(user): raise Http404("You are not allowed to see this page.") else: @@ -787,26 +888,12 @@ def download_file(request, experiment_id): except FileNotFoundError as fnfe: raise fnfe -@login_required -def turn_on_all_boards(request): - user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to see this page.") - else: - return HttpResponseRedirect(reverse('moderator_dashboard')) -@login_required -def turn_off_all_boards(request): - user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to see this page.") - else: - return HttpResponseRedirect(reverse('moderator_dashboard')) +################## Webcam Views ############################# -@login_required -def book_all_suser_slots(request): - user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to see this page.") - else: - return HttpResponseRedirect(reverse('moderator_dashboard')) +def reload(request, mid): + Webcam.load_image(mid) + return HttpResponse("") + +def show_video(request): + board = request.user diff --git a/sbhs_server/settings.py b/sbhs_server/settings.py index b0930fa..433ffa9 100644 --- a/sbhs_server/settings.py +++ b/sbhs_server/settings.py @@ -26,11 +26,7 @@ SECRET_KEY = credentials.PROJECT_SECRET_KEY # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [ - '10.119.31.68', - 'localhost', - '127.0.0.1' -] +ALLOWED_HOSTS = [] # Application definition @@ -44,6 +40,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'widget_tweaks', + 'crispy_forms', ] MIDDLEWARE = [ @@ -127,14 +124,15 @@ USE_TZ = True STATIC_URL = '/static/' -SLOT_DURATION = 55 +SLOT_DURATION = 59 +STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'experiments/') EMAIL_USE_TLS = True -EMAIL_HOST = 'smtp-auth.iitb.ac.in' +EMAIL_HOST = 'Write SMTP Server Email ID' EMAIL_PORT = 25 @@ -151,29 +149,28 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # This email id will be used as <from address> for sending emails. # For example no_reply@<your_organization>.in can be used. -SENDER_EMAIL = 'iakashchavan@gmail.com' +SENDER_EMAIL = 'Write Sender Email ID' # Organisation/Indivudual Name. SENDER_NAME = 'SBHS Team' # This email id will be used by users to send their queries # For example queries@<your_organization>.in can be used. -REPLY_EMAIL = 'mahesh_gudi@iitb.ac.in' +REPLY_EMAIL = 'Write Reply-to Email ID' # This url will be used in email verification to create activation link. # Add your hosted url to this variable. # For example https://127.0.0.1:8000 or 127.0.0.1:8000 PRODUCTION_URL = '127.0.0.1:8000' - # Set this variable to <False> once the project is in production. # If this variable is kept <True> in production, email will not be verified. -IS_DEVELOPMENT=False +IS_DEVELOPMENT=True DEFAULT_FROM_EMAIL = EMAIL_HOST_USER RASP_PI_IPS = [] -# RASP_PI_IPS = ['10.101.202.74:1234'] + # enter comma separated raspberry pi IPs here with ports # for e.g 127.0.0.1:1234 @@ -185,7 +182,3 @@ EXPERIMENT_LOGS_DIR = os.path.join(BASE_DIR, 'experiments') SBHS_GLOBAL_LOG_DIR = os.path.join(BASE_DIR, 'log') # Path for sbhs global log directory on Django server. - -SESSION_EXPIRE_AT_BROWSER_CLOSE = True -CSRF_COOKIE_NAME = "pfesgbxra" -SESSION_COOKIE_NAME = "frffvbaVq" |