diff --git a/DOSPORTAL/forms.py b/DOSPORTAL/forms.py index f87c0ef..b3ede71 100644 --- a/DOSPORTAL/forms.py +++ b/DOSPORTAL/forms.py @@ -6,6 +6,18 @@ from django.contrib.auth.forms import UserCreationForm +class LoginForm(forms.Form): + username = forms.CharField( + widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Username', 'autofocus': False }), + label="Username", + help_text="Enter your username." + ) + password = forms.CharField( + label="Password", + widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password', 'autofocus': False }), + help_text="Enter your password." + ) + class UserRegisterForm(UserCreationForm): email = forms.EmailField() diff --git a/DOSPORTAL/migrations/0008_record_created_alter_record_data_file.py b/DOSPORTAL/migrations/0008_record_created_alter_record_data_file.py new file mode 100644 index 0000000..b8c003e --- /dev/null +++ b/DOSPORTAL/migrations/0008_record_created_alter_record_data_file.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.11 on 2024-04-04 12:59 + +import DOSPORTAL.models +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('DOSPORTAL', '0007_record_data_file'), + ] + + operations = [ + migrations.AddField( + model_name='record', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Time of creation'), + preserve_default=False, + ), + migrations.AlterField( + model_name='record', + name='data_file', + field=models.FileField(blank=True, help_text='Processed spectral file', null=True, upload_to=DOSPORTAL.models.Record.user_directory_path_data, verbose_name='Log file'), + ), + ] diff --git a/DOSPORTAL/models.py b/DOSPORTAL/models.py index beff7df..3ea9f3c 100644 --- a/DOSPORTAL/models.py +++ b/DOSPORTAL/models.py @@ -493,6 +493,13 @@ def user_directory_path_data(instance, filename): null=True, ) + created = models.DateTimeField( + verbose_name = _("Time of creation"), + null=False, + editable=False, + auto_now_add=True + ) + record_duration = models.DurationField( verbose_name = _("Record duration"), help_text=_("Duration of record"), @@ -552,7 +559,6 @@ def description(self) -> str: - class Trajectory(UUIDMixin): name = models.CharField( max_length = 80 diff --git a/DOSPORTAL/settings.py b/DOSPORTAL/settings.py index ddb7376..4e49895 100644 --- a/DOSPORTAL/settings.py +++ b/DOSPORTAL/settings.py @@ -49,7 +49,7 @@ 'import_export', 'django_select2', 'martor', - #'django_tables2', + 'django_tables2', 'DOSPORTAL', 'django_q', @@ -176,7 +176,14 @@ +DJANGO_TABLES2_TEMPLATE = "django_tables2/bootstrap5-responsive.html" +DJANGO_TABLES2_TABLE_ATTRS = { + 'class': 'table table-hover', + 'thead': { + 'class': 'table-light', + }, +} MARTOR_THEME = 'bootstrap' MARTOR_ENABLE_CONFIGS = { diff --git a/DOSPORTAL/signals.py b/DOSPORTAL/signals.py index f96fbb1..1e6794f 100644 --- a/DOSPORTAL/signals.py +++ b/DOSPORTAL/signals.py @@ -4,6 +4,7 @@ from .models import Profile, Record, SpectrumData import json import pandas as pd +import datetime @receiver(post_save, sender=User) @@ -20,15 +21,8 @@ def save_profile(sender, instance, **kwargs): @receiver(post_save, sender=Record) def save_record(sender, instance, created = None, **kwargs): - print("AFTER SAVE.... ") - print(sender, created) - print(instance) - print(kwargs) - print(".................") - print(instance.log_file.path, type(instance.log_file)) if created: - filepath = instance.log_file.path print(filepath) @@ -37,10 +31,6 @@ def save_record(sender, instance, created = None, **kwargs): if type(metadata) is not dict: metadata = {} - print("MEDATADA") - print(type(metadata)) - print(metadata) - metadata['log_device_info'] = {} metadata['log_runs_count'] = 0 @@ -95,24 +85,23 @@ def save_record(sender, instance, created = None, **kwargs): } df = pd.read_csv(instance.log_file.path, sep = ',', header = None, names=range(max_size)) - print(df) + df = df [df[0] == '$HIST'] - df = df.drop(columns=[0, 1, 3, 4, 5, 6, 7]) + df = df.drop(columns=[0, 1, 3, 4, 5, 6, 7, 8]) new_columns = ['time'] + list(range(df.shape[1] - 1)) df.columns = new_columns + duration = df['time'].max() + instance.record_duration = datetime.timedelta(seconds=float(duration)) + new_name = instance.user_directory_path_data('pk') - print("BUDU TO UKLADAT DO ",'data/media/'+new_name) df.to_pickle('data/media/'+new_name) instance.data_file.name = new_name print(instance.data_file) - - print("Po ") - print(df) instance.metadata = json.dumps(metadata) instance.save() diff --git a/DOSPORTAL/static/img/login_background.png b/DOSPORTAL/static/img/login_background.png new file mode 100644 index 0000000..0126dc6 Binary files /dev/null and b/DOSPORTAL/static/img/login_background.png differ diff --git a/DOSPORTAL/templates/records/record_detail.html b/DOSPORTAL/templates/records/record_detail.html index 1914b11..ece1549 100644 --- a/DOSPORTAL/templates/records/record_detail.html +++ b/DOSPORTAL/templates/records/record_detail.html @@ -1,6 +1,5 @@ - - {% extends "base.html" %} +{% load filters %} {% block content %} @@ -8,284 +7,318 @@
-
- Record: {{record.id}} - - {% if user.is_authenticated %} - - {%endif%} - -
-
- - - +
+ Record: {{record.log_original_filename}} ({{record.id}}) + + {% if user.is_authenticated %} + + {%endif%} +
-
+
+ + + +
+
-

Graphs:

-
-
-
-
+
+

Graphs:

+
+
+
+
- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/DOSPORTAL/templates/records/records_list.html b/DOSPORTAL/templates/records/records_list.html index 87e3aad..b6d082f 100644 --- a/DOSPORTAL/templates/records/records_list.html +++ b/DOSPORTAL/templates/records/records_list.html @@ -1,5 +1,7 @@ {% extends "base.html" %} {% load martortags %} +{% load render_table from django_tables2 %} + {% block content %}
Recorded data
@@ -7,34 +9,11 @@ {{ records_list|length}} {% if user.is_authenticated %} - New record + New record {% endif %}
- {% if records_list %} - - - - - - - - - - - {% for record in records_list.reverse %} - - - - - - - + {% render_table table %} + - {% endfor %} - -
#NameTypeOrganization/Author
{{ forloop.counter }}{{ record | truncatechars:50 }}{{ record.record_type }}{{record.belongs}}, {{record.author}}
{{ record }}
- {% else %} -

There are no records in the database.

- {% endif %} {% endblock %} diff --git a/DOSPORTAL/templates/user/login.html b/DOSPORTAL/templates/user/login.html new file mode 100644 index 0000000..4b735a1 --- /dev/null +++ b/DOSPORTAL/templates/user/login.html @@ -0,0 +1,120 @@ +{% load crispy_forms_tags %} +{% load static %} + + + + + + + + + + DOSPORTAL: Login + + {% load bootstrap5 %} + {% bootstrap_css %} + {% bootstrap_javascript %} + + + + + + + + + +
+
+
{% csrf_token %} + +

Please sign in ...

+ + {{ form | crispy }} + + +

Universal Scientific Technologies s.r.o.
© 2023–2024

+
+
+
+ + + + + + \ No newline at end of file diff --git a/DOSPORTAL/templatetags/__init__.py b/DOSPORTAL/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DOSPORTAL/templatetags/filters.py b/DOSPORTAL/templatetags/filters.py new file mode 100644 index 0000000..18667bf --- /dev/null +++ b/DOSPORTAL/templatetags/filters.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + +@register.filter(name='filesize_mb') +def filesize_mb(value): + """Převede velikost souboru na megabajty.""" + return f"{value.size / (1024 * 1024):.2f} MB" diff --git a/DOSPORTAL/urls.py b/DOSPORTAL/urls.py index cee1467..d1ea3c8 100644 --- a/DOSPORTAL/urls.py +++ b/DOSPORTAL/urls.py @@ -23,12 +23,12 @@ import uuid -from .users.views_users import user_profile +from .users.views_users import user_profile, login_view from .users import urls as user_urls from .views import MeasurementsListView, MeasurementDetailView, MeasurementNewView, MeasurementNewView, MeasurementDataView, measuredDataGet, measuredSpectraGet, MeasurementRecordNewView from .views_detectors import DetectorView, DetectorEditView,DetectorOverview, DetectorNewLogbookRecord from .views_flights import FlightView -from .views_record import RecordsListView, RecordView, RecordNewView, GetSpectrum, GetEvolution +from .views_record import RecordsListView, RecordView, RecordNewView, GetSpectrum, GetEvolution, GetHistogram #from organizations.backends import invitation_backend @@ -37,6 +37,8 @@ path('admin/', admin.site.urls), path("accounts/", include("django.contrib.auth.urls")), + path('login/', login_view, name='login'), + #path(r'accounts/', include('organizations.urls')), #path(r'invitations/', include(invitation_backend().get_urls())), @@ -58,12 +60,13 @@ path('measurement//', MeasurementDetailView, name='measurement-detail'), - path('records/', RecordsListView.as_view(), name='records'), + path('records/', RecordsListView, name='records'), path('record/new/', RecordNewView, name='record-new'), path('record//', RecordView, name='record-view'), path('record//get_spectrum/', GetSpectrum, name='record-GetSpectrum'), path('record//get_evolution/', GetEvolution, name='record-GetEvolution'), + path('record//get_histogram/', GetHistogram, name='record-GetHistogram'), path('flight//', FlightView, name='flight-detail'), diff --git a/DOSPORTAL/users/views_users.py b/DOSPORTAL/users/views_users.py index 29e798b..f3e887d 100644 --- a/DOSPORTAL/users/views_users.py +++ b/DOSPORTAL/users/views_users.py @@ -10,6 +10,9 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User +from django.contrib.auth import authenticate, login + +from ..forms import LoginForm @login_required def user_profile(request, username = None): @@ -22,4 +25,21 @@ def user_profile(request, username = None): 'user': user } - return render(request, 'user/user_profile.html', context) \ No newline at end of file + return render(request, 'user/user_profile.html', context) + + + + +def login_view(request): + if request.method == 'POST': + form = LoginForm(request.POST) + if form.is_valid(): + user = authenticate(request, username=form.cleaned_data['username'], password=form.cleaned_data['password']) + if user is not None: + login(request, user) + return redirect('home') + else: + return render(request, 'login.html', {'form': form, 'error': 'Invalid username or password'}) + else: + form = LoginForm() + return render(request, 'user/login.html', {'form': form}) \ No newline at end of file diff --git a/DOSPORTAL/views_record.py b/DOSPORTAL/views_record.py index f912bbd..d9a4194 100644 --- a/DOSPORTAL/views_record.py +++ b/DOSPORTAL/views_record.py @@ -13,6 +13,17 @@ from django.views import generic from django.views.generic import ListView +from django_filters.views import FilterView +from django_tables2.views import SingleTableMixin, SingleTableView +import django_tables2 as tables +from django_tables2.utils import Accessor +from django_tables2 import RequestConfig +from django.utils.html import format_html +from django.urls import reverse + + +import itertools + import pandas as pd from .forms import RecordForm @@ -20,15 +31,42 @@ FIRST_CHANNEL = 10 -class RecordsListView(generic.ListView): - model = Record - context_object_name = 'records_list' - queryset = Record.objects.all() - template_name = 'records/records_list.html' +# class RecordsListView(generic.ListView): +# model = Record +# context_object_name = 'records_list' +# queryset = Record.objects.all() +# template_name = 'records/records_list.html' + +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# return context + + + +class RecordTable(tables.Table): + row_number = tables.Column(empty_values=(), verbose_name='#') + link = tables.LinkColumn('record-view', args=[tables.A('pk')], verbose_name='Link', accessor='pk', attrs={'a': {'target': '_blank'}}) + + class Meta: + model = Record + fields = ("row_number", "belongs", "author", "data_policy", "log_original_filename") # replace with your field names + + def render_row_number(self, record): + self.row_number = getattr(self, 'row_number', itertools.count(self.page.start_index())) + return format_html('{}', reverse('record-view', args=[record.pk]), next(self.row_number)) + + + +def RecordsListView(request): + table = RecordTable(Record.objects.all(), + template_name="django_tables2/bootstrap5-responsive.html") + + table.paginate(page=request.GET.get("page", 1), per_page=25) + + return render(request, "records/records_list.html", { + "table": table + }) - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - return context def handle_uploaded_file(f, file): @@ -126,13 +164,13 @@ def RecordNewView(request): pk = data.pk data.author = request.user - print(data) + #print(data) data.save() return redirect("record-view", pk=pk) else: - print("Form není validni") + print("Form neni validni") print(form.errors) return render(request, 'records/record_new.html', {'form': form}) @@ -147,25 +185,69 @@ def RecordView(request, pk): def GetSpectrum(request, pk): - print("Get Spectrum", pk) + minEnergy = request.GET.get('minEnergy', 'nan') # nan string je tu z duvodu, ze to je vychozi hodnota v js + maxEnergy = request.GET.get('maxEnergy', 'nan') - record = Record.objects.filter(pk=pk) - print(record) + #print("Get Spectrum", pk) + record = Record.objects.filter(pk=pk) df = pd.read_pickle(record[0].data_file.path) - print(df) + # if not (minEnergy != 'nan' and maxEnergy != 'nan'): + # minEnergy = float(minEnergy) + # maxEnergy = float(maxEnergy) + # df = df[(df['energy'] > minEnergy) & (df['energy'] < maxEnergy)] + + total_time = df['time'].max() + sums = df.drop('time', axis=1).sum().to_frame('counts') + + sums_list = sums + sums_list['channel'] = sums_list.index + + sums_list = sums_list[['channel', 'counts']].apply(tuple, axis=1).tolist() - return JsonResponse({'data': 'data'}) + return JsonResponse({'spectrum_values': sums_list, 'total_time': total_time}) def GetEvolution(request, pk): - record_o = Record.objects.filter(pk=pk) - print(record_o) - - + minTime = request.GET.get('minTime', 'nan') # nan string je tu z duvodu, ze to je vychozi hodnota v js + maxTime = request.GET.get('maxTime', 'nan') + + record = Record.objects.filter(pk=pk) + df = pd.read_pickle(record[0].data_file.path) + + df['time'] = df['time'].astype(float) + + start_time = record[0].time_start.timestamp()*1000 + print(start_time) + + if not (minTime != 'nan' and maxTime != 'nan'): + minTime = (float(minTime)-start_time)*1000 + maxTime = (float(maxTime)-start_time)*1000 + df = df[(df['time'] >= minTime) & (df['time'] <= maxTime)] + + time = df['time'].astype(float).add(start_time)*1000 + sums = df.drop('time', axis=1).sum(axis=1) + + data = pd.DataFrame({'time': time, 'value': sums}) + data_list = data[['time', 'value']].apply(tuple, axis=1).tolist() + + + return JsonResponse({'evolution_values': data_list}) + +def GetHistogram(request, pk): + + record = Record.objects.filter(pk=pk) + df = pd.read_pickle(record[0].data_file.path).drop('time', axis=1) + + #print(df) + + data_list = [] + for row in df.iterrows(): + for column in df.columns: + data_list.append([column, row[0], row[1][column]]) - return JsonResponse({'data': 'data'}) \ No newline at end of file + return JsonResponse({'histogram_values': data_list[1:]}) \ No newline at end of file