Add utilization factor to station model and UI
parent
e5be997a7a
commit
5e925a4ed6
|
@ -88,7 +88,8 @@ class StationSerializer(serializers.ModelSerializer):
|
|||
model = Station
|
||||
fields = ('id', 'name', 'altitude', 'min_horizon', 'lat', 'lng',
|
||||
'qthlocator', 'location', 'antenna', 'created', 'last_seen',
|
||||
'status', 'observations', 'description', 'client_version')
|
||||
'status', 'observations', 'description', 'client_version',
|
||||
'target_utilization')
|
||||
|
||||
def get_altitude(self, obj):
|
||||
return obj.alt
|
||||
|
|
|
@ -74,6 +74,7 @@ class StationViewApiTest(TestCase):
|
|||
u'name': self.station.name,
|
||||
u'observations': 0,
|
||||
u'qthlocator': self.station.qthlocator,
|
||||
u'target_utilization': self.station.target_utilization,
|
||||
u'status': self.station.get_status_display()}
|
||||
|
||||
response = self.client.get('/api/stations/')
|
||||
|
|
|
@ -26,7 +26,7 @@ class AntennaAdmin(admin.ModelAdmin):
|
|||
@admin.register(Station)
|
||||
class StationAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name', 'owner', 'get_email', 'lng', 'lat', 'qthlocator',
|
||||
'client_version', 'created_date', 'state')
|
||||
'client_version', 'created_date', 'state', 'target_utilization')
|
||||
list_filter = ('status', 'created', 'client_version')
|
||||
|
||||
actions = [export_as_csv]
|
||||
|
@ -62,7 +62,7 @@ class TransmitterAdmin(admin.ModelAdmin):
|
|||
list_display = ('uuid', 'description', 'satellite', 'type', 'alive', 'mode', 'uplink_low',
|
||||
'uplink_high', 'uplink_drift', 'downlink_low', 'downlink_high',
|
||||
'downlink_drift', 'baud', 'sync_to_db')
|
||||
search_fields = ('satellite', 'uuid', 'satellite__name', 'satellite__norad_cat_id')
|
||||
search_fields = ('uuid', 'satellite__name', 'satellite__norad_cat_id')
|
||||
list_filter = ('type', 'mode', 'alive', 'sync_to_db')
|
||||
readonly_fields = ('uuid', 'description', 'satellite', 'type', 'uplink_low', 'uplink_high',
|
||||
'uplink_drift', 'downlink_low', 'downlink_high', 'downlink_drift',
|
||||
|
|
|
@ -7,7 +7,8 @@ class StationForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Station
|
||||
fields = ['name', 'image', 'alt', 'lat', 'lng', 'qthlocator',
|
||||
'horizon', 'antenna', 'testing', 'description']
|
||||
'horizon', 'antenna', 'testing', 'description',
|
||||
'target_utilization']
|
||||
image = forms.ImageField(required=False)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.18 on 2019-02-07 17:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0053_remove_station_uuid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='station',
|
||||
name='target_utilization',
|
||||
field=models.IntegerField(blank=True, help_text=b'Target utilization factor for your station', null=True, validators=[django.core.validators.MaxValueValidator(100), django.core.validators.MinValueValidator(0)]),
|
||||
),
|
||||
]
|
|
@ -167,6 +167,11 @@ class Station(models.Model):
|
|||
horizon = models.PositiveIntegerField(help_text='In degrees above 0', default=10)
|
||||
description = models.TextField(max_length=500, blank=True, help_text='Max 500 characters')
|
||||
client_version = models.CharField(max_length=45, blank=True)
|
||||
target_utilization = models.IntegerField(validators=[MaxValueValidator(100),
|
||||
MinValueValidator(0)],
|
||||
help_text='Target utilization factor for '
|
||||
' your station',
|
||||
null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-status']
|
||||
|
|
|
@ -115,3 +115,11 @@
|
|||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
#horizon_value, #utilization_value {
|
||||
margin: 0 0 0 20px;
|
||||
|
||||
.slider-selection {
|
||||
background: #5bc0de;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
$(document).ready(function() {
|
||||
'use strict';
|
||||
|
||||
$('.selectpicker').selectpicker();
|
||||
|
||||
var horizon_value = $('#horizon').val();
|
||||
$('#horizon').slider({
|
||||
id: 'horizon_value',
|
||||
min: 0,
|
||||
max: 90,
|
||||
step: 1,
|
||||
value: horizon_value});
|
||||
|
||||
var utilization_value = $('#utilization').val();
|
||||
$('#utilization').slider({
|
||||
id: 'utilization_value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
value: utilization_value});
|
||||
|
||||
var station_image = $('#station-image').data('existing');
|
||||
$('#station-image').fileinput({
|
||||
initialPreview: station_image,
|
||||
initialPreviewAsData: true,
|
||||
});
|
||||
});
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
{% block title %}{% if station %} - Edit Ground Station {{ station.name }}{% else %} - Add Ground Station{% endif %}{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link href="{% static 'lib/bootstrap-select/dist/css/bootstrap-select.min.css' %}" rel="stylesheet" >
|
||||
<link href="{% static 'lib/bootstrap-slider/dist/css/bootstrap-slider.min.css' %}" rel="stylesheet" >
|
||||
<link href="{% static 'lib/bootstrap-fileinput/css/fileinput.min.css' %}" rel="stylesheet" >
|
||||
{% endblock css %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
@ -18,77 +24,135 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<form role="form" enctype="multipart/form-data" method="post">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="name" class="control-label">Name</label>
|
||||
<input value="{{ form.name.value|default_if_none:"" }}" id="name" type="text" class="form-control" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image" class="control-label">Image</label>
|
||||
<input id="image" type="file" name="image">
|
||||
{% if form.image.value %}
|
||||
<img src="{{ MEDIA_URL }}{{ form.image.value }}"
|
||||
class="station-edit-image">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="alt" class="control-label">Altitude</label>
|
||||
<input value="{{ form.alt.value|default_if_none:"" }}" id="alt" type="number" class="form-control" name="alt" placeholder="{{ form.alt.help_text }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lat" class="control-label">Latitude</label>
|
||||
<input value="{{ form.lat.value|default_if_none:"" }}" id="lat" type="text" class="form-control" name="lat" placeholder="{{ form.lat.help_text }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lng" class="control-label">Longtitude</label>
|
||||
<input value="{{ form.lng.value|default_if_none:"" }}" id="lng" type="text" class="form-control" name="lng" placeholder="{{ form.lng.help_text }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="qthlocator" class="control-label">QTH Locator</label>
|
||||
<input class="form-control"
|
||||
id="qthlocator"
|
||||
type="text"
|
||||
name="qthlocator"
|
||||
value="{{ form.qthlocator.value|default_if_none:"" }}"
|
||||
readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="horizon" class="control-label">Minimum Horizon</label>
|
||||
<input value="{{ form.horizon.value|default_if_none:"" }}" id="horizon" type="number" class="form-control" name="horizon" placeholder="{{ form.horizon.help_text }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="antennas" class="control-label">Antennas</label>
|
||||
<select multiple class="form-control" name="antenna">
|
||||
{% for antenna in antennas %}
|
||||
<option value="{{ antenna.id }}" {% if antenna in station.antenna.all %}selected{% endif %}>
|
||||
{{ antenna.band}} {{ antenna.get_antenna_type_display }} | {{ antenna.frequency|frq }} - {{ antenna.frequency_max|frq }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="help-block">{{ form.antenna.help_text|safe }}</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description" class="control-label">Description</label>
|
||||
<textarea class="form-control" name="description"
|
||||
id="description" rows="3" maxlength="500">{{ form.description.value|default_if_none:"" }}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="testing" {% if form.testing.value %}checked="True"{% endif %} {% if not station.id %}disabled{% endif %}>
|
||||
Testing?
|
||||
<span class="glyphicon glyphicon-question-sign" aria-hidden="true" data-toggle="tooltip"
|
||||
title="Make sure you station is performing well for a period of time before you remove the Testing flag"></span>
|
||||
</label>
|
||||
<form role="form" enctype="multipart/form-data" method="post">{% csrf_token %}
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">General Info</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label for="name" class="control-label">Name</label>
|
||||
<input value="{{ form.name.value|default_if_none:"" }}" id="station-name" type="text" class="form-control" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description" class="control-label">Description</label>
|
||||
<textarea class="form-control" name="description"
|
||||
id="description" rows="3" maxlength="500">{{ form.description.value|default_if_none:"" }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Location</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="lat" class="control-label">Latitude</label>
|
||||
<input value="{{ form.lat.value|default_if_none:"" }}" id="lat" type="text" class="form-control" name="lat" placeholder="{{ form.lat.help_text }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="lng" class="control-label">Longtitude</label>
|
||||
<input value="{{ form.lng.value|default_if_none:"" }}" id="lng" type="text" class="form-control" name="lng" placeholder="{{ form.lng.help_text }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="alt" class="control-label">Altitude (ASL)</label>
|
||||
<input value="{{ form.alt.value|default_if_none:"" }}" id="alt" type="number" class="form-control" name="alt" placeholder="{{ form.alt.help_text }}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="qthlocator" class="control-label">QTH Locator</label>
|
||||
<input class="form-control"
|
||||
id="qthlocator"
|
||||
type="text"
|
||||
name="qthlocator"
|
||||
value="{{ form.qthlocator.value|default_if_none:"" }}"
|
||||
readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Image</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<input id="station-image" type="file" name="image" data-existing="{{ MEDIA_URL }}{{ form.image.value }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Settings</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label for="horizon" class="control-label">Minimum Horizon</label>
|
||||
<input id="horizon" type="number" value="{{ form.horizon.value|default_if_none:"" }}" class="form-control" name="horizon" placeholder="{{ form.horizon.help_text }}">
|
||||
<span class="glyphicon glyphicon-question-sign" aria-hidden="true" data-toggle="tooltip"
|
||||
title="Your station minimum observable elevation"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="antennas" class="control-label">Antennas</label>
|
||||
<select id="antenna-selection" class="form-control selectpicker" multiple name="antenna"
|
||||
data-selected-text-format="count" data-count-selected-text="Selected {0} antennas">
|
||||
{% for antenna in antennas %}
|
||||
<option value="{{ antenna.id }}" {% if antenna in station.antenna.all %}selected{% endif %} data-content="
|
||||
<span class='label label-info'>{{ antenna.band }} {{ antenna.get_antenna_type_display }}</span> {{ antenna.frequency|frq }} - {{ antenna.frequency_max|frq }}
|
||||
">
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="help-block">{{ form.antenna.help_text|safe }}</span>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="utilization" class="control-label">Target Utilization</label>
|
||||
<input id="utilization" type="number" value="{{ form.target_utilization.value|default_if_none:"" }}" class="form-control" name="target_utilization" placeholder="{{ form.utilization.help_text }}">
|
||||
<span class="glyphicon glyphicon-question-sign" aria-hidden="true" data-toggle="tooltip"
|
||||
title="Your station target utilization"></span>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="testing" {% if form.testing.value %}checked="True"{% endif %} {% if not station.id %}disabled{% endif %}>
|
||||
Testing?
|
||||
<span class="glyphicon glyphicon-question-sign" aria-hidden="true" data-toggle="tooltip"
|
||||
title="Make sure you station is performing well for a period of time before you remove the Testing flag"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
<script src="{% static 'js/gridsquare.js' %}"></script>
|
||||
<script src="{% static 'lib/bootstrap-select/dist/js/bootstrap-select.min.js' %}"></script>
|
||||
<script src="{% static 'lib/bootstrap-slider/dist/bootstrap-slider.min.js' %}"></script>
|
||||
<script src="{% static 'lib/bootstrap-fileinput/js/fileinput.min.js' %}"></script>
|
||||
<script src="{% static 'js/station_edit.js' %}"></script>
|
||||
{% endblock javascript %}
|
||||
|
|
|
@ -81,6 +81,14 @@
|
|||
{{ station.horizon }}°
|
||||
</span>
|
||||
</div>
|
||||
{% if station.target_utilization %}
|
||||
<div class="front-line">
|
||||
<span class="label label-default">Target Utilization</span>
|
||||
<span class="front-data">
|
||||
{{ station.target_utilization }} %
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if station.antenna %}
|
||||
<div class="font-line station-antennas">
|
||||
<span class="label label-default">Antennas</span>
|
||||
|
|
Loading…
Reference in New Issue