diff --git a/tracex_project/db.sqlite3 b/tracex_project/db.sqlite3 index beddcb41..5bedc0f8 100644 Binary files a/tracex_project/db.sqlite3 and b/tracex_project/db.sqlite3 differ diff --git a/tracex_project/db_results/templates/db_results_overview.html b/tracex_project/db_results/templates/db_results_overview.html index cbf6d18e..fcc79376 100644 --- a/tracex_project/db_results/templates/db_results_overview.html +++ b/tracex_project/db_results/templates/db_results_overview.html @@ -3,7 +3,7 @@ Database Results {% load static %} - + Database Results LogoWelcome to the Database Results - + diff --git a/tracex_project/db_results/templates/evaluation.html b/tracex_project/db_results/templates/evaluation.html index f1052508..c212fb5a 100644 --- a/tracex_project/db_results/templates/evaluation.html +++ b/tracex_project/db_results/templates/evaluation.html @@ -6,7 +6,7 @@ Evaluation View {% load static %} - + @@ -17,7 +17,7 @@

Evaluation View

- +
- + - + \ No newline at end of file diff --git a/tracex_project/tracex/templates/landing_page.html b/tracex_project/tracex/templates/landing_page.html index 12cc6aaa..1f6a9035 100644 --- a/tracex_project/tracex/templates/landing_page.html +++ b/tracex_project/tracex/templates/landing_page.html @@ -1,10 +1,22 @@ + + TracEX {% load static %} - + @@ -17,25 +29,25 @@
- + Trace Testing Environment diff --git a/tracex_project/tracex/tests/test_utils.py b/tracex_project/tracex/tests/test_utils.py index b5d0bc47..a45e28af 100644 --- a/tracex_project/tracex/tests/test_utils.py +++ b/tracex_project/tracex/tests/test_utils.py @@ -104,7 +104,7 @@ class DataframeUtilitiesTests(TestCase): fixtures = ["tracex_project/tracex/fixtures/dataframe_fixtures.json"] def test_get_events_df(self): - """Tests if get_events_df returns all events in a dataframe correctly.""" + """Test if get_events_df returns all events in a dataframe correctly.""" events_df = DataFrameUtilities.get_events_df() self.assertIsInstance(events_df, pd.DataFrame) diff --git a/tracex_project/tracex/urls.py b/tracex_project/tracex/urls.py index 8772a057..ebb9ea4e 100644 --- a/tracex_project/tracex/urls.py +++ b/tracex_project/tracex/urls.py @@ -3,7 +3,7 @@ from django.urls import include, path from django.conf import settings from django.conf.urls.static import static -from . import views +from tracex import views urlpatterns = [ path("", include("patient_journey_generator.urls")), diff --git a/tracex_project/tracex/views.py b/tracex_project/tracex/views.py index 06db54b5..a16d518c 100644 --- a/tracex_project/tracex/views.py +++ b/tracex_project/tracex/views.py @@ -1,7 +1,16 @@ -"""This file contains the views for the landing page of the tracex app.""" +""" +Provide class-based views for the tracex app. + +Views: +TracexLandingPage -- View for the landing page of the tracex app. +ResetApiKey -- View for the resetting the API key. +DownloadXesView -- View for the download of XES file(s). +""" import os +import traceback import zipfile from tempfile import NamedTemporaryFile +from typing import List from django.views import View from django.http import HttpResponse, FileResponse @@ -13,31 +22,32 @@ class TracexLandingPage(TemplateView): """View for the landing page of the tracex app.""" + template_name = "landing_page.html" - def get(self, _request, *_args, **kwargs): - """Handles GET requests by initializing a form for API key entry and adding it to the context.""" - form = ApiKeyForm() - context = self.get_context_data(**kwargs) - context['form'] = form + def get(self, _request, *_args, **_kwargs): + """Handle GET requests by initializing a form for API key entry and adding it to the context.""" + context = self.get_context_data(**_kwargs) + context['form'] = ApiKeyForm() return self.render_to_response(context) def post(self, request): - """Handles POST requests by processing a submitted form containing an API key. - If valid, saves the API key to the environment and redirects to the landing page; - otherwise, renders the form with errors.""" + """Handle POST requests by processing a submitted form containing an API key.""" form = ApiKeyForm(request.POST) if form.is_valid(): api_key = form.cleaned_data['api_key'] - os.environ['OPENAI_API_KEY'] = api_key # This sets it for the current process only + os.environ['OPENAI_API_KEY'] = api_key + return redirect('landing_page') - return render(request, self.template_name, {'form': form}) + context = self.get_context_data() + context['form'] = form + + return render(request, self.template_name, context) def get_context_data(self, **kwargs): - """Retrieves and returns the base context data enhanced with the presence check for the API key. - Indicates if a prompt for the API key is needed based on its absence.""" + """Return context data and adds a flag indicating whether an API key is set.""" context = super().get_context_data(**kwargs) self.request.session.flush() api_key = os.getenv('OPENAI_API_KEY') @@ -47,35 +57,45 @@ def get_context_data(self, **kwargs): class ResetApiKey(RedirectView): - """View for the resetting the API key.""" + """View for resetting the API key.""" url = reverse_lazy("landing_page") def get(self, request, *args, **kwargs): - """Handles GET requests by deleting the API key from the environment and redirecting to the landing page.""" - del os.environ['OPENAI_API_KEY'] + """Handle GET requests by deleting the API key from the environment and redirecting to the landing page.""" + try: + del os.environ['OPENAI_API_KEY'] + except KeyError as e: + return render( + self.request, + 'error_page.html', + {'type': type(e).__name__,'error_traceback': traceback.format_exc()} + ) return super().get(request, *args, **kwargs) class DownloadXesView(View): - """Download one or more XES files based on the types specified in POST request, - bundled into a ZIP file if multiple.""" + """View for the downloading of XES file(s).""" def post(self, request, *_args, **_kwargs): - """Processes a POST request to download specified trace types as XES files. - Validates trace types and prepares the appropriate file response.""" + """ + Process a POST request to download specified trace types as XES files. + + Validates trace types and returns the appropriate file response. + """ trace_types = self.get_trace_types(request) if not trace_types: return HttpResponse("No file type specified.", status=400) files_to_download = self.collect_files(request, trace_types) - if ( - files_to_download is None - ): # Check for None explicitly to handle error scenario + if files_to_download is None: return HttpResponse("One or more files could not be found.", status=404) - return self.prepare_response(files_to_download) + if len(files_to_download) == 1: + return self.single_file_response(files_to_download[0]) + + return self.zip_files_response(files_to_download) @staticmethod def get_trace_types(request): @@ -83,7 +103,7 @@ def get_trace_types(request): return request.POST.getlist("trace_type[]") - def collect_files(self, request, trace_types): + def collect_files(self, request, trace_types: List[str]): """Collects file for the specified trace types to download, checking for their existence.""" files_to_download = [] for trace_type in trace_types: @@ -92,7 +112,7 @@ def collect_files(self, request, trace_types): if os.path.exists(file_path): files_to_download.append(file_path) else: - return None # Return None if any file path is invalid + return None return files_to_download @@ -100,40 +120,28 @@ def collect_files(self, request, trace_types): def process_trace_type(request, trace_type): """Process and provide the XES files to be downloaded based on the trace type.""" - def prepare_response(self, files_to_download): - """Prepares the appropriate response based on the number of files to be downloaded.""" - if len(files_to_download) == 1: - return self.single_file_response(files_to_download[0]) - - return self.zip_files_response(files_to_download) - # pylint: disable=consider-using-with @staticmethod def single_file_response(file_path): - """Prepares a file if there is only a single XES file.""" - file = open(file_path, "rb") - response = FileResponse(file, as_attachment=True) - response[ - "Content-Disposition" - ] = f'attachment; filename="{os.path.basename(file_path)}"' + """Prepare a single XES file for download.""" + file_name = os.path.basename(file_path) + response = FileResponse(open(file_path, "rb"), as_attachment=True) + response["Content-Disposition"] = f'attachment; filename="{file_name}"' return response @staticmethod def zip_files_response(files_to_download): - """Prepares a zip file if there are multiple XES files using a temporary file.""" - temp_zip = NamedTemporaryFile(mode="w+b", suffix=".zip", delete=False) - zipf = zipfile.ZipFile(temp_zip, "w", zipfile.ZIP_DEFLATED) - for file_path in files_to_download: - zipf.write(file_path, arcname=os.path.basename(file_path)) - zipf.close() - temp_zip_path = temp_zip.name - temp_zip.close() - - file = open(temp_zip_path, "rb") - response = FileResponse(file, as_attachment=True) - response[ - "Content-Disposition" - ] = 'attachment; filename="downloaded_xes_files.zip"' + """Prepare a ZIP file for multiple files to download.""" + with NamedTemporaryFile(mode="w+b", suffix=".zip", delete=False) as temp_zip: + with zipfile.ZipFile(temp_zip, "w", zipfile.ZIP_DEFLATED) as zipf: + for file_path in files_to_download: + file_name = os.path.basename(file_path) + zipf.write(file_path, arcname=file_name) + + temp_zip.close() + + response = FileResponse(open(temp_zip.name, "rb"), as_attachment=True) + response["Content-Disposition"] = 'attachment; filename="downloaded_files.zip"' return response