diff --git a/apps/abs/urls.py b/apps/abs/urls.py index 418d4a0..5e26c6b 100644 --- a/apps/abs/urls.py +++ b/apps/abs/urls.py @@ -1,13 +1,15 @@ from django.conf.urls import url +import views + urlpatterns = ( - url(r'^(?P-?\d+)/$', 'apps.abs.views.abs_conf', name='url_abs_conf'), - url(r'^(?P-?\d+)/edit/$', 'apps.abs.views.abs_conf_edit', name='url_edit_abs_conf'), - url(r'^(?P-?\d+)/read/$', 'apps.main.views.dev_conf_read', name='url_read_abs_conf'), - url(r'^(?P-?\d+)/import/$', 'apps.main.views.dev_conf_import', name='url_import_abs_conf'), - url(r'^(?P-?\d+)/export/$', 'apps.main.views.dev_conf_export', name='url_export_abs_conf'), - url(r'^(?P-?\d+)/plot/$', 'apps.abs.views.plot_patterns', name='url_plot_abs_patterns'), - url(r'^(?P-?\d+)/add_beam/$', 'apps.abs.views.add_beam', name='url_add_abs_beam'), - url(r'^(?P-?\d+)/beam/(?P-?\d+)/delete/$', 'apps.abs.views.remove_beam', name='url_remove_abs_beam'), - url(r'^(?P-?\d+)/beam/(?P-?\d+)/edit/$', 'apps.abs.views.edit_beam', name='url_edit_abs_beam'), + url(r'^(?P-?\d+)/$', views.abs_conf, name='url_abs_conf'), + url(r'^(?P-?\d+)/edit/$', views.abs_conf_edit, name='url_edit_abs_conf'), + url(r'^(?P-?\d+)/read/$', views.dev_conf_read, name='url_read_abs_conf'), + url(r'^(?P-?\d+)/import/$', views.dev_conf_import, name='url_import_abs_conf'), + url(r'^(?P-?\d+)/export/$', views.dev_conf_export, name='url_export_abs_conf'), + url(r'^(?P-?\d+)/plot/$', views.plot_patterns, name='url_plot_abs_patterns'), + url(r'^(?P-?\d+)/add_beam/$', views.add_beam, name='url_add_abs_beam'), + url(r'^(?P-?\d+)/beam/(?P-?\d+)/delete/$', views.remove_beam, name='url_remove_abs_beam'), + url(r'^(?P-?\d+)/beam/(?P-?\d+)/edit/$', views.edit_beam, name='url_edit_abs_beam'), ) diff --git a/apps/accounts/templates/login.html b/apps/accounts/templates/login.html index 98ca915..8b08507 100644 --- a/apps/accounts/templates/login.html +++ b/apps/accounts/templates/login.html @@ -7,7 +7,7 @@ {% block content %}
-
+ {% csrf_token %} {% bootstrap_form form %} diff --git a/apps/accounts/urls.py b/apps/accounts/urls.py index 41c052c..66d48f6 100644 --- a/apps/accounts/urls.py +++ b/apps/accounts/urls.py @@ -1,7 +1,7 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from django.contrib.auth import views as auth_views -urlpatterns = patterns('', - url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}), - url(r'^login/$', auth_views.login, {'template_name': 'login.html'}), +urlpatterns = ( + url(r'^logout/$', auth_views.logout, {'next_page': '/'}, name='url_logout'), + url(r'^login/$', auth_views.login, {'template_name': 'login.html'}, name='url_login'), ) diff --git a/apps/cgs/models.py b/apps/cgs/models.py index 3adc74f..bbde0cb 100644 --- a/apps/cgs/models.py +++ b/apps/cgs/models.py @@ -2,82 +2,79 @@ from django.db import models from apps.main.models import Configuration from django.core.validators import MinValueValidator, MaxValueValidator - -from apps.main.models import Device, Experiment - -from files import read_json_file +from .files import read_json_file # Create your models here. validators=[MinValueValidator(62.5e6), MaxValueValidator(450e6)] class CGSConfiguration(Configuration): - + freq0 = models.PositiveIntegerField(verbose_name='Frequency 0',validators=[MaxValueValidator(450e6)], default = 60) freq1 = models.PositiveIntegerField(verbose_name='Frequency 1',validators=[MaxValueValidator(450e6)], default = 60) freq2 = models.PositiveIntegerField(verbose_name='Frequency 2',validators=[MaxValueValidator(450e6)], default = 60) freq3 = models.PositiveIntegerField(verbose_name='Frequency 3',validators=[MaxValueValidator(450e6)], default = 60) - + def verify_frequencies(self): - + return True - - + + def update_from_file(self, fp): - + kwargs = read_json_file(fp) - + if not kwargs: return False - + self.freq0 = kwargs['freq0'] self.freq1 = kwargs['freq1'] self.freq2 = kwargs['freq2'] self.freq3 = kwargs['freq3'] - + return True - + def parms_to_dict(self): - + parameters = {} - + parameters['device_id'] = self.device.id - + if self.freq0 == None or self.freq0 == '': parameters['freq0'] = 0 else: parameters['freq0'] = self.freq0 - + if self.freq1 == None or self.freq1 == '': parameters['freq1'] = 0 else: parameters['freq1'] = self.freq1 - + if self.freq2 == None or self.freq2 == '': parameters['freq2'] = 0 else: - parameters['freq2'] = self.freq2 - + parameters['freq2'] = self.freq2 + if self.freq3 == None or self.freq3 == '': parameters['freq3'] = 0 else: parameters['freq3'] = self.freq3 - + return parameters - - + + def dict_to_parms(self, parameters): - + self.freq0 = parameters['freq0'] self.freq1 = parameters['freq1'] self.freq2 = parameters['freq2'] self.freq3 = parameters['freq3'] - - + + def status_device(self): - + import requests - + ip=self.device.ip_address port=self.device.port_address - + route = "http://" + str(ip) + ":" + str(port) + "/status/ad9548" try: r = requests.get(route,timeout=0.5) @@ -85,13 +82,13 @@ class CGSConfiguration(Configuration): self.device.status = 0 self.device.save() return self.device.status - + response = str(r.text) response = response.split(";") icon = response[0] - status = response[-1] - - print icon, status + status = response[-1] + + #print(icon, status) #"icon" could be: "alert" or "okay" if "alert" in icon: if "Starting Up" in status: #No Esta conectado @@ -102,52 +99,52 @@ class CGSConfiguration(Configuration): self.device.status = 3 else: self.device.status = 1 - + self.message = status self.device.save() - - + + return self.device.status - - + + def read_device(self): - + import requests - + ip=self.device.ip_address port=self.device.port_address - + route = "http://" + str(ip) + ":" + str(port) + "/frequencies/" try: frequencies = requests.get(route,timeout=0.5) - + except: self.message = "Could not read CGS parameters from this device" return None - + frequencies = frequencies.json() frequencies = frequencies.get("Frecuencias") f0 = frequencies.get("f0") f1 = frequencies.get("f1") f2 = frequencies.get("f2") f3 = frequencies.get("f3") - + parms = {'freq0': f0, 'freq1': f1, 'freq2': f2, 'freq3': f3} - + self.message = "" return parms - - + + def write_device(self): - + import requests - + ip=self.device.ip_address port=self.device.port_address - + #---Frequencies from form f0 = self.freq0 f1 = self.freq1 @@ -155,16 +152,16 @@ class CGSConfiguration(Configuration): f3 = self.freq3 post_data = {"f0":f0, "f1":f1, "f2":f2, "f3":f3} route = "http://" + str(ip) + ":" + str(port) + "/frequencies/" - - try: + + try: r = requests.post(route, post_data, timeout=0.5) except: self.message = "Could not write CGS parameters" return None - + text = r.text text = text.split(',') - + if len(text)>1: title = text[0] status = text[1] @@ -174,9 +171,9 @@ class CGSConfiguration(Configuration): else: self.message = title + ", " + status return 1 - + return 1 - + class Meta: db_table = 'cgs_configurations' diff --git a/apps/cgs/urls.py b/apps/cgs/urls.py index ceb1b12..6e05d0d 100644 --- a/apps/cgs/urls.py +++ b/apps/cgs/urls.py @@ -1,14 +1,8 @@ from django.conf.urls import url +from apps.cgs import views + urlpatterns = ( - url(r'^(?P-?\d+)/$', 'apps.cgs.views.cgs_conf', name='url_cgs_conf'), - url(r'^(?P-?\d+)/edit/$', 'apps.cgs.views.cgs_conf_edit', name='url_edit_cgs_conf'), - #url(r'^(?P-?\d+)/(?P-?\d+)/$', 'apps.cgs.views.cgs_conf', name='url_cgs_conf'), -# url(r'^(?P-?\d+)/edit/$', 'apps.cgs.views.cgs_conf_edit', name='url_edit_cgs_conf'), -# url(r'^(?P-?\d+)/write/$', 'apps.cgs.views.cgs_conf_write', name='url_write_cgs_conf'), -# url(r'^(?P-?\d+)/read/$', 'apps.cgs.views.cgs_conf_read', name='url_read_cgs_conf'), -# url(r'^(?P-?\d+)/import/$', 'apps.cgs.views.cgs_conf_import', name='url_import_cgs_conf'), -# url(r'^(?P-?\d+)/export/$', 'apps.cgs.views.cgs_conf_export', name='url_export_cgs_conf'), - #url(r'^(?P-?\d+)/export/$', 'apps.main.views.dev_conf_export', name='url_export_cgs_conf'), + url(r'^(?P-?\d+)/$', views.cgs_conf, name='url_cgs_conf'), + url(r'^(?P-?\d+)/edit/$', views.cgs_conf_edit, name='url_edit_cgs_conf'), ) - diff --git a/apps/cgs/views.py b/apps/cgs/views.py index 623e2d2..0b711b9 100644 --- a/apps/cgs/views.py +++ b/apps/cgs/views.py @@ -2,7 +2,7 @@ from django.shortcuts import redirect, render, get_object_or_404 from django.contrib import messages from django.http import HttpResponse -from apps.main.models import Experiment, Configuration +from apps.main.models import Experiment from .models import CGSConfiguration from .forms import CGSConfigurationForm, UploadFileForm @@ -14,16 +14,16 @@ import json # Create your views here. def cgs_conf(request, id_conf): - + conf = get_object_or_404(CGSConfiguration, pk=id_conf) - + ip=conf.device.ip_address port=conf.device.port_address - + kwargs = {} - + kwargs['status'] = conf.device.get_status_display() - + #if request.method=='GET': #r: response = icon, status # try: @@ -32,7 +32,7 @@ def cgs_conf(request, id_conf): # response = str(r.text) # response = response.split(";") # icon = response[0] - # status = response[-1] + # status = response[-1] #print r.text #"icon" could be: "alert" or "okay" # Si hay alerta pero esta conectado @@ -45,131 +45,131 @@ def cgs_conf(request, id_conf): # kwargs['connected'] = True # else: # kwargs['connected'] = False - - # except: + + # except: # kwargs['connected'] = False # status = "The Device is not connected." - + #if not kwargs['connected']: # messages.error(request, message=status) - + kwargs['dev_conf'] = conf kwargs['dev_conf_keys'] = ['name', 'freq0', 'freq1', 'freq2', 'freq3'] - + kwargs['title'] = 'CGS Configuration' kwargs['suptitle'] = 'Details' - + kwargs['button'] = 'Edit Configuration' - + kwargs['no_play'] = True - + ###### SIDEBAR ###### kwargs.update(sidebar(conf=conf)) - + return render(request, 'cgs_conf.html', kwargs) def cgs_conf_edit(request, id_conf): - + conf = get_object_or_404(CGSConfiguration, pk=id_conf) - + if request.method=='GET': form = CGSConfigurationForm(instance=conf) - + if request.method=='POST': form = CGSConfigurationForm(request.POST, instance=conf) - + if form.is_valid(): if conf.freq0 == None: conf.freq0 = 0 if conf.freq1 == None: conf.freq1 = 0 if conf.freq2 == None: conf.freq2 = 0 if conf.freq3 == None: conf.freq3 = 0 - + conf = form.save(commit=False) - + if conf.verify_frequencies(): conf.save() return redirect('url_cgs_conf', id_conf=conf.id) - + ##ERRORS - + kwargs = {} kwargs['id_dev'] = conf.id kwargs['form'] = form kwargs['title'] = 'Device Configuration' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Save' - + return render(request, 'cgs_conf_edit.html', kwargs) -# +# # def cgs_conf_write(request, id_conf): -# +# # conf = get_object_or_404(CGSConfiguration, pk=id_conf) # ip=conf.device.ip_address # port=conf.device.port_address -# +# # #Frequencies from form # f0 = conf.freq0 # f1 = conf.freq1 # f2 = conf.freq2 # f3 = conf.freq3 -# -# try: +# +# try: # post_data = {"f0":f0, "f1":f1, "f2":f2, "f3":f3} # route = "http://" + str(ip) + ":" + str(port) + "/frequencies/" # r = requests.post(route, post_data) # text = r.text # text = text.split(',') -# -# try: +# +# try: # if len(text)>1: # title = text[0] # status = text[1] -# status_ok = r.status_code +# status_ok = r.status_code # if title == "okay": # messages.success(request, status) # else: # messages.error(request, status) -# +# # else: # title = text[0] # messages.error(request, title) -# +# # except: # messages.error(request, "An hardware error was found.") -# +# # except: # messages.error(request, "Could not write parameters.") -# -# -# -# +# +# +# +# # return redirect('url_cgs_conf', id_conf=conf.id) -# +# # def cgs_conf_read(request, id_conf): -# +# # conf = get_object_or_404(CGSConfiguration, pk=id_conf) -# +# # ip=conf.device.ip_address # port=conf.device.port_address -# +# # if request.method=='POST': # form = CGSConfigurationForm(request.POST, instance=conf) -# +# # if form.is_valid(): # cgs_model = form.save(commit=False) -# +# # if cgs_model.verify_frequencies(): -# +# # cgs_model.save() # return redirect('url_cgs_conf', id_conf=conf.id) -# +# # messages.error(request, "Parameters could not be saved. Invalid parameters") -# +# # data = {} -# -# +# +# # if request.method=='GET': # #r: response = icon, status # route = "http://" + str(ip) + ":" + str(port) + "/status/ad9548" @@ -178,7 +178,7 @@ def cgs_conf_edit(request, id_conf): # response = str(r.text) # response = response.split(";") # icon = response[0] -# status = response[-1] +# status = response[-1] # print r.text # #"icon" could be: "alert" or "okay" # if "okay" in icon: @@ -196,12 +196,12 @@ def cgs_conf_edit(request, id_conf): # f2 = frequencies.get("f2") # f3 = frequencies.get("f3") # print f0,f1,f2,f3 -# -# +# +# # if not response: # messages.error(request, "Could not read parameters from Device") # return redirect('url_cgs_conf', id_conf=conf.id) -# +# # data = {'experiment' : conf.experiment.id, # 'device' : conf.device.id, # 'freq0' : f0, @@ -210,7 +210,7 @@ def cgs_conf_edit(request, id_conf): # 'freq3' : f3, # } # except: -# messages.error(request, "Could not read parameters from Device") +# messages.error(request, "Could not read parameters from Device") # data = {'experiment' : conf.experiment.id, # 'device' : conf.device.id, # 'freq0' : None, @@ -219,79 +219,79 @@ def cgs_conf_edit(request, id_conf): # 'freq3' : None, # } # return redirect('url_cgs_conf', id_conf=conf.id) -# +# # form = CGSConfigurationForm(initial = data) -# +# # kwargs = {} # kwargs['id_dev'] = conf.id # kwargs['form'] = form # kwargs['title'] = 'Device Configuration' # kwargs['suptitle'] = 'Parameters read from device' # kwargs['button'] = 'Save' -# +# # ###### SIDEBAR ###### # kwargs.update(sidebar(conf)) -# +# # return render(request, 'cgs_conf_edit.html', kwargs) -# +# # def cgs_conf_import(request, id_conf): -# +# # conf = get_object_or_404(CGSConfiguration, pk=id_conf) -# +# # if request.method == 'POST': # file_form = UploadFileForm(request.POST, request.FILES) -# +# # if file_form.is_valid(): -# +# # try: # if conf.update_from_file(request.FILES['file']): -# +# # try: # conf.full_clean() # except ValidationError as e: # messages.error(request, e) # else: # conf.save() -# +# # messages.success(request, "Parameters imported from file: '%s'." %request.FILES['file'].name) # #messages.warning(request,"") # return redirect('url_cgs_conf', id_conf=conf.id) # except: # messages.error(request, "No JSON object could be decoded.") -# +# # messages.error(request, "Could not import parameters from file") -# +# # else: # file_form = UploadFileForm(initial={'title': '.json'}) -# -# +# +# # kwargs = {} # kwargs['id_dev'] = conf.id # kwargs['title'] = 'Device Configuration' # kwargs['form'] = file_form # kwargs['suptitle'] = 'Importing file' # kwargs['button'] = 'Import' -# +# # kwargs.update(sidebar(conf)) -# +# # return render(request, 'cgs_conf_import.html', kwargs) -# +# # def handle_uploaded_file(f): -# +# # data = {'freq0' : 62500000, # 'freq1' : 62500000, # 'freq2' : 62500000, # 'freq3' : 62500000, # } -# +# # return data -# +# # def cgs_conf_export(request, id_conf): -# +# # conf = get_object_or_404(CGSConfiguration, pk=id_conf) # ip=conf.device.ip_address # port=conf.device.port_address -# +# # #if request.method=='GET': # # data = {"Frequencies": [ # # ["freq0", conf.freq0], @@ -303,20 +303,20 @@ def cgs_conf_edit(request, id_conf): # # conf.parameters = json_data # # response = HttpResponse(conf.parameters, content_type="application/json") # # response['Content-Disposition'] = 'attachment; filename="data.json"' -# +# # # return response -# +# # kwargs = {} # kwargs['dev_conf'] = conf # kwargs['dev_conf_keys'] = ['experiment', 'device', # 'freq0', 'freq1', # 'freq2', 'freq3'] -# +# # kwargs['title'] = 'CGS Configuration' # kwargs['suptitle'] = 'Details' -# +# # kwargs['button'] = 'Edit Configuration' -# +# # ###### SIDEBAR ###### # kwargs.update(sidebar(conf)) -# return render(request, 'cgs_conf.html', kwargs) \ No newline at end of file +# return render(request, 'cgs_conf.html', kwargs) diff --git a/apps/dds/models.py b/apps/dds/models.py index a18cd09..9eac772 100644 --- a/apps/dds/models.py +++ b/apps/dds/models.py @@ -18,11 +18,11 @@ MOD_TYPES = ( (3, 'Chirp'), (4, 'BPSK'), ) - + class DDSConfiguration(Configuration): - + DDS_NBITS = 48 - + clock = models.FloatField(verbose_name='Clock In (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True, default=60) multiplier = models.PositiveIntegerField(verbose_name='Multiplier',validators=[MinValueValidator(1), MaxValueValidator(20)], default=4) @@ -31,98 +31,98 @@ class DDSConfiguration(Configuration): frequencyB_Mhz = models.DecimalField(verbose_name='Frequency B (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) frequencyB = models.BigIntegerField(verbose_name='Frequency B (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - + phaseA_degrees = models.FloatField(verbose_name='Phase A (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], default=0) - + phaseB_degrees = models.FloatField(verbose_name='Phase B (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], blank=True, null=True) - + modulation = models.PositiveIntegerField(verbose_name='Modulation Type', choices = MOD_TYPES, default = 0) - + amplitude_enabled = models.BooleanField(verbose_name='Amplitude Control', choices=ENABLE_TYPE, default=False) - + amplitudeI = models.PositiveIntegerField(verbose_name='Amplitude CH1',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) amplitudeQ = models.PositiveIntegerField(verbose_name='Amplitude CH2',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) - - + + def get_nbits(self): - + return self.DDS_NBITS - + def clean(self): - + if self.modulation in [1,2,3]: if self.frequencyB is None or self.frequencyB_Mhz is None: raise ValidationError({ 'frequencyB': 'Frequency modulation has to be defined when FSK or Chirp modulation is selected' }) - + if self.modulation in [4,]: if self.phaseB_degrees is None: raise ValidationError({ 'phaseB': 'Phase modulation has to be defined when BPSK modulation is selected' }) - + self.frequencyA_Mhz = data.binary_to_freq(self.frequencyA, self.clock*self.multiplier) self.frequencyB_Mhz = data.binary_to_freq(self.frequencyB, self.clock*self.multiplier) - + def verify_frequencies(self): - + return True - + def parms_to_dict(self): - + parameters = {} - + parameters['device_id'] = self.device.id - + parameters['clock'] = float(self.clock) parameters['multiplier'] = int(self.multiplier) - + parameters['frequencyA'] = int(self.frequencyA) parameters['frequencyA_Mhz'] = float(self.frequencyA_Mhz) - + parameters['phaseA'] = data.phase_to_binary(self.phaseA_degrees) parameters['phaseA_degrees'] = float(self.phaseA_degrees) - + parameters['modulation'] = int(self.modulation) parameters['amplitude_enabled'] = bool(self.amplitude_enabled) - + if self.frequencyB: parameters['frequencyB'] = int(self.frequencyB) parameters['frequencyB_Mhz'] = float(self.frequencyB_Mhz) else: parameters['frequencyB'] = 0 parameters['frequencyB_Mhz'] = 0 - + if self.phaseB_degrees: parameters['phaseB_degrees'] = float(self.phaseB_degrees) parameters['phaseB'] = data.phase_to_binary(self.phaseB_degrees) else: parameters['phaseB_degrees'] = 0 parameters['phaseB'] = 0 - + if self.amplitudeI: parameters['amplitudeI'] = int(self.amplitudeI) else: parameters['amplitudeI'] = 0 - + if self.amplitudeQ: parameters['amplitudeQ'] = int(self.amplitudeQ) else: parameters['amplitudeQ'] = 0 - + return parameters - + def parms_to_text(self): - + my_dict = self.parms_to_dict() - + text = data.dict_to_text(my_dict) - + return text - + def dict_to_parms(self, parameters): - + self.clock = parameters['clock'] self.multiplier = parameters['multiplier'] self.frequencyA = parameters['frequencyA'] @@ -133,98 +133,97 @@ class DDSConfiguration(Configuration): self.phaseB_degrees = parameters['phaseB_degrees'] self.modulation = parameters['modulation'] self.amplitude_enabled = parameters['amplitude_enabled'] - + def import_from_file(self, fp): - + import os, json - + parms = {} - + path, ext = os.path.splitext(fp.name) - + if ext == '.json': parms = json.load(fp) - + if ext == '.dds': lines = fp.readlines() parms = data.text_to_dict(lines) - + return parms - + def status_device(self): - + answer = api.status(ip = self.device.ip_address, port = self.device.port_address) - + self.device.status = int(answer[0]) self.message = answer[2:] - + self.device.save() - + return self.device.status - + def reset_device(self): - + answer = api.reset(ip = self.device.ip_address, port = self.device.port_address) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 - + def stop_device(self): - + answer = api.disable_rf(ip = self.device.ip_address, port = self.device.port_address) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 - + def start_device(self): - + answer = api.enable_rf(ip = self.device.ip_address, port = self.device.port_address) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 - + def read_device(self): - + parms = api.read_config(ip = self.device.ip_address, port = self.device.port_address) - + if not parms: self.message = "Could not read DDS parameters from this device" return parms - + self.message = "" return parms - - + + def write_device(self): - + answer = api.write_config(ip = self.device.ip_address, port = self.device.port_address, parms = self.parms_to_dict()) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 - + class Meta: db_table = 'dds_configurations' - \ No newline at end of file diff --git a/apps/dds/urls.py b/apps/dds/urls.py index c13d405..f007952 100644 --- a/apps/dds/urls.py +++ b/apps/dds/urls.py @@ -1,14 +1,9 @@ from django.conf.urls import url +from apps.dds import views + urlpatterns = ( - url(r'^(?P-?\d+)/$', 'apps.dds.views.dds_conf', name='url_dds_conf'), - url(r'^(?P-?\d+)/(?P-?\d+)/$', 'apps.dds.views.dds_conf', name='url_dds_conf'), - url(r'^(?P-?\d+)/edit/$', 'apps.dds.views.dds_conf_edit', name='url_edit_dds_conf'), -# url(r'^(?P-?\d+)/write/$', 'apps.dds.views.dds_conf_write', name='url_write_dds_conf'), -# url(r'^(?P-?\d+)/read/$', 'apps.dds.views.dds_conf_read', name='url_read_dds_conf'), -# url(r'^(?P-?\d+)/import/$', 'apps.dds.views.dds_conf_import', name='url_import_dds_conf'), -# url(r'^(?P-?\d+)/export/$', 'apps.dds.views.dds_conf_export', name='url_export_dds_conf'), -# url(r'^(?P-?\d+)/start/$', 'apps.dds.views.dds_conf_start', name='url_start_dds_conf'), -# url(r'^(?P-?\d+)/stop/$', 'apps.dds.views.dds_conf_stop', name='url_stop_dds_conf'), -# url(r'^(?P-?\d+)/status/$', 'apps.dds.views.dds_conf_status', name='url_status_dds_conf'), + url(r'^(?P-?\d+)/$', views.dds_conf, name='url_dds_conf'), + url(r'^(?P-?\d+)/(?P-?\d+)/$', views.dds_conf, name='url_dds_conf'), + url(r'^(?P-?\d+)/edit/$', views.dds_conf_edit, name='url_edit_dds_conf'), ) diff --git a/apps/jars/fixtures/initial_filters_data.json b/apps/jars/fixtures/initial_filters_data.json index f4c6f32..17c914d 100644 --- a/apps/jars/fixtures/initial_filters_data.json +++ b/apps/jars/fixtures/initial_filters_data.json @@ -1,2 +1,2 @@ -[{"fields": {"name": "49_920MHz_clock60MHz_F0MHz_12_25_2", "clock": "60", "mult": 5, "fch": 49.92, "fch_decimal": 721554505, "filter_fir": 6, "filter_2": 10, "filter_5": 1, "speed": 0}, "model": "jars.jarsfilter", "pk": 1}, - ] \ No newline at end of file +[{"fields": {"name": "49_920MHz_clock60MHz_F0MHz_12_25_2", "clock": "60", "mult": 5, "fch": 49.92, "fch_decimal": 721554505, "filter_fir": 6, "filter_2": 10, "filter_5": 1, "speed": 0}, "model": "jars.jarsfilter", "pk": 1} + ] diff --git a/apps/jars/models.py b/apps/jars/models.py index 2f5fb4c..6dacfa2 100644 --- a/apps/jars/models.py +++ b/apps/jars/models.py @@ -20,9 +20,9 @@ DATA_TYPE = ( ) class JARSfilter(models.Model): - + JARS_NBITS = 32 - + name = models.CharField(max_length=60, unique=True, default='') clock = models.FloatField(verbose_name='Clock In (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True, default=60) mult = models.PositiveIntegerField(verbose_name='Multiplier',validators=[MinValueValidator(1), MaxValueValidator(20)], default=5) @@ -32,17 +32,17 @@ class JARSfilter(models.Model): filter_2 = models.PositiveIntegerField(verbose_name='Filter 2',validators=[MinValueValidator(1), MaxValueValidator(20)], default = 10) filter_5 = models.PositiveIntegerField(verbose_name='Filter 5',validators=[MinValueValidator(1), MaxValueValidator(20)], default = 1) speed = models.PositiveIntegerField(verbose_name='Speed',validators=[MinValueValidator(0), MaxValueValidator(100000)], default = 0) - + class Meta: db_table = 'jars_filters' - + def __unicode__(self): return u'%s' % (self.name) - + def parms_to_dict(self): - + parameters = {} - + parameters['name'] = self.name parameters['clock'] = float(self.clock) parameters['mult'] = int(self.mult) @@ -52,11 +52,11 @@ class JARSfilter(models.Model): parameters['filter_2'] = int(self.filter_2) parameters['filter_5'] = int(self.filter_5) parameters['speed'] = int(self.speed) - + return parameters - + def dict_to_parms(self, parameters): - + self.name = parameters['name'] self.clock = parameters['clock'] self.mult = parameters['mult'] @@ -66,16 +66,16 @@ class JARSfilter(models.Model): self.filter_2 = parameters['filter_2'] self.filter_5 = parameters['filter_5'] self.speed = parameters['speed'] - + class JARSConfiguration(Configuration): - + ADC_RESOLUTION = 8 PCI_DIO_BUSWIDTH = 32 HEADER_VERSION = 1103 BEGIN_ON_START = True REFRESH_RATE = 1 - + #rc = models.ForeignKey(RCConfiguration, on_delete=models.CASCADE, null=True) exp_type = models.PositiveIntegerField(verbose_name='Experiment Type', choices=EXPERIMENT_TYPE, default=0) cards_number = models.PositiveIntegerField(verbose_name='Number of Cards', validators=[MinValueValidator(1), MaxValueValidator(4)], default = 1) @@ -105,11 +105,11 @@ class JARSConfiguration(Configuration): class Meta: db_table = 'jars_configurations' - + def parms_to_dict(self): - + parameters = {} - + parameters['device_id'] = self.device.id parameters['name'] = self.name #parameters['rc'] = self.rc.name @@ -138,31 +138,31 @@ class JARSConfiguration(Configuration): #parameters['view_raw_data'] = bool(self.view_raw_data) parameters['save_ch_dc'] = bool(self.save_ch_dc) parameters['save_data'] = bool(self.save_data) - + if parameters['exptype'] == 'PDATA': parameters['incohe_integr'] = self.incohe_integr parameters['spectral_number'] = self.spectral_number parameters['spectral'] = self.spectral parameters['pd_directory'] = self.pd_directory - + return parameters - + def add_parms_to_filter(self): self.filter_parms = self.filter.parms_to_dict() self.save() - + def dict_to_parms(self, parameters): - + self.name = parameters['name'] self.device.id = int(parameters['device_id']) - + self.exp_type = int(parameters['exp_type']) - if parameters['exptype'] == 'PDATA': + if parameters['exptype'] == 'PDATA': self.incohe_integr = parameters['incohe_integr'] self.spectral_number = parameters['spectral_number'] self.spectral = parameters['spectral'] self.pd_directory = parameters['pd_directory'] - + self.cards_number = int(parameters['cards_number']) self.channels_number = int(parameters['channels_number']) self.channels = parameters['channels'] @@ -175,39 +175,39 @@ class JARSConfiguration(Configuration): self.ftp_interval = parameters['ftp_interval'] self.fftpoints = parameters['fftpoints'] self.cohe_integr = parameters['cohe_integr'] - + filter_name = parameters['filter'] self.filter = JARSfilter.objects.get(name=filter_name) self.add_parms_to_filter() self.filter_parms = parameters['filter_parms'] - + self.create_directory = bool(parameters['create_directory']) self.include_expname = bool(parameters['include_expname']) #self.acq_link = bool(parameters['acq_link']) #self.view_raw_data = bool(parameters['view_raw_data']) self.save_ch_dc = bool(parameters['save_ch_dc']) self.save_data = bool(parameters['save_data']) - + def status_device(self): - + answer = api.status(self.device.ip_address,self.device.port_address) self.device.status = int(answer[0]) self.message = answer[2:] self.device.save() - + return self.device.status - + def stop_device(self): - + answer = api.stop(self.device.ip_address,self.device.port_address) self.device.status = int(answer[0]) self.message = answer[2:] self.device.save() - + return self.device.status - + def read_device(self): - + answer = api.read(self.device.ip_address,self.device.port_address) self.device.status = int(answer[0]) try: @@ -218,15 +218,15 @@ class JARSConfiguration(Configuration): self.device.save() self.message = 'Could not read JARS configuration.' return '' - + #self.dict_to_parms(parms) self.message = 'Current JARS configuration was read successfully.' self.device.save() return parms - - + + def write_device(self): - + data = self.experiment.parms_to_dict() data = json.loads(data) data['configurations']['dds'] ='' @@ -234,40 +234,40 @@ class JARSConfiguration(Configuration): data['configurations']['rc']['pulses']='' data['configurations']['rc']['delays']='' json_data = json.dumps(data) - + answer = api.configure(self.device.ip_address,self.device.port_address,json_data) #print answer self.device.status = int(answer[0]) self.message = answer[2:] - + self.device.save() - + return self.device.status - - + + def start_device(self): - + self.write_device() - - + + def echo(self): - + answer = api.echo(self.device.ip_address,self.device.port_address,'(=') #print answer self.device.status = int(answer[0]) self.message = answer[2:] - + self.device.save() - + return #self.device.status - + def update_from_file(self, parameters): - + self.dict_to_parms(parameters) self.save() - + def get_absolute_url_import(self): return reverse('url_import_jars_conf', args=[str(self.id)]) - + def get_absolute_url_read(self): - return reverse('url_read_jars_conf', args=[str(self.id)]) \ No newline at end of file + return reverse('url_read_jars_conf', args=[str(self.id)]) diff --git a/apps/jars/urls.py b/apps/jars/urls.py index 8287982..b9836f9 100644 --- a/apps/jars/urls.py +++ b/apps/jars/urls.py @@ -1,11 +1,13 @@ from django.conf.urls import url +from apps.jars import views + urlpatterns = ( - url(r'^(?P-?\d+)/$', 'apps.jars.views.jars_conf', name='url_jars_conf'), - url(r'^(?P-?\d+)/edit/$', 'apps.jars.views.jars_conf_edit', name='url_edit_jars_conf'), - url(r'^(?P-?\d+)/new_filter/$', 'apps.jars.views.new_filter', name='url_new_jars_filter'), - url(r'^(?P-?\d+)/view_filter/(?P-?\d+)/$', 'apps.jars.views.view_filter', name='url_jars_filter'), - url(r'^(?P-?\d+)/view_filter/(?P-?\d+)/edit$', 'apps.jars.views.edit_filter', name='url_edit_jars_filter'), - url(r'^(?P-?\d+)/import/$', 'apps.jars.views.import_file', name='url_import_jars_conf'), - url(r'^(?P-?\d+)/read/$', 'apps.jars.views.read_conf', name='url_read_jars_conf'), + url(r'^(?P-?\d+)/$', views.jars_conf, name='url_jars_conf'), + url(r'^(?P-?\d+)/edit/$', views.jars_conf_edit, name='url_edit_jars_conf'), + url(r'^(?P-?\d+)/new_filter/$', views.new_filter, name='url_new_jars_filter'), + url(r'^(?P-?\d+)/view_filter/(?P-?\d+)/$', views.view_filter, name='url_jars_filter'), + url(r'^(?P-?\d+)/view_filter/(?P-?\d+)/edit$', views.edit_filter, name='url_edit_jars_filter'), + url(r'^(?P-?\d+)/import/$', views.import_file, name='url_import_jars_conf'), + url(r'^(?P-?\d+)/read/$', views.read_conf, name='url_read_jars_conf'), ) diff --git a/apps/jars/widgets.py b/apps/jars/widgets.py index 197f46f..cf159a4 100644 --- a/apps/jars/widgets.py +++ b/apps/jars/widgets.py @@ -5,14 +5,13 @@ from itertools import chain from django import forms from django.utils.safestring import mark_safe -from django.utils.encoding import force_unicode from django.utils.html import conditional_escape - + class SpectralWidget(forms.widgets.TextInput): - + def render(self, label, value, attrs=None): - + disabled = 'disabled' if attrs.get('disabled', False) else '' name = attrs.get('name', label) if '[' in value: @@ -21,7 +20,7 @@ class SpectralWidget(forms.widgets.TextInput): else: value = value + "," value = ast.literal_eval(value) - + codes = value if not isinstance(value, list): text='' @@ -33,7 +32,7 @@ class SpectralWidget(forms.widgets.TextInput): codes=text else: codes='' - + html = ''' @@ -43,23 +42,23 @@ class SpectralWidget(forms.widgets.TextInput): '''.format(disabled, label, name, codes) - + script = ''' - ''' - - return mark_safe(html+script) \ No newline at end of file + + return mark_safe(html+script) diff --git a/apps/main/fixtures/main_initial_data.json b/apps/main/fixtures/main_initial_data.json index b497e95..018f83a 100644 --- a/apps/main/fixtures/main_initial_data.json +++ b/apps/main/fixtures/main_initial_data.json @@ -1,10 +1,11 @@ [ -{"fields": {"name": "JRO Imaging", "description": ""}, "model": "main.location", "pk": 1}, +{"fields": {"name": "JRO", "description": ""}, "model": "main.location", "pk": 1}, {"fields": {"name": "JASMET", "description": ""}, "model": "main.location", "pk": 2}, {"fields": {"name": "SOUSY", "description": ""}, "model": "main.location", "pk": 3}, {"fields": {"name": "JULIA", "description": ""}, "model": "main.location", "pk": 4}, -{"fields": {"name": "rc", "description": ""}, "model": "main.devicetype", "pk": 1}, -{"fields": {"name": "dds", "description": ""}, "model": "main.devicetype", "pk": 2}, -{"fields": {"name": "cgs", "description": ""}, "model": "main.devicetype", "pk": 3}, -{"fields": {"name": "jars", "description": ""}, "model": "main.devicetype", "pk": 4} +{"fields": {"name": "rc", "description": ""}, "model": "main.devicetype", "pk": 1}, +{"fields": {"name": "dds", "description": ""}, "model": "main.devicetype", "pk": 2}, +{"fields": {"name": "cgs", "description": ""}, "model": "main.devicetype", "pk": 3}, +{"fields": {"name": "jars", "description": ""}, "model": "main.devicetype", "pk": 4}, +{"fields": {"name": "abs", "description": ""}, "model": "main.devicetype", "pk": 5} ] diff --git a/apps/main/forms.py b/apps/main/forms.py index ead9841..5089a5f 100644 --- a/apps/main/forms.py +++ b/apps/main/forms.py @@ -1,7 +1,7 @@ from django import forms from django.utils.safestring import mark_safe - -from .models import DeviceType, Device, Experiment, Campaign, Configuration, Location +from .models import Device, Experiment, Campaign, Location +from apps.main.models import Configuration FILE_FORMAT = ( ('json', 'json'), @@ -35,7 +35,7 @@ class DatepickerWidget(forms.widgets.TextInput): class DateRangepickerWidget(forms.widgets.TextInput): def render(self, name, value, attrs=None): start = attrs['start_date'] - end = attrs['end_date'] + end = attrs['end_date'] html = '''
@@ -53,32 +53,32 @@ class TimepickerWidget(forms.widgets.TextInput): return mark_safe(html) class CampaignForm(forms.ModelForm): - + experiments = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=Experiment.objects.filter(template=True), required=False) - + def __init__(self, *args, **kwargs): super(CampaignForm, self).__init__(*args, **kwargs) self.fields['start_date'].widget = DatepickerWidget(self.fields['start_date'].widget.attrs) self.fields['end_date'].widget = DatepickerWidget(self.fields['end_date'].widget.attrs) self.fields['description'].widget.attrs = {'rows': 2} - + if self.instance.pk: self.fields['experiments'].queryset |= self.instance.experiments.all() - + class Meta: model = Campaign exclude = [''] - - + + class ExperimentForm(forms.ModelForm): - + def __init__(self, *args, **kwargs): super(ExperimentForm, self).__init__(*args, **kwargs) self.fields['start_time'].widget = TimepickerWidget(self.fields['start_time'].widget.attrs) self.fields['end_time'].widget = TimepickerWidget(self.fields['end_time'].widget.attrs) - + class Meta: model = Experiment exclude = ['status'] @@ -87,104 +87,96 @@ class LocationForm(forms.ModelForm): class Meta: model = Location exclude = [''] - + class DeviceForm(forms.ModelForm): class Meta: model = Device exclude = ['status'] class ConfigurationForm(forms.ModelForm): - + def __init__(self, *args, **kwargs): super(ConfigurationForm, self).__init__(*args, **kwargs) - + if 'initial' in kwargs and 'experiment' in kwargs['initial'] and kwargs['initial']['experiment'] not in (0, '0'): self.fields['experiment'].widget.attrs['disabled'] = 'disabled' - + class Meta: model = Configuration exclude = ['type', 'created_date', 'programmed_date', 'parameters'] - -class DeviceTypeForm(forms.Form): - device_type = forms.ChoiceField(choices=add_empty_choice(DeviceType.objects.all().order_by('name').values_list('id', 'name'))) + +#class DeviceTypeForm(forms.Form): +# device_type = forms.ChoiceField(choices=add_empty_choice(DeviceType.objects.all().order_by('name').values_list('id', 'name'))) class UploadFileForm(forms.Form): - + file = forms.FileField() class DownloadFileForm(forms.Form): - + format = forms.ChoiceField(choices= ((0, 'json'),) ) - + def __init__(self, device_type, *args, **kwargs): - + super(DownloadFileForm, self).__init__(*args, **kwargs) - + self.fields['format'].choices = FILE_FORMAT - + if device_type == 'dds': self.fields['format'].choices = DDS_FILE_FORMAT - + if device_type == 'rc': self.fields['format'].choices = RC_FILE_FORMAT - + class OperationForm(forms.Form): -# today = datetime.today() - # -----Campaigns from this month------[:5] + campaign = forms.ChoiceField(label="Campaign") - + def __init__(self, *args, **kwargs): - - if 'length' not in kwargs.keys(): - length = None - else: - length = kwargs['length'] - - kwargs.pop('length') - + + campaigns = kwargs.pop('campaigns') super(OperationForm, self).__init__(*args, **kwargs) - self.fields['campaign'].choices=Campaign.objects.all().order_by('-start_date').values_list('id', 'name')[:length] - + self.fields['campaign'].choices=add_empty_choice(campaigns.values_list('id', 'name')) + class OperationSearchForm(forms.Form): # -----ALL Campaigns------ campaign = forms.ChoiceField(label="Campaign") - + def __init__(self, *args, **kwargs): super(OperationSearchForm, self).__init__(*args, **kwargs) self.fields['campaign'].choices=Campaign.objects.all().order_by('-start_date').values_list('id', 'name') - + class NewForm(forms.Form): - + create_from = forms.ChoiceField(choices=((0, '-----'), (1, 'Empty (blank)'), (2, 'Template'))) choose_template = forms.ChoiceField() - + def __init__(self, *args, **kwargs): - + template_choices = kwargs.pop('template_choices', []) super(NewForm, self).__init__(*args, **kwargs) self.fields['choose_template'].choices = add_empty_choice(template_choices) - + class FilterForm(forms.Form): - + def __init__(self, *args, **kwargs): extra_fields = kwargs.pop('extra_fields', []) super(FilterForm, self).__init__(*args, **kwargs) - - for field in extra_fields: + + for field in extra_fields: if 'range_date' in field: self.fields[field] = forms.CharField(required=False) self.fields[field].widget = DateRangepickerWidget() if 'initial' in kwargs: self.fields[field].widget.attrs = {'start_date':kwargs['initial'].get('start_date', ''), - 'end_date':kwargs['initial'].get('end_date', '')} + 'end_date':kwargs['initial'].get('end_date', '')} elif 'template' in field: self.fields['template'] = forms.BooleanField(required=False) else: self.fields[field] = forms.CharField(required=False) - \ No newline at end of file diff --git a/apps/main/models.py b/apps/main/models.py index 52fdd35..68e2b26 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404, HttpResponse from datetime import datetime from django.db import models -from polymorphic import PolymorphicModel +from polymorphic.models import PolymorphicModel from django.core.urlresolvers import reverse @@ -71,7 +71,7 @@ class Location(models.Model): class Meta: db_table = 'db_location' - def __unicode__(self): + def __str__(self): return u'%s' % self.name def get_absolute_url(self): @@ -86,7 +86,7 @@ class DeviceType(models.Model): class Meta: db_table = 'db_device_types' - def __unicode__(self): + def __str__(self): return u'%s' % self.get_name_display() class Device(models.Model): @@ -103,7 +103,7 @@ class Device(models.Model): class Meta: db_table = 'db_devices' - def __unicode__(self): + def __str__(self): return u'[{}]: {}'.format(self.device_type.name.upper(), self.name) @@ -142,13 +142,12 @@ class Campaign(models.Model): db_table = 'db_campaigns' ordering = ('name',) - def __unicode__(self): + def __str__(self): if self.template: return u'{} (template)'.format(self.name) else: return u'{}'.format(self.name) - def parms_to_dict(self): import json @@ -166,7 +165,7 @@ class Campaign(models.Model): parameters['experiments'] = exp_parameters parameters['end_date'] = self.end_date.strftime("%Y-%m-%d") parameters['start_date'] = self.start_date.strftime("%Y-%m-%d") - parameters['campaign'] = self.__unicode__() + parameters['campaign'] = self.__str__() parameters['tags'] =self.tags parameters = json.dumps(parameters, indent=2, sort_keys=False) @@ -182,7 +181,7 @@ class Campaign(models.Model): path, ext = os.path.splitext(fp.name) if ext == '.json': - parms = json.load(fp) + parms = json.loads(fp.read()) return parms @@ -220,6 +219,19 @@ class Campaign(models.Model): return self + def get_experiments_by_location(self): + + ret = [] + locations = set([e.location for e in self.experiments.all()]) + for loc in locations: + dum = {} + dum['name'] = loc.name + dum['id'] = loc.pk + dum['experiments'] = [e for e in self.experiments.all() if e.location==loc] + ret.append(dum) + + return ret + def get_absolute_url(self): return reverse('url_campaign', args=[str(self.id)]) @@ -253,7 +265,7 @@ class Experiment(models.Model): db_table = 'db_experiments' ordering = ('template', 'name') - def __unicode__(self): + def __str__(self): if self.template: return u'%s (template)' % (self.name) else: @@ -282,7 +294,6 @@ class Experiment(models.Model): configurations = Configuration.objects.filter(experiment=self) exp_status=[] for conf in configurations: - print conf.status_device() exp_status.append(conf.status_device()) if not exp_status: #No Configuration @@ -360,7 +371,7 @@ class Experiment(models.Model): path, ext = os.path.splitext(fp.name) if ext == '.json': - parms = json.load(fp) + parms = json.loads(fp.read().decode('utf-8')) return parms @@ -460,11 +471,11 @@ class Configuration(PolymorphicModel): class Meta: db_table = 'db_configurations' - def __unicode__(self): + def __str__(self): device = '{}:'.format(self.device.device_type.name.upper()) - if 'mix' in self._meta.get_all_field_names(): + if 'mix' in [f.name for f in self._meta.get_fields()]: if self.mix: device = '{} MIXED:'.format(self.device.device_type.name.upper()) @@ -495,15 +506,13 @@ class Configuration(PolymorphicModel): def parms_to_text(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return '' def parms_to_binary(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return '' def dict_to_parms(self, parameters): @@ -556,33 +565,28 @@ class Configuration(PolymorphicModel): def status_device(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return None def stop_device(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return None def start_device(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return None def write_device(self, parms): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return None def read_device(self): - raise NotImplementedError, "This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper() + raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) - return None def get_absolute_url(self): return reverse('url_%s_conf' % self.device.device_type.name, args=[str(self.id)]) diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html index 2c87e2e..8df34d6 100644 --- a/apps/main/templates/base.html +++ b/apps/main/templates/base.html @@ -73,11 +73,11 @@ Hi {{ user.username }} {% else %} -
  • Login
  • +
  • Login
  • {% endif %}
    diff --git a/apps/main/templates/operation.html b/apps/main/templates/operation.html index 25d9adf..cf523ae 100644 --- a/apps/main/templates/operation.html +++ b/apps/main/templates/operation.html @@ -6,50 +6,33 @@ {% endblock %} -{% block camp-active %}active{% endblock %} +{% block operation-active %}active{% endblock %} -{% block content-title %}{{title}}{% endblock %} -{% block content-suptitle %}{{suptitle}}{% endblock %} {% block content %} - -{% if details %} +{% bootstrap_form form layout='horizontal' size='medium' %} +
    - - {% csrf_token %} +{% if campaign %} - {% bootstrap_form form layout='horizontal' size='medium' %} -
    -
    - - {% if search_button == True %} - - {% endif %} -
    -
    - -
    -
    - -{% endif %}

    Radar Systems


    - + {% for location in locations %} - +
    - +
    @@ -71,10 +54,10 @@ {% endfor%} - - {% for item in experiments %} + + {% for item in location.experiments %} {% if location.name in item.location.name %} - + {% for key in experiment_keys %} @@ -87,14 +70,14 @@ {% endif %} {% endfor %} -
    {{ header|title }}
    {{ forloop.counter }}
    +
    {% endfor %}
    - +{% endif %} {% endblock %} @@ -102,28 +85,28 @@ {% block extra-js%} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/apps/main/templates/sidebar_devices.html b/apps/main/templates/sidebar_devices.html index 0e1eac0..dd56417 100644 --- a/apps/main/templates/sidebar_devices.html +++ b/apps/main/templates/sidebar_devices.html @@ -1,34 +1,34 @@ - + {% if campaign %}

    Campaign

    - {% endif %} - + {% if experiment %}

    Experiments

    -
    +
    {% for item in side_experiments %} - {{item.name}} + {{item.name}} {% endfor %}
    {% endif %} - + {% if dev_conf %}

    Device Configurations

    -
    +
    {% for item in side_configurations %} {{item}} {% endfor %} diff --git a/apps/main/urls.py b/apps/main/urls.py index 9854607..1842bf5 100644 --- a/apps/main/urls.py +++ b/apps/main/urls.py @@ -1,59 +1,61 @@ from django.conf.urls import url +from apps.main import views + urlpatterns = ( - url(r'^location/new/$', 'apps.main.views.location_new', name='url_add_location'), - url(r'^location/$', 'apps.main.views.locations', name='url_locations'), - url(r'^location/(?P-?\d+)/$', 'apps.main.views.location', name='url_location'), - url(r'^location/(?P-?\d+)/edit/$', 'apps.main.views.location_edit', name='url_edit_location'), - url(r'^location/(?P-?\d+)/delete/$', 'apps.main.views.location_delete', name='url_delete_location'), - - url(r'^device/new/$', 'apps.main.views.device_new', name='url_add_device'), - url(r'^device/$', 'apps.main.views.devices', name='url_devices'), - url(r'^device/(?P-?\d+)/$', 'apps.main.views.device', name='url_device'), - url(r'^device/(?P-?\d+)/edit/$', 'apps.main.views.device_edit', name='url_edit_device'), - url(r'^device/(?P-?\d+)/delete/$', 'apps.main.views.device_delete', name='url_delete_device'), - - url(r'^campaign/new/$', 'apps.main.views.campaign_new', name='url_add_campaign'), - url(r'^campaign/$', 'apps.main.views.campaigns', name='url_campaigns'), - url(r'^campaign/(?P-?\d+)/$', 'apps.main.views.campaign', name='url_campaign'), - url(r'^campaign/(?P-?\d+)/edit/$', 'apps.main.views.campaign_edit', name='url_edit_campaign'), - url(r'^campaign/(?P-?\d+)/delete/$', 'apps.main.views.campaign_delete', name='url_delete_campaign'), - url(r'^campaign/(?P-?\d+)/export/$', 'apps.main.views.campaign_export', name='url_export_campaign'), - url(r'^campaign/(?P-?\d+)/import/$', 'apps.main.views.campaign_import', name='url_import_campaign'), - - url(r'^experiment/new/$', 'apps.main.views.experiment_new', name='url_add_experiment'), - url(r'^experiment/$', 'apps.main.views.experiments', name='url_experiments'), - url(r'^experiment/(?P-?\d+)/$', 'apps.main.views.experiment', name='url_experiment'), - url(r'^experiment/(?P-?\d+)/edit/$', 'apps.main.views.experiment_edit', name='url_edit_experiment'), - url(r'^experiment/(?P-?\d+)/delete/$', 'apps.main.views.experiment_delete', name='url_delete_experiment'), - url(r'^experiment/(?P-?\d+)/export/$', 'apps.main.views.experiment_export', name='url_export_experiment'), - url(r'^experiment/(?P-?\d+)/import/$', 'apps.main.views.experiment_import', name='url_import_experiment'), - url(r'^experiment/(?P-?\d+)/mix/$', 'apps.main.views.experiment_mix', name='url_mix_experiment'), - url(r'^experiment/(?P-?\d+)/mix/delete/$', 'apps.main.views.experiment_mix_delete', name='url_delete_mix_experiment'), - url(r'^experiment/(?P-?\d+)/summary/$', 'apps.main.views.experiment_summary', name='url_sum_experiment'), - url(r'^experiment/(?P-?\d+)/verify/$', 'apps.main.views.experiment_verify', name='url_verify_experiment'), - - url(r'^experiment/(?P-?\d+)/new_dev_conf/$', 'apps.main.views.dev_conf_new', name='url_add_dev_conf'), - url(r'^experiment/(?P-?\d+)/new_dev_conf/(?P-?\d+)/$', 'apps.main.views.dev_conf_new', name='url_add_dev_conf'), - url(r'^dev_conf/$', 'apps.main.views.dev_confs', name='url_dev_confs'), - url(r'^dev_conf/(?P-?\d+)/$', 'apps.main.views.dev_conf', name='url_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/edit/$', 'apps.main.views.dev_conf_edit', name='url_edit_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/delete/$', 'apps.main.views.dev_conf_delete', name='url_delete_dev_conf'), - - url(r'^dev_conf/(?P-?\d+)/write/$', 'apps.main.views.dev_conf_write', name='url_write_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/read/$', 'apps.main.views.dev_conf_read', name='url_read_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/import/$', 'apps.main.views.dev_conf_import', name='url_import_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/export/$', 'apps.main.views.dev_conf_export', name='url_export_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/start/$', 'apps.main.views.dev_conf_start', name='url_start_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/stop/$', 'apps.main.views.dev_conf_stop', name='url_stop_dev_conf'), - url(r'^dev_conf/(?P-?\d+)/status/$', 'apps.main.views.dev_conf_status', name='url_status_dev_conf'), - - url(r'^operation/$', 'apps.main.views.operation', name='url_operation'), - url(r'^operation/(?P-?\d+)/$', 'apps.main.views.operation', name='url_operation'), - url(r'^operation/search/$', 'apps.main.views.operation_search', name='url_operation_search'), - url(r'^operation/search/(?P-?\d+)/$', 'apps.main.views.operation_search', name='url_operation_search'), - url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/play/$', 'apps.main.views.radar_play', name='url_radar_play'), - url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/stop/$', 'apps.main.views.radar_stop', name='url_radar_stop'), - url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/refresh/$', 'apps.main.views.radar_refresh', name='url_radar_refresh'), - + url(r'^$', views.index, name='index'), + url(r'^location/new/$', views.location_new, name='url_add_location'), + url(r'^location/$', views.locations, name='url_locations'), + url(r'^location/(?P-?\d+)/$', views.location, name='url_location'), + url(r'^location/(?P-?\d+)/edit/$', views.location_edit, name='url_edit_location'), + url(r'^location/(?P-?\d+)/delete/$', views.location_delete, name='url_delete_location'), + + url(r'^device/new/$', views.device_new, name='url_add_device'), + url(r'^device/$', views.devices, name='url_devices'), + url(r'^device/(?P-?\d+)/$', views.device, name='url_device'), + url(r'^device/(?P-?\d+)/edit/$', views.device_edit, name='url_edit_device'), + url(r'^device/(?P-?\d+)/delete/$', views.device_delete, name='url_delete_device'), + + url(r'^campaign/new/$', views.campaign_new, name='url_add_campaign'), + url(r'^campaign/$', views.campaigns, name='url_campaigns'), + url(r'^campaign/(?P-?\d+)/$', views.campaign, name='url_campaign'), + url(r'^campaign/(?P-?\d+)/edit/$', views.campaign_edit, name='url_edit_campaign'), + url(r'^campaign/(?P-?\d+)/delete/$', views.campaign_delete, name='url_delete_campaign'), + url(r'^campaign/(?P-?\d+)/export/$', views.campaign_export, name='url_export_campaign'), + url(r'^campaign/(?P-?\d+)/import/$', views.campaign_import, name='url_import_campaign'), + + url(r'^experiment/new/$', views.experiment_new, name='url_add_experiment'), + url(r'^experiment/$', views.experiments, name='url_experiments'), + url(r'^experiment/(?P-?\d+)/$', views.experiment, name='url_experiment'), + url(r'^experiment/(?P-?\d+)/edit/$', views.experiment_edit, name='url_edit_experiment'), + url(r'^experiment/(?P-?\d+)/delete/$', views.experiment_delete, name='url_delete_experiment'), + url(r'^experiment/(?P-?\d+)/export/$', views.experiment_export, name='url_export_experiment'), + url(r'^experiment/(?P-?\d+)/import/$', views.experiment_import, name='url_import_experiment'), + url(r'^experiment/(?P-?\d+)/mix/$', views.experiment_mix, name='url_mix_experiment'), + url(r'^experiment/(?P-?\d+)/mix/delete/$', views.experiment_mix_delete, name='url_delete_mix_experiment'), + url(r'^experiment/(?P-?\d+)/summary/$', views.experiment_summary, name='url_sum_experiment'), + url(r'^experiment/(?P-?\d+)/verify/$', views.experiment_verify, name='url_verify_experiment'), + + url(r'^experiment/(?P-?\d+)/new_dev_conf/$', views.dev_conf_new, name='url_add_dev_conf'), + url(r'^experiment/(?P-?\d+)/new_dev_conf/(?P-?\d+)/$', views.dev_conf_new, name='url_add_dev_conf'), + url(r'^dev_conf/$', views.dev_confs, name='url_dev_confs'), + url(r'^dev_conf/(?P-?\d+)/$', views.dev_conf, name='url_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/edit/$', views.dev_conf_edit, name='url_edit_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/delete/$', views.dev_conf_delete, name='url_delete_dev_conf'), + + url(r'^dev_conf/(?P-?\d+)/write/$', views.dev_conf_write, name='url_write_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/read/$', views.dev_conf_read, name='url_read_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/import/$', views.dev_conf_import, name='url_import_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/export/$', views.dev_conf_export, name='url_export_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/start/$', views.dev_conf_start, name='url_start_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/stop/$', views.dev_conf_stop, name='url_stop_dev_conf'), + url(r'^dev_conf/(?P-?\d+)/status/$', views.dev_conf_status, name='url_status_dev_conf'), + + url(r'^operation/$', views.operation, name='url_operation'), + url(r'^operation/(?P-?\d+)/$', views.operation, name='url_operation'), + url(r'^operation/search/$', views.operation_search, name='url_operation_search'), + url(r'^operation/search/(?P-?\d+)/$', views.operation_search, name='url_operation_search'), + url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/play/$', views.radar_play, name='url_radar_play'), + url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/stop/$', views.radar_stop, name='url_radar_stop'), + url(r'^operation/(?P-?\d+)/radar/(?P-?\d+)/refresh/$', views.radar_refresh, name='url_radar_refresh'), ) diff --git a/apps/main/views.py b/apps/main/views.py index bfb9245..69eff11 100644 --- a/apps/main/views.py +++ b/apps/main/views.py @@ -5,17 +5,23 @@ from django.core.urlresolvers import reverse from django.db.models import Q from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.contrib import messages +from django.http.request import QueryDict from datetime import datetime -import urllib + +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode from .forms import CampaignForm, ExperimentForm, DeviceForm, ConfigurationForm, LocationForm, UploadFileForm, DownloadFileForm, OperationForm, NewForm from .forms import OperationSearchForm, FilterForm -from apps.cgs.forms import CGSConfigurationForm + +from apps.rc.forms import RCConfigurationForm +from apps.dds.forms import DDSConfigurationForm from apps.jars.forms import JARSConfigurationForm -from apps.usrp.forms import USRPConfigurationForm +from apps.cgs.forms import CGSConfigurationForm from apps.abs.forms import ABSConfigurationForm -from apps.rc.forms import RCConfigurationForm, RCMixConfigurationForm -from apps.dds.forms import DDSConfigurationForm +from apps.usrp.forms import USRPConfigurationForm from .models import Campaign, Experiment, Device, Configuration, Location, RunningExperiment from apps.cgs.models import CGSConfiguration @@ -62,309 +68,309 @@ MIX_OPERATIONS = { def index(request): kwargs = {} - + return render(request, 'index.html', kwargs) def locations(request): - + page = request.GET.get('page') order = ('name',) - - kwargs = get_paginator(Location, page, order) - + + kwargs = get_paginator(Location, page, order) + kwargs['keys'] = ['name', 'description'] kwargs['title'] = 'Radar System' kwargs['suptitle'] = 'List' - + return render(request, 'base_list.html', kwargs) def location(request, id_loc): - + location = get_object_or_404(Location, pk=id_loc) - + kwargs = {} kwargs['location'] = location kwargs['location_keys'] = ['name', 'description'] - + kwargs['title'] = 'Location' kwargs['suptitle'] = 'Details' - + return render(request, 'location.html', kwargs) def location_new(request): - + if request.method == 'GET': form = LocationForm() - + if request.method == 'POST': form = LocationForm(request.POST) - + if form.is_valid(): form.save() return redirect('url_locations') - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Radar System' kwargs['suptitle'] = 'New' kwargs['button'] = 'Create' - + return render(request, 'base_edit.html', kwargs) def location_edit(request, id_loc): - + location = get_object_or_404(Location, pk=id_loc) - + if request.method=='GET': form = LocationForm(instance=location) - + if request.method=='POST': form = LocationForm(request.POST, instance=location) - + if form.is_valid(): form.save() return redirect('url_locations') - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Location' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' - + return render(request, 'base_edit.html', kwargs) def location_delete(request, id_loc): - + location = get_object_or_404(Location, pk=id_loc) - + if request.method=='POST': - + if request.user.is_staff: location.delete() return redirect('url_locations') - + messages.error(request, 'Not enough permission to delete this object') return redirect(location.get_absolute_url()) - + kwargs = { 'title': 'Delete', 'suptitle': 'Location', - 'object': location, + 'object': location, 'previous': location.get_absolute_url(), 'delete': True } - + return render(request, 'confirm.html', kwargs) def devices(request): - + page = request.GET.get('page') order = ('device_type', 'name') - - kwargs = get_paginator(Device, page, order) - kwargs['keys'] = ['name', 'ip_address', 'port_address', 'device_type'] + + kwargs = get_paginator(Device, page, order) + kwargs['keys'] = ['name', 'ip_address', 'port_address', 'device_type'] kwargs['title'] = 'Device' kwargs['suptitle'] = 'List' - + return render(request, 'base_list.html', kwargs) def device(request, id_dev): - + device = get_object_or_404(Device, pk=id_dev) - + kwargs = {} kwargs['device'] = device kwargs['device_keys'] = ['device_type', 'name', 'ip_address', 'port_address', 'description'] - + kwargs['title'] = 'Device' kwargs['suptitle'] = 'Details' - + return render(request, 'device.html', kwargs) def device_new(request): - + if request.method == 'GET': form = DeviceForm() - + if request.method == 'POST': form = DeviceForm(request.POST) - + if form.is_valid(): form.save() return redirect('url_devices') - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Device' kwargs['suptitle'] = 'New' kwargs['button'] = 'Create' - + return render(request, 'base_edit.html', kwargs) def device_edit(request, id_dev): - + device = get_object_or_404(Device, pk=id_dev) - + if request.method=='GET': form = DeviceForm(instance=device) - + if request.method=='POST': form = DeviceForm(request.POST, instance=device) - + if form.is_valid(): form.save() return redirect(device.get_absolute_url()) - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Device' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' - + return render(request, 'base_edit.html', kwargs) def device_delete(request, id_dev): - + device = get_object_or_404(Device, pk=id_dev) - + if request.method=='POST': - + if request.user.is_staff: device.delete() return redirect('url_devices') - + messages.error(request, 'Not enough permission to delete this object') return redirect(device.get_absolute_url()) - + kwargs = { 'title': 'Delete', 'suptitle': 'Device', - 'object': device, + 'object': device, 'previous': device.get_absolute_url(), 'delete': True } - + return render(request, 'confirm.html', kwargs) - + def campaigns(request): - + page = request.GET.get('page') order = ('start_date',) filters = request.GET.copy() kwargs = get_paginator(Campaign, page, order, filters) - + form = FilterForm(initial=request.GET, extra_fields=['range_date', 'tags','template']) - kwargs['keys'] = ['name', 'start_date', 'end_date'] + kwargs['keys'] = ['name', 'start_date', 'end_date'] kwargs['title'] = 'Campaign' kwargs['suptitle'] = 'List' - kwargs['form'] = form + kwargs['form'] = form filters.pop('page', None) - kwargs['q'] = urllib.urlencode(filters) - + kwargs['q'] = urlencode(filters) + return render(request, 'base_list.html', kwargs) def campaign(request, id_camp): - + campaign = get_object_or_404(Campaign, pk=id_camp) experiments = Experiment.objects.filter(campaign=campaign) - + form = CampaignForm(instance=campaign) - + kwargs = {} kwargs['campaign'] = campaign kwargs['campaign_keys'] = ['template', 'name', 'start_date', 'end_date', 'tags', 'description'] - + kwargs['experiments'] = experiments kwargs['experiment_keys'] = ['name', 'radar_system', 'start_time', 'end_time'] - + kwargs['title'] = 'Campaign' kwargs['suptitle'] = 'Details' - + kwargs['form'] = form kwargs['button'] = 'Add Experiment' - + return render(request, 'campaign.html', kwargs) def campaign_new(request): - + kwargs = {} - + if request.method == 'GET': - + if 'template' in request.GET: if request.GET['template']=='0': form = NewForm(initial={'create_from':2}, template_choices=Campaign.objects.filter(template=True).values_list('id', 'name')) else: kwargs['button'] = 'Create' - kwargs['experiments'] = Configuration.objects.filter(experiment=request.GET['template']) + kwargs['experiments'] = Configuration.objects.filter(experiment=request.GET['template']) kwargs['experiment_keys'] = ['name', 'start_time', 'end_time'] camp = Campaign.objects.get(pk=request.GET['template']) form = CampaignForm(instance=camp, initial={'name':'{} [{:%Y/%m/%d}]'.format(camp.name, datetime.now()), - 'template':False}) + 'template':False}) elif 'blank' in request.GET: kwargs['button'] = 'Create' form = CampaignForm() else: form = NewForm() - + if request.method == 'POST': kwargs['button'] = 'Create' post = request.POST.copy() experiments = [] - + for id_exp in post.getlist('experiments'): exp = Experiment.objects.get(pk=id_exp) new_exp = exp.clone(template=False) experiments.append(new_exp) - + post.setlist('experiments', []) - + form = CampaignForm(post) - + if form.is_valid(): campaign = form.save() for exp in experiments: campaign.experiments.add(exp) campaign.save() return redirect('url_campaign', id_camp=campaign.id) - + kwargs['form'] = form kwargs['title'] = 'Campaign' kwargs['suptitle'] = 'New' - + return render(request, 'campaign_edit.html', kwargs) def campaign_edit(request, id_camp): - + campaign = get_object_or_404(Campaign, pk=id_camp) - + if request.method=='GET': form = CampaignForm(instance=campaign) - + if request.method=='POST': exps = campaign.experiments.all().values_list('pk', flat=True) post = request.POST.copy() new_exps = post.getlist('experiments') post.setlist('experiments', []) form = CampaignForm(post, instance=campaign) - + if form.is_valid(): camp = form.save() for id_exp in new_exps: @@ -376,146 +382,146 @@ def campaign_edit(request, id_camp): camp.experiments.add(exp.clone(template=False)) else: camp.experiments.add(exp) - + for id_exp in exps: camp.experiments.remove(Experiment.objects.get(pk=id_exp)) - + return redirect('url_campaign', id_camp=id_camp) - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Campaign' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' - + return render(request, 'campaign_edit.html', kwargs) def campaign_delete(request, id_camp): - + campaign = get_object_or_404(Campaign, pk=id_camp) - + if request.method=='POST': if request.user.is_staff: - + for exp in campaign.experiments.all(): for conf in Configuration.objects.filter(experiment=exp): conf.delete() exp.delete() campaign.delete() - + return redirect('url_campaigns') - + messages.error(request, 'Not enough permission to delete this object') return redirect(campaign.get_absolute_url()) - + kwargs = { 'title': 'Delete', 'suptitle': 'Campaign', - 'object': campaign, + 'object': campaign, 'previous': campaign.get_absolute_url(), 'delete': True } - + return render(request, 'confirm.html', kwargs) def campaign_export(request, id_camp): - + campaign = get_object_or_404(Campaign, pk=id_camp) - content = campaign.parms_to_dict() + content = campaign.parms_to_dict() content_type = 'application/json' filename = '%s_%s.json' %(campaign.name, campaign.id) - + response = HttpResponse(content_type=content_type) response['Content-Disposition'] = 'attachment; filename="%s"' %filename response.write(content) - + return response def campaign_import(request, id_camp): - + campaign = get_object_or_404(Campaign, pk=id_camp) - + if request.method == 'GET': file_form = UploadFileForm() - + if request.method == 'POST': file_form = UploadFileForm(request.POST, request.FILES) - + if file_form.is_valid(): - + parms = campaign.import_from_file(request.FILES['file']) - + if parms: parms['name'] = parms['campaign'] - + new_camp = campaign.dict_to_parms(parms, CONF_MODELS) - + messages.success(request, "Parameters imported from: '%s'." %request.FILES['file'].name) - + return redirect(new_camp.get_absolute_url_edit()) - + messages.error(request, "Could not import parameters from file") - + kwargs = {} kwargs['title'] = 'Campaign' kwargs['form'] = file_form kwargs['suptitle'] = 'Importing file' kwargs['button'] = 'Import' - + return render(request, 'campaign_import.html', kwargs) def experiments(request): - + page = request.GET.get('page') order = ('location',) filters = request.GET.copy() kwargs = get_paginator(Experiment, page, order, filters) - - form = FilterForm(initial=request.GET, extra_fields=['tags','template']) - - kwargs['keys'] = ['name', 'radar_system', 'start_time', 'end_time'] + + form = FilterForm(initial=request.GET, extra_fields=['tags','template']) + + kwargs['keys'] = ['name', 'radar_system', 'start_time', 'end_time'] kwargs['title'] = 'Experiment' kwargs['suptitle'] = 'List' kwargs['form'] = form filters.pop('page', None) - kwargs['q'] = urllib.urlencode(filters) - + kwargs['q'] = urlencode(filters) + return render(request, 'base_list.html', kwargs) def experiment(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) - + configurations = Configuration.objects.filter(experiment=experiment, type=0) - + kwargs = {} - + kwargs['experiment_keys'] = ['template', 'radar_system', 'name', 'start_time', 'end_time'] kwargs['experiment'] = experiment - + kwargs['configuration_keys'] = ['name', 'device__ip_address', 'device__port_address', 'device__status'] kwargs['configurations'] = configurations - + kwargs['title'] = 'Experiment' kwargs['suptitle'] = 'Details' - + kwargs['button'] = 'Add Configuration' - + ###### SIDEBAR ###### kwargs.update(sidebar(experiment=experiment)) - + return render(request, 'experiment.html', kwargs) def experiment_new(request, id_camp=None): - + kwargs = {} - + if request.method == 'GET': if 'template' in request.GET: if request.GET['template']=='0': @@ -523,146 +529,145 @@ def experiment_new(request, id_camp=None): template_choices=Experiment.objects.filter(template=True).values_list('id', 'name')) else: kwargs['button'] = 'Create' - kwargs['configurations'] = Configuration.objects.filter(experiment=request.GET['template']) + kwargs['configurations'] = Configuration.objects.filter(experiment=request.GET['template']) kwargs['configuration_keys'] = ['name', 'device__name', 'device__ip_address', 'device__port_address'] exp=Experiment.objects.get(pk=request.GET['template']) - form = ExperimentForm(instance=exp, + form = ExperimentForm(instance=exp, initial={'name': '{} [{:%Y/%m/%d}]'.format(exp.name, datetime.now()), - 'template': False}) + 'template': False}) elif 'blank' in request.GET: kwargs['button'] = 'Create' form = ExperimentForm() else: form = NewForm() - + if request.method == 'POST': form = ExperimentForm(request.POST) - print form.data if form.is_valid(): experiment = form.save() if 'template' in request.GET: - configurations = Configuration.objects.filter(experiment=request.GET['template'], type=0) + configurations = Configuration.objects.filter(experiment=request.GET['template'], type=0) for conf in configurations: conf.clone(experiment=experiment, template=False) - + return redirect('url_experiment', id_exp=experiment.id) - + kwargs['form'] = form kwargs['title'] = 'Experiment' kwargs['suptitle'] = 'New' - + return render(request, 'experiment_edit.html', kwargs) def experiment_edit(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) - + if request.method == 'GET': form = ExperimentForm(instance=experiment) - + if request.method=='POST': form = ExperimentForm(request.POST, instance=experiment) - + if form.is_valid(): experiment = form.save() return redirect('url_experiment', id_exp=experiment.id) - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Experiment' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' - + return render(request, 'experiment_edit.html', kwargs) def experiment_delete(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) - + if request.method=='POST': if request.user.is_staff: for conf in Configuration.objects.filter(experiment=experiment): - conf.delete() + conf.delete() experiment.delete() return redirect('url_experiments') - + messages.error(request, 'Not enough permission to delete this object') - return redirect(experiment.get_absolute_url()) - + return redirect(experiment.get_absolute_url()) + kwargs = { 'title': 'Delete', 'suptitle': 'Experiment', - 'object': experiment, + 'object': experiment, 'previous': experiment.get_absolute_url(), 'delete': True } - + return render(request, 'confirm.html', kwargs) def experiment_export(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) - content = experiment.parms_to_dict() + content = experiment.parms_to_dict() content_type = 'application/json' filename = '%s_%s.json' %(experiment.name, experiment.id) - + response = HttpResponse(content_type=content_type) response['Content-Disposition'] = 'attachment; filename="%s"' %filename response.write(content) - + return response def experiment_import(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) configurations = Configuration.objects.filter(experiment=experiment) - + if request.method == 'GET': file_form = UploadFileForm() - + if request.method == 'POST': file_form = UploadFileForm(request.POST, request.FILES) - + if file_form.is_valid(): - + parms = experiment.import_from_file(request.FILES['file']) - + if parms: - + new_exp = experiment.dict_to_parms(parms, CONF_MODELS) - + messages.success(request, "Parameters imported from: '%s'." %request.FILES['file'].name) - - return redirect(new_exp.get_absolute_url_edit()) + + return redirect(new_exp.get_absolute_url_edit()) messages.error(request, "Could not import parameters from file") - + kwargs = {} kwargs['title'] = 'Experiment' kwargs['form'] = file_form kwargs['suptitle'] = 'Importing file' kwargs['button'] = 'Import' - + kwargs.update(sidebar(experiment=experiment)) - + return render(request, 'experiment_import.html', kwargs) def experiment_mix(request, id_exp): - + experiment = get_object_or_404(Experiment, pk=id_exp) rc_confs = [conf for conf in RCConfiguration.objects.filter(experiment=id_exp, mix=False)] - + if len(rc_confs)<2: messages.warning(request, 'You need at least two RC Configurations to make a mix') return redirect(experiment.get_absolute_url()) - + mix_confs = RCConfiguration.objects.filter(experiment=id_exp, mix=True) - + if mix_confs: mix = mix_confs[0] else: @@ -671,7 +676,7 @@ def experiment_mix(request, id_exp): ipp=rc_confs[0].ipp, clock_in=rc_confs[0].clock_in, clock_divider=rc_confs[0].clock_divider, - mix=True, + mix=True, parameters='') mix.save() @@ -679,16 +684,16 @@ def experiment_mix(request, id_exp): for i in range(len(rc_confs[0].get_lines())): line = RCLine(rc_configuration=mix, line_type=line_type, channel=i) line.save() - + initial = {'name': mix.name, 'result': parse_mix_result(mix.parameters), 'delay': 0, 'mask': [0,1,2,3,4,5,6,7] } - - if request.method=='GET': + + if request.method=='GET': form = RCMixConfigurationForm(confs=rc_confs, initial=initial) - + if request.method=='POST': result = mix.parameters @@ -699,9 +704,9 @@ def experiment_mix(request, id_exp): operation = MIX_OPERATIONS[request.POST['operation']] else: operation = ' ' - + mode = MIX_MODES[request.POST['mode']] - + if result: result = '{}-{}|{}|{}|{}|{}'.format(mix.parameters, request.POST['experiment'], @@ -717,18 +722,18 @@ def experiment_mix(request, id_exp): float(request.POST['delay']), parse_mask(request.POST.getlist('mask')) ) - + mix.parameters = result mix.name = request.POST['name'] mix.save() mix.update_pulses() - + initial['result'] = parse_mix_result(result) initial['name'] = mix.name - + form = RCMixConfigurationForm(initial=initial, confs=rc_confs) - - + + kwargs = { 'title': 'Experiment', 'suptitle': 'Mix Configurations', @@ -740,46 +745,46 @@ def experiment_mix(request, id_exp): 'id_exp':id_exp, } - + return render(request, 'experiment_mix.html', kwargs) def experiment_mix_delete(request, id_exp): - + conf = RCConfiguration.objects.get(experiment=id_exp, mix=True) values = conf.parameters.split('-') conf.parameters = '-'.join(values[:-1]) conf.save() - + return redirect('url_mix_experiment', id_exp=id_exp) def experiment_summary(request, id_exp): - + import json import ast - + experiment = get_object_or_404(Experiment, pk=id_exp) experiment_data = json.loads(experiment.parms_to_dict()) configurations = Configuration.objects.filter(experiment=experiment, type=0) - + kwargs = {} - + kwargs['experiment_keys'] = ['template', 'radar_system', 'name', 'start_time', 'end_time'] kwargs['experiment'] = experiment - + kwargs['configuration_keys'] = ['name', 'device__ip_address', 'device__port_address', 'device__status'] kwargs['configurations'] = configurations kwargs['experiment_data'] = experiment_data - + kwargs['title'] = 'Experiment Summary' kwargs['suptitle'] = 'Details' - + kwargs['button'] = 'Verify Parameters' - + jars_conf = False rc_conf = False - + for configuration in configurations: #-------------------- JARS -----------------------: if configuration.device.device_type.name == 'jars': @@ -792,7 +797,7 @@ def experiment_summary(request, id_exp): filter_parms = configuration.filter_parms filter_parms = ast.literal_eval(filter_parms) spectral_number = configuration.spectral_number - + #--------------------- RC ----------------------: if configuration.device.device_type.name == 'rc': rc_conf = True @@ -805,13 +810,13 @@ def experiment_summary(request, id_exp): window = '' else: code = rc_lines[3]['code'] - + window_data = rc_lines[6]['params'][0] h0 = str(window_data['first_height']) dh = str(window_data['resolution']) nsa = str(window_data['number_of_samples']) window = 'Ho='+h0+'km\nDH='+dh+'km\nNSA='+nsa - + tx = '' if float(rc_lines[1]['delays']) == 0: tx = rc_lines[2]['pulse_width'] @@ -819,22 +824,22 @@ def experiment_summary(request, id_exp): tx = rc_lines[1]['pulse_width'] else: tx = rc_lines[1]['pulse_width']+' | '+rc_lines[2]['pulse_width'] - + kwargs['tx'] = tx kwargs['code'] = code kwargs['window'] = window - + #-------------------- DDS -----------------------: if configuration.device.device_type.name == 'dds': dds_conf = True kwargs['dds_conf'] = dds_conf - + #------ RC & JARS ------: ipp = 937.5 # nsa = 200# dh = 1.5 # channels_number = 5 # - + if rc_conf and jars_conf: if exp_type == 0: #Short bytes = 2 @@ -843,7 +848,7 @@ def experiment_summary(request, id_exp): bytes = 4 channels = channels_number + spectral_number b = nsa*2*bytes*fftpoints*channels - + ipps = (ipp*pow(10,-6))/0.15 GB = 1048576.0*1024.0 Hour = 3600 @@ -852,39 +857,39 @@ def experiment_summary(request, id_exp): kwargs['rate'] = str(rate)+" GB/h" else: kwargs['rate'] = '' - + ###### SIDEBAR ###### kwargs.update(sidebar(experiment=experiment)) - + return render(request, 'experiment_summary.html', kwargs) def experiment_verify(request, id_exp): - - import json + + import json import ast - + experiment = get_object_or_404(Experiment, pk=id_exp) experiment_data = json.loads(experiment.parms_to_dict()) configurations = Configuration.objects.filter(experiment=experiment, type=0) - + kwargs = {} - + kwargs['experiment_keys'] = ['template', 'radar_system', 'name', 'start_time', 'end_time'] kwargs['experiment'] = experiment - + kwargs['configuration_keys'] = ['name', 'device__ip_address', 'device__port_address', 'device__status'] kwargs['configurations'] = configurations kwargs['experiment_data'] = experiment_data - + kwargs['title'] = 'Verify Experiment' kwargs['suptitle'] = 'Parameters' - + kwargs['button'] = 'Update' - + jars_conf = False rc_conf = False dds_conf = False - + for configuration in configurations: #-------------------- JARS -----------------------: if configuration.device.device_type.name == 'jars': @@ -899,35 +904,35 @@ def experiment_verify(request, id_exp): filter_5 = filter_parms['filter_5'] filter_fir = filter_parms['filter_fir'] samp_freq_jars = clock/filter_2/filter_5/filter_fir - + kwargs['samp_freq_jars'] = samp_freq_jars kwargs['jars'] = configuration - + #--------------------- RC ----------------------: if configuration.device.device_type.name == 'rc': rc_conf = True rc_parms = configuration.parms_to_dict() if rc_parms['mix'] == 'True': pass - else: + else: rc_lines = rc_parms['lines'] dh = rc_lines[6]['params'][0]['resolution'] #--Sampling Frequency - samp_freq_rc = 0.15/dh + samp_freq_rc = 0.15/dh kwargs['samp_freq_rc'] = samp_freq_rc - + kwargs['rc_conf'] = rc_conf kwargs['rc'] = configuration - + #-------------------- DDS ----------------------: if configuration.device.device_type.name == 'dds': dds_conf = True dds_parms = configuration.parms_to_dict() - + kwargs['dds_conf'] = dds_conf kwargs['dds'] = configuration - - + + #------------Validation------------: #Clock if dds_conf and rc_conf and jars_conf: @@ -941,34 +946,34 @@ def experiment_verify(request, id_exp): messages.warning(request, "Devices don't have the same clock.") if float(samp_freq_rc) != float(dds_parms['frequencyA']): messages.warning(request, "Devices don't have the same Frequency A.") - - - + + + ###### SIDEBAR ###### kwargs.update(sidebar(experiment=experiment)) - - - - - + + + + + return render(request, 'experiment_verify.html', kwargs) def parse_mix_result(s): - + values = s.split('-') - html = 'EXP MOD OPE DELAY MASK\r\n' - + html = 'EXP MOD OPE DELAY MASK\r\n' + if not values or values[0] in ('', ' '): return mark_safe(html) - + for i, value in enumerate(values): if not value: continue pk, mode, operation, delay, mask = value.split('|') conf = RCConfiguration.objects.get(pk=pk) if i==0: - html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( + html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( conf.name, mode, ' ', @@ -981,70 +986,70 @@ def parse_mix_result(s): operation, delay, mask) - + return mark_safe(html) def parse_mask(l): - + values = [] - + for x in range(8): if '{}'.format(x) in l: values.append(1) else: values.append(0) - + values.reverse() - + return int(''.join([str(x) for x in values]), 2) - + def dev_confs(request): - - + + page = request.GET.get('page') order = ('type', 'device__device_type', 'experiment') filters = request.GET.copy() kwargs = get_paginator(Configuration, page, order, filters) - + form = FilterForm(initial=request.GET, extra_fields=['tags','template']) - kwargs['keys'] = ['name', 'experiment', 'type', 'programmed_date'] + kwargs['keys'] = ['name', 'experiment', 'type', 'programmed_date'] kwargs['title'] = 'Configuration' kwargs['suptitle'] = 'List' - kwargs['form'] = form + kwargs['form'] = form filters.pop('page', None) - kwargs['q'] = urllib.urlencode(filters) - + kwargs['q'] = urlencode(filters) + return render(request, 'base_list.html', kwargs) def dev_conf(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - + return redirect(conf.get_absolute_url()) - + def dev_conf_new(request, id_exp=0, id_dev=0): - + initial = {} kwargs = {} - - if id_exp<>0: + + if id_exp!=0: initial['experiment'] = id_exp - - if id_dev<>0: + + if id_dev!=0: initial['device'] = id_dev if request.method == 'GET': - + if id_dev: kwargs['button'] = 'Create' device = Device.objects.get(pk=id_dev) DevConfForm = CONF_FORMS[device.device_type.name] - initial['name'] = request.GET['name'] - form = DevConfForm(initial=initial) + initial['name'] = request.GET['name'] + form = DevConfForm(initial=initial) else: if 'template' in request.GET: if request.GET['template']=='0': @@ -1052,225 +1057,200 @@ def dev_conf_new(request, id_exp=0, id_dev=0): form = NewForm(initial={'create_from':2}, template_choices=choices) else: - kwargs['button'] = 'Create' + kwargs['button'] = 'Create' conf = Configuration.objects.get(pk=request.GET['template']) id_dev = conf.device.pk DevConfForm = CONF_FORMS[conf.device.device_type.name] - form = DevConfForm(instance=conf, + form = DevConfForm(instance=conf, initial={'name': '{} [{:%Y/%m/%d}]'.format(conf.name, datetime.now()), 'template': False, - 'experiment':id_exp}) + 'experiment':id_exp}) elif 'blank' in request.GET: - kwargs['button'] = 'Create' + kwargs['button'] = 'Create' form = ConfigurationForm(initial=initial) else: - form = NewForm() - + form = NewForm() + if request.method == 'POST': - + device = Device.objects.get(pk=request.POST['device']) DevConfForm = CONF_FORMS[device.device_type.name] - + form = DevConfForm(request.POST) kwargs['button'] = 'Create' if form.is_valid(): conf = form.save() - + if 'template' in request.GET and conf.device.device_type.name=='rc': lines = RCLine.objects.filter(rc_configuration=request.GET['template']) for line in lines: line.clone(rc_configuration=conf) - - if conf.device.device_type.name=='jars': + + if conf.device.device_type.name=='jars': conf.add_parms_to_filter() - - return redirect('url_dev_conf', id_conf=conf.pk) - + + return redirect('url_dev_conf', id_conf=conf.pk) + kwargs['id_exp'] = id_exp kwargs['form'] = form kwargs['title'] = 'Configuration' kwargs['suptitle'] = 'New' - - + + if id_dev != 0: device = Device.objects.get(pk=id_dev) kwargs['device'] = device.device_type.name - + return render(request, 'dev_conf_edit.html', kwargs) def dev_conf_edit(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] + DevConfForm = CONF_FORMS[conf.device.device_type.name] - - dev_conf = DevConfModel.objects.get(pk=id_conf) - + if request.method=='GET': - form = DevConfForm(instance=dev_conf) - + form = DevConfForm(instance=conf) + if request.method=='POST': - form = DevConfForm(request.POST, instance=dev_conf) - + form = DevConfForm(request.POST, instance=conf) + if form.is_valid(): form.save() return redirect('url_dev_conf', id_conf=id_conf) - + kwargs = {} kwargs['form'] = form kwargs['title'] = 'Device Configuration' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' - + ###### SIDEBAR ###### kwargs.update(sidebar(conf=conf)) - + return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) def dev_conf_start(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + if conf.start_device(): messages.success(request, conf.message) else: messages.error(request, conf.message) - + conf.status_device() - + return redirect(conf.get_absolute_url()) def dev_conf_stop(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + if conf.stop_device(): messages.success(request, conf.message) else: messages.error(request, conf.message) - + conf.status_device() - + return redirect(conf.get_absolute_url()) def dev_conf_status(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + if conf.status_device(): messages.success(request, conf.message) else: messages.error(request, conf.message) - + return redirect(conf.get_absolute_url()) def dev_conf_write(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + answer = conf.write_device() conf.status_device() - + if answer: messages.success(request, conf.message) - - #Creating a historical configuration + + #Creating a historical configuration conf.clone(type=1, template=False) - + #Original configuration conf = DevConfModel.objects.get(pk=id_conf) else: messages.error(request, conf.message) - + return redirect(conf.get_absolute_url()) def dev_conf_read(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] + DevConfForm = CONF_FORMS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + if request.method=='GET': - + parms = conf.read_device() conf.status_device() - + if not parms: messages.error(request, conf.message) return redirect(conf.get_absolute_url()) - + form = DevConfForm(initial=parms, instance=conf) - + if request.method=='POST': form = DevConfForm(request.POST, instance=conf) - + if form.is_valid(): form.save() return redirect(conf.get_absolute_url()) - + messages.error(request, "Parameters could not be saved") - + kwargs = {} kwargs['id_dev'] = conf.id kwargs['form'] = form kwargs['title'] = 'Device Configuration' kwargs['suptitle'] = 'Parameters read from device' kwargs['button'] = 'Save' - + ###### SIDEBAR ###### kwargs.update(sidebar(conf=conf)) - + return render(request, '%s_conf_edit.html' %conf.device.device_type.name, kwargs) def dev_conf_import(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - DevConfForm = CONF_FORMS[conf.device.device_type.name] - conf = DevConfModel.objects.get(pk=id_conf) - + DevConfForm = CONF_FORMS[conf.device.device_type.name] + if request.method == 'GET': file_form = UploadFileForm() - + if request.method == 'POST': file_form = UploadFileForm(request.POST, request.FILES) - + if file_form.is_valid(): - + parms = conf.import_from_file(request.FILES['file']) - + if parms: messages.success(request, "Parameters imported from: '%s'." %request.FILES['file'].name) form = DevConfForm(initial=parms, instance=conf) - + kwargs = {} kwargs['id_dev'] = conf.id kwargs['form'] = form @@ -1279,94 +1259,90 @@ def dev_conf_import(request, id_conf): kwargs['button'] = 'Save' kwargs['action'] = conf.get_absolute_url_edit() kwargs['previous'] = conf.get_absolute_url() - + ###### SIDEBAR ###### kwargs.update(sidebar(conf=conf)) - + return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) messages.error(request, "Could not import parameters from file") - + kwargs = {} kwargs['id_dev'] = conf.id kwargs['title'] = 'Device Configuration' kwargs['form'] = file_form kwargs['suptitle'] = 'Importing file' kwargs['button'] = 'Import' - + kwargs.update(sidebar(conf=conf)) - + return render(request, 'dev_conf_import.html', kwargs) def dev_conf_export(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - - DevConfModel = CONF_MODELS[conf.device.device_type.name] - - conf = DevConfModel.objects.get(pk=id_conf) - + if request.method == 'GET': file_form = DownloadFileForm(conf.device.device_type.name) - + if request.method == 'POST': file_form = DownloadFileForm(conf.device.device_type.name, request.POST) - + if file_form.is_valid(): fields = conf.export_to_file(format = file_form.cleaned_data['format']) - + response = HttpResponse(content_type=fields['content_type']) response['Content-Disposition'] = 'attachment; filename="%s"' %fields['filename'] response.write(fields['content']) - + return response - + messages.error(request, "Could not export parameters") - + kwargs = {} kwargs['id_dev'] = conf.id kwargs['title'] = 'Device Configuration' kwargs['form'] = file_form kwargs['suptitle'] = 'Exporting file' kwargs['button'] = 'Export' - + return render(request, 'dev_conf_export.html', kwargs) def dev_conf_delete(request, id_conf): - + conf = get_object_or_404(Configuration, pk=id_conf) - + if request.method=='POST': if request.user.is_staff: conf.delete() return redirect('url_dev_confs') - + messages.error(request, 'Not enough permission to delete this object') - return redirect(conf.get_absolute_url()) - + return redirect(conf.get_absolute_url()) + kwargs = { 'title': 'Delete', 'suptitle': 'Experiment', - 'object': conf, + 'object': conf, 'previous': conf.get_absolute_url(), 'delete': True } - + return render(request, 'confirm.html', kwargs) def sidebar(**kwargs): - + side_data = {} - + conf = kwargs.get('conf', None) experiment = kwargs.get('experiment', None) - + if not experiment: experiment = conf.experiment - + if experiment: side_data['experiment'] = experiment campaign = experiment.campaign_set.all() @@ -1378,126 +1354,109 @@ def sidebar(**kwargs): configurations = experiment.configuration_set.filter(type=0) side_data['side_experiments'] = experiments side_data['side_configurations'] = configurations - + return side_data def get_paginator(model, page, order, filters={}, n=10): - + kwargs = {} query = Q() if isinstance(filters, QueryDict): filters = filters.dict() [filters.pop(key) for key in filters.keys() if filters[key] in ('', ' ')] filters.pop('page', None) - + if 'start_date' in filters: filters['start_date__gte'] = filters.pop('start_date') if 'end_date' in filters: filters['start_date__lte'] = filters.pop('end_date') if 'tags' in filters: - tags = filters.pop('tags') + tags = filters.pop('tags') if 'tags' in model._meta.get_all_field_names(): - query = query | Q(tags__icontains=tags) + query = query | Q(tags__icontains=tags) if 'name' in model._meta.get_all_field_names(): - query = query | Q(name__icontains=tags) + query = query | Q(name__icontains=tags) if 'location' in model._meta.get_all_field_names(): query = query | Q(location__name__icontains=tags) if 'device' in model._meta.get_all_field_names(): - query = query | Q(device__name__icontains=tags) + query = query | Q(device__name__icontains=tags) object_list = model.objects.filter(query, **filters).order_by(*order) paginator = Paginator(object_list, n) - + try: objects = paginator.page(page) except PageNotAnInteger: objects = paginator.page(1) except EmptyPage: objects = paginator.page(paginator.num_pages) - + kwargs['objects'] = objects kwargs['offset'] = (int(page)-1)*n if page else 0 - + return kwargs def operation(request, id_camp=None): + + kwargs = {} + campaigns = Campaign.objects.filter(start_date__lte=datetime.now(), + end_date__gte=datetime.now()).order_by('-start_date') - if not id_camp: - campaigns = Campaign.objects.all().order_by('-start_date') - - if not campaigns: - kwargs = {} - kwargs['title'] = 'No Campaigns' - kwargs['suptitle'] = 'Empty' - return render(request, 'operation.html', kwargs) - - id_camp = campaigns[0].id - - campaign = get_object_or_404(Campaign, pk = id_camp) - if request.method=='GET': - form = OperationForm(initial={'campaign': campaign.id}, length = 5) - - if request.method=='POST': - form = OperationForm(request.POST, initial={'campaign':campaign.id}, length = 5) + if id_camp: + campaign = get_object_or_404(Campaign, pk = id_camp) + form = OperationForm(initial={'campaign': campaign.id}, campaigns=campaigns) + kwargs['campaign'] = campaign + else: + form = OperationForm(campaigns=campaigns) + kwargs['form'] = form + return render(request, 'operation.html', kwargs) + - if form.is_valid(): - return redirect('url_operation', id_camp=campaign.id) - #locations = Location.objects.filter(experiment__campaign__pk = campaign.id).distinct() - experiments = Experiment.objects.filter(campaign__pk=campaign.id) - #for exs in experiments: - # exs.get_status() - locations= Location.objects.filter(experiment=experiments).distinct() - #experiments = [Experiment.objects.filter(location__pk=location.id).filter(campaign__pk=campaign.id) for location in locations] - kwargs = {} - #---Campaign - kwargs['campaign'] = campaign - kwargs['campaign_keys'] = ['name', 'start_date', 'end_date', 'tags', 'description'] + #---Experiment keys = ['id', 'name', 'start_time', 'end_time', 'status'] kwargs['experiment_keys'] = keys[1:] kwargs['experiments'] = experiments #---Radar - kwargs['locations'] = locations + kwargs['locations'] = campaign.get_experiments_by_location() + print kwargs['locations'] #---Else kwargs['title'] = 'Campaign' kwargs['suptitle'] = campaign.name - kwargs['form'] = form - kwargs['button'] = 'Search' - kwargs['details'] = True - kwargs['search_button'] = True - + kwargs['form'] = form + return render(request, 'operation.html', kwargs) def operation_search(request, id_camp=None): - - + + if not id_camp: campaigns = Campaign.objects.all().order_by('-start_date') - + if not campaigns: return render(request, 'operation.html', {}) - + id_camp = campaigns[0].id campaign = get_object_or_404(Campaign, pk = id_camp) - + if request.method=='GET': form = OperationSearchForm(initial={'campaign': campaign.id}) - + if request.method=='POST': form = OperationSearchForm(request.POST, initial={'campaign':campaign.id}) - + if form.is_valid(): return redirect('url_operation', id_camp=campaign.id) - + #locations = Location.objects.filter(experiment__campaign__pk = campaign.id).distinct() experiments = Experiment.objects.filter(campaign__pk=campaign.id) #for exs in experiments: # exs.get_status() locations= Location.objects.filter(experiment=experiments).distinct() form = OperationSearchForm(initial={'campaign': campaign.id}) - + kwargs = {} #---Campaign kwargs['campaign'] = campaign @@ -1515,7 +1474,7 @@ def operation_search(request, id_camp=None): kwargs['button'] = 'Select' kwargs['details'] = True kwargs['search_button'] = False - + return render(request, 'operation.html', kwargs) @@ -1524,14 +1483,14 @@ def radar_play(request, id_camp, id_radar): radar = get_object_or_404(Location, pk = id_radar) today = datetime.today() now = today.time() - + #--Clear Old Experiments From RunningExperiment Object running_experiment = RunningExperiment.objects.filter(radar=radar) if running_experiment: running_experiment = running_experiment[0] running_experiment.running_experiment.clear() running_experiment.save() - + #--If campaign datetime is ok: if today >= campaign.start_date and today <= campaign.end_date: experiments = Experiment.objects.filter(campaign=campaign).filter(location=radar) @@ -1562,7 +1521,7 @@ def radar_play(request, id_camp, id_radar): new_running_experiment.save() new_running_experiment.running_experiment.add(exp) new_running_experiment.save() - + if answer: messages.success(request, conf.message) exp.status=2 @@ -1573,8 +1532,8 @@ def radar_play(request, id_camp, id_radar): if exp.status == 1 or exp.status == 3: exp.status=3 exp.save() - - + + route = request.META['HTTP_REFERER'] route = str(route) if 'search' in route: @@ -1587,7 +1546,7 @@ def radar_stop(request, id_camp, id_radar): campaign = get_object_or_404(Campaign, pk = id_camp) radar = get_object_or_404(Location, pk = id_radar) experiments = Experiment.objects.filter(campaign=campaign).filter(location=radar) - + for exp in experiments: configurations = Configuration.objects.filter(experiment = exp) for conf in configurations: @@ -1596,14 +1555,14 @@ def radar_stop(request, id_camp, id_radar): else: answer = conf.stop_device() conf.status_device() - + if answer: messages.success(request, conf.message) exp.status=1 exp.save() else: messages.error(request, conf.message) - + route = request.META['HTTP_REFERER'] route = str(route) @@ -1611,10 +1570,10 @@ def radar_stop(request, id_camp, id_radar): return HttpResponseRedirect(reverse('url_operation_search', args=[id_camp])) else: return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) - + def radar_refresh(request, id_camp, id_radar): - + campaign = get_object_or_404(Campaign, pk = id_camp) radar = get_object_or_404(Location, pk = id_radar) experiments = Experiment.objects.filter(campaign=campaign).filter(location=radar) @@ -1627,3 +1586,4 @@ def radar_refresh(request, id_camp, id_radar): return HttpResponseRedirect(reverse('url_operation_search', args=[id_camp])) else: return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) + diff --git a/apps/misc/urls.py b/apps/misc/urls.py index a633f51..959712f 100644 --- a/apps/misc/urls.py +++ b/apps/misc/urls.py @@ -1,5 +1,5 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url -urlpatterns = patterns('apps.misc.views', +urlpatterns = ( ) diff --git a/apps/rc/forms.py b/apps/rc/forms.py index 285e992..c7a6e53 100644 --- a/apps/rc/forms.py +++ b/apps/rc/forms.py @@ -9,7 +9,7 @@ from .models import RCConfiguration, RCLine, RCLineType, RCLineCode from .widgets import KmUnitWidget, KmUnitHzWidget, KmUnitDcWidget, UnitKmWidget, DefaultWidget, CodesWidget, HiddenWidget, HCheckboxSelectMultiple def create_choices_from_model(model, conf_id, all_choice=False): - + if model=='RCLine': instance = RCConfiguration.objects.get(pk=conf_id) choices = [(line.pk, line.get_name()) for line in instance.get_lines(line_type__name='tx')] @@ -18,7 +18,7 @@ def create_choices_from_model(model, conf_id, all_choice=False): else: instance = globals()[model] choices = instance.objects.all().values_list('pk', 'name') - + return choices @@ -51,45 +51,45 @@ class ExtFileField(forms.FileField): ext = ext.lower() if ext not in self.extensions: raise forms.ValidationError('Not allowed file type: %s' % ext) - + class RCConfigurationForm(forms.ModelForm): - + def __init__(self, *args, **kwargs): super(RCConfigurationForm, self).__init__(*args, **kwargs) - + instance = getattr(self, 'instance', None) - + if instance and instance.pk: - + devices = Device.objects.filter(device_type__name='rc') if instance.experiment: self.fields['experiment'].widget.attrs['read_only'] = True - #self.fields['experiment'].widget.choices = [(instance.experiment.id, instance.experiment)] + #self.fields['experiment'].widget.choices = [(instance.experiment.id, instance.experiment)] self.fields['device'].widget.choices = [(device.id, device) for device in devices] self.fields['ipp'].widget = KmUnitHzWidget(attrs={'km2unit':instance.km2unit}) self.fields['clock'].widget.attrs['readonly'] = True self.fields['time_before'].label = mark_safe(self.fields['time_before'].label) self.fields['time_after'].label = mark_safe(self.fields['time_after'].label) - + if 'initial' in kwargs and 'experiment' in kwargs['initial'] and kwargs['initial']['experiment'] not in (0, '0'): self.fields['experiment'].widget.attrs['readonly'] = True - + class Meta: model = RCConfiguration exclude = ('type', 'parameters', 'status', 'total_units', 'mix') def clean(self): form_data = super(RCConfigurationForm, self).clean() - + if 'clock_divider' in form_data: if form_data['clock_divider']<1: self.add_error('clock_divider', 'Invalid Value') else: - if form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider']))%10<>0: + if form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider']))%10!=0: self.add_error('ipp', 'Invalid IPP units={}'.format(form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider'])))) - + return form_data def save(self): @@ -97,26 +97,26 @@ class RCConfigurationForm(forms.ModelForm): conf.total_units = conf.ipp*conf.ntx*conf.km2unit conf.save() return conf - + class RCMixConfigurationForm(forms.Form): - + clock_in = forms.CharField(widget=forms.HiddenInput()) clock_divider = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField() experiment = forms.ChoiceField() mode = forms.ChoiceField(widget=forms.RadioSelect(), - choices=[(0, 'Parallel'), (1, 'Sequence')], + choices=[(0, 'Parallel'), (1, 'Sequence')], initial=0) operation = forms.ChoiceField(widget=forms.RadioSelect(), - choices=[(0, 'OR'), (1, 'XOR'), (2, 'AND'), (3, 'NAND')], + choices=[(0, 'OR'), (1, 'XOR'), (2, 'AND'), (3, 'NAND')], initial=1) delay = forms.CharField() mask = forms.MultipleChoiceField(choices=[(0, 'L1'),(1, 'L2'),(2, 'L3'),(3, 'L4'),(4, 'L5'),(5, 'L6'),(6, 'L7'),(7, 'L8')], widget=HCheckboxSelectMultiple()) result = forms.CharField(required=False, widget=forms.Textarea(attrs={'readonly':True, 'rows':5, 'class':'tabuled'})) - + def __init__(self, *args, **kwargs): confs = kwargs.pop('confs', []) if confs: @@ -130,103 +130,103 @@ class RCMixConfigurationForm(forms.Form): self.fields['delay'].widget = KmUnitWidget(attrs = {'km2unit':km2unit}) self.fields['clock_in'].initial = clock_in self.fields['clock_divider'].initial = clock_divider - - + + class RCLineForm(forms.ModelForm): - + def __init__(self, *args, **kwargs): self.extra_fields = kwargs.pop('extra_fields', []) super(RCLineForm, self).__init__(*args, **kwargs) - + if 'initial' in kwargs and 'line_type' in kwargs['initial']: line_type = RCLineType.objects.get(pk=kwargs['initial']['line_type']) - + if 'code_id' in kwargs['initial']: model_initial = kwargs['initial']['code_id'] else: model_initial = 0 - + params = json.loads(line_type.params) - + for label, value in self.extra_fields.items(): if label=='params': continue - + if 'model' in params[label]: - self.fields[label] = forms.ChoiceField(choices=create_choices_from_model(params[label]['model'], + self.fields[label] = forms.ChoiceField(choices=create_choices_from_model(params[label]['model'], kwargs['initial']['rc_configuration']), initial=model_initial) - - + + else: if label=='codes' and 'code_id' in kwargs['initial']: self.fields[label] = forms.CharField(initial=RCLineCode.objects.get(pk=kwargs['initial']['code_id']).codes) else: self.fields[label] = forms.CharField(initial=value['value']) - + if label=='codes': self.fields[label].widget = CodesWidget() - + if self.data: line_type = RCLineType.objects.get(pk=self.data['line_type']) - + if 'code_id' in self.data: model_initial = self.data['code_id'] else: model_initial = 0 - + params = json.loads(line_type.params) - + for label, value in self.extra_fields.items(): if label=='params': continue - + if 'model' in params[label]: - self.fields[label] = forms.ChoiceField(choices=create_choices_from_model(params[label]['model'], + self.fields[label] = forms.ChoiceField(choices=create_choices_from_model(params[label]['model'], self.data['rc_configuration']), initial=model_initial) - - + + else: if label=='codes' and 'code' in self.data: - self.fields[label] = forms.CharField(initial=self.data['codes']) + self.fields[label] = forms.CharField(initial=self.data['codes']) else: self.fields[label] = forms.CharField(initial=self.data[label]) - + if label=='codes': self.fields[label].widget = CodesWidget() - - + + class Meta: model = RCLine fields = ('rc_configuration', 'line_type', 'channel') widgets = { 'channel': forms.HiddenInput(), } - - + + def clean(self): - + form_data = self.cleaned_data - if 'code' in self.data and self.data['TX_ref']=="0": + if 'code' in self.data and self.data['TX_ref']=="0": self.add_error('TX_ref', 'Choose a valid TX reference') - + if RCLineType.objects.get(pk=self.data['line_type']).name=='mix': self.add_error('line_type', 'Invalid Line type') - + return form_data - - - def save(self): + + + def save(self): line = super(RCLineForm, self).save() - + #auto add channel line.channel = RCLine.objects.filter(rc_configuration=line.rc_configuration).count()-1 - + #auto add position for TX, TR & CODE if line.line_type.name in ('tx', ): line.position = RCLine.objects.filter(rc_configuration=line.rc_configuration, line_type=line.line_type).count()-1 - + #save extra fields in params params = {} for label, value in self.extra_fields.items(): @@ -239,24 +239,24 @@ class RCLineForm(forms.ModelForm): line.params = json.dumps(params) line.save() return - - + + class RCLineViewForm(forms.Form): - + def __init__(self, *args, **kwargs): - + extra_fields = kwargs.pop('extra_fields') line = kwargs.pop('line') subform = kwargs.pop('subform', False) super(RCLineViewForm, self).__init__(*args, **kwargs) - + if subform: params = json.loads(line.line_type.params)['params'] else: params = json.loads(line.line_type.params) - + for label, value in extra_fields.items(): - + if label=='params': continue if 'ref' in label: @@ -266,9 +266,9 @@ class RCLineViewForm(forms.Form): value = RCLine.objects.get(pk=value).get_name() elif label=='code': value = RCLineCode.objects.get(pk=value).name - - self.fields[label] = forms.CharField(initial=value) - + + self.fields[label] = forms.CharField(initial=value) + if 'widget' in params[label]: km2unit = line.rc_configuration.km2unit if params[label]['widget']=='km': @@ -281,35 +281,35 @@ class RCLineViewForm(forms.Form): self.fields[label].widget = CodesWidget(attrs={'line':line, 'km2unit':km2unit, 'disabled':True}) else: self.fields[label].widget = DefaultWidget(attrs={'disabled':True}) - - + + class RCLineEditForm(forms.ModelForm): - + def __init__(self, *args, **kwargs): - + extra_fields = kwargs.pop('extra_fields', []) conf = kwargs.pop('conf', False) line = kwargs.pop('line') subform = kwargs.pop('subform', False) - + super(RCLineEditForm, self).__init__(*args, **kwargs) - + if subform is not False: params = json.loads(line.line_type.params)['params'] count = subform else: params = json.loads(line.line_type.params) - count = -1 - + count = -1 + for label, value in extra_fields.items(): - + if label in ('params',): continue if 'help' in params[label]: help_text = params[label]['help'] else: help_text = '' - + if 'model' in params[label]: if line.line_type.name=='tr': all_choice = True @@ -319,66 +319,64 @@ class RCLineEditForm(forms.ModelForm): initial=value, widget=forms.Select(attrs={'name':'%s|%s|%s' % (count, line.id, label)}), help_text=help_text) - - else: + + else: self.fields[label] = forms.CharField(initial=value, help_text=help_text) - - if label in ('code', ): - self.fields[label].widget = HiddenWidget(attrs={'name':'%s|%s|%s' % (count, line.id, label)}) - + + if label in ('code', ): + self.fields[label].widget = HiddenWidget(attrs={'name':'%s|%s|%s' % (count, line.id, label)}) + elif 'widget' in params[label]: - km2unit = line.rc_configuration.km2unit - if params[label]['widget']=='km': + km2unit = line.rc_configuration.km2unit + if params[label]['widget']=='km': self.fields[label].widget = KmUnitWidget(attrs={'line':line, 'km2unit':km2unit, 'name':'%s|%s|%s' % (count, line.id, label)}) elif params[label]['widget']=='unit': self.fields[label].widget = UnitKmWidget(attrs={'line':line, 'km2unit':km2unit, 'name':'%s|%s|%s' % (count, line.id, label)}) elif params[label]['widget']=='dc': self.fields[label].widget = KmUnitDcWidget(attrs={'line':line, 'km2unit':km2unit, 'name':'%s|%s|%s' % (count, line.id, label)}) elif params[label]['widget']=='codes': - self.fields[label].widget = CodesWidget(attrs={'line':line, 'km2unit':km2unit, 'name':'%s|%s|%s' % (count, line.id, label)}) + self.fields[label].widget = CodesWidget(attrs={'line':line, 'km2unit':km2unit, 'name':'%s|%s|%s' % (count, line.id, label)}) else: self.fields[label].widget = DefaultWidget(attrs={'line':line, 'name':'%s|%s|%s' % (count, line.id, label)}) - - + + class Meta: model = RCLine exclude = ('rc_configuration', 'line_type', 'channel', 'position', 'params', 'pulses') - - + + class RCSubLineEditForm(forms.Form): - + def __init__(self, *args, **kwargs): extra_fields = kwargs.pop('extra_fields') count = kwargs.pop('count') line = kwargs.pop('line') super(RCSubLineEditForm, self).__init__(*args, **kwargs) - for label, value in extra_fields.items(): + for label, value in extra_fields.items(): self.fields[label] = forms.CharField(initial=value, widget=forms.TextInput(attrs={'name':'%s|%s|%s' % (count, line, label)})) class RCImportForm(forms.Form): - + file_name = ExtFileField(extensions=['.racp', '.json', '.dat']) - - -class RCLineCodesForm(forms.ModelForm): - + + +class RCLineCodesForm(forms.ModelForm): + def __init__(self, *args, **kwargs): super(RCLineCodesForm, self).__init__(*args, **kwargs) - + if 'initial' in kwargs: self.fields['code'] = forms.ChoiceField(choices=RCLineCode.objects.all().values_list('pk', 'name'), initial=kwargs['initial']['code']) if 'instance' in kwargs: self.fields['code'] = forms.ChoiceField(choices=RCLineCode.objects.all().values_list('pk', 'name'), initial=kwargs['instance'].pk) - + self.fields['codes'].widget = CodesWidget() - - + + class Meta: model = RCLineCode exclude = ('name',) - - \ No newline at end of file diff --git a/apps/rc/models.py b/apps/rc/models.py index 3ef3c73..0da7146 100644 --- a/apps/rc/models.py +++ b/apps/rc/models.py @@ -3,8 +3,6 @@ import ast import json import numpy as np -from polymorphic import PolymorphicModel - from django.db import models from django.core.urlresolvers import reverse from django.core.validators import MinValueValidator, MaxValueValidator @@ -39,12 +37,12 @@ DAT_CMDS = { # Pulse Design commands 'DISABLE' : 0, # Disables pulse generation 'ENABLE' : 24, # Enables pulse generation - 'DELAY_START' : 40, # Write delay status to memory + 'DELAY_START' : 40, # Write delay status to memory 'FLIP_START' : 48, # Write flip status to memory 'SAMPLING_PERIOD' : 64, # Establish Sampling Period - 'TX_ONE' : 72, # Output '0' in line TX - 'TX_ZERO' : 88, # Output '0' in line TX - 'SW_ONE' : 104, # Output '0' in line SW + 'TX_ONE' : 72, # Output '0' in line TX + 'TX_ZERO' : 88, # Output '0' in line TX + 'SW_ONE' : 104, # Output '0' in line SW 'SW_ZERO' : 112, # Output '1' in line SW 'RESTART': 120, # Restarts CR8 Firmware 'CONTINUE' : 253, # Function Unknown @@ -52,14 +50,14 @@ DAT_CMDS = { # In Pulse Design Executable, the clock divisor code is written as 12 at the start, but it should be written as code 22(below) just before the final enable. 'CLOCK_DIVISOR_INIT' : 12, # Specifies Clock Divisor. Legacy command, ignored in the actual .dat conversion 'CLOCK_DIVISOR_LAST' : 22, # Specifies Clock Divisor (default 60 if not included) syntax: 255,22 254,N-1. - 'CLOCK_DIVIDER' : 8, + 'CLOCK_DIVIDER' : 8, } class RCConfiguration(Configuration): - + ipp = models.FloatField(verbose_name='IPP [Km]', validators=[MinValueValidator(1), MaxValueValidator(9000)], default=300) - ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1), MaxValueValidator(400)], default=1) + ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1), MaxValueValidator(400)], default=1) clock_in = models.FloatField(verbose_name='Clock in [MHz]', validators=[MinValueValidator(1), MaxValueValidator(80)], default=1) clock_divider = models.PositiveIntegerField(verbose_name='Clock divider', validators=[MinValueValidator(1), MaxValueValidator(256)], default=1) clock = models.FloatField(verbose_name='Clock Master [MHz]', blank=True, default=1) @@ -74,100 +72,100 @@ class RCConfiguration(Configuration): class Meta: db_table = 'rc_configurations' - + def get_absolute_url_plot(self): return reverse('url_plot_rc_pulses', args=[str(self.id)]) - + def get_absolute_url_import(self): return reverse('url_import_rc_conf', args=[str(self.id)]) - + @property def ipp_unit(self): - + return '{} ({})'.format(self.ipp, int(self.ipp*self.km2unit)) - + @property def us2unit(self): - + return self.clock_in/self.clock_divider @property def km2unit(self): - + return 20./3*(self.clock_in/self.clock_divider) def clone(self, **kwargs): - + lines = self.get_lines() self.pk = None self.id = None for attr, value in kwargs.items(): setattr(self, attr, value) self.save() - + for line in lines: line.clone(rc_configuration=self) - - return self + + return self def get_lines(self, **kwargs): ''' - Retrieve configuration lines + Retrieve configuration lines ''' - + return RCLine.objects.filter(rc_configuration=self.pk, **kwargs) - + def clean_lines(self): ''' ''' - + empty_line = RCLineType.objects.get(name='none') - + for line in self.get_lines(): line.line_type = empty_line line.params = '{}' line.save() def parms_to_dict(self): - ''' ''' - + ''' + ignored = ('parameters', 'type', 'polymorphic_ctype', 'configuration_ptr', 'created_date', 'programmed_date') - + data = {} for field in self._meta.fields: if field.name in ignored: continue data[field.name] = '{}'.format(field.value_from_object(self)) - + data['device_id'] = data.pop('device') data['lines'] = [] - + for line in self.get_lines(): - line_data = json.loads(line.params) + line_data = json.loads(line.params) if 'TX_ref' in line_data and line_data['TX_ref'] not in (0, '0'): - line_data['TX_ref'] = line.get_name() + line_data['TX_ref'] = RCLine.objects.get(pk=line_data['TX_ref']).get_name() if 'code' in line_data: line_data['code'] = RCLineCode.objects.get(pk=line_data['code']).name line_data['type'] = line.line_type.name line_data['name'] = line.get_name() data['lines'].append(line_data) - + data['delays'] = self.get_delays() data['pulses'] = self.get_pulses() - + return data - + def dict_to_parms(self, data): ''' ''' - + self.name = data['name'] self.ipp = float(data['ipp']) self.ntx = int(data['ntx']) - self.clock_in = float(data['clock_in']) + self.clock_in = float(data['clock_in']) self.clock_divider = int(data['clock_divider']) self.clock = float(data['clock']) self.time_before = data['time_before'] @@ -177,36 +175,37 @@ class RCConfiguration(Configuration): self.total_units = self.ipp*self.ntx*self.km2unit self.save() self.clean_lines() - + lines = [] - positions = {'tx':0, 'tr':0} - - for i, line_data in enumerate(data['lines']): + positions = {'tx':0, 'tr':0} + + for i, line_data in enumerate(data['lines']): + name = line_data.pop('name', '') line_type = RCLineType.objects.get(name=line_data.pop('type')) if line_type.name=='codes': code = RCLineCode.objects.get(name=line_data['code']) - line_data['code'] = code.pk + line_data['code'] = code.pk line = RCLine.objects.filter(rc_configuration=self, channel=i) if line: line = line[0] line.line_type = line_type line.params = json.dumps(line_data) else: - line = RCLine(rc_configuration=self, line_type=line_type, + line = RCLine(rc_configuration=self, line_type=line_type, params=json.dumps(line_data), channel=i) - + if line_type.name=='tx': line.position = positions['tx'] positions['tx'] += 1 - + if line_type.name=='tr': line.position = positions['tr'] positions['tr'] += 1 - + line.save() lines.append(line) - + for line, line_data in zip(lines, data['lines']): if 'TX_ref' in line_data: params = json.loads(line.params) @@ -216,101 +215,101 @@ class RCConfiguration(Configuration): params['TX_ref'] = [l.pk for l in lines if l.line_type.name=='tx' and line_data['TX_ref'] in l.get_name()][0] line.params = json.dumps(params) line.save() - - + + def get_delays(self): - + pulses = [line.pulses_as_points() for line in self.get_lines()] points = [tup for tups in pulses for tup in tups] points = set([x for tup in points for x in tup]) points = list(points) - points.sort() - - if points[0]<>0: + points.sort() + + if points[0]!=0: points.insert(0, 0) - + return [points[i+1]-points[i] for i in range(len(points)-1)] - - + + def get_pulses(self, binary=True): - + pulses = [line.pulses_as_points() for line in self.get_lines()] points = [tup for tups in pulses for tup in tups] points = set([x for tup in points for x in tup]) points = list(points) - points.sort() - + points.sort() + line_points = [line.pulses_as_points() for line in self.get_lines()] line_points = [[(x, x+y) for x,y in tups] for tups in line_points] - line_points = [[t for x in tups for t in x] for tups in line_points] + line_points = [[t for x in tups for t in x] for tups in line_points] states = [[1 if x in tups else 0 for tups in line_points] for x in points] - + if binary: states.reverse() states = [int(''.join([str(x) for x in flips]), 2) for flips in states] - + return states[:-1] - + def add_cmd(self, cmd): - + if cmd in DAT_CMDS: return (255, DAT_CMDS[cmd]) - - def add_data(self, value): - + + def add_data(self, value): + return (254, value-1) - + def parms_to_binary(self): ''' Create "dat" stream to be send to CR ''' - + data = [] # create header data.append(self.add_cmd('DISABLE')) data.append(self.add_cmd('CONTINUE')) data.append(self.add_cmd('RESTART')) - + if self.control_sw: data.append(self.add_cmd('SW_ONE')) else: data.append(self.add_cmd('SW_ZERO')) - + if self.control_tx: data.append(self.add_cmd('TX_ONE')) else: data.append(self.add_cmd('TX_ZERO')) - + # write divider data.append(self.add_cmd('CLOCK_DIVIDER')) data.append(self.add_data(self.clock_divider)) - + # write delays data.append(self.add_cmd('DELAY_START')) # first delay is always zero data.append(self.add_data(1)) - + delays = self.get_delays() - - for delay in delays: - while delay>252: + + for delay in delays: + while delay>252: data.append(self.add_data(253)) delay -= 253 data.append(self.add_data(delay)) - + # write flips - data.append(self.add_cmd('FLIP_START')) - + data.append(self.add_cmd('FLIP_START')) + states = self.get_pulses(binary=False) for flips, delay in zip(states, delays): flips.reverse() - flip = int(''.join([str(x) for x in flips]), 2) + flip = int(''.join([str(x) for x in flips]), 2) data.append(self.add_data(flip+1)) while delay>252: data.append(self.add_data(1)) delay -= 253 - + # write sampling period data.append(self.add_cmd('SAMPLING_PERIOD')) wins = self.get_lines(line_type__name='windows') @@ -323,91 +322,92 @@ class RCConfiguration(Configuration): else: dh = 1 data.append(self.add_data(dh)) - + # write enable data.append(self.add_cmd('ENABLE')) - + return '\n'.join(['{}'.format(x) for tup in data for x in tup]) - + def update_from_file(self, filename): ''' Update instance from file ''' - + f = RCFile(filename) self.dict_to_parms(f.data) self.update_pulses() def update_pulses(self): - + for line in self.get_lines(): line.update_pulses() - + def plot_pulses(self, km=False): - + import matplotlib.pyplot as plt from bokeh.resources import CDN from bokeh.embed import components - from bokeh.mpl import to_bokeh - from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, PreviewSaveTool - - lines = self.get_lines() - + from bokeh.mpl import to_bokeh + from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool + + lines = self.get_lines() + N = len(lines) - npoints = self.total_units/self.km2unit if km else self.total_units + npoints = self.total_units/self.km2unit if km else self.total_units fig = plt.figure(figsize=(10, 2+N*0.5)) ax = fig.add_subplot(111) - labels = ['IPP'] - + labels = ['IPP'] + for i, line in enumerate(lines): - labels.append(line.get_name(channel=True)) + labels.append(line.get_name(channel=True)) l = ax.plot((0, npoints),(N-i-1, N-i-1)) - points = [(tup[0], tup[1]-tup[0]) for tup in line.pulses_as_points(km=km) if tup<>(0,0)] - ax.broken_barh(points, (N-i-1, 0.5), + points = [(tup[0], tup[1]-tup[0]) for tup in line.pulses_as_points(km=km) if tup!=(0,0)] + ax.broken_barh(points, (N-i-1, 0.5), edgecolor=l[0].get_color(), facecolor='none') - + n = 0 f = ((self.ntx+50)/100)*5 if ((self.ntx+50)/100)*10>0 else 2 for x in np.arange(0, npoints, self.ipp if km else self.ipp*self.km2unit): if n%f==0: ax.text(x, N, '%s' % n, size=10) n += 1 - - + + labels.reverse() + ax.set_yticks(range(len(labels))) ax.set_yticklabels(labels) - ax.set_xlabel = 'Units' - plot = to_bokeh(fig, use_pandas=False) - plot.tools = [PanTool(dimensions=['width']), WheelZoomTool(dimensions=['width']), ResetTool(), PreviewSaveTool()] - + plot = to_bokeh(fig, use_pandas=False) + plot.tools = [PanTool(dimensions=['width']), WheelZoomTool(dimensions=['width']), ResetTool(), SaveTool()] + plot.toolbar_location="above" + return components(plot, CDN) - + def status_device(self): - + return 0 def stop_device(self): - + answer = api.disable(ip = self.device.ip_address, port = self.device.port_address) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 - + def start_device(self): - + answer = api.enable(ip = self.device.ip_address, port = self.device.port_address) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 @@ -415,138 +415,138 @@ class RCConfiguration(Configuration): answer = api.write_config(ip = self.device.ip_address, port = self.device.port_address, parms = self.parms_to_dict()) - + if answer[0] != "1": self.message = answer[0:] return 0 - + self.message = answer[2:] return 1 class RCLineCode(models.Model): - + name = models.CharField(max_length=40) bits_per_code = models.PositiveIntegerField(default=0) number_of_codes = models.PositiveIntegerField(default=0) codes = models.TextField(blank=True, null=True) - + class Meta: db_table = 'rc_line_codes' ordering = ('name',) - - def __unicode__(self): - return u'%s' % self.name + + def __str__(self): + return u'%s' % self.name class RCLineType(models.Model): - + name = models.CharField(choices=LINE_TYPES, max_length=40) description = models.TextField(blank=True, null=True) params = models.TextField(default='[]') - + class Meta: db_table = 'rc_line_types' - def __unicode__(self): + def __str__(self): return u'%s - %s' % (self.name.upper(), self.get_name_display()) - - + + class RCLine(models.Model): - + rc_configuration = models.ForeignKey(RCConfiguration, on_delete=models.CASCADE) line_type = models.ForeignKey(RCLineType) channel = models.PositiveIntegerField(default=0) position = models.PositiveIntegerField(default=0) params = models.TextField(default='{}') pulses = models.TextField(default='') - + class Meta: db_table = 'rc_lines' ordering = ['channel'] - - def __unicode__(self): + + def __str__(self): if self.rc_configuration: return u'%s - %s' % (self.rc_configuration, self.get_name()) - + def clone(self, **kwargs): - + self.pk = None - + for attr, value in kwargs.items(): setattr(self, attr, value) - - self.save() + + self.save() return self - + def get_name(self, channel=False): - + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - s = '' - + s = '' + if self.line_type.name in ('tx',): s = chars[self.position] elif self.line_type.name in ('codes', 'windows', 'tr'): if 'TX_ref' in json.loads(self.params): pk = json.loads(self.params)['TX_ref'] if pk in (0, '0'): - s = ','.join(chars[l.position] for l in self.rc_configuration.get_lines(line_type__name='tx')) + s = ','.join(chars[l.position] for l in self.rc_configuration.get_lines(line_type__name='tx')) else: ref = RCLine.objects.get(pk=pk) s = chars[ref.position] s = '({})'.format(s) - + s = '{}{}'.format(self.line_type.name.upper(), s) - + if channel: return '{} {}'.format(s, self.channel) else: return s def get_lines(self, **kwargs): - - return RCLine.objects.filter(rc_configuration=self.rc_configuration, **kwargs) - + + return RCLine.objects.filter(rc_configuration=self.rc_configuration, **kwargs) + def pulses_as_array(self): - + y = np.zeros(self.rc_configuration.total_units) - + for tup in ast.literal_eval(self.pulses): y[tup[0]:tup[1]] = 1 - + return y.astype(np.int8) - + def pulses_as_points(self, km=False): - + if km: unit2km = 1/self.rc_configuration.km2unit return [(tup[0]*unit2km, tup[1]*unit2km) for tup in ast.literal_eval(self.pulses)] else: return ast.literal_eval(self.pulses) - + def get_win_ref(self, params, tx_id, km2unit): - + ref = self.rc_configuration.sampling_reference codes = [line for line in self.get_lines(line_type__name='codes') if int(json.loads(line.params)['TX_ref'])==int(tx_id)] - - if codes: + + if codes: tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit/len(json.loads(codes[0].params)['codes'][0]) else: tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit - + if ref=='first_baud': return int(1 + (tx_width + 1)/2 + params['first_height']*km2unit - params['resolution']*km2unit) elif ref=='sub_baud': return int(1 + params['first_height']*km2unit - params['resolution']*km2unit/2) else: return 0 - + def update_pulses(self): ''' - Update pulses field + Update pulses field ''' - + km2unit = self.rc_configuration.km2unit us2unit = self.rc_configuration.us2unit ipp = self.rc_configuration.ipp @@ -554,89 +554,89 @@ class RCLine(models.Model): ipp_u = int(ipp*km2unit) total = ipp_u*ntx if self.rc_configuration.total_units==0 else self.rc_configuration.total_units y = [] - + if self.line_type.name=='tr': tr_params = json.loads(self.params) - + if tr_params['TX_ref'] in ('0', 0): txs = self.get_lines(line_type__name='tx') else: txs = RCLine.objects.filter(pk=tr_params['TX_ref']) - + for tx in txs: params = json.loads(tx.params) - + if float(params['pulse_width'])==0: continue - delays = [float(d)*km2unit for d in params['delays'].split(',') if d] + delays = [float(d)*km2unit for d in params['delays'].split(',') if d] width = float(params['pulse_width'])*km2unit+int(self.rc_configuration.time_before*us2unit) before = 0 after = int(self.rc_configuration.time_after*us2unit) - + y_tx = self.points(ntx, ipp_u, width, delay=delays, before=before, after=after, sync=self.rc_configuration.sync) - + ranges = params['range'].split(',') - - if len(ranges)>0 and ranges[0]<>'0': + + if len(ranges)>0 and ranges[0]!='0': y_tx = self.mask_ranges(y_tx, ranges) - + tr_ranges = tr_params['range'].split(',') - - if len(tr_ranges)>0 and tr_ranges[0]<>'0': + + if len(tr_ranges)>0 and tr_ranges[0]!='0': y_tx = self.mask_ranges(y_tx, tr_ranges) - + y.extend(y_tx) - self.pulses = unicode(y) + self.pulses = str(y) y = self.array_to_points(self.pulses_as_array()) - + elif self.line_type.name=='tx': - params = json.loads(self.params) - delays = [float(d)*km2unit for d in params['delays'].split(',') if d] + params = json.loads(self.params) + delays = [float(d)*km2unit for d in params['delays'].split(',') if d] width = float(params['pulse_width'])*km2unit - - if width>0: + + if width>0: before = int(self.rc_configuration.time_before*us2unit) after = 0 - + y = self.points(ntx, ipp_u, width, delay=delays, before=before, after=after, - sync=self.rc_configuration.sync) - + sync=self.rc_configuration.sync) + ranges = params['range'].split(',') - - if len(ranges)>0 and ranges[0]<>'0': + + if len(ranges)>0 and ranges[0]!='0': y = self.mask_ranges(y, ranges) - + elif self.line_type.name=='flip': n = float(json.loads(self.params)['number_of_flips']) width = n*ipp*km2unit y = self.points(int((ntx+1)/(2*n)), ipp_u*n*2, width) - + elif self.line_type.name=='codes': params = json.loads(self.params) tx = RCLine.objects.get(pk=params['TX_ref']) tx_params = json.loads(tx.params) - delays = [float(d)*km2unit for d in tx_params['delays'].split(',') if d] - f = int(float(tx_params['pulse_width'])*km2unit)/len(params['codes'][0]) - codes = [(np.fromstring(''.join([s*f for s in code]), dtype=np.uint8)-48).astype(np.int8) for code in params['codes']] + delays = [float(d)*km2unit for d in tx_params['delays'].split(',') if d] + f = int(float(tx_params['pulse_width'])*km2unit/len(params['codes'][0])) + codes = [(np.fromstring(''.join([s*f for s in code]), dtype=np.uint8)-48).astype(np.int8) for code in params['codes']] codes = [self.array_to_points(code) for code in codes] n = len(codes) - + for i, tup in enumerate(tx.pulses_as_points()): code = codes[i%n] y.extend([(c[0]+tup[0], c[1]+tup[0]) for c in code]) - + ranges = tx_params['range'].split(',') - if len(ranges)>0 and ranges[0]<>'0': + if len(ranges)>0 and ranges[0]!='0': y = self.mask_ranges(y, ranges) - + elif self.line_type.name=='sync': params = json.loads(self.params) n = ipp_u*ntx @@ -644,7 +644,7 @@ class RCLine(models.Model): y = [(n-1, n)] else: y = [(0, 1)] - + elif self.line_type.name=='prog_pulses': params = json.loads(self.params) if int(params['periodic'])==0: @@ -653,32 +653,32 @@ class RCLine(models.Model): else: nntx = ntx nipp = ipp_u - - if 'params' in params and len(params['params'])>0: + + if 'params' in params and len(params['params'])>0: for p in params['params']: - y_pp = self.points(nntx, nipp, - p['end']-p['begin'], - before=p['begin']) - - y.extend(y_pp) - + y_pp = self.points(nntx, nipp, + p['end']-p['begin'], + before=p['begin']) + + y.extend(y_pp) + elif self.line_type.name=='windows': - params = json.loads(self.params) - + params = json.loads(self.params) + if 'params' in params and len(params['params'])>0: tr_params = json.loads(self.get_lines(line_type__name='tr')[0].params) tr_ranges = tr_params['range'].split(',') for p in params['params']: - y_win = self.points(ntx, ipp_u, - p['resolution']*p['number_of_samples']*km2unit, + y_win = self.points(ntx, ipp_u, + p['resolution']*p['number_of_samples']*km2unit, before=int(self.rc_configuration.time_before*us2unit)+self.get_win_ref(p, params['TX_ref'], km2unit), sync=self.rc_configuration.sync) - - if len(tr_ranges)>0 and tr_ranges[0]<>'0': + + if len(tr_ranges)>0 and tr_ranges[0]!='0': y_win = self.mask_ranges(y_win, tr_ranges) - - y.extend(y_win) - + + y.extend(y_win) + elif self.line_type.name=='mix': values = self.rc_configuration.parameters.split('-') confs = [RCConfiguration.objects.get(pk=value.split('|')[0]) for value in values] @@ -692,19 +692,19 @@ class RCLine(models.Model): y = np.zeros(confs[0].total_units, dtype=np.int8) else: y = confs[0].get_lines(channel=self.channel)[0].pulses_as_array() - + for i in range(1, len(values)): mask = list('{:8b}'.format(int(masks[i]))) - mask.reverse() - + mask.reverse() + if mask[self.channel] in ('0', '', ' '): continue Y = confs[i].get_lines(channel=self.channel)[0].pulses_as_array() delay = float(delays[i])*km2unit - + if modes[i]=='P': if delay>0: - if delay total: + + if self.rc_configuration.total_units != total: self.rc_configuration.total_units = total self.rc_configuration.save() - - self.pulses = unicode(y) + + self.pulses = str(y) self.save() - + @staticmethod def array_to_points(X): - + d = X[1:]-X[:-1] - + up = np.where(d==1)[0] if X[0]==1: up = np.concatenate((np.array([-1]), up)) up += 1 - + dw = np.where(d==-1)[0] if X[-1]==1: dw = np.concatenate((dw, np.array([len(X)-1]))) dw += 1 - + return [(tup[0], tup[1]) for tup in zip(up, dw)] @staticmethod def mask_ranges(Y, ranges): - + y = [(0, 0) for __ in Y] - + for index in ranges: if '-' in index: args = [int(a) for a in index.split('-')] - y[args[0]-1:args[1]] = Y[args[0]-1:args[1]] + y[args[0]-1:args[1]] = Y[args[0]-1:args[1]] else: - y[int(index-1)] = Y[int(index-1)] - + y[int(index-1)] = Y[int(index-1)] + return y - + @staticmethod def points(ntx, ipp, width, delay=[0], before=0, after=0, sync=0): - + delays = len(delay) - + Y = [(ipp*x+before+delay[x%delays], ipp*x+width+before+delay[x%delays]+after) for x in range(ntx)] - - return Y \ No newline at end of file + + return Y diff --git a/apps/rc/static/js/bokeh-0.12.1.min.js b/apps/rc/static/js/bokeh-0.12.1.min.js new file mode 100644 index 0000000..390b187 --- /dev/null +++ b/apps/rc/static/js/bokeh-0.12.1.min.js @@ -0,0 +1,269 @@ +window.Bokeh=Bokeh=function(){var t=void 0;return function e(t,r,n){function o(i){if(!r[i]){if(!t[i]){var s=new Error("Cannot find module '"+i+"'");throw s.code="MODULE_NOT_FOUND",s}var a=r[i]={exports:{}},l=function(e){var r=t[i][1][e];return o(r?r:e)};l.modules=o.modules,t[i][0].call(a.exports,l,a,a.exports,e,t,r,n)}return r[i].exports}o.modules=t;for(var i=null,s=0;s=.6},d=function(t,e){var r,n,o,s,l,d,g,y,v,b,x,w,M,k,T,j,S,z,P,A,E,C,O,N,q,D,R,I,F,B,L,G,V,U,Y,H,$,X,W,Q,J,K,Z,tt,et;for(null==e&&(e={}),S=[],Z=[],k=j=0,N=Math.min(t.labels.length,t.values.length);0<=N?jN;k=0<=N?++j:--j)t.values[k]>0&&(S.push(t.labels[k]),Z.push(t.values[k]));return V=null!=(q=e.start_angle)?q:0,d=null!=(D=e.end_angle)?D:V+2*Math.PI,r=Math.abs(d-V),Q=function(t){return r*t},K=m(Z),z=Z.map(function(t){return t/K}),o=a(z),g=o.map(function(t){return V+Q(t)}),U=[V].concat(g.slice(0,-1)),x=i.zip(U,g).map(function(t){return function(t){var e,r;return r=t[0],e=t[1],(r+e)/2}}(this)),null==e.center?(s=0,l=0):i.isArray(e.center)?(s=e.center[0],l=e.center[1]):(s=e.center.x,l=e.center.y),T=null!=(R=e.inner_radius)?R:0,P=null!=(I=e.outer_radius)?I:1,A=i.isArray(e.palette)?e.palette:_[null!=(F=e.palette)?F:"Spectral11"].map(p),n=function(){var t,e,r;for(r=[],k=t=0,e=z.length;0<=e?te;k=0<=e?++t:--t)r.push(A[k%A.length]);return r}(),H=n.map(function(t){return c(u(t))?"white":"black"}),W=function(t,e){return[t*Math.cos(e),t*Math.sin(e)]},w=(T+P)/2,B=i.unzip(x.map(function(t){return function(t){return W(w,t)}}(this))),$=B[0],X=B[1],$=$.map(function(t){return t+s}),X=X.map(function(t){return t+l}),Y=x.map(function(t){return t>=Math.PI/2&&t<=3*Math.PI/2?t+Math.PI:t}),G=new Bokeh.ColumnDataSource({data:{labels:S,values:Z,percentages:z.map(function(t){return function(t){return f("%.2f%%",100*t)}}(this)),start_angles:U,end_angles:g,text_angles:Y,colors:n,text_colors:H,text_cx:$,text_cy:X}}),y=new h.AnnularWedge({x:s,y:l,inner_radius:T,outer_radius:P,start_angle:{field:"start_angles"},end_angle:{field:"end_angles"},line_color:null,line_width:1,fill_color:{field:"colors"}}),b=new h.AnnularWedge({x:s,y:l,inner_radius:T,outer_radius:P,start_angle:{field:"start_angles"},end_angle:{field:"end_angles"},line_color:null,line_width:1,fill_color:{field:"colors"},fill_alpha:.8}),C=new h.GlyphRenderer({data_source:G,glyph:y,hover_glyph:b}),v=new h.Text({x:{field:"text_cx"},y:{field:"text_cy"},text:{field:null!=(L=e.slice_labels)?L:"labels"},angle:{field:"text_angles"},text_align:"center",text_baseline:"middle",text_color:{field:"text_colors"},text_font_size:"9pt"}),O=new h.GlyphRenderer({data_source:G,glyph:v}),tt=new h.DataRange1d({renderers:[C],range_padding:.2}),et=new h.DataRange1d({renderers:[C],range_padding:.2}),E=new h.Plot({x_range:tt,y_range:et}),null!=e.width&&(E.plot_width=e.width),null!=e.height&&(E.plot_height=e.height),E.add_renderers(C,O),J="
    @labels
    @values (@percentages)
    ",M=new h.HoverTool({renderers:[C],tooltips:J}),E.add_tools(M),E},s=function(t,e){var r,n,o,s,a,l,u,c,d,f,m,g,y,v,b,x,w,M,k,T,j,S,z,P,A,E,C,O,N,q,D,R,I,F,B,L,G,V,U,Y,H,$,X,W,Q,J,K,Z,tt,et,rt,nt,ot,it;for(null==e&&(e={}),s=t[0],X=t.slice(1),a=function(){var t,e,r;for(r=[],t=0,e=s.length;tF;d=0<=F?++j:--j){for(o=[],Z=[],f=z=0,w=y.length;zB;d=0<=B?++A:--A){for(v=[],H=[],o=[],Z=[],f=O=0,M=y.length;O@labels
    @columns: @values
    ","horizontal"===P?(r="right_center",n="horizontal"):(r="top_center",n="vertical"),c=new h.HoverTool({renderers:Y,tooltips:K,point_policy:"snap_to_data",anchor:r,attachment:n}),C.add_tools(c),C},e.exports={pie:d,bar:s}},{"../document":"document","../embed":"embed","../palettes/palettes":"palettes/palettes","./models":"api/models",jquery:"jquery",sprintf:"sprintf",underscore:"underscore"}],"api/linalg":[function(t,e,r){"use strict";function n(t){for(var e=t.length,r=t[0].length,n=[],o=0;o 1");return e}return null==e?0:"auto"===e?t===m.LogAxis?10:5:void 0},e.prototype._process_tools=function(t){var e,r;return a.isString(t)&&(t=t.split(/\s*,\s*/)),e=function(){var e,n,o;for(o=[],e=0,n=t.length;e"),n(null!=e?e:"body").append(s),p=_.add_document_standalone(l,s),h?p:p[t.id]},p=function(t,e,r){return y("#%02x%02x%02x",t,e,r)},f=function(t,e){var r,n,o,i,s,l,u,c,h,p,_,d,f,g,y,v,b,x,w;for(null==e&&(e={}),b=a.isUndefined(e.toolbar_location)?"above":e.toolbar_location,y=a.isUndefined(e.sizing_mode)?"fixed":e.sizing_mode,x="fixed"===e.sizing_mode?"scale_width":y,w=[],g=[],o=0,u=t.length;o0&&(f.debug("Sending "+n.events.length+" changes from model construction back to server"),i=u.create("PATCH-DOC",{},n),t.send(i)),t.session=new o(t,r,t.id),f.debug("Created a new session from new pulled doc"),null!=t._on_have_session_hook?(t._on_have_session_hook(t.session),t._on_have_session_hook=null):void 0)}}(this),function(t){throw t})["catch"](function(t){return null!=console.trace&&console.trace(t),f.error("Failed to repull session "+t)})},t.prototype._on_open=function(t,e){return f.info("Websocket connection "+this._number+" is now open"),this._pending_ack=[t,e],this._current_handler=function(t){return function(e){return t._awaiting_ack_handler(e)}}(this)},t.prototype._on_message=function(t){var e,r;try{return this._on_message_unchecked(t)}catch(r){return e=r,f.error("Error handling message: "+e+", "+t)}},t.prototype._on_message_unchecked=function(t){var e,r;if(null==this._current_handler&&f.error("got a message but haven't set _current_handler"),t.data instanceof ArrayBuffer?null==this._partial||this._partial.complete()?this._close_bad_protocol("Got binary from websocket but we were expecting text"):this._partial.add_buffer(t.data):null!=this._partial?this._close_bad_protocol("Got text from websocket but we were expecting binary"):(this._fragments.push(t.data),3===this._fragments.length&&(this._partial=u.assemble(this._fragments[0],this._fragments[1],this._fragments[2]),this._fragments=[],r=this._partial.problem(),null!==r&&this._close_bad_protocol(r))),null!=this._partial&&this._partial.complete())return e=this._partial,this._partial=null,this._current_handler(e)},t.prototype._on_close=function(t){var e,r;for(f.info("Lost websocket "+this._number+" connection, "+t.code+" ("+t.reason+")"),this.socket=null,null!=this._pending_ack&&(this._pending_ack[1](new Error("Lost websocket connection, "+t.code+" ("+t.reason+")")),this._pending_ack=null),e=function(){var t,e,r;e=this._pending_replies;for(r in e)return t=e[r],delete this._pending_replies[r],t;return null},r=e();null!==r;)r[1]("Disconnected"),r=e();if(!this.closed_permanently)return this._schedule_reconnect(2e3)},t.prototype._on_error=function(t){return f.debug("Websocket error on socket "+this._number),t(new Error("Could not open websocket"))},t.prototype._close_bad_protocol=function(t){if(f.error("Closing connection: "+t),null!=this.socket)return this.socket.close(1002,t)},t.prototype._awaiting_ack_handler=function(t){return"ACK"!==t.msgtype()?this._close_bad_protocol("First message was not an ACK"):(this._current_handler=function(t){return function(e){return t._steady_state_handler(e)}}(this),this._repull_session_doc(),null!=this._pending_ack?(this._pending_ack[0](this),this._pending_ack=null):void 0)},t.prototype._steady_state_handler=function(t){var e;return t.reqid()in this._pending_replies?(e=this._pending_replies[t.reqid()],delete this._pending_replies[t.reqid()],e[0](t)):t.msgtype()in m?m[t.msgtype()](this,t):f.debug("Doing nothing with message "+t.msgtype())},t}(),o=function(){function t(t,e,r){this._connection=t,this.document=e,this.id=r,this._current_patch=null,this.document_listener=function(t){return function(e){return t._document_changed(e)}}(this),this.document.on_change(this.document_listener)}return t.prototype.close=function(){return this._connection.close()},t.prototype._connection_closed=function(){return this.document.remove_on_change(this.document_listener)},t.prototype.request_server_info=function(){var t,e;return t=u.create("SERVER-INFO-REQ",{}),e=this._connection.send_with_reply(t),e.then(function(t){return t.content})},t.prototype.force_roundtrip=function(){return this.request_server_info().then(function(t){})},t.prototype._should_suppress_on_change=function(t,e){var r,n,o,i,s,a,u,h,f,m,g,y,v,b;if(e instanceof c){for(g=t.content.events,n=0,a=g.length;na;o=0<=a?++s:--s)u=r[o],h=n[o],ci&&(r=[i,o],o=r[0],i=r[1]),s>a&&(n=[a,s],s=n[0],a=n[1]),{minX:o,minY:s,maxX:i,maxY:a}},h=function(t){return t*t},s=function(t,e,r,n){return h(t-r)+h(e-n)},l=function(t,e,r){var n,o;return n=s(e.x,e.y,r.x,r.y),0===n?s(t.x,t.y,e.x,e.y):(o=((t.x-e.x)*(r.x-e.x)+(t.y-e.y)*(r.y-e.y))/n,o<0?s(t.x,t.y,e.x,e.y):o>1?s(t.x,t.y,r.x,r.y):s(t.x,t.y,e.x+o*(r.x-e.x),e.y+o*(r.y-e.y)))},a=function(t,e,r){return Math.sqrt(l(t,e,r))},o=function(t,e,r,n,o,i,s,a){var l,u,c,h,p,_,d;return c=(a-i)*(r-t)-(s-o)*(n-e),0===c?{hit:!1,x:null,y:null}:(l=e-i,u=t-o,h=(s-o)*l-(a-i)*u,p=(r-t)*l-(n-e)*u,l=h/c,u=p/c,_=t+l*(r-t),d=e+l*(n-e),{hit:l>0&&l<1&&u>0&&u<1,x:_,y:d})},e.exports={point_in_poly:c,HitTestResult:n,create_hit_test_result:i,dist_2_pts:s,dist_to_segment:a,check_2_segments_intersect:o,validate_bbox_coords:p}},{}],"common/models":[function(t,e,r){e.exports={SelectionManager:t("./selection_manager"),Selector:t("./selector"),ToolEvents:t("./tool_events"),Arrow:t("../models/annotations/arrow"),BoxAnnotation:t("../models/annotations/box_annotation"),Label:t("../models/annotations/label"),LabelSet:t("../models/annotations/label_set"),Legend:t("../models/annotations/legend"),PolyAnnotation:t("../models/annotations/poly_annotation"),Span:t("../models/annotations/span"),Title:t("../models/annotations/title"),Tooltip:t("../models/annotations/tooltip"),OpenHead:t("../models/annotations/arrow_head").OpenHead,NormalHead:t("../models/annotations/arrow_head").NormalHead,VeeHead:t("../models/annotations/arrow_head").VeeHead,CategoricalAxis:t("../models/axes/categorical_axis"),DatetimeAxis:t("../models/axes/datetime_axis"),LinearAxis:t("../models/axes/linear_axis"),LogAxis:t("../models/axes/log_axis"),CustomJS:t("../models/callbacks/customjs"),OpenURL:t("../models/callbacks/open_url"),Canvas:t("../models/canvas/canvas"),CartesianFrame:t("../models/canvas/cartesian_frame"),BasicTickFormatter:t("../models/formatters/basic_tick_formatter"),CategoricalTickFormatter:t("../models/formatters/categorical_tick_formatter"),DatetimeTickFormatter:t("../models/formatters/datetime_tick_formatter"),LogTickFormatter:t("../models/formatters/log_tick_formatter"),FuncTickFormatter:t("../models/formatters/func_tick_formatter"),NumeralTickFormatter:t("../models/formatters/numeral_tick_formatter"),PrintfTickFormatter:t("../models/formatters/printf_tick_formatter"),AnnularWedge:t("../models/glyphs/annular_wedge"),Annulus:t("../models/glyphs/annulus"),Arc:t("../models/glyphs/arc"),Bezier:t("../models/glyphs/bezier"),Circle:t("../models/glyphs/circle"),Ellipse:t("../models/glyphs/ellipse"),Gear:t("../models/glyphs/gear"),HBar:t("../models/glyphs/hbar"),Image:t("../models/glyphs/image"),ImageRGBA:t("../models/glyphs/image_rgba"),ImageURL:t("../models/glyphs/image_url"),Line:t("../models/glyphs/line"),MultiLine:t("../models/glyphs/multi_line"),Oval:t("../models/glyphs/oval"),Patch:t("../models/glyphs/patch"),Patches:t("../models/glyphs/patches"),Quad:t("../models/glyphs/quad"),Quadratic:t("../models/glyphs/quadratic"),Ray:t("../models/glyphs/ray"),Rect:t("../models/glyphs/rect"),Segment:t("../models/glyphs/segment"),Text:t("../models/glyphs/text"),VBar:t("../models/glyphs/vbar"),Wedge:t("../models/glyphs/wedge"),Grid:t("../models/grids/grid"),Column:t("../models/layouts/column"),Row:t("../models/layouts/row"),Spacer:t("../models/layouts/spacer"),WidgetBox:t("../models/layouts/widget_box"),CategoricalMapper:t("../models/mappers/categorical_mapper"),GridMapper:t("../models/mappers/grid_mapper"),LinearColorMapper:t("../models/mappers/linear_color_mapper"),LinearMapper:t("../models/mappers/linear_mapper"),LogColorMapper:t("../models/mappers/log_color_mapper"),LogMapper:t("../models/mappers/log_mapper"),Transform:t("../models/transforms/transform"),Jitter:t("../models/transforms/jitter"),Interpolator:t("../models/transforms/interpolator"),LinearInterpolator:t("../models/transforms/linear_interpolator"),StepInterpolator:t("../models/transforms/step_interpolator"),Asterisk:t("../models/markers/asterisk"),CircleCross:t("../models/markers/circle_cross"),CircleX:t("../models/markers/circle_x"),Cross:t("../models/markers/cross"),Diamond:t("../models/markers/diamond"),DiamondCross:t("../models/markers/diamond_cross"),InvertedTriangle:t("../models/markers/inverted_triangle"),Square:t("../models/markers/square"),SquareCross:t("../models/markers/square_cross"),SquareX:t("../models/markers/square_x"),Triangle:t("../models/markers/triangle"),X:t("../models/markers/x"),Plot:t("../models/plots/plot"),GMapPlot:t("../models/plots/gmap_plot"),DataRange1d:t("../models/ranges/data_range1d"),FactorRange:t("../models/ranges/factor_range"),Range1d:t("../models/ranges/range1d"),GlyphRenderer:t("../models/renderers/glyph_renderer"),AjaxDataSource:t("../models/sources/ajax_data_source"),ColumnDataSource:t("../models/sources/column_data_source"),GeoJSONDataSource:t("../models/sources/geojson_data_source"),AdaptiveTicker:t("../models/tickers/adaptive_ticker"),BasicTicker:t("../models/tickers/basic_ticker"),CategoricalTicker:t("../models/tickers/categorical_ticker"),CompositeTicker:t("../models/tickers/composite_ticker"),ContinuousTicker:t("../models/tickers/continuous_ticker"),DatetimeTicker:t("../models/tickers/datetime_ticker"),DaysTicker:t("../models/tickers/days_ticker"),FixedTicker:t("../models/tickers/fixed_ticker"),LogTicker:t("../models/tickers/log_ticker"),MonthsTicker:t("../models/tickers/months_ticker"),SingleIntervalTicker:t("../models/tickers/single_interval_ticker"),YearsTicker:t("../models/tickers/years_ticker"),TileRenderer:t("../models/tiles/tile_renderer"),TileSource:t("../models/tiles/tile_source"),TMSTileSource:t("../models/tiles/tms_tile_source"),WMTSTileSource:t("../models/tiles/wmts_tile_source"),QUADKEYTileSource:t("../models/tiles/quadkey_tile_source"),BBoxTileSource:t("../models/tiles/bbox_tile_source"),DynamicImageRenderer:t("../models/tiles/dynamic_image_renderer"),ImageSource:t("../models/tiles/image_source"),Toolbar:t("../models/tools/toolbar"),ToolbarBox:t("../models/tools/toolbar_box"),ButtonTool:t("../models/tools/button_tool"),ActionTool:t("../models/tools/actions/action_tool"),SaveTool:t("../models/tools/actions/save_tool"),UndoTool:t("../models/tools/actions/undo_tool"),RedoTool:t("../models/tools/actions/redo_tool"),ResetTool:t("../models/tools/actions/reset_tool"),HelpTool:t("../models/tools/actions/help_tool"),BoxSelectTool:t("../models/tools/gestures/box_select_tool"),BoxZoomTool:t("../models/tools/gestures/box_zoom_tool"),GestureTool:t("../models/tools/gestures/gesture_tool"),LassoSelectTool:t("../models/tools/gestures/lasso_select_tool"),PanTool:t("../models/tools/gestures/pan_tool"),PolySelectTool:t("../models/tools/gestures/poly_select_tool"),SelectTool:t("../models/tools/gestures/select_tool"),ResizeTool:t("../models/tools/gestures/resize_tool"),TapTool:t("../models/tools/gestures/tap_tool"),WheelZoomTool:t("../models/tools/gestures/wheel_zoom_tool"),CrosshairTool:t("../models/tools/inspectors/crosshair_tool"),HoverTool:t("../models/tools/inspectors/hover_tool"),InspectTool:t("../models/tools/inspectors/inspect_tool")}},{"../models/annotations/arrow":"models/annotations/arrow","../models/annotations/arrow_head":"models/annotations/arrow_head","../models/annotations/box_annotation":"models/annotations/box_annotation","../models/annotations/label":"models/annotations/label","../models/annotations/label_set":"models/annotations/label_set","../models/annotations/legend":"models/annotations/legend","../models/annotations/poly_annotation":"models/annotations/poly_annotation","../models/annotations/span":"models/annotations/span","../models/annotations/title":"models/annotations/title","../models/annotations/tooltip":"models/annotations/tooltip","../models/axes/categorical_axis":"models/axes/categorical_axis","../models/axes/datetime_axis":"models/axes/datetime_axis","../models/axes/linear_axis":"models/axes/linear_axis","../models/axes/log_axis":"models/axes/log_axis","../models/callbacks/customjs":"models/callbacks/customjs","../models/callbacks/open_url":"models/callbacks/open_url","../models/canvas/canvas":"models/canvas/canvas","../models/canvas/cartesian_frame":"models/canvas/cartesian_frame","../models/formatters/basic_tick_formatter":"models/formatters/basic_tick_formatter","../models/formatters/categorical_tick_formatter":"models/formatters/categorical_tick_formatter","../models/formatters/datetime_tick_formatter":"models/formatters/datetime_tick_formatter","../models/formatters/func_tick_formatter":"models/formatters/func_tick_formatter","../models/formatters/log_tick_formatter":"models/formatters/log_tick_formatter","../models/formatters/numeral_tick_formatter":"models/formatters/numeral_tick_formatter","../models/formatters/printf_tick_formatter":"models/formatters/printf_tick_formatter","../models/glyphs/annular_wedge":"models/glyphs/annular_wedge","../models/glyphs/annulus":"models/glyphs/annulus","../models/glyphs/arc":"models/glyphs/arc","../models/glyphs/bezier":"models/glyphs/bezier","../models/glyphs/circle":"models/glyphs/circle","../models/glyphs/ellipse":"models/glyphs/ellipse","../models/glyphs/gear":"models/glyphs/gear","../models/glyphs/hbar":"models/glyphs/hbar","../models/glyphs/image":"models/glyphs/image","../models/glyphs/image_rgba":"models/glyphs/image_rgba","../models/glyphs/image_url":"models/glyphs/image_url","../models/glyphs/line":"models/glyphs/line","../models/glyphs/multi_line":"models/glyphs/multi_line","../models/glyphs/oval":"models/glyphs/oval","../models/glyphs/patch":"models/glyphs/patch","../models/glyphs/patches":"models/glyphs/patches","../models/glyphs/quad":"models/glyphs/quad","../models/glyphs/quadratic":"models/glyphs/quadratic","../models/glyphs/ray":"models/glyphs/ray","../models/glyphs/rect":"models/glyphs/rect","../models/glyphs/segment":"models/glyphs/segment","../models/glyphs/text":"models/glyphs/text","../models/glyphs/vbar":"models/glyphs/vbar","../models/glyphs/wedge":"models/glyphs/wedge","../models/grids/grid":"models/grids/grid","../models/layouts/column":"models/layouts/column","../models/layouts/row":"models/layouts/row","../models/layouts/spacer":"models/layouts/spacer","../models/layouts/widget_box":"models/layouts/widget_box","../models/mappers/categorical_mapper":"models/mappers/categorical_mapper","../models/mappers/grid_mapper":"models/mappers/grid_mapper","../models/mappers/linear_color_mapper":"models/mappers/linear_color_mapper","../models/mappers/linear_mapper":"models/mappers/linear_mapper","../models/mappers/log_color_mapper":"models/mappers/log_color_mapper","../models/mappers/log_mapper":"models/mappers/log_mapper","../models/markers/asterisk":"models/markers/asterisk","../models/markers/circle_cross":"models/markers/circle_cross","../models/markers/circle_x":"models/markers/circle_x","../models/markers/cross":"models/markers/cross","../models/markers/diamond":"models/markers/diamond","../models/markers/diamond_cross":"models/markers/diamond_cross","../models/markers/inverted_triangle":"models/markers/inverted_triangle","../models/markers/square":"models/markers/square","../models/markers/square_cross":"models/markers/square_cross","../models/markers/square_x":"models/markers/square_x","../models/markers/triangle":"models/markers/triangle","../models/markers/x":"models/markers/x","../models/plots/gmap_plot":"models/plots/gmap_plot","../models/plots/plot":"models/plots/plot","../models/ranges/data_range1d":"models/ranges/data_range1d","../models/ranges/factor_range":"models/ranges/factor_range","../models/ranges/range1d":"models/ranges/range1d","../models/renderers/glyph_renderer":"models/renderers/glyph_renderer","../models/sources/ajax_data_source":"models/sources/ajax_data_source","../models/sources/column_data_source":"models/sources/column_data_source","../models/sources/geojson_data_source":"models/sources/geojson_data_source","../models/tickers/adaptive_ticker":"models/tickers/adaptive_ticker","../models/tickers/basic_ticker":"models/tickers/basic_ticker","../models/tickers/categorical_ticker":"models/tickers/categorical_ticker","../models/tickers/composite_ticker":"models/tickers/composite_ticker","../models/tickers/continuous_ticker":"models/tickers/continuous_ticker","../models/tickers/datetime_ticker":"models/tickers/datetime_ticker","../models/tickers/days_ticker":"models/tickers/days_ticker","../models/tickers/fixed_ticker":"models/tickers/fixed_ticker","../models/tickers/log_ticker":"models/tickers/log_ticker","../models/tickers/months_ticker":"models/tickers/months_ticker","../models/tickers/single_interval_ticker":"models/tickers/single_interval_ticker","../models/tickers/years_ticker":"models/tickers/years_ticker","../models/tiles/bbox_tile_source":"models/tiles/bbox_tile_source","../models/tiles/dynamic_image_renderer":"models/tiles/dynamic_image_renderer","../models/tiles/image_source":"models/tiles/image_source","../models/tiles/quadkey_tile_source":"models/tiles/quadkey_tile_source","../models/tiles/tile_renderer":"models/tiles/tile_renderer","../models/tiles/tile_source":"models/tiles/tile_source","../models/tiles/tms_tile_source":"models/tiles/tms_tile_source","../models/tiles/wmts_tile_source":"models/tiles/wmts_tile_source","../models/tools/actions/action_tool":"models/tools/actions/action_tool","../models/tools/actions/help_tool":"models/tools/actions/help_tool","../models/tools/actions/redo_tool":"models/tools/actions/redo_tool","../models/tools/actions/reset_tool":"models/tools/actions/reset_tool","../models/tools/actions/save_tool":"models/tools/actions/save_tool","../models/tools/actions/undo_tool":"models/tools/actions/undo_tool","../models/tools/button_tool":"models/tools/button_tool","../models/tools/gestures/box_select_tool":"models/tools/gestures/box_select_tool","../models/tools/gestures/box_zoom_tool":"models/tools/gestures/box_zoom_tool","../models/tools/gestures/gesture_tool":"models/tools/gestures/gesture_tool","../models/tools/gestures/lasso_select_tool":"models/tools/gestures/lasso_select_tool","../models/tools/gestures/pan_tool":"models/tools/gestures/pan_tool","../models/tools/gestures/poly_select_tool":"models/tools/gestures/poly_select_tool","../models/tools/gestures/resize_tool":"models/tools/gestures/resize_tool","../models/tools/gestures/select_tool":"models/tools/gestures/select_tool","../models/tools/gestures/tap_tool":"models/tools/gestures/tap_tool","../models/tools/gestures/wheel_zoom_tool":"models/tools/gestures/wheel_zoom_tool", +"../models/tools/inspectors/crosshair_tool":"models/tools/inspectors/crosshair_tool","../models/tools/inspectors/hover_tool":"models/tools/inspectors/hover_tool","../models/tools/inspectors/inspect_tool":"models/tools/inspectors/inspect_tool","../models/tools/toolbar":"models/tools/toolbar","../models/tools/toolbar_box":"models/tools/toolbar_box","../models/transforms/interpolator":"models/transforms/interpolator","../models/transforms/jitter":"models/transforms/jitter","../models/transforms/linear_interpolator":"models/transforms/linear_interpolator","../models/transforms/step_interpolator":"models/transforms/step_interpolator","../models/transforms/transform":"models/transforms/transform","./selection_manager":"common/selection_manager","./selector":"common/selector","./tool_events":"common/tool_events"}],"common/selection_manager":[function(t,e,r){var n,o,i,s,a,l,u,c=function(t,e){function r(){this.constructor=t}for(var n in e)h.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},h={}.hasOwnProperty;s=t("underscore"),n=t("../core/has_props"),l=t("../core/logging").logger,i=t("./selector"),a=t("./hittest"),u=t("../core/properties"),o=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return c(e,t),e.prototype.type="SelectionManager",e.internal({source:[u.Any]}),e.prototype.initialize=function(t,r){return e.__super__.initialize.call(this,t,r),this.selectors={},this.inspectors={},this.last_inspection_was_empty={}},e.prototype.select=function(t,e,r,n,o){var i,s,a;return null==o&&(o=!1),a=this.get("source"),a!==e.mget("data_source")&&l.warn("select called with mis-matched data sources"),i=e.hit_test(r),null!=i&&(s=this._get_selector(e),s.update(i,n,o),this.get("source").set({selected:s.get("indices")}),a.trigger("select"),a.trigger("select-"+e.mget("id")),!i.is_empty())},e.prototype.inspect=function(t,e,r,n){var o,i,s,a;if(a=this.get("source"),a!==e.mget("data_source")&&l.warn("inspect called with mis-matched data sources"),o=e.hit_test(r),null!=o){if(s=e.model.id,o.is_empty()){if(null==this.last_inspection_was_empty[s]&&(this.last_inspection_was_empty[s]=!1),this.last_inspection_was_empty[s])return;this.last_inspection_was_empty[s]=!0}else this.last_inspection_was_empty[s]=!1;return i=this._get_inspector(e),i.update(o,!0,!1,!0),this.get("source").set({inspected:i.get("indices")},{silent:!0}),a.trigger("inspect",o,t,e,a,n),a.trigger("inspect"+e.mget("id"),o,t,e,a,n),!o.is_empty()}return!1},e.prototype.clear=function(t){var e,r,n,o;if(null!=t)o=this._get_selector(t),o.clear();else{r=this.selectors;for(e in r)n=r[e],n.clear()}return this.get("source").set({selected:a.create_hit_test_result()})},e.prototype._get_selector=function(t){return s.setdefault(this.selectors,t.model.id,new i),this.selectors[t.model.id]},e.prototype._get_inspector=function(t){return s.setdefault(this.inspectors,t.model.id,new i),this.inspectors[t.model.id]},e}(n),e.exports=o},{"../core/has_props":"core/has_props","../core/logging":"core/logging","../core/properties":"core/properties","./hittest":"common/hittest","./selector":"common/selector",underscore:"underscore"}],"common/selector":[function(t,e,r){var n,o,i,s,a,l,u=function(t,e){function r(){this.constructor=t}for(var n in e)c.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},c={}.hasOwnProperty;i=t("underscore"),n=t("../core/has_props"),s=t("./hittest"),a=t("../core/logging").logger,l=t("../core/properties"),o=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return u(e,t),e.prototype.type="Selector",e.prototype.update=function(t,e,r,n){return null==n&&(n=!1),this.set("timestamp",new Date,{silent:n}),this.set("final",e,{silent:n}),r&&(t["0d"].indices=i.union(this.get("indices")["0d"].indices,t["0d"].indices),t["0d"].glyph=this.get("indices")["0d"].glyph||t["0d"].glyph,t["1d"].indices=i.union(this.get("indices")["1d"].indices,t["1d"].indices),t["2d"].indices=i.union(this.get("indices")["2d"].indices,t["2d"].indices)),this.set("indices",t,{silent:n})},e.prototype.clear=function(){return this.set("timestamp",new Date),this.set("final",!0),this.set("indices",s.create_hit_test_result())},e.internal({indices:[l.Any,function(){return s.create_hit_test_result()}],"final":[l.Boolean],timestamp:[l.Any]}),e}(n),e.exports=o},{"../core/has_props":"core/has_props","../core/logging":"core/logging","../core/properties":"core/properties","./hittest":"common/hittest",underscore:"underscore"}],"common/tool_events":[function(t,e,r){var n,o,i,s,a,l=function(t,e){function r(){this.constructor=t}for(var n in e)u.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},u={}.hasOwnProperty;i=t("underscore"),n=t("../model"),s=t("../core/logging").logger,a=t("../core/properties"),o=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return l(e,t),e.prototype.type="ToolEvents",e.define({geometries:[a.Array,[]]}),e}(n),e.exports={Model:o}},{"../core/logging":"core/logging","../core/properties":"core/properties","../model":"model",underscore:"underscore"}],"common/ui_events":[function(t,e,r){var n,o,i,s,a,l,u=function(t,e){function r(){this.constructor=t}for(var n in e)c.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},c={}.hasOwnProperty;n=t("jquery"),o=t("backbone"),i=t("hammerjs"),l=t("jquery-mousewheel")(n),a=t("../core/logging").logger,s=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return u(e,t),e.prototype.initialize=function(t,r){return e.__super__.initialize.call(this,t,r),this._hammer_element()},e.prototype._hammer_element=function(){var t;return t=this.get("hit_area"),this.hammer=new i(t[0]),this.hammer.get("doubletap").recognizeWith("tap"),this.hammer.get("tap").requireFailure("doubletap"),this.hammer.get("doubletap").dropRequireFailure("tap"),this.hammer.on("doubletap",function(t){return function(e){return t._doubletap(e)}}(this)),this.hammer.on("tap",function(t){return function(e){return t._tap(e)}}(this)),this.hammer.on("press",function(t){return function(e){return t._press(e)}}(this)),this.hammer.get("pan").set({direction:i.DIRECTION_ALL}),this.hammer.on("panstart",function(t){return function(e){return t._pan_start(e)}}(this)),this.hammer.on("pan",function(t){return function(e){return t._pan(e)}}(this)),this.hammer.on("panend",function(t){return function(e){return t._pan_end(e)}}(this)),this.hammer.get("pinch").set({enable:!0}),this.hammer.on("pinchstart",function(t){return function(e){return t._pinch_start(e)}}(this)),this.hammer.on("pinch",function(t){return function(e){return t._pinch(e)}}(this)),this.hammer.on("pinchend",function(t){return function(e){return t._pinch_end(e)}}(this)),this.hammer.get("rotate").set({enable:!0}),this.hammer.on("rotatestart",function(t){return function(e){return t._rotate_start(e)}}(this)),this.hammer.on("rotate",function(t){return function(e){return t._rotate(e)}}(this)),this.hammer.on("rotateend",function(t){return function(e){return t._rotate_end(e)}}(this)),t.mousemove(function(t){return function(e){return t._mouse_move(e)}}(this)),t.mouseenter(function(t){return function(e){return t._mouse_enter(e)}}(this)),t.mouseleave(function(t){return function(e){return t._mouse_exit(e)}}(this)),t.mousewheel(function(t){return function(e,r){return t._mouse_wheel(e,r)}}(this)),n(document).keydown(function(t){return function(e){return t._key_down(e)}}(this)),n(document).keyup(function(t){return function(e){return t._key_up(e)}}(this))},e.prototype.register_tool=function(t){var e,r,n;return e=t.model.event_type,r=t.model.id,n=t.model.type,null==e?void a.debug("Button tool: "+n):("pan"===e||"pinch"===e||"rotate"===e?(a.debug("Registering tool: "+n+" for event '"+e+"'"),null!=t["_"+e+"_start"]&&t.listenTo(this,e+":start:"+r,t["_"+e+"_start"]),t["_"+e]&&t.listenTo(this,e+":"+r,t["_"+e]),t["_"+e+"_end"]&&t.listenTo(this,e+":end:"+r,t["_"+e+"_end"])):"move"===e?(a.debug("Registering tool: "+n+" for event '"+e+"'"),null!=t._move_enter&&t.listenTo(this,"move:enter",t._move_enter),t.listenTo(this,"move",t._move),null!=t._move_exit&&t.listenTo(this,"move:exit",t._move_exit)):(a.debug("Registering tool: "+n+" for event '"+e+"'"),t.listenTo(this,e+":"+r,t["_"+e])),null!=t._keydown&&(a.debug("Registering tool: "+n+" for event 'keydown'"),t.listenTo(this,"keydown",t._keydown)),null!=t._keyup&&(a.debug("Registering tool: "+n+" for event 'keyup'"),t.listenTo(this,"keyup",t._keyup)),null!=t._doubletap&&(a.debug("Registering tool: "+n+" for event 'doubletap'"),t.listenTo(this,"doubletap",t._doubletap)),("ontouchstart"in window||navigator.maxTouchPoints>0)&&"pinch"===e?(a.debug("Registering scroll on touch screen"),t.listenTo(this,"scroll:"+r,t._scroll)):void 0)},e.prototype._trigger=function(t,e){var r,n,o,i;if(i=this.get("toolbar"),n=t.split(":")[0],("ontouchstart"in window||navigator.maxTouchPoints>0)&&"scroll"===t&&(n="pinch"),o=i.get("gestures"),r=o[n].active,null!=r)return this._trigger_event(t,r,e)},e.prototype._trigger_event=function(t,e,r){if(e.get("active")===!0)return"scroll"===t&&(r.preventDefault(),r.stopPropagation()),this.trigger(t+":"+e.id,r)},e.prototype._bokify_hammer=function(t){var e,r,o,i,s,a,l;return"mouse"===t.pointerType?(a=t.srcEvent.pageX,l=t.srcEvent.pageY):(a=t.pointers[0].pageX,l=t.pointers[0].pageY),r=n(t.target).offset(),e=null!=(o=r.left)?o:0,s=null!=(i=r.top)?i:0,t.bokeh={sx:a-e,sy:l-s}},e.prototype._bokify_jq=function(t){var e,r,o,i,s;return r=n(t.currentTarget).offset(),e=null!=(o=r.left)?o:0,s=null!=(i=r.top)?i:0,t.bokeh={sx:t.pageX-e,sy:t.pageY-s}},e.prototype._tap=function(t){return this._bokify_hammer(t),this._trigger("tap",t)},e.prototype._doubletap=function(t){return this._bokify_hammer(t),this.trigger("doubletap",t)},e.prototype._press=function(t){return this._bokify_hammer(t),this._trigger("press",t)},e.prototype._pan_start=function(t){return this._bokify_hammer(t),t.bokeh.sx-=t.deltaX,t.bokeh.sy-=t.deltaY,this._trigger("pan:start",t)},e.prototype._pan=function(t){return this._bokify_hammer(t),this._trigger("pan",t)},e.prototype._pan_end=function(t){return this._bokify_hammer(t),this._trigger("pan:end",t)},e.prototype._pinch_start=function(t){return this._bokify_hammer(t),this._trigger("pinch:start",t)},e.prototype._pinch=function(t){return this._bokify_hammer(t),this._trigger("pinch",t)},e.prototype._pinch_end=function(t){return this._bokify_hammer(t),this._trigger("pinch:end",t)},e.prototype._rotate_start=function(t){return this._bokify_hammer(t),this._trigger("rotate:start",t)},e.prototype._rotate=function(t){return this._bokify_hammer(t),this._trigger("rotate",t)},e.prototype._rotate_end=function(t){return this._bokify_hammer(t),this._trigger("rotate:end",t)},e.prototype._mouse_enter=function(t){return this._bokify_jq(t),this.trigger("move:enter",t)},e.prototype._mouse_move=function(t){return this._bokify_jq(t),this.trigger("move",t)},e.prototype._mouse_exit=function(t){return this._bokify_jq(t),this.trigger("move:exit",t)},e.prototype._mouse_wheel=function(t,e){return this._bokify_jq(t),t.bokeh.delta=e,this._trigger("scroll",t)},e.prototype._key_down=function(t){return this.trigger("keydown",t)},e.prototype._key_up=function(t){return this.trigger("keyup",t)},e}(o.Model),e.exports=s},{"../core/logging":"core/logging",backbone:"backbone",hammerjs:"hammerjs/hammer",jquery:"jquery","jquery-mousewheel":"jquery-mousewheel"}],"core/bokeh_view":[function(t,e,r){var n,o,i,s=function(t,e){function r(){this.constructor=t}for(var n in e)a.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},a={}.hasOwnProperty;i=t("underscore"),n=t("backbone"),o=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return s(e,t),e.prototype.initialize=function(t){if(!i.has(t,"id"))return this.id=i.uniqueId("BokehView")},e.prototype.bind_bokeh_events=function(){},e.prototype.remove=function(){var t,r,n;if(i.has(this,"eventers")){t=this.eventers;for(r in t)a.call(t,r)&&(n=t[r],n.off(null,null,this))}return this.trigger("remove",this),e.__super__.remove.call(this)},e.prototype.mget=function(){return this.model.get.apply(this.model,arguments)},e.prototype.mset=function(){return this.model.set.apply(this.model,arguments)},e}(n.View),e.exports=o},{backbone:"backbone",underscore:"underscore"}],"core/enums":[function(t,e,r){e.exports={AngleUnits:["deg","rad"],Dimension:["width","height"],Direction:["clock","anticlock"],FontStyle:["normal","italic","bold"],LineCap:["butt","round","square"],LineJoin:["miter","round","bevel"],Location:["above","below","left","right"],LegendLocation:["top_left","top_center","top_right","left_center","center","right_center","bottom_left","bottom_center","bottom_right"],Orientation:["vertical","horizontal"],RenderLevel:["image","underlay","glyph","annotation","overlay","tool"],RenderMode:["canvas","css"],Side:["left","right"],SpatialUnits:["screen","data"],StartEnd:["start","end"],TextAlign:["left","right","center"],TextBaseline:["top","middle","bottom","alphabetic","hanging","ideographic"],DistributionTypes:["uniform","normal"],TransformStepModes:["after","before","center"],SizingMode:["stretch_both","scale_width","scale_height","scale_both","fixed"]}},{}],"core/has_props":[function(t,e,r){var n,o,i,s,a,l,u,c=function(t,e){function r(){this.constructor=t}for(var n in e)h.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},h={}.hasOwnProperty,p=[].slice;n=t("jquery"),s=t("underscore"),o=t("backbone"),a=t("./logging").logger,l=t("./property_mixins"),u=t("./util/refs"),i=function(t){function e(t,e){var r,n,o,i,a,l;this.document=null,r=t||{},e||(e={}),this.cid=s.uniqueId("c"),this.attributes={},this.properties={},i=this.props;for(o in i){if(a=i[o],l=a.type,n=a.default_value,null==l)throw new Error("undefined property type for "+this.type+"."+o);this.properties[o]=new l({obj:this,attr:o,default_value:n})}e.parse&&(r=this.parse(r,e)||{}),this._set_after_defaults={},this.set(r,e),this.changed={},this._computed={},s.has(r,this.idAttribute)||(this.id=s.uniqueId(this.type),this.attributes[this.idAttribute]=this.id),e.defer_initialization||this.initialize.apply(this,arguments)}return c(e,t),e.prototype.props={},e.prototype.mixins=[],e.define=function(t){var e,r,n;n=[];for(e in t)r=t[e],n.push(function(t){return function(e,r){var n,o,i,a,l;if(null!=t.prototype.props[e])throw new Error("attempted to redefine property '"+t.name+"."+e+"'");if(null!=t.prototype[e]&&"url"!==e)throw new Error("attempted to redefine attribute '"+t.name+"."+e+"'");return Object.defineProperty(t.prototype,e,{get:function(){return this.get(e)},set:function(t){return this.set(e,t)}},{configurable:!1,enumerable:!0}),l=r[0],n=r[1],o=r[2],a={type:l,default_value:n,internal:null!=o&&o},i=s.clone(t.prototype.props),i[e]=a,t.prototype.props=i}}(this)(e,r));return n},e.internal=function(t){var e,r,n,o;e={},r=function(t){return function(t,r){var n,o;return o=r[0],n=r[1],e[t]=[o,n,!0]}}(this);for(n in t)o=t[n],r(n,o);return this.define(e)},e.mixin=function(){var t,e;return e=1<=arguments.length?p.call(arguments,0):[],this.define(l.create(e)),t=this.prototype.mixins.concat(e),this.prototype.mixins=t},e.mixins=function(t){return this.mixin.apply(this,t)},e.override=function(t,e){var r,n,o;s.isString(t)?(n={},n[r]=e):n=t,o=[];for(r in n)e=n[r],o.push(function(t){return function(e,r){var n,o;if(o=t.prototype.props[e],null==o)throw new Error("attempted to override nonexistent '"+t.name+"."+e+"'");return n=s.clone(t.prototype.props),n[e]=s.extend({},o,{default_value:r}),t.prototype.props=n}}(this)(r,e));return o},e.prototype.toString=function(){return this.type+"("+this.id+")"},e.prototype.destroy=function(t){return e.__super__.destroy.call(this,t),this.stopListening()},e.prototype.set=function(t,r,n){var o,i,a,l,u;s.isObject(t)||null===t?(o=t,n=r):(o={},o[t]=r);for(t in o)if(h.call(o,t)){if(u=o[t],a=t,"id"!==a&&!this.props[a])throw new Error(this.type+".set('"+a+"'): "+a+" wasn't declared");null!=n&&n.defaults||(this._set_after_defaults[t]=!0)}if(!s.isEmpty(o)){i={};for(t in o)r=o[t],i[t]=this.get(t);if(e.__super__.set.call(this,o,n),null==(null!=n?n.silent:void 0)){l=[];for(t in o)r=o[t],l.push(this._tell_document_about_change(t,i[t],this.get(t)));return l}}},e.prototype.add_dependencies=function(t,e,r){var n,o,i,a,l;for(s.isArray(r)||(r=[r]),a=this._computed[t],a.dependencies=a.dependencies.concat({obj:e,fields:r}),l=[],o=0,i=r.length;o0&&(n="middle",r=y[o]),t.textBaseline=n,t.textAlign=r,t},e.prototype.get_label_angle_heuristic=function(t){var e;return e=this.get("side"),v[e][t]},e}(c.Model),e.exports={Model:_,update_constraints:T}},{"../../core/logging":"core/logging","../../core/properties":"core/properties","./layout_canvas":"core/layout/layout_canvas","./solver":"core/layout/solver",underscore:"underscore"}],"core/layout/solver":[function(t,e,r){var n,o,i,s,a,l,u,c,h,p,_;c=t("underscore"),n=t("backbone"),_=t("kiwi"),u=_.Variable,i=_.Expression,o=_.Constraint,s=_.Operator,l=_.Strength,h=function(t){return function(e){return function(){var e;return e=Object.create(i.prototype),i.apply(e,arguments),new o(e,t)}}(this)},p=function(t){return function(){var e,r,n,s;for(r=[null],n=0,s=arguments.length;ne;r=0<=e?++t:--t)o.push(i);return o}();return null!=this.spec.transform&&(o=this.spec.transform.v_compute(o)),o},e.prototype._init=function(t){var e,r,n,o;if(null==t&&(t=!0),o=this.get("obj"),null==o)throw new Error("missing property object");if(null==o.properties)throw new Error("property object must be a HasProps");if(e=this.get("attr"),null==e)throw new Error("missing property attr");if(r=o.get(e),B.isUndefined(r)&&(n=this.get("default_value"),r=function(){switch(!1){case!B.isUndefined(n):return null;case!B.isArray(n):return B.clone(n);case!B.isFunction(n):return n(o);default:return n}}(),o.set(e,r,{silent:!0,defaults:!0})),B.isArray(r)?this.spec={value:r}:B.isObject(r)&&1===B.size(B.pick.apply(null,[r].concat(this.specifiers)))?this.spec=r:this.spec={ +value:r},null!=this.spec.field&&!B.isString(this.spec.field))throw new Error("field value for property '"+e+"' is not a string");if(null!=this.spec.value&&this.validate(this.spec.value),this.init(),t)return this.trigger("change")},e}(u.Model),V=function(t,e){var r;return r=function(r){function n(){return n.__super__.constructor.apply(this,arguments)}return $(n,r),n.prototype.toString=function(){return t+"(obj: "+this.get(obj).id+", spec: "+JSON.stringify(this.spec)+")"},n.prototype.validate=function(r){var n;if(!e(r))throw n=this.get("attr"),new Error(t+" property '"+n+"' given invalid value: "+r)},n}(A)},a=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Any",function(t){return!0})),l=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Array",function(t){return B.isArray(t)||t instanceof Float64Array})),c=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Bool",B.isBoolean)),h=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Color",function(t){return null!=U[t.toLowerCase()]||"#"===t.substring(0,1)||H(t)})),w=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Instance",function(t){return null!=t.properties})),S=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("Number",function(t){return B.isNumber(t)||B.isBoolean(t)})),q=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(V("String",B.isString)),v=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(q),L=function(t,e){var r;return r=function(e){function r(){return r.__super__.constructor.apply(this,arguments)}return $(r,e),r.prototype.toString=function(){return t+"(obj: "+this.get(obj).id+", spec: "+JSON.stringify(this.spec)+")"},r}(V(t,function(t){return W.call(e,t)>=0}))},n=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("Anchor",G.LegendLocation)),s=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("AngleUnits",G.AngleUnits)),d=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e.prototype.transform=function(t){var e,r,n,o;for(o=new Uint8Array(t.length),e=r=0,n=t.length;0<=n?rn;e=0<=n?++r:--r)switch(t[e]){case"clock":o[e]=!1;break;case"anticlock":o[e]=!0}return o},e}(L("Direction",G.Direction)),_=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("Dimension",G.Dimension)),x=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("FontStyle",G.FontStyle)),k=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("LineCap",G.LineCap)),T=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("LineJoin",G.LineJoin)),M=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("LegendLocation",G.LegendLocation)),j=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("Location",G.Location)),P=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("Orientation",G.Orientation)),R=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("TextAlign",G.TextAlign)),I=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("TextBaseline",G.TextBaseline)),E=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("RenderLevel",G.RenderLevel)),C=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("RenderMode",G.RenderMode)),O=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("SizingMode",G.SizingMode)),N=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("SpatialUnits",G.SpatialUnits)),y=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("Distribution",G.DistributionTypes)),F=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e}(L("TransformStepMode",G.TransformStepModes)),Y=function(t,e,r){var n;return n=function(n){function o(){return o.__super__.constructor.apply(this,arguments)}return $(o,n),o.prototype.toString=function(){return t+"(obj: "+this.get(obj).id+", spec: "+JSON.stringify(this.spec)+")"},o.prototype.init=function(){var n;if(null==this.spec.units&&(this.spec.units=r),this.units=this.spec.units,n=this.spec.units,W.call(e,n)<0)throw new Error(t+" units must be one of "+e+", given invalid value: "+n)},o}(S)},o=function(t){function e(){return e.__super__.constructor.apply(this,arguments)}return $(e,t),e.prototype.transform=function(t){var r;return"deg"===this.spec.units&&(t=function(){var e,n,o;for(o=[],e=0,n=t.length;e=0)throw new Error("color expects rgb to have value between 0 and 255");return!0},e.exports={color2hex:o,color2rgba:i,valid_rgb:a}},{"./svg_colors":"core/util/svg_colors"}],"core/util/data_structures":[function(t,e,r){var n,o,i;i=t("underscore"),n=function(){function t(){this._dict={}}return t.prototype._existing=function(t){return t in this._dict?this._dict[t]:null},t.prototype.add_value=function(t,e){var r;if(null===e)throw new Error("Can't put null in this dict");if(i.isArray(e))throw new Error("Can't put arrays in this dict");return r=this._existing(t),null===r?this._dict[t]=e:i.isArray(r)?r.push(e):this._dict[t]=[r,e]},t.prototype.remove_value=function(t,e){var r,n;return r=this._existing(t),i.isArray(r)?(n=i.without(r,e),n.length>0?this._dict[t]=n:delete this._dict[t]):i.isEqual(r,e)?delete this._dict[t]:void 0},t.prototype.get_one=function(t,e){var r;if(r=this._existing(t),i.isArray(r)){if(1===r.length)return r[0];throw new Error(e)}return r},t}(),o=function(){function t(e){if(e){if(e.constructor===t)return new t(e.values);e.constructor===Array?this.values=t.compact(e):this.values=[e]}else this.values=[]}return t.compact=function(t){var e,r,n,o;for(o=[],r=0,n=t.length;rr&&(r=n);return r},i=function(t){for(;t<0;)t+=2*Math.PI;for(;t>2*Math.PI;)t-=2*Math.PI;return t},o=function(t,e){return Math.abs(i(t-e))},n=function(t,e,r,n){var s;return t=i(t),s=o(e,r),"anticlock"===n?o(e,t)<=s&&o(t,r)<=s:!(o(e,t)<=s&&o(t,r)<=s)},u=function(){return Math.random()},l=function(t,e){return Math.atan2(e[1]-t[1],e[0]-t[0])},c=function(t,e){var r,n,o;for(r=null,n=null;;)if(r=u(),n=u(),n=(2*n-1)*Math.sqrt(2*(1/Math.E)),-4*r*r*Math.log(r)>=n*n)break;return o=n/r,o=t+e*o},e.exports={array_min:a,array_max:s,angle_norm:i,angle_dist:o,angle_between:n,atan2:l,rnorm:c,random:u}},{}],"core/util/refs":[function(t,e,r){var n,o,i,s,a;o=t("underscore"),n=t("../has_props"),s=function(t){var e;if(!(t instanceof n.constructor))throw new Error("can only create refs for HasProps subclasses");return e={type:t.type,id:t.id},null!=t._subtype&&(e.subtype=t._subtype),e},a=function(t){var e;if(o.isObject(t)){if(e=o.keys(t).sort(),2===e.length)return"id"===e[0]&&"type"===e[1];if(3===e.length)return"id"===e[0]&&"subtype"===e[1]&&"type"===e[2]}return!1},i=function(t){return o.isArray(t)?o.map(t,i):t instanceof n.constructor?t.ref():void 0},e.exports={convert_to_ref:i,create_ref:s,is_ref:a}},{"../has_props":"core/has_props",underscore:"underscore"}],"core/util/svg_colors":[function(t,e,r){e.exports={indianred:"#CD5C5C",lightcoral:"#F08080",salmon:"#FA8072",darksalmon:"#E9967A",lightsalmon:"#FFA07A",crimson:"#DC143C",red:"#FF0000",firebrick:"#B22222",darkred:"#8B0000",pink:"#FFC0CB",lightpink:"#FFB6C1",hotpink:"#FF69B4",deeppink:"#FF1493",mediumvioletred:"#C71585",palevioletred:"#DB7093",coral:"#FF7F50",tomato:"#FF6347",orangered:"#FF4500",darkorange:"#FF8C00",orange:"#FFA500",gold:"#FFD700",yellow:"#FFFF00",lightyellow:"#FFFFE0",lemonchiffon:"#FFFACD",lightgoldenrodyellow:"#FAFAD2",papayawhip:"#FFEFD5",moccasin:"#FFE4B5",peachpuff:"#FFDAB9",palegoldenrod:"#EEE8AA",khaki:"#F0E68C",darkkhaki:"#BDB76B",lavender:"#E6E6FA",thistle:"#D8BFD8",plum:"#DDA0DD",violet:"#EE82EE",orchid:"#DA70D6",fuchsia:"#FF00FF",magenta:"#FF00FF",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",blueviolet:"#8A2BE2",darkviolet:"#9400D3",darkorchid:"#9932CC",darkmagenta:"#8B008B",purple:"#800080",indigo:"#4B0082",slateblue:"#6A5ACD",darkslateblue:"#483D8B",mediumslateblue:"#7B68EE",greenyellow:"#ADFF2F",chartreuse:"#7FFF00",lawngreen:"#7CFC00",lime:"#00FF00",limegreen:"#32CD32",palegreen:"#98FB98",lightgreen:"#90EE90",mediumspringgreen:"#00FA9A",springgreen:"#00FF7F",mediumseagreen:"#3CB371",seagreen:"#2E8B57",forestgreen:"#228B22",green:"#008000",darkgreen:"#006400",yellowgreen:"#9ACD32",olivedrab:"#6B8E23",olive:"#808000",darkolivegreen:"#556B2F",mediumaquamarine:"#66CDAA",darkseagreen:"#8FBC8F",lightseagreen:"#20B2AA",darkcyan:"#008B8B",teal:"#008080",aqua:"#00FFFF",cyan:"#00FFFF",lightcyan:"#E0FFFF",paleturquoise:"#AFEEEE",aquamarine:"#7FFFD4",turquoise:"#40E0D0",mediumturquoise:"#48D1CC",darkturquoise:"#00CED1",cadetblue:"#5F9EA0",steelblue:"#4682B4",lightsteelblue:"#B0C4DE",powderblue:"#B0E0E6",lightblue:"#ADD8E6",skyblue:"#87CEEB",lightskyblue:"#87CEFA",deepskyblue:"#00BFFF",dodgerblue:"#1E90FF",cornflowerblue:"#6495ED",royalblue:"#4169E1",blue:"#0000FF",mediumblue:"#0000CD",darkblue:"#00008B",navy:"#000080",midnightblue:"#191970",cornsilk:"#FFF8DC",blanchedalmond:"#FFEBCD",bisque:"#FFE4C4",navajowhite:"#FFDEAD",wheat:"#F5DEB3",burlywood:"#DEB887",tan:"#D2B48C",rosybrown:"#BC8F8F",sandybrown:"#F4A460",goldenrod:"#DAA520",darkgoldenrod:"#B8860B",peru:"#CD853F",chocolate:"#D2691E",saddlebrown:"#8B4513",sienna:"#A0522D",brown:"#A52A2A",maroon:"#800000",white:"#FFFFFF",snow:"#FFFAFA",honeydew:"#F0FFF0",mintcream:"#F5FFFA",azure:"#F0FFFF",aliceblue:"#F0F8FF",ghostwhite:"#F8F8FF",whitesmoke:"#F5F5F5",seashell:"#FFF5EE",beige:"#F5F5DC",oldlace:"#FDF5E6",floralwhite:"#FFFAF0",ivory:"#FFFFF0",antiquewhite:"#FAEBD7",linen:"#FAF0E6",lavenderblush:"#FFF0F5",mistyrose:"#FFE4E1",gainsboro:"#DCDCDC",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",silver:"#C0C0C0",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",gray:"#808080",grey:"#808080",dimgray:"#696969",dimgrey:"#696969",lightslategray:"#778899",lightslategrey:"#778899",slategray:"#708090",slategrey:"#708090",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",black:"#000000"}},{}],"core/util/text":[function(t,e,r){var n,o,i;n=t("jquery"),o={},i=function(t){var e,r,i,s,a;if(null!=o[t])return o[t];a=n("Hg").css({font:t}),e=n('
    '),i=n("
    "),i.append(a,e),r=n("body"),r.append(i);try{s={},e.css({verticalAlign:"baseline"}),s.ascent=e.offset().top-a.offset().top,e.css({verticalAlign:"bottom"}),s.height=e.offset().top-a.offset().top,s.descent=s.height-s.ascent}finally{i.remove()}return o[t]=s,s},e.exports={get_text_height:i}},{jquery:"jquery"}],"core/util/throttle":[function(t,e,r){var n,o,i;n=function(t){return t()},o=("undefined"!=typeof window&&null!==window?window.requestAnimationFrame:void 0)||("undefined"!=typeof window&&null!==window?window.mozRequestAnimationFrame:void 0)||("undefined"!=typeof window&&null!==window?window.webkitRequestAnimationFrame:void 0)||("undefined"!=typeof window&&null!==window?window.msRequestAnimationFrame:void 0)||n,i=function(t,e){var r,n,i,s,a,l,u,c;return l=[null,null,null,null],n=l[0],r=l[1],c=l[2],u=l[3],a=0,s=!1,i=function(){return a=new Date,c=null,s=!1,u=t.apply(n,r)},function(){var t,l;return t=new Date,l=e-(t-a),n=this,r=arguments,l<=0&&!s?(clearTimeout(c),s=!0,o(i)):c||s||(c=setTimeout(function(){return o(i)},l)),u}},e.exports={throttle:i}},{}],"core/util/underscore":[function(t,e,r){var n,o;n=t("underscore"),o=function(){return n.uniqueId=function(t){var e,r,n,o,i;for(o=[],e="0123456789ABCDEF",r=n=0;n<=31;r=++n)o[r]=e.substr(Math.floor(16*Math.random()),1);return o[12]="4",o[16]=e.substr(3&o[16]|8,1),i=o.join(""),t?t+"-"+i:i}},n.isNullOrUndefined=function(t){return n.isNull(t)||n.isUndefined(t)},n.setdefault=function(t,e,r){return n.has(t,e)?t[e]:(t[e]=r,r)},e.exports={patch:o}},{underscore:"underscore"}],document:[function(t,e,r){var n,o,i,s,a,l,u,c,h,p,_,d,f,m,g,y,v,b,x,w,M,k,T=function(t,e){function r(){this.constructor=t}for(var n in e)j.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},j={}.hasOwnProperty,S=[].indexOf||function(t){for(var e=0,r=this.length;e0;)t.push(this.remove_root(this._roots[0]));return t}finally{this._pop_all_models_freeze()}},t.prototype._destructively_move=function(t){var e,r,n,o,i,s;if(t===this)throw new Error("Attempted to overwrite a document with itself");t.clear(),s=[],this._push_all_models_freeze();try{for(;this._roots.length>0;)this.remove_root(this._roots[0]),s.push(i)}finally{this._pop_all_models_freeze()}for(e=0,n=s.length;e=0)){this._push_all_models_freeze();try{this._roots.push(t),t._is_root=!0}finally{this._pop_all_models_freeze()}return this._init_solver(),this._trigger_on_change(new _(this,t))}},t.prototype.remove_root=function(t){var e;if(e=this._roots.indexOf(t),!(e<0)){this._push_all_models_freeze();try{this._roots.splice(e,1),t._is_root=!1}finally{this._pop_all_models_freeze()}return this._init_solver(),this._trigger_on_change(new d(this,t))}},t.prototype.title=function(){return this._title},t.prototype.set_title=function(t){if(t!==this._title)return this._title=t,this._trigger_on_change(new g(this,t))},t.prototype.get_model_by_id=function(t){return t in this._all_models?this._all_models[t]:null},t.prototype.get_model_by_name=function(t){return this._all_models_by_name.get_one(t,"Multiple models are named '"+t+"'")},t.prototype.on_change=function(t){if(!(S.call(this._callbacks,t)>=0))return this._callbacks.push(t)},t.prototype.remove_on_change=function(t){var e;if(e=this._callbacks.indexOf(t),e>=0)return this._callbacks.splice(e,1)},t.prototype._trigger_on_change=function(t){var e,r,n,o,i;for(o=this._callbacks,i=[],r=0,n=o.length;r0||v.difference(w,i).length>0)throw new Error("Not implemented: computing add/remove of document roots");T={},n=[],g=r._all_models;for(a in g)_=g[a],a in o&&(k=t._events_to_sync_objects(o[a],x[a],r,T),n=n.concat(k));return{events:n,references:t._references_json(v.values(T),l=!1)}},t.prototype.to_json_string=function(t){return null==t&&(t=!0),JSON.stringify(this.to_json(t))},t.prototype.to_json=function(e){var r,n,o,i,s,a;for(null==e&&(e=!0),s=[],i=this._roots,r=0,n=i.length;r"),n("body").append(e)},S=function(t){var e;return e=n("