diff --git a/docker-compose.yml b/docker-compose.yml index 57e9cb9..8a2586c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,12 +18,12 @@ services: - 15672:15672 app: - # build: - # context: . - image: maxwelldps/trunk-player:latest + build: + context: . + #image: maxwelldps/trunk-player:latest ports: - "8080:8080" - - "" + - "8443:8443" volumes: #- .:/app/trunkplayer - audio:/app/trunkplayer/audio_files diff --git a/radio/consumers.py b/radio/consumers.py index d94556e..269f1ae 100644 --- a/radio/consumers.py +++ b/radio/consumers.py @@ -1,9 +1,11 @@ +from os import system import re import json import logging from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer -from .models import ScanList, TalkGroup +from .models import ScanList, TalkGroup, System + logging.basicConfig(format='%(asctime)s %(message)s') log = logging.getLogger(__name__) @@ -73,4 +75,4 @@ def radio_message(self, event): message = event['text'] # Send message to WebSocket - self.send(text_data=(message)) \ No newline at end of file + self.send(text_data=(message)) diff --git a/radio/migrations/0004_system_recorder_uuid.py b/radio/migrations/0004_system_recorder_uuid.py new file mode 100644 index 0000000..26fa10b --- /dev/null +++ b/radio/migrations/0004_system_recorder_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.7 on 2021-09-19 16:52 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('radio', '0003_defaultagenecy'), + ] + + operations = [ + migrations.AddField( + model_name='system', + name='recorder_uuid', + field=models.UUIDField(default=uuid.uuid4), + ), + ] diff --git a/radio/models.py b/radio/models.py index 46a99d8..520a74d 100644 --- a/radio/models.py +++ b/radio/models.py @@ -55,6 +55,7 @@ def save(self, *args, **kwargs): class System(models.Model): name = models.CharField(max_length=100, db_index=True, unique=True) system_id = models.CharField(max_length=20, blank=True, null=True) + recorder_uuid = models.UUIDField(default=uuid.uuid4) def __str__(self): return self.name diff --git a/telemetry/__init__.py b/telemetry/__init__.py new file mode 100644 index 0000000..90c81ee --- /dev/null +++ b/telemetry/__init__.py @@ -0,0 +1,20 @@ +import logging + +from django.utils.version import get_version +from subprocess import check_output, CalledProcessError + +logger = logging.getLogger(__name__) + + +VERSION = (0, 0, 7, 'beta', 1) + +__version__ = get_version(VERSION) + +try: + __git_hash__ = check_output(['git', 'rev-parse', '--short', 'HEAD']).strip().decode() +except (FileNotFoundError, CalledProcessError): + __git_hash__ = '0' + +__fullversion__ = '{} #{}'.format(__version__,__git_hash__) + +logger.info('Trunk-Player Version ' + __fullversion__) diff --git a/telemetry/admin.py b/telemetry/admin.py new file mode 100644 index 0000000..fd60404 --- /dev/null +++ b/telemetry/admin.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from django import forms +from django.contrib.admin.widgets import FilteredSelectMultiple +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from django.contrib.auth.models import User + +from .models import * + +admin.site.register(Call) +admin.site.register(Freq) +admin.site.register(Source) +admin.site.register(SystemStatus) \ No newline at end of file diff --git a/telemetry/apps.py b/telemetry/apps.py new file mode 100644 index 0000000..5aed839 --- /dev/null +++ b/telemetry/apps.py @@ -0,0 +1,9 @@ +from importlib import import_module +from django.db.models.signals import post_migrate + +from django.apps import AppConfig + +class TelemetryConfig(AppConfig): + name = 'telemetry' + + diff --git a/telemetry/consumers.py b/telemetry/consumers.py new file mode 100644 index 0000000..7671636 --- /dev/null +++ b/telemetry/consumers.py @@ -0,0 +1,60 @@ +from os import system +import re +import json +import logging +from asgiref.sync import async_to_sync +from channels.generic.websocket import WebsocketConsumer +from radio.models import System +from .telemetry import handleMessage + +logging.basicConfig(format='%(asctime)s %(message)s') +log = logging.getLogger(__name__) + +class TelemetryConsumer(WebsocketConsumer): + def connect(self): + try: + recorder_uuid = self.scope['url_route']["kwargs"]["recorder_uuid"] + except: + # setup fake channel so the javascript does not try and reconnect + recorder_uuid = None + self.close() + + # log.error('user %s connect %s=%s client=%s:%s', + # message.user, tg_type, label, message['client'][0], message['client'][1]) + + + self.accept() + self.recorder_uuid=recorder_uuid + + def disconnect(self, close_code): + try: + async_to_sync(self.channel_layer.group_discard)( + self.recorder_uuid, + self.channel_name + ) + except (KeyError, System.DoesNotExist): + pass + + + # Leave room group + + + # Receive message from WebSocket + def receive(self, text_data): + message = json.loads(text_data) + tx_type = message["type"] + try: + System.objects.get(recorder_uuid=self.recorder_uuid) + except KeyError: + log.error('no valid system found') + return + + + # conform to the expected message format. + #try: + log.error(f"Got {tx_type} Message") + handleMessage(message, tx_type, self.recorder_uuid) + #except ValueError: + # log.error("ws message isn't json text=%s", text_data) + # return + diff --git a/telemetry/management/__init__.py b/telemetry/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/telemetry/management/commands/__init__.py b/telemetry/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/telemetry/migrations/0001_initial.py b/telemetry/migrations/0001_initial.py new file mode 100644 index 0000000..9b41754 --- /dev/null +++ b/telemetry/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# Generated by Django 3.2.7 on 2021-09-19 19:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('radio', '0004_system_recorder_uuid'), + ] + + operations = [ + migrations.CreateModel( + name='Call', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('call_id', models.CharField(max_length=255, unique=True)), + ('freq', models.IntegerField()), + ('sysNum', models.IntegerField()), + ('shortName', models.CharField(max_length=255)), + ('talkgroup', models.IntegerField()), + ('talkgrouptag', models.CharField(max_length=255)), + ('elasped', models.IntegerField()), + ('length', models.IntegerField()), + ('state', models.IntegerField()), + ('recNum', models.IntegerField()), + ('srcNum', models.IntegerField()), + ('recState', models.IntegerField()), + ('sigmffilename', models.TextField()), + ('debugfilename', models.TextField()), + ('filename', models.TextField()), + ('statusfilename', models.TextField()), + ('startTime', models.DateTimeField()), + ('stopTime', models.DateTimeField()), + ('phase2', models.BooleanField(default=False)), + ('conventional', models.BooleanField(default=False)), + ('encrypted', models.BooleanField(default=False)), + ('emergency', models.BooleanField(default=False)), + ('analog', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Freq', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('freq', models.IntegerField()), + ('spikes', models.IntegerField()), + ('time', models.DateTimeField()), + ('errors', models.IntegerField()), + ('position', models.IntegerField()), + ('length', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='Source', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('source', models.IntegerField()), + ('position', models.IntegerField()), + ('time', models.DateTimeField()), + ('signal_system', models.CharField(max_length=50)), + ('emergency', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='SystemStatus', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('decoderate', models.IntegerField(blank=True, null=True)), + ('activeCalls', models.ManyToManyField(to='telemetry.Call')), + ('system', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='radio.system', unique=True)), + ], + ), + migrations.AddField( + model_name='call', + name='freqList', + field=models.ManyToManyField(to='telemetry.Freq'), + ), + migrations.AddField( + model_name='call', + name='sourceList', + field=models.ManyToManyField(to='telemetry.Source'), + ), + ] diff --git a/telemetry/migrations/0002_auto_20210919_1300.py b/telemetry/migrations/0002_auto_20210919_1300.py new file mode 100644 index 0000000..5e6ffb5 --- /dev/null +++ b/telemetry/migrations/0002_auto_20210919_1300.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.7 on 2021-09-19 20:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('telemetry', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='source', + name='position', + ), + migrations.RemoveField( + model_name='source', + name='time', + ), + ] diff --git a/telemetry/migrations/0003_auto_20210919_1305.py b/telemetry/migrations/0003_auto_20210919_1305.py new file mode 100644 index 0000000..9a17ed9 --- /dev/null +++ b/telemetry/migrations/0003_auto_20210919_1305.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.7 on 2021-09-19 20:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('telemetry', '0002_auto_20210919_1300'), + ] + + operations = [ + migrations.RemoveField( + model_name='freq', + name='errors', + ), + migrations.RemoveField( + model_name='freq', + name='length', + ), + migrations.RemoveField( + model_name='freq', + name='position', + ), + migrations.RemoveField( + model_name='freq', + name='spikes', + ), + migrations.RemoveField( + model_name='freq', + name='time', + ), + ] diff --git a/telemetry/migrations/__init__.py b/telemetry/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/telemetry/models.py b/telemetry/models.py new file mode 100644 index 0000000..7b92302 --- /dev/null +++ b/telemetry/models.py @@ -0,0 +1,79 @@ +import json +import logging +import uuid +import urllib.parse + +from django.db import models +from datetime import timedelta +from django.db.models.deletion import CASCADE +from django.db.models.fields import DateField +from django.utils import timezone +from django.utils.text import slugify +from django.conf import settings +from asgiref.sync import async_to_sync +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.contrib.auth.models import User +from django.core.mail import send_mail +from django.db.utils import OperationalError + +from radio.models import System + +log = logging.getLogger(__name__) + + +class Source(models.Model): + source = models.IntegerField() + signal_system = models.CharField(max_length=50) + emergency = models.BooleanField(default=False) + + def __str__(self): + return f"[{self.signal_system}] {str(self.source)}" + +class Freq(models.Model): + freq = models.IntegerField() + + def __str__(self): + return str(self.freq/1000000) + +class Call(models.Model): + call_id = models.CharField(unique=True, max_length=255) + freq = models.IntegerField() + sysNum = models.IntegerField() + shortName = models.CharField(max_length=255) + talkgroup = models.IntegerField() + talkgrouptag = models.CharField(max_length=255) + elasped = models.IntegerField() + length = models.IntegerField() + state = models.IntegerField() + recNum = models.IntegerField() + srcNum = models.IntegerField() + recState = models.IntegerField() + sigmffilename = models.TextField() + debugfilename = models.TextField() + filename = models.TextField() + statusfilename = models.TextField() + startTime = models.DateTimeField() + stopTime = models.DateTimeField() + phase2 = models.BooleanField(default=False) + conventional = models.BooleanField(default=False) + encrypted = models.BooleanField(default=False) + emergency = models.BooleanField(default=False) + analog = models.BooleanField(default=False) + + + sourceList = models.ManyToManyField(Source) + freqList = models.ManyToManyField(Freq) + + def __str__(self): + return f"[{self.call_id}] {self.talkgrouptag}" + +class SystemStatus(models.Model): + system = models.ForeignKey(System, unique=True, on_delete=CASCADE) + + activeCalls = models.ManyToManyField(Call) + decoderate = models.IntegerField(null=True, blank=True) + + + def __str__(self): + return str(self.system) diff --git a/telemetry/serializers.py b/telemetry/serializers.py new file mode 100644 index 0000000..bc8e340 --- /dev/null +++ b/telemetry/serializers.py @@ -0,0 +1,51 @@ +from django.contrib.auth.models import User, Group +from rest_framework import serializers +from .models import Transmission, TalkGroup, Unit, ScanList, MenuScanList, MenuTalkGroupList, MessagePopUp +from rest_framework.fields import CurrentUserDefault, SerializerMethodField + + +# class TalkGroupSerializer(serializers.HyperlinkedModelSerializer): +# url = serializers.HyperlinkedIdentityField(view_name="talkgroups-detail") +# class Meta: +# model = TalkGroup +# fields = ('url', 'dec_id', 'alpha_tag', 'description', 'slug') + +# class UnitListField(serializers.RelatedField): + +# def to_representation(self, value): +# return { "pk": value.pk, "dec_id": value.dec_id, "description": value.description } + +# class TransmissionSerializer(serializers.ModelSerializer): +# talkgroup_info = TalkGroupSerializer() +# audio_file = SerializerMethodField() +# units = UnitListField(many=True, read_only=True) + +# class Meta: +# model = Transmission +# fields = ('pk', 'url', 'start_datetime', 'local_start_datetime', 'audio_file', 'talkgroup', 'talkgroup_info', 'freq', 'emergency', 'units', 'play_length', 'print_play_length', 'slug', 'freq_mhz', 'tg_name', 'source', 'audio_url', 'system', 'audio_file_type') + +# def get_audio_file(self, obj): +# return obj.audio_file_history_check(self.context.get('request').user) + +# class ScanListSerializer(serializers.HyperlinkedModelSerializer): +# class Meta: +# model = ScanList +# fields = ('pk', 'name', 'description', 'slug') + +# class MenuScanListSerializer(serializers.ModelSerializer): + +# class Meta: +# model = MenuScanList +# fields = ('pk', 'name', 'scan_name', 'scan_description', 'scan_slug') + +# class MenuTalkGroupListSerializer(serializers.ModelSerializer): + +# class Meta: +# model = MenuTalkGroupList +# fields = ('pk', 'name', 'tg_name', 'tg_slug') + +# class MessageSerializer(serializers.ModelSerializer): + +# class Meta: +# model = MessagePopUp +# fields = ('pk', 'mesg_type', 'mesg_html' ) diff --git a/telemetry/telemetry.py b/telemetry/telemetry.py new file mode 100644 index 0000000..393f2cc --- /dev/null +++ b/telemetry/telemetry.py @@ -0,0 +1,203 @@ +from datetime import datetime +from radio.models import System +from telemetry.models import Call, Freq, Source, SystemStatus + +class source: + def __init__(self, json): + self.json = json + self.source = int(json["source"]) + self.signal_system = json["signal_system"] + + if json["emergency"] == "false": + self.emergency = False + else: + self.emergency = True + +class freq: + def __init__(self, json): + self.json = json + self.freq = int(float(json["freq"])) + +class call: + def __init__(self, json): + self.json = json + + self.id = json["id"] + self.freq = int(json["freq"]) + self.sysNum = int(json["sysNum"]) + self.shortName = json["shortName"] + self.talkgroup = int(json["talkgroup"]) + self.talkgrouptag = json["talkgrouptag"] + self.elasped = int(json["elasped"]) + self.length = int(float(json["length"])) + self.state = int(json["state"]) + self.recNum = int(json["recNum"]) + self.srcNum = int(json["srcNum"]) + self.recState = int(json["recState"]) + self.sigmffilename = json["sigmffilename"] + self.debugfilename = json["debugfilename"] + self.filename = json["filename"] + self.statusfilename = json["statusfilename"] + self.startTime = datetime.fromtimestamp(int(json["startTime"])) + self.stopTime = datetime.fromtimestamp(int(json["stopTime"])) + + + if json["phase2"] == "false": + self.phase2 = False + else: + self.phase2 = True + + if json["conventional"] == "false": + self.conventional = False + else: + self.conventional = True + + if json["encrypted"] == "false": + self.encrypted = False + else: + self.encrypted = True + + if json["emergency"] == "false": + self.emergency = False + else: + self.emergency = True + + if json["analog"] == "false": + self.analog = False + else: + self.analog = True + + self.sourceList = [] + for src in json["sourceList"]: + self.sourceList.append(source(src)) + + self.freqList = [] + for freqx in json["freqList"]: + self.freqList.append(freq(freqx)) + +def get_or_create_Source(sourcex:source): + if Source.objects.filter(source = sourcex.source, + signal_system = sourcex.signal_system, + emergency = sourcex.emergency): + return Source.objects.get(source = sourcex.source, + signal_system = sourcex.signal_system, + emergency = sourcex.emergency) + else: + sourceX = Source( + source = sourcex.source, + signal_system = sourcex.signal_system, + emergency = sourcex.emergency + ) + sourceX.save() + return sourceX + +def get_or_create_Freq(freqx:freq): + if Freq.objects.filter(freq = freqx.freq): + return Freq.objects.get(freq = freqx.freq) + else: + FreqX = Freq(freq = freqx.freq) + FreqX.save() + return FreqX + +def createCall(Callx:call): + callObject = Call( + call_id = Callx.id, + freq = Callx.freq, + sysNum = Callx.sysNum, + shortName = Callx.shortName, + talkgroup = Callx.talkgroup, + talkgrouptag = Callx.talkgrouptag, + elasped = Callx.elasped, + length = Callx.length, + state = Callx.state, + recNum = Callx.recNum, + srcNum = Callx.srcNum, + recState = Callx.recState, + sigmffilename = Callx.sigmffilename, + debugfilename = Callx.debugfilename, + filename = Callx.filename, + statusfilename = Callx.statusfilename, + startTime = Callx.startTime, + stopTime = Callx.stopTime, + phase2 = Callx.phase2, + conventional = Callx.conventional, + encrypted = Callx.encrypted, + emergency = Callx.emergency, + analog = Callx.analog + ) + callObject.save() + + for sourcex in Callx.sourceList: + sourcex:source + callObject.sourceList.add( + get_or_create_Source(sourcex) + ) + callObject.save() + + for freqx in Callx.freqList: + freqx:freq + callObject.freqList.add( + get_or_create_Freq(freqx) + ) + callObject.save() + return callObject + +def endCall(Callx:call): + callObject:Call = Call.objects.get(call_id=Callx.id) + callObject.stopTime = Callx.stopTime + + callObject.sourceList.clear() + callObject.freqList.clear() + + for sourcex in Callx.sourceList: + sourcex:source + callObject.sourceList.add( + get_or_create_Source(sourcex) + ) + callObject.save() + + for freqx in Callx.freqList: + freqx:freq + callObject.freqList.add( + get_or_create_Freq(freqx) + ) + callObject.save() + return callObject + +def GetSystemStatus(system:System): + if SystemStatus.objects.filter(system=system): + return SystemStatus.objects.get(system=system) + else: + systemX = SystemStatus(system=system) + systemX.save() + return systemX + +def handleMessage(message, type, uuid): + system = System.objects.get(recorder_uuid=uuid) + SystemObject = GetSystemStatus(system) + + if type == "call_start": + callX = call(message["call"]) + SystemObject.activeCalls.add(createCall(callX)) + elif type == "call_end": + callX = call(message["call"]) + SystemObject.activeCalls.remove(endCall(callX)) + elif type == "calls_active": + callIDs = [] + for callX in message["calls"]: + callIDs.append(callX["id"]) + for active in SystemObject.activeCalls.all(): + if not active.call_id in callIDs: + SystemObject.activeCalls.remove(active) + elif type == "rates": + if len(message["rates"]) == 1: + SystemObject.decoderate = int(float(message["rates"][0]["decoderate"])) + + else: + for rate in message["rates"]: + try: + system = System.objects.get(system_id=rate["id"]) + SystemObject = GetSystemStatus(system) + SystemObject.decoderate = int(float(rate["decoderate"])) + except: pass + SystemObject.save() \ No newline at end of file diff --git a/telemetry/utility.py b/telemetry/utility.py new file mode 100644 index 0000000..642a59a --- /dev/null +++ b/telemetry/utility.py @@ -0,0 +1,38 @@ +import redis + +class RedisQueue(object): + """Simple Queue with Redis Backend""" + def __init__(self, name, namespace='tp', **redis_kwargs): + """The default connection parameters are: host='localhost', port=6379, db=0""" + self.__db= redis.Redis(**redis_kwargs) + self.key = '%s:%s' %(namespace, name) + + def qsize(self): + """Return the approximate size of the queue.""" + return self.__db.llen(self.key) + + def empty(self): + """Return True if the queue is empty, False otherwise.""" + return self.qsize() == 0 + + def put(self, item): + """Put item into the queue.""" + self.__db.rpush(self.key, item) + + def get(self, block=True, timeout=None): + """Remove and return an item from the queue. + + If optional args block is true and timeout is None (the default), block + if necessary until an item is available.""" + if block: + item = self.__db.blpop(self.key, timeout=timeout) + else: + item = self.__db.lpop(self.key) + + if item: + item = item[1] + return item + + def get_nowait(self): + """Equivalent to get(False).""" + return self.get(False) diff --git a/telemetry/views.py b/telemetry/views.py new file mode 100644 index 0000000..f5fb173 --- /dev/null +++ b/telemetry/views.py @@ -0,0 +1,573 @@ +#import functools +import sys +import re +import json +import pytz +from itertools import chain +from django.shortcuts import render, get_object_or_404, redirect +from django.http import Http404 +from django.views.generic import ListView +from django.db.models import Q +from django.views.decorators.csrf import csrf_protect, csrf_exempt +from django.contrib.auth.decorators import login_required +from django.http import HttpResponseRedirect, HttpResponse +from django.template import RequestContext +from django.contrib.auth import authenticate, login +from django.conf import settings +from django.views.generic import ListView, UpdateView +from django.views.generic.detail import DetailView +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.core.exceptions import ImproperlyConfigured +from .models import * +from rest_framework import viewsets, generics +from .serializers import TransmissionSerializer, TalkGroupSerializer, ScanListSerializer, MenuScanListSerializer, MenuTalkGroupListSerializer, MessageSerializer +from datetime import datetime, timedelta +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from django.core.exceptions import ObjectDoesNotExist +from django.core.mail import mail_admins + + +from pprint import pprint +from django.contrib import messages +import logging + +from .forms import * + +logger = logging.getLogger(__name__) + + +# def check_anonymous(decorator): +# """ +# Decarator used to see if we allow anonymous access +# """ +# anonymous = getattr(settings, 'ALLOW_ANONYMOUS', True) +# return decorator if not anonymous else lambda x: x + + +# @login_required +# def userScanList(request): +# template = 'radio/userscanlist.html' +# if request.method == "POST": +# form = UserScanForm(request.POST) +# if form.is_valid(): +# print('Form Valid') +# name = form.cleaned_data['name'] +# tgs = form.cleaned_data['talkgroups'] +# print('Form Data [{}] [{}]'.format(name, tgs)) +# sl = ScanList() +# sl.created_by = request.user +# sl.name = name +# sl.description = name +# sl.save() +# sl.talkgroups.add(*tgs) +# return redirect('user_profile') +# else: +# print('Form not Valid') +# else: +# form = UserScanForm() +# return render(request, template, {'form': form}) + +# @login_required +# def userProfile(request): +# template = 'radio/profile.html' +# if request.method == "POST": +# form = UserForm(request.POST, instance=request.user) +# if form.is_valid(): +# form.save() +# return redirect('user_profile') +# else: +# profile_form = UserForm(instance=request.user) +# profile = Profile.objects.get(user=request.user) +# scan_lists = ScanList.objects.filter(created_by=request.user) +# return render(request, template, {'profile_form': profile_form, 'profile': profile, 'scan_lists': scan_lists} ) + +# def agencyList(request): +# template = 'radio/agency_list.html' +# query_data = Agency.objects.exclude(short='_DEF_').order_by('name') + +# return render(request, template, {'agency': query_data}) + + +# def cityListView(request): +# template = 'radio/city_list.html' +# query_data = City.objects.filter(visible=True) + +# return render(request, template, {'cities': query_data}) + + +# def cityDetailView(request, slug): +# template = 'radio/city_detail.html' +# query_data = City.objects.get(slug=slug) + +# return render(request, template, {'object': query_data}) + + +# def TransDetailView(request, slug): +# template = 'radio/transmission_detail.html' +# status = 'Good' +# try: +# query_data = Transmission.objects.filter(slug=slug) +# if not query_data: +# raise Http404 +# except Transmission.DoesNotExist: +# raise Http404 +# query_data2 = limit_transmission_history(request, query_data) +# if not query_data2 and not query_data[0].incident_set.filter(public=True): +# query_data[0].audio_file = None +# status = 'Expired' +# restricted, new_query = restrict_talkgroups(request, query_data) +# if not new_query: +# raise Http404 +# return render(request, template, {'object': query_data[0], 'status': status}) + +# def transDownloadView(request, slug): +# import urllib.request +# try: +# query_data = Transmission.objects.filter(slug=slug) +# if not query_data: +# raise Http404 +# except Transmission.DoesNotExist: +# raise Http404 +# query_data2 = limit_transmission_history(request, query_data) +# if not query_data2: +# raise Http404 # Just raise 404 if its too old +# restricted, new_query = restrict_talkgroups(request, query_data) +# if not new_query: +# raise Http404 +# trans = new_query[0] +# if trans.audio_file_type == 'm4a': +# audio_type = 'audio/m4a' +# else: +# audio_type = 'audio/mp3' +# response = HttpResponse(content_type=audio_type) +# start_time = timezone.localtime(trans.start_datetime).strftime('%Y%m%d_%H%M%S') +# filename = '{}_{}.{}'.format(start_time, trans.talkgroup_info.slug, trans.audio_file_type) +# response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) +# url = 'https:{}{}.{}'.format(trans.audio_url, trans.audio_file, trans.audio_file_type) +# if trans.audio_url[:2] != '//': +# url = 'http:' +# if request.is_secure(): +# url = 'https:' +# url += '//{}/{}{}.{}'.format(request.get_host(), trans.audio_url, trans.audio_file, trans.audio_file_type) +# req = urllib.request.Request(url) +# with urllib.request.urlopen(req) as web_response: +# response.write(web_response.read()) +# return response + + +# class TransmissionViewSet(viewsets.ModelViewSet): +# """ +# API endpoint that allows users to be viewed or edited. +# """ +# queryset = Transmission.objects.none() +# serializer_class = TransmissionSerializer + +# def get_serializer_context(self): +# return {'request': self.request} + + +# class ScanListViewSet(viewsets.ModelViewSet): +# queryset = ScanList.objects.all().prefetch_related('talkgroups') +# serializer_class = ScanListSerializer + + +# class TalkGroupViewSet(viewsets.ModelViewSet): +# """ +# API endpoint that allows groups to be viewed or edited. +# """ +# # queryset = TalkGroup.objects.filter(public=True) +# serializer_class = TalkGroupSerializer +# base_name = 'TalkGroup' + +# def get_queryset(self): +# if settings.ACCESS_TG_RESTRICT: +# tg = allowed_tg_list(self.request.user) +# else: +# tg = TalkGroup.objects.filter(public=True) +# return tg + + + +# class TransmissionView(ListView): +# model = Transmission +# paginate_by = 50 + + +# def ScanListFilter(request, filter_val): +# template = 'radio/transmission.html' +# return render(request, template, {'filter_data': filter_val, 'api_url': '/api_v1/ScanList'}) + + +# def TalkGroupFilterNew(request, filter_val): +# template = 'radio/transmission_play.html' +# return render(request,template, {'filter_data': filter_val}) + + +# def TalkGroupFilterjq(request, filter_val): +# template = 'radio/transmission_list_jq.html' +# return TalkGroupFilterBase(request, filter_val, template) + + +# def TalkGroupFilter(request, filter_val): +# template = 'radio/transmission_list.html' +# return TalkGroupFilterBase(request, filter_val, template) + +# # Open to anyone +# def Generic(request, page_name): +# template = 'radio/generic.html' +# query_data = WebHtml.objects.get(name=page_name) +# return render(request, template, {'html_object': query_data}) + +# def get_user_profile(user): +# if user.is_authenticated: +# user_profile = Profile.objects.get(user=user) +# else: +# try: +# anon_user = User.objects.get(username='ANONYMOUS_USER') +# except User.DoesNotExist: +# raise ImproperlyConfigured('ANONYMOUS_USER is missing from User table, was "./manage.py migrations" not run?') +# user_profile = Profile.objects.get(user=anon_user) +# return user_profile + +# def get_history_allow(user): +# user_profile = get_user_profile(user) +# if user_profile: +# history_minutes = 0 +# else: +# history_minutes = settings.ANONYMOUS_TIME +# return history_minutes + + +# def limit_transmission_history(request, query_data): +# history_minutes = get_history_allow(request.user) +# if history_minutes > 0: +# time_threshold = timezone.now() - timedelta(minutes=history_minutes) +# query_data = query_data.filter(start_datetime__gt=time_threshold) +# return query_data + +# def limit_transmission_history_six_months(request, query_data): +# history_minutes = 259200 +# time_threshold = timezone.now() - timedelta(minutes=history_minutes) +# query_data = query_data.filter(start_datetime__gt=time_threshold) +# return query_data + + + +# def allowed_tg_list(user): +# user_profile = get_user_profile(user) +# tg_list = None +# for group in user_profile.talkgroup_access.all(): +# if tg_list is None: +# tg_list = group.talkgroups.all() +# else: +# tg_list = tg_list | group.talkgroups.all() +# if tg_list: +# tg_list = tg_list.distinct() +# else: +# # Set blank talkgroup queryset +# tg_list = TalkGroup.objects.none() +# return tg_list + + +# def restrict_talkgroups(request, query_data): +# ''' Checks to make sure the user can view +# each of the talkgroups in the query_data +# returns ( was_restricted, new query_data ) +# ''' +# if not settings.ACCESS_TG_RESTRICT: +# return False, query_data +# tg_list = allowed_tg_list(request.user) +# query_data = query_data.filter(talkgroup_info__in=tg_list) +# return None, query_data + + +# def TalkGroupFilterBase(request, filter_val, template): +# try: +# tg = TalkGroup.objects.get(alpha_tag__startswith=filter_val) +# except TalkGroup.DoesNotExist: +# raise Http404 +# try: +# query_data = Transmission.objects.filter(talkgroup_info=tg).prefetch_related('units') +# #query_data = limit_transmission_history(self.request, rc_data) +# query_data = limit_transmission_history_six_months(self.request, rc_data) +# restrict_talkgroups(self.request, rc_data) +# except Transmission.DoesNotExist: +# raise Http404 +# return render(request, template, {'object_list': query_data, 'filter_data': filter_val}) + + +# class ScanViewSet(generics.ListAPIView): +# serializer_class = TransmissionSerializer + +# def get_queryset(self): +# scanlist = self.kwargs['filter_val'] +# try: +# sl = ScanList.objects.get(slug__iexact=scanlist) +# except ScanList.DoesNotExist: +# if scanlist == 'default': +# tg = TalkGroup.objects.all() +# else: +# print("Scan list does not match") +# raise +# else: +# tg = sl.talkgroups.all() +# rc_data = Transmission.objects.filter(talkgroup_info__in=tg).prefetch_related('units').prefetch_related('talkgroup_info') +# #rc_data = limit_transmission_history(self.request, rc_data) +# rc_data = limit_transmission_history_six_months(self.request, rc_data) +# restricted, rc_data = restrict_talkgroups(self.request, rc_data) +# return rc_data + + +# class IncViewSet(generics.ListAPIView): +# serializer_class = TransmissionSerializer + +# def get_queryset(self): +# inc = self.kwargs['filter_val'] +# try: +# if self.request.user.is_staff: +# rc_data = Incident.objects.get(slug__iexact=inc).transmissions.all() +# else: +# rc_data = Incident.objects.get(slug__iexact=inc, public=True).transmissions.all() +# except Incident.DoesNotExist: +# print("Incident does not exist") +# raise +# restricted, rc_data = restrict_talkgroups(self.request, rc_data) +# return rc_data + + +# class MessagePopUpViewSet(generics.ListAPIView): +# serializer_class = MessageSerializer + +# def get_queryset(self): +# return MessagePopUp.objects.filter(active=True) + + +# class TalkGroupFilterViewSet(generics.ListAPIView): +# serializer_class = TransmissionSerializer + +# def get_queryset(self): +# tg_var = self.kwargs['filter_val'] +# search_tgs = re.split('[\+]', tg_var) +# q = Q() +# for stg in search_tgs: +# q |= Q(common_name__iexact=stg) +# q |= Q(slug__iexact=stg) +# tg = TalkGroup.objects.filter(q) +# rc_data = Transmission.objects.filter(talkgroup_info__in=tg).prefetch_related('units') +# #rc_data = limit_transmission_history(self.request, rc_data) +# rc_data = limit_transmission_history_six_months(self.request, rc_data) +# restricted, rc_data = restrict_talkgroups(self.request, rc_data) +# return rc_data + + +# class UnitFilterViewSet(generics.ListAPIView): +# serializer_class = TransmissionSerializer + +# def get_queryset(self): +# unit_var = self.kwargs['filter_val'] +# search_unit = re.split('[\+]', unit_var) +# q = Q() +# for s_unit in search_unit: +# q |= Q(slug__iexact=s_unit) +# units = Unit.objects.filter(q) +# rc_data = Transmission.objects.filter(units__in=units).filter(talkgroup_info__public=True).prefetch_related('units').distinct() +# #rc_data = limit_transmission_history(self.request, rc_data) +# rc_data = limit_transmission_history_six_months(self.request, rc_data) +# restricted, rc_data = restrict_talkgroups(self.request, rc_data) +# return rc_data + + +# class TalkGroupList(ListView): +# model = TalkGroup +# context_object_name = 'talkgroups' +# template_name = 'radio/talkgroup_list.html' + +# #queryset = TalkGroup.objects.filter(public=True) +# def get_queryset(self): +# if settings.ACCESS_TG_RESTRICT: +# tg = allowed_tg_list(self.request.user) +# else: +# tg = TalkGroup.objects.filter(public=True) +# if self.request.GET.get('recent', None): +# tg = tg.order_by('-recent_usage', '-last_transmission') +# return tg + + + + + +# @csrf_protect +# def register(request): +# if request.method == 'POST': +# form = RegistrationForm(request.POST) +# if form.is_valid(): +# user = User.objects.create_user( +# username=form.cleaned_data['username'], +# password=form.cleaned_data['password1'], +# email=form.cleaned_data['email'] +# ) +# username = form.cleaned_data['username'] +# password = form.cleaned_data['password1'] +# new_user = authenticate(username=username, password=password) +# if new_user is not None: +# if new_user.is_active: +# stripe_actions.customers.create(user=new_user) +# login(request, new_user) +# return HttpResponseRedirect('/scan/default/') +# else: +# # this would be weird to get here +# return HttpResponseRedirect('/register/success/') +# else: +# return HttpResponseRedirect('/register/success/') +# else: +# form = RegistrationForm() + +# return render( +# request, +# 'registration/register.html', +# { 'form': form }, +# ) + +# def register_success(request): +# return render( +# request, +# 'registration/success.html', {}, +# ) + + +# class MenuScanListViewSet(viewsets.ModelViewSet): +# serializer_class = MenuScanListSerializer +# queryset = MenuScanList.objects.all() + + +# class MenuTalkGroupListViewSet(viewsets.ModelViewSet): +# serializer_class = MenuTalkGroupListSerializer +# queryset = MenuTalkGroupList.objects.all() + + +# class UnitUpdateView(PermissionRequiredMixin, UpdateView): +# model = Unit +# form_class = UnitEditForm +# success_url = '/unitupdategood/' +# permission_required = ('radio.change_unit') + +# def form_valid(self, form): +# try: +# update_unit_email = SiteOption.objects.get(name='SEND_ADMIN_EMAIL_ON_UNIT_NAME') +# if update_unit_email.value_boolean_or_string() == True: +# Unit = form.save() +# send_mail( +# 'Unit ID Change', +# 'User {} updated unit ID {} Now {}'.format(self.request.user, Unit.dec_id, Unit.description), +# settings.SERVER_EMAIL, +# [ mail for name, mail in settings.ADMINS], +# fail_silently=False, +# ) +# except SiteOption.DoesNotExist: +# pass +# return super().form_valid(form) + + +# def ScanDetailsList(request, name): +# template = 'radio/scandetaillist.html' +# scanlist = None +# try: +# scanlist = ScanList.objects.get(name=name) +# except ScanList.DoesNotExist: +# if name == 'default': +# query_data = TalkGroup.objects.all() +# else: +# raise Http404 +# if scanlist: +# query_data = scanlist.talkgroups.all() +# return render(request,template, {'object_list': query_data, 'scanlist': scanlist, 'request': request}) + + + + +# def incident(request, inc_slug): +# template = 'radio/player_main.html' +# try: +# if request.user.is_staff: +# inc = Incident.objects.get(slug=inc_slug) +# else: +# inc = Incident.objects.get(slug=inc_slug, public=True) +# except Incident.DoesNotExist: +# raise Http404 +# return render(request, template, {'inc':inc}) + + +# @csrf_exempt +# def import_transmission(request): +# if request.method == "POST": +# settings_auth_token = getattr(settings, 'ADD_TRANS_AUTH_TOKEN', None) +# if settings_auth_token == '7cf5857c61284': # Check is default is still set +# return HttpResponse('Unauthorized, default ADD_TRANS_AUTH_TOKEN still set.', status=401) +# body_unicode = request.body.decode('utf-8') +# request_data = json.loads(body_unicode) +# auth_token = request_data.get('auth_token') +# if auth_token != settings_auth_token: +# return HttpResponse('Unauthorized, check auth_token', status=401) +# # System +# system_name = request_data.get('system') +# if system_name is None: +# return HttpResponse('system is missing', status=400) +# system, created = System.objects.get_or_create(name=system_name) +# # Source +# source_name = request_data.get('source') +# if source_name is None: +# return HttpResponse('source is missing', status=400) +# source, created = Source.objects.get_or_create(description=source_name) +# # TalkGroup +# tg_dec = request_data.get('talkgroup') +# if tg_dec is None: +# return HttpResponse('talkgroup is missing', status=400) +# try: +# tg = TalkGroup.objects.get(dec_id=tg_dec, system=system) +# except TalkGroup.DoesNotExist: +# name = '#{}'.format(tg_dec) +# tg = TalkGroup.objects.create(dec_id=tg_dec, system=system, alpha_tag=name, description='TalkGroup {}'.format(name)) +# # Transmission start +# epoc_ts = request_data.get('start_time') +# start_dt = datetime.fromtimestamp(int(epoc_ts), pytz.UTC) +# epoc_end_ts = request_data.get('stop_time') +# end_dt = datetime.fromtimestamp(int(epoc_end_ts), pytz.UTC) +# play_length = epoc_end_ts - epoc_ts +# audio_filename = request_data.get('audio_filename') +# audio_file_url_path = request_data.get('audio_file_url_path') +# freq = request_data.get('freq') # This should be depricated +# audio_file_type = request_data.get('audio_file_type') +# audio_file_play_length = request_data.get('audio_file_play_length', play_length) +# has_audio = request_data.get('has_audio', True) + +# t = Transmission( start_datetime = start_dt, +# end_datetime = end_dt, +# audio_file = audio_filename, +# talkgroup = tg_dec, +# talkgroup_info = tg, +# freq = int(float(freq)), +# emergency = False, +# source = source, +# system = system, +# audio_file_url_path = audio_file_url_path, +# audio_file_type = audio_file_type, +# play_length = audio_file_play_length, +# has_audio = has_audio, +# ) +# t.save() + +# # Units +# count = 0 +# for unit in request_data.get('srcList'): +# try: +# trans_unit = unit['src'] +# except TypeError: +# trans_unit = unit +# u,created = Unit.objects.get_or_create(dec_id=trans_unit,system=t.system) +# tu = TranmissionUnit.objects.create(transmission=t, unit=u, order=count) +# count=count+1 + +# return HttpResponse("Transmission added [{}]".format(t.pk)) +# else: +# return HttpResponse(status=405) diff --git a/trunk_player/asgi.py b/trunk_player/asgi.py index b4e6ea5..7dd393c 100644 --- a/trunk_player/asgi.py +++ b/trunk_player/asgi.py @@ -9,6 +9,7 @@ from channels.routing import ProtocolTypeRouter, URLRouter from radio.consumers import RadioConsumer +from telemetry.consumers import TelemetryConsumer channel_layer = ProtocolTypeRouter({ "http": django_asgi_app, @@ -18,7 +19,8 @@ URLRouter([ url(r"^ws-calls/(?P[^/]+)/(?P