Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/automation/db/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
Column("no_differing", Integer),
Column("date", DateTime),
Column("comparison_status", Enum(ComparisonStatus)),
Column("note", String),
# Column for distinguishing between ResultCommit and ResultVersion
Column("type", String(20)),
# ResultCommit specific columns
Expand Down Expand Up @@ -182,6 +183,13 @@ def _result_fun_list_to_dict(self, result: ResultSubType | None) -> None:
if hasattr(result, "_functions_list"):
delattr(result, "_functions_list")

def update(self, result: ResultSubType) -> None:
"""Update a function result in the database."""
with Session() as session:
# Merge the detached object back into the session
session.merge(result)
session.commit()


class FunctionResultsRepo:
"""Repository for working with function results in DB."""
Expand Down
2 changes: 2 additions & 0 deletions backend/automation/models/results/commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(
date: Optional[datetime] = None,
functions: Optional[Dict[str, FunctionResult]] = None,
comparison_status: ComparisonStatus = ComparisonStatus.SUCCESS,
note: str = "",
):
"""
:param commit: For which commit are the results.
Expand All @@ -56,6 +57,7 @@ def __init__(
date=date,
functions=functions,
comparison_status=comparison_status,
note=note,
)

@classmethod
Expand Down
2 changes: 2 additions & 0 deletions backend/automation/models/results/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(
date: Optional[datetime] = None,
functions: Optional[Dict[str, FunctionResult]] = None,
comparison_status: ComparisonStatus = ComparisonStatus.SUCCESS,
note: str = "",
) -> None:
"""
:param name: Name of project (used primary for showing to user).
Expand All @@ -66,6 +67,7 @@ def __init__(
self.non_equal = non_equal
self.unknown = unknown
self.date = date if date else datetime.now()
self.note = note

self.functions = functions if functions is not None else {}
self.comparison_status = comparison_status
Expand Down
2 changes: 2 additions & 0 deletions backend/automation/models/results/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(
date: Optional[datetime] = None,
functions: Optional[Dict[str, FunctionResult]] = None,
comparison_status: ComparisonStatus = ComparisonStatus.SUCCESS,
note: str = "",
) -> None:
"""
:param old_tag: Old tag of the project, that was compared.
Expand All @@ -61,6 +62,7 @@ def __init__(
date=date,
functions=functions,
comparison_status=comparison_status,
note=note,
)

@classmethod
Expand Down
84 changes: 75 additions & 9 deletions backend/automation/web/api.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
"""File defining HTML REST API endpoints."""
from functools import wraps
from typing import Any, Callable

from flask import Blueprint, Response, abort, request, session

from automation.db import FunctionResultsRepo
from automation.db import FunctionResultsRepo, ResultsRepo
from automation.models.results.types import ExpectedResultType

api_bp = Blueprint("api_bp", __name__, url_prefix="/api")


def login_required(f: Callable[..., Response]) -> Callable[..., Response]:
"""Decorator to require user to be logged in."""
@wraps(f)
def decorated_function(*args: Any, **kwargs: Any) -> Response:
if "user" not in session:
abort(403)
return f(*args, **kwargs)
return decorated_function


@api_bp.put(
"/commits/<config_name>/<commit>/<diffkemp_sha>"
"/functions/<function_name>/expected-result"
)
@login_required
def update_fun_expected_res_for_commit(
config_name: str,
commit: str,
diffkemp_sha: str,
function_name: str,
) -> Response:
"""Update the expected result for a function."""
# Check if user is logged in
if "user" not in session:
abort(403)

repo = FunctionResultsRepo()
function_result = repo.getForCommit(
config_name=config_name,
Expand All @@ -45,6 +55,7 @@ def update_fun_expected_res_for_commit(
"/versions/<config_name>/<old_tag>/<new_tag>/<diffkemp_sha>"
"/functions/<function_name>/expected-result"
)
@login_required
def update_fun_expected_res_for_version(
config_name: str,
old_tag: str,
Expand All @@ -53,10 +64,6 @@ def update_fun_expected_res_for_version(
function_name: str,
) -> Response:
"""Update the expected result for a function."""
# Check if user is logged in
if "user" not in session:
abort(403)

repo = FunctionResultsRepo()
function_result = repo.getForVersion(
config_name=config_name,
Expand All @@ -75,3 +82,62 @@ def update_fun_expected_res_for_version(

repo.update(function_result)
return Response(status=200)


@api_bp.put(
"/commits/<config_name>/<commit>/<diffkemp_sha>"
"/note"
)
@login_required
def update_res_note_for_commit(
config_name: str,
commit: str,
diffkemp_sha: str,
) -> Response:
"""Update the result note for a commit result."""
repo = ResultsRepo()
results = repo.get_commits(
config_file_name=config_name,
commit=commit,
diffkemp_sha=diffkemp_sha,
)

if not results:
abort(404)

result = results[0]
note = request.json["note"]

result.note = note
repo.update(result)
return Response(status=200)


@api_bp.put(
"/versions/<config_name>/<old_tag>/<new_tag>/<diffkemp_sha>"
"/note"
)
def update_res_note_for_version(
config_name: str,
old_tag: str,
new_tag: str,
diffkemp_sha: str,
) -> Response:
"""Update the result note for version result."""
repo = ResultsRepo()
results = repo.get_versions(
config_file_name=config_name,
old_tag=old_tag,
new_tag=new_tag,
diffkemp_sha=diffkemp_sha,
)

if not results:
abort(404)

result = results[0]
note = request.json["note"]

result.note = note
repo.update(result)
return Response(status=200)
2 changes: 2 additions & 0 deletions backend/automation/web/dtos/results/commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class ResultDTO:
differences_url: Optional[str]
viewer_url: Optional[str]
commit_info_url: str
note: str

@classmethod
def create(
Expand Down Expand Up @@ -129,6 +130,7 @@ def create(
differences_url=differences_url,
viewer_url=viewer_url,
commit_info_url=commit_info_url,
note=result.note,
)

def get_function_bg_class(self, function_name: str) -> str:
Expand Down
2 changes: 2 additions & 0 deletions backend/automation/web/dtos/results/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ResultDTO:
result_url: str
differences_url: Optional[str]
viewer_url: Optional[str]
note: str

@classmethod
def create(
Expand Down Expand Up @@ -101,6 +102,7 @@ def create(
result_url=result_url,
differences_url=differences_url,
viewer_url=viewer_url,
note=result.note,
)

def get_function_bg_class(self, function_name: str) -> str:
Expand Down
13 changes: 12 additions & 1 deletion backend/automation/web/templates/commits/result.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "commits/layout.html" %}
{% from "macros.html" import expected_fun_result_select %}
{% from "macros.html" import expected_fun_result_select, result_note_text_area %}

{% block subcontent %}
<h2>Results</h2>
Expand All @@ -11,6 +11,17 @@ <h2>Results</h2>
<li><b>Commit summary</b>: {{ result.commit.summary }}</li>
</ul>

{{ result_note_text_area(
result.note,
url_for(
'api_bp.update_res_note_for_commit',
config_name=result.config_name,
commit=result.commit.SHA,
diffkemp_sha=result.diffkemp_sha,
)
)
}}

<h3>Compared functions</h3>

<table class="table">
Expand Down
38 changes: 36 additions & 2 deletions backend/automation/web/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@
// to the one set before it was changed by the user.
// This is just in case error occurs in the backend.
const currentExpected = selectElement.dataset.currentExpected;
console.log(currentExpected);
fetch(url, {
method: "PUT",
headers: {
Expand All @@ -126,7 +125,6 @@
// Success, update the current expected result
selectElement.dataset.currentExpected = expectedResult;
}).catch(() => {
console.log("here");
alert(
"Error occurred while changing the expected result for function " +
`'${selectElement.dataset.function}' from '${currentExpected}' to ` +
Expand All @@ -137,6 +135,42 @@
selectElement.value = currentExpected;
});
}

function updateNoteForResult() {
const noteTextArea = document.getElementById('result-note');
const nextNote = noteTextArea.value;
const url = noteTextArea.dataset.apiUrl;
const currentNote = noteTextArea.dataset.currentNote;
fetch(url, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ "note": nextNote})
}).then((response) => {
if (!response.ok) {
throw new Error('Something went wrong');
}
// Success, update the current note
noteTextArea.dataset.currentNote = nextNote;
}).catch(() => {
alert(
"Error occurred while updating note for the result.\n" +
"Please try it again."
);
});
}

window.addEventListener('beforeunload', (e) => {
const noteTextArea = document.getElementById('result-note');
if (noteTextArea) {
const hasUnsavedNote = (noteTextArea.value !== noteTextArea.dataset.currentNote);
if (hasUnsavedNote) {
e.preventDefault();
e.returnValue = "";
}
}
});
</script>

{% block scripts %}{% endblock %}
Expand Down
16 changes: 16 additions & 0 deletions backend/automation/web/templates/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@
{% endif %}
</div>
{% endmacro %}

{% macro result_note_text_area(note, api_url) %}
<div>
<b>Note:</b>
<textarea class="form-control" {{ 'disabled' if 'user' not in session else ''}}
id="result-note"
data-api-url="{{ api_url }}"
data-current-note="{{ note }}"
rows="3"
>{{ note }}</textarea>
<button class="mt-1 btn btn-outline-primary {{ 'disabled' if 'user' not in session else ''}}"
onclick="updateNoteForResult()">
Update
</button>
</div>
{% endmacro %}
14 changes: 13 additions & 1 deletion backend/automation/web/templates/versions/result.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "versions/layout.html" %}
{% from "macros.html" import expected_fun_result_select %}
{% from "macros.html" import expected_fun_result_select, result_note_text_area %}

{% block subcontent %}

Expand All @@ -11,6 +11,18 @@ <h2>Results</h2>
<li><b>New tag</b>: {{ result.new_tag }}</li>
</ul>

{{ result_note_text_area(
result.note,
url_for(
'api_bp.update_res_note_for_version',
config_name=result.config_name,
old_tag=result.old_tag,
new_tag=result.new_tag,
diffkemp_sha=result.diffkemp_sha,
)
)
}}

<h3>Compared functions</h3>

<table class="table">
Expand Down