1
0
Fork 0

Support suggestions for creating/editing satellite

Signed-off-by: Alfredos-Panagiotis Damkalis <fredy@fredy.gr>
spacecruft
Alfredos-Panagiotis Damkalis 2021-04-26 12:20:33 +03:00
parent c75b491b9b
commit 418fe5ab3d
10 changed files with 203 additions and 38 deletions

View File

@ -1,7 +1,7 @@
"""SatNOGS DB django base Forms class"""
from bootstrap_modal_forms.forms import BSModalModelForm
from django.core.exceptions import ValidationError
from django.forms import NumberInput, TextInput
from django.forms import TextInput
from django.utils.translation import ugettext_lazy as _
from db.base.models import SatelliteEntry, Transmitter, TransmitterEntry
@ -19,7 +19,7 @@ def existing_uuid(value):
) from error
class TransmitterModelForm(BSModalModelForm): # pylint: disable=too-many-ancestors
class TransmitterCreateForm(BSModalModelForm): # pylint: disable=too-many-ancestors
"""Model Form class for TransmitterEntry objects"""
class Meta:
model = TransmitterEntry
@ -57,16 +57,18 @@ class TransmitterUpdateForm(BSModalModelForm): # pylint: disable=too-many-ances
}
class SatelliteModelForm(BSModalModelForm):
class SatelliteCreateForm(BSModalModelForm):
"""Form that uses django-bootstrap-modal-forms for satellite editing"""
class Meta:
model = SatelliteEntry
fields = [
'norad_cat_id', 'name', 'names', 'operator', 'status', 'description', 'countries',
'website', 'dashboard_url', 'launched', 'deployed', 'decayed', 'image'
'norad_cat_id', 'norad_follow_id', 'name', 'names', 'description', 'operator',
'status', 'countries', 'website', 'dashboard_url', 'launched', 'deployed', 'decayed',
'image', 'citation'
]
labels = {
'norad_cat_id': _('Norad ID'),
'norad_follow_id': _('Followed Norad ID'),
'names': _('Other names'),
'countries': _('Countries of Origin'),
'launched': _('Launch Date'),
@ -76,4 +78,28 @@ class SatelliteModelForm(BSModalModelForm):
'dashboard_url': _('Dashboard URL'),
'operator': _('Owner/Operator'),
}
widgets = {'norad_cat_id': NumberInput(attrs={'readonly': True}), 'names': TextInput()}
widgets = {'names': TextInput()}
class SatelliteUpdateForm(BSModalModelForm):
"""Form that uses django-bootstrap-modal-forms for satellite editing"""
class Meta:
model = SatelliteEntry
fields = [
'norad_cat_id', 'norad_follow_id', 'name', 'names', 'description', 'operator',
'status', 'countries', 'website', 'dashboard_url', 'launched', 'deployed', 'decayed',
'image', 'citation'
]
labels = {
'norad_cat_id': _('Norad ID'),
'norad_follow_id': _('Followed Norad ID'),
'names': _('Other names'),
'countries': _('Countries of Origin'),
'launched': _('Launch Date'),
'deployed': _('Deploy Date'),
'decayed': _('Re-entry Date'),
'description': _('Description'),
'dashboard_url': _('Dashboard URL'),
'operator': _('Owner/Operator'),
}
widgets = {'names': TextInput()}

View File

@ -254,10 +254,13 @@ class Satellite(models.Model):
last_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return '{1} ({2}) | {0}'.format(
self.satellite_identifier.sat_id, self.satellite_entry.name,
self.satellite_entry.norad_cat_id
)
if self.satellite_entry:
name = self.satellite_entry.name
norad_cat_id = self.satellite_entry.norad_cat_id
else:
name = '-'
norad_cat_id = '-'
return '{1} ({2}) | {0}'.format(self.satellite_identifier.sat_id, name, norad_cat_id)
@property
def transmitters(self):

View File

@ -28,6 +28,7 @@ BASE_URLPATTERNS = (
path('users/edit/', views.users_edit, name='users_edit'),
path(r'robots\.txt', views.robots, name='robots'),
path('search/', views.search, name='search_results'),
path('create_satellite/', views.SatelliteCreateView.as_view(), name='create_satellite'),
path(
'update_satellite/<int:pk>/',
views.SatelliteUpdateView.as_view(),

View File

@ -19,10 +19,11 @@ from django.urls import reverse
from django.utils.timezone import now
from django.views.decorators.http import require_POST
from db.base.forms import SatelliteModelForm, TransmitterModelForm, TransmitterUpdateForm
from db.base.forms import SatelliteCreateForm, SatelliteUpdateForm, TransmitterCreateForm, \
TransmitterUpdateForm
from db.base.helpers import get_apikey
from db.base.models import DemodData, Satellite, SatelliteEntry, Transmitter, TransmitterEntry, \
TransmitterSuggestion
from db.base.models import DemodData, Satellite, SatelliteEntry, SatelliteIdentifier, \
Transmitter, TransmitterEntry, TransmitterSuggestion
from db.base.tasks import export_frames, notify_suggestion
from db.base.utils import cache_statistics, millify, read_influx
@ -353,7 +354,7 @@ class TransmitterCreateView(LoginRequiredMixin, BSModalCreateView):
"""A django-bootstrap-modal-forms view for creating transmitter suggestions"""
template_name = 'base/modals/transmitter_create.html'
model = TransmitterEntry
form_class = TransmitterModelForm
form_class = TransmitterCreateForm
success_message = 'Your transmitter suggestion was stored successfully and will be \
reviewed by a moderator. Thanks for contibuting!'
@ -421,11 +422,63 @@ class TransmitterUpdateView(LoginRequiredMixin, BSModalUpdateView):
return self.request.META.get('HTTP_REFERER')
class SatelliteCreateView(LoginRequiredMixin, BSModalCreateView):
"""A django-bootstrap-modal-forms view for creating satellite suggestions"""
template_name = 'base/modals/satellite_create.html'
model = SatelliteEntry
form_class = SatelliteCreateForm
success_message = 'Your satellite suggestion was stored successfully and will be \
reviewed by a moderator. Thanks for contributing!'
user = get_user_model()
def dispatch(self, request, *args, **kwargs):
self.user = request.user
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
satellite_entry = form.instance
satellite_obj = None
# Create Satellite Identifier only when POST request is for saving and
# NORAD ID is not used by other Satellite.
if not self.request.is_ajax():
try:
satellite_obj = Satellite.objects.get(
satellite_entry__norad_cat_id=satellite_entry.norad_cat_id
)
satellite_entry.satellite_identifier = satellite_obj.satellite_identifier
except Satellite.DoesNotExist:
satellite_entry.satellite_identifier = SatelliteIdentifier.objects.create()
satellite_entry.created = now()
satellite_entry.created_by = self.user
# form_valid triggers also save() allowing us to use satellite_entry
# for creating Satellite object, see comment bellow.
response = super().form_valid(form)
# Prevents sending notification twice as form_valid is triggered for
# validation and saving. Also create and Satellite object only when POST
# request is for saving and NORAD ID is not used by other Satellite.
if not self.request.is_ajax():
if not satellite_obj:
satellite_obj = Satellite.objects.create(
satellite_identifier=satellite_entry.satellite_identifier,
satellite_entry=satellite_entry
)
notify_suggestion.delay(satellite_obj.satellite_entry.pk, self.user.id, 'satellite')
return response
def get_success_url(self):
return self.request.META.get('HTTP_REFERER')
class SatelliteUpdateView(LoginRequiredMixin, BSModalUpdateView):
"""A django-bootstrap-modal-forms view for updating satellite fields"""
"""A django-bootstrap-modal-forms view for updating satellite entries"""
template_name = 'base/modals/satellite_update.html'
model = SatelliteEntry
form_class = SatelliteModelForm
form_class = SatelliteUpdateForm
success_message = 'Your satellite suggestion was stored successfully and will be \
reviewed by a moderator. Thanks for contributing!'

View File

@ -33,6 +33,14 @@ $(document).ready(function() {
pageLength: 50
} );
// Create Satellite
$('.create-satellite-link').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url'),
modalID: '#create-satellite-modal'
});
});
// Update Satellite
$('.update-satellite-link').each(function () {
$(this).modalForm({

View File

@ -0,0 +1,44 @@
{% load widget_tweaks %}
{% if request.user.is_authenticated %}
<form action="" method="post" id="satellite_create-form" enctype="multipart/form-data" >
{% csrf_token %}
<div class="modal-header">
<h3 class="modal-title">Create Satellite</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% for field in form %}
<div class="input-group my-1">
<label class="input-group-prepend input-group-text" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" placeholder=field.label %}
<div class="{% if field.errors %} invalid{% endif %}">
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="submit-btn btn btn-satnogs-primary">Create</button>
</div>
</form>
{% else %}
<div class="modal-body">
<div class="text-danger">You need to login first to add a new transmitter suggestion.</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<a href="{% url 'account_login' %}" class="btn btn-satnogs-primary">Log In</a>
</div>
{% endif %}

View File

@ -11,7 +11,6 @@
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
@ -66,4 +65,4 @@
<button class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<a href="{% url 'account_login' %}" class="btn btn-satnogs-primary">Log In</a>
</div>
{% endif %}
{% endif %}

View File

@ -13,6 +13,27 @@
<span class="h4 mb-0 mr-3 text-truncate d-none d-md-block">Satellites</span>
{% endblock %}
{% block top-menu-right %}
{% if request.user.is_authenticated %}
<ul class="navbar-nav nav nav-pills" data-widget="treeview" role="menu" data-accordion="false" id="tabs" role="tablist">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" aria-label="Edit Menu">
<i class="nav-icon fas fa-edit"></i>
<p class="d-none d-lg-inline-block text-sm">Edit</p>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item d-flex align-items-center create-satellite-link" href="#" id="create-satellite-side"
data-form-url="{% url 'create_satellite' %}" aria-label="Submit new satellite">
<i class="nav-icon mr-2 fas fa-plus-square"></i>
<p class="mb-0">Submit New Satellite</p>
</a>
</div>
</li>
</ul>
{% endif %}
{% endblock %}
{% block top %}
<span class="h4 mb-0">Satellites</span>
{% endblock %}
@ -66,7 +87,7 @@
</td>
<td>{{ sat.satellite_entry.names|upper }}</td>
<td>{{ sat.approved_transmitters|length }}</td>
<td>{{ sat.satellite_entry.norad_follow_id }}</a></td>
<td>{{ sat.satellite_entry.norad_follow_id }}</td>
<td>{{ sat.satellite_entry.operator }}</td>
<td>{{ sat.satellite_entry.launched }}</td>
<td>{{ sat.satellite_entry.website }}</td>
@ -76,6 +97,7 @@
<td><button type="button" class="update-satellite-link btn btn-sm btn-satnogs-primary"
data-form-url="{% url 'update_satellite' sat.satellite_entry.id %}">
<span class="fa fa-edit"></span>
</button>
</td>
{% endif %}
</tr>
@ -83,14 +105,21 @@
</tbody>
</table>
<!-- Satellite update modal -->
<!-- django-bootstrap-modal-forms modals -->
{% if request.user.is_authenticated %}
<div class="modal fade" id="create-satellite-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
</div>
</div>
</div>
<div class="modal fade" id="update-satellite-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content"></div>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -108,6 +108,8 @@
<i class="fas fa-search"></i> UHF Amateur</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascript %}

View File

@ -84,7 +84,7 @@
{% if transmitter.downlink_drift %}
<dt class="col-sm-6">Downlink Drifted</dt>
<dd class="col-sm-6" data-toggle="tooltip" data-placement="bottom" title="{{ transmitter.downlink_drift }} ppb">
<span class="frequency drifted" data-freq_or="{{ transmitter.downlink_low }}" data-drift="{{ transmitter.downlink_drift }}">{{ transmitter.downlink_drift }}</dd>
<span class="frequency drifted" data-freq_or="{{ transmitter.downlink_low }}" data-drift="{{ transmitter.downlink_drift }}">{{ transmitter.downlink_drift }}</span></dd>
{% endif %}
{% if transmitter.baud %}
<dt class="col-sm-6">Baud</dt>
@ -97,22 +97,22 @@
{% if transmitter.downlink_high %}
<dt class="col-sm-6">Downlink High</dt>
<dd class="col-sm-6" data-toggle="tooltip" data-placement="bottom" title="{{ transmitter.downlink_high }}">
<span class="frequency">{{ transmitter.downlink_high }}</dd>
<span class="frequency">{{ transmitter.downlink_high }}</span></dd>
{% endif %}
{% if transmitter.uplink_low %}
<dt class="col-sm-6">Uplink Frequency</dt>
<dd class="col-sm-6" data-toggle="tooltip" data-placement="bottom" title="{{ transmitter.uplink_low }}">
<span class="frequency">{{ transmitter.uplink_low }}</dd>
<span class="frequency">{{ transmitter.uplink_low }}</span></dd>
{% endif %}
{% if transmitter.uplink_drift %}
<dt class="col-sm-6">Uplink Drift</dt>
<dd class="col-sm-6" data-toggle="tooltip" data-placement="bottom" title="{{ transmitter.uplink_drift }} ppb">
<span class="frequency drifted" data-freq_or="{{ transmitter.uplink_low }}" data-drift="{{ transmitter.uplink_drift }}">{{ transmitter.uplink_drift }}</dd>
<span class="frequency drifted" data-freq_or="{{ transmitter.uplink_low }}" data-drift="{{ transmitter.uplink_drift }}">{{ transmitter.uplink_drift }}</span></dd>
{% endif %}
{% if transmitter.uplink_high %}
<dt class="col-sm-6">Uplink High</dt>
<dd class="col-sm-6" data-toggle="tooltip" data-placement="bottom" title="{{ transmitter.uplink_high }}">
<span class="frequency">{{ transmitter.uplink_high }}</dd>
<span class="frequency">{{ transmitter.uplink_high }}</span></dd>
{% endif %}
{% if transmitter.invert %}
<dt class="col-sm-6">Inverted</dt>
@ -142,23 +142,23 @@
{% endif %}
</div>
</div>
</div>
<!-- Citation Modal -->
<div class="modal fade" id="TransmitterCitation-{{ transmitter.id }}" tabindex="-1" role="dialog"
aria-labelledby="CitationModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="CitationModalLabel">Citation for {{ transmitter.uuid }}</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
{{ transmitter.citation }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="CitationModalLabel">Citation for {{ transmitter.uuid }}</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
{{ transmitter.citation }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>