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}}
- - Detector: {{ record.detector }}
-
- Record start time: {{ record.time_start }}
-
- Record duration: {{ record.record_duration }}
- - Type: {{ record.record_type }}
-
- Organization: {{ record.belongs }}, {{record.data_policy}}, {{record.author}}
- - Advanced metadata:
- {{ record.metadata }}
- - Description: {{record}}
- {#
- File size: {{record.log_file.size}} #}
-
-
- {{record.log_file}}
- - {{record.log_file.size}}
- - {{record.log_file.url}}
- - {{record.log_original_filename}}
- - {{record.data_file}}
-
+
-
+
+
+
+
+ - Detector: {{ record.detector }}
+
- Record start time: {{ record.time_start }}
+
- Record duration: {{ record.record_duration }}
+ - Type: {{ record.record_type }}
+
- Organization: {{ record.belongs }}, {{record.data_policy}}, {{record.author}}
+ - Advanced metadata:
+ {{ record.metadata }}
+ - Description: {{record}}
+
- Log file: {{record.log_original_filename}} ({{ record.log_file | filesize_mb }})
+
+
- {{record.log_file}}
+ - {{record.log_file.size}}
+ - {{record.log_file.url}}
+ - {{record.log_original_filename}}
+ - {{record.data_file}}
+
+
+
-
-{% 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 %}
-
-
-
- # |
- Name |
- Type |
- Organization/Author |
-
-
-
- {% for record in records_list.reverse %}
-
- {{ forloop.counter }} |
- {{ record | truncatechars:50 }} |
- {{ record.record_type }} |
- {{record.belongs}}, {{record.author}} |
- {{ record }} |
-
+ {% render_table table %}
+
- {% endfor %}
-
-
- {% 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 %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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