Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edf fields #215

Merged
merged 20 commits into from
Nov 24, 2022
6 changes: 5 additions & 1 deletion battDB/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ class Meta:
"name",
"machine",
"notes",
"settings_file",
"binary_file",
]
help_texts = {
"machine": mark_safe(
Expand Down Expand Up @@ -382,10 +384,12 @@ def __init__(self, *args, **kwargs):

self.helper.layout = Layout(
Div(
Div(HTML(f"<h1> {mode} file </h1>")),
Div(HTML(f"<h1> {mode} experiment data </h1>")),
Column("name", css_class="col-6"),
Column("machine", css_class="col-6"),
fieldset,
Column("settings_file", css_class="col-6"),
Column("binary_file", css_class="col-6"),
Field("notes"),
HTML("<br>"),
Field("make_public"),
Expand Down
39 changes: 39 additions & 0 deletions battDB/migrations/0031_experimentdatafile_binary_file_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 4.1.3 on 2022-11-21 12:07

from django.db import migrations, models

import common.validators


class Migration(migrations.Migration):

dependencies = [
("battDB", "0030_experiment_external_link_experiment_summary"),
]

operations = [
migrations.AddField(
model_name="experimentdatafile",
name="binary_file",
field=models.FileField(
blank=True,
help_text="Binary file version of this data output by the cycler (if available)",
null=True,
upload_to="uploaded_files",
validators=[common.validators.validate_binary_file],
verbose_name="Binary file",
),
),
migrations.AddField(
model_name="experimentdatafile",
name="settings_file",
field=models.FileField(
blank=True,
help_text="Input settings file for the cycler used to produce this data (if available)",
null=True,
upload_to="uploaded_files",
validators=[common.validators.validate_settings_file],
verbose_name="Settings file",
),
),
]
26 changes: 25 additions & 1 deletion battDB/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

import common.models as cm
import dfndb.models as dfn
from common.validators import validate_pdf_file
from common.validators import (
validate_binary_file,
validate_pdf_file,
validate_settings_file,
)
from parsing_engines import available_parsing_engines, parse_data_file


Expand Down Expand Up @@ -522,6 +526,26 @@ class ExperimentDataFile(cm.BaseModel):
help_text="Test protocol used in this experiment",
)

settings_file = models.FileField(
upload_to="uploaded_files",
null=True,
blank=True,
validators=(validate_settings_file,),
verbose_name="Settings file",
help_text="Input settings file for the cycler used to produce this data (if "
"available)",
)

binary_file = models.FileField(
upload_to="uploaded_files",
null=True,
blank=True,
validators=(validate_binary_file,),
verbose_name="Binary file",
help_text="Binary file version of this data output by the cycler (if "
"available)",
)

def num_cycles(self):
return (
self.ranges.filter(step_action="cycle").count()
Expand Down
16 changes: 12 additions & 4 deletions battDB/templates/experiment.html
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,9 @@ <h5>Measurement data: {{data_file.name}}</h5>
<!--Display the actual plots-->
<div class="tab-content" id="nav-tabContent">
{% if not plot_set %}
<br>
<li class="list-group-item list-group-item-warning">This file was uploaded without being processed. You can still
download the raw data file using the button below.</li>
<br>
<li class="list-group-item list-group-item-warning">This file was uploaded without being processed. You can still
download the raw data file using the button below.</li>
{% endif %}
{% for plot in plot_set %}
{% if forloop.first %}
Expand All @@ -192,6 +192,14 @@ <h5>Measurement data: {{data_file.name}}</h5>
<!--Buttons for downloading, editing and deleting data-->
<a href=/battDB/data_files/download/{{data_file.pk}} class="btn btn-primary" , role="button"> Download raw data
file</a>
{% if data_file.settings_file %}
<a href=/battDB/settings_files/download/{{data_file.pk}} class="btn btn-secondary" , role="button"> Download
settings file</a>
{% endif %}
{% if data_file.binary_file %}
<a href=/battDB/binary_files/download/{{data_file.pk}} class="btn btn-secondary" , role="button"> Download binary
file</a>
{% endif %}
{% if "change_experimentdatafile" in df_perms %}
<a href="/battDB/data_files/delete/{{data_file.pk}}" class="btn btn-danger" , role="button"> Delete </a>
<a href="/battDB/exps/update_data/{{data_file.pk}}" class="btn btn-warning" , role="button"> Edit </a>
Expand Down Expand Up @@ -266,4 +274,4 @@ <h5>Metadata</h5>
<a href="/battDB/exps/delete/{{ object.pk }}" class="btn btn-danger" , role="button"> Delete entry</a>
{% endif %}

{% endblock %}
{% endblock %}
12 changes: 12 additions & 0 deletions battDB/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
DeleteExperimentView,
DeviceSpecificationTableView,
DeviceSpecificationView,
DownloadBinaryFileView,
DownloadRawDataFileView,
DownloadSettingsFileView,
DownloadSpecFileView,
EquipmentTableView,
EquipmentView,
Expand Down Expand Up @@ -87,6 +89,16 @@
DownloadRawDataFileView.as_view(),
name="Download File",
),
path(
"binary_files/download/<int:pk>/",
DownloadBinaryFileView.as_view(),
name="Download Binary File",
),
path(
"settings_files/download/<int:pk>/",
DownloadSettingsFileView.as_view(),
name="Download Settings File",
),
path(
"spec_files/download/<int:pk>/",
DownloadSpecFileView.as_view(),
Expand Down
35 changes: 24 additions & 11 deletions battDB/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ def post(self, request, *args, **kwargs):
instance=self.object,
)
context = self.get_context_data()
formset = context["raw_data_file"]
if form.is_valid():
# Save instance incluing setting user owner and status
with transaction.atomic():
Expand All @@ -272,16 +271,6 @@ def post(self, request, *args, **kwargs):
else:
self.object.status = "private"
self.object.save()
# Save individual parameters from inline form
if formset.is_valid():
formset.instance = self.object
# Handle uploaded files in formsets slightly differently to usual
formset[0].instance.status = self.object.status
if formset[0].instance.use_parser:
formset[0].instance.parse = True
formset.save()
form.instance.full_clean()

messages.success(request, self.success_message)
# Redirect to experiment detail view or stay on form if "add another"
if "another" in request.POST:
Expand Down Expand Up @@ -564,6 +553,30 @@ def get(self, request, *args, **kwargs):
return redirect(f"{blob_url}?{sas_token}")


class DownloadBinaryFileView(PermissionRequiredMixin, DetailView):
model = ExperimentDataFile
permission_required = "battDB.view_experimentdatafile"

def get(self, request, *args, **kwargs):
self.object = self.get_object()
blob_name = self.object.binary_file.name
blob_url = get_blob_url(self.object.binary_file)
sas_token = generate_sas_token(blob_name)
return redirect(f"{blob_url}?{sas_token}")


class DownloadSettingsFileView(PermissionRequiredMixin, DetailView):
model = ExperimentDataFile
permission_required = "battDB.view_experimentdatafile"

def get(self, request, *args, **kwargs):
self.object = self.get_object()
blob_name = self.object.settings_file.name
blob_url = get_blob_url(self.object.settings_file)
sas_token = generate_sas_token(blob_name)
return redirect(f"{blob_url}?{sas_token}")


class DownloadSpecFileView(PermissionRequiredMixin, DetailView):
model = DeviceSpecification
permission_required = "battDB.view_devicespecification"
Expand Down
43 changes: 43 additions & 0 deletions common/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,46 @@ def validate_pdf_file(file) -> None:
raise ValidationError(
"Unsupported file type and/or extension - must be a PDF file."
)


def validate_settings_file(file) -> None:
"""
Validates the settings file against the mime type and extension.

Args:
file (FileField): File to validate.

Raises:
ValidationError if validation not passed.
"""
allowed_mime_and_extensions = [
("text/plain", ".txt"),
("text/plain", ".mps"),
]
extension = os.path.splitext(file.name)[1]
file_mime_type = magic.from_buffer(file.read(1024), mime=True)
if (file_mime_type, extension) not in allowed_mime_and_extensions:
raise ValidationError(
"Unsupported file type and/or extension - must be a text file."
)


def validate_binary_file(file) -> None:
"""
Validates the binary file against the mime type and extension.

Args:
file (FileField): File to validate.

Raises:
ValidationError if validation not passed.
"""
allowed_mime_and_extensions = [
("application/octet-stream", ".mpr"),
]
extension = os.path.splitext(file.name)[1]
file_mime_type = magic.from_buffer(file.read(1024), mime=True)
if (file_mime_type, extension) not in allowed_mime_and_extensions:
raise ValidationError(
"Unsupported file type and/or extension - must be a binary file."
)
2 changes: 2 additions & 0 deletions tests/battDB/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ def test_definition(self):
self.assertTrue(hasattr(self.model, "machine"))
self.assertTrue(hasattr(self.model, "devices"))
self.assertTrue(hasattr(self.model, "protocol"))
self.assertTrue(hasattr(self.model, "binary_file"))
self.assertTrue(hasattr(self.model, "settings_file"))

def test_num_cycles(self):
self.assertEqual(self.model.num_cycles(), 0)
Expand Down
Loading