Skip to content

Commit

Permalink
Introduce comments for marks
Browse files Browse the repository at this point in the history
  • Loading branch information
vmordan committed Nov 17, 2023
1 parent ad8d64a commit 1b3fde8
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 10 deletions.
27 changes: 27 additions & 0 deletions web/marks/static/marks/css/comments.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* CVV is a continuous verification visualizer.
* Copyright (c) 2023 ISP RAS (http://www.ispras.ru)
* Ivannikov Institute for System Programming of the Russian Academy of Sciences
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* ee the License for the specific language governing permissions and
* limitations under the License.
*/

blockquote {
font-style: italic;
position: relative;
background: #f9f9f9;
border-left: 2px solid #ccc;
margin: 0px;
padding: 3px;
margin-left: 20px;
}
108 changes: 108 additions & 0 deletions web/marks/static/marks/js/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* CVV is a continuous verification visualizer.
* Copyright (c) 2023 ISP RAS (http://www.ispras.ru)
* Ivannikov Institute for System Programming of the Russian Academy of Sciences
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* ee the License for the specific language governing permissions and
* limitations under the License.
*/

function clear_reply_form(mark_id) {
let previous_comment = document.getElementById('edit_comment_id_' + mark_id);
if (previous_comment.value) {
document.getElementById("comment_text_" + previous_comment.value).style.background = "#ffffff";
}
previous_comment.value = "";
}

function show_comments(mark_id) {
$('.comment.collapsed').each(function () { !$(this).toggleClass("collapsed")});
$('#show_comments_btn_' + mark_id).modal('hide');
}

function reply_comment(comment_id, username, mark_id) {
const orig_text = document.getElementById("comment_text_" + comment_id).innerHTML;
document.getElementById('new_comment_field_' + mark_id).value = username + ":<blockquote>" + orig_text + "</blockquote>\n";
$("#new_comment_field_" + mark_id).focus();
clear_reply_form(mark_id);
}

function edit_comment(comment_id, mark_id) {
const orig_text = document.getElementById("comment_text_" + comment_id).innerHTML;
document.getElementById('new_comment_field_' + mark_id).value = orig_text;
$("#new_comment_field_" + mark_id).focus();
clear_reply_form(mark_id);
document.getElementById('edit_comment_id_' + mark_id).value = comment_id;
document.getElementById("comment_text_" + comment_id).style.background = "#f9f9f9";
}

function new_comment(mark_id) {
let comment_id = document.getElementById('edit_comment_id_' + mark_id).value;
var desc = $('#new_comment_field_' + mark_id).val();
if (!desc) {
return;
}
const root_comment = $('#comments_root_' + mark_id);

var cmt_data = {
description: desc, mark_id: mark_id, comment_id: comment_id
};

$.post('/marks/create-comment/', cmt_data, function (data) {
if (data.error) {
err_notify(data.error);
return false;
}
if (comment_id) {
// Edit comment
document.getElementById("comment_text_" + comment_id).innerHTML = desc;
} else {
// New comment
const user_name = data['user_name'];
const user_id = data['user_id'];
comment_id = data['comment_id'];
let new_comment = document.createElement('div');
new_comment.className = 'comment';
new_comment.innerHTML = `
<div id="comment_${comment_id}" class="content">
<a href="/users/profile/${user_id}" class="author">${user_name}</a>
<div class="metadata">
<span class="date">Now</span>
</div>
<div id="comment_text_${comment_id}" class="text">${desc}</div>
<div class="actions">
<a class="reply" onclick="reply_comment('${comment_id}', '${user_name}', '${mark_id}')">Reply</a>
<a class="edit" onclick="edit_comment(${comment_id}, ${mark_id})">Edit</a>
<a class="delete" onclick="delete_comment(${comment_id}, ${mark_id})">Delete</a>
</div>
</div>`;
root_comment.prepend(new_comment);
}
clear_reply_form(mark_id);
document.getElementById('new_comment_field_' + mark_id).value = "";
});
}

function delete_comment(comment_id, mark_id) {
var cmt_data = {
comment_id: comment_id
};
$.post('/marks/delete-comment/', cmt_data, function (data) {
if (data.error) {
err_notify(data.error);
return false;
}
clear_reply_form(mark_id);
let removed_comment = document.getElementById("comment_" + comment_id);
removed_comment.parentNode.removeChild(removed_comment);
});
}
5 changes: 5 additions & 0 deletions web/marks/static/marks/js/inlineMarkForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
* limitations under the License.
*/

function show_comments_for_mark(mark_id) {
$('#comments_' + mark_id).modal('setting', 'closable', false);
$('#comments_' + mark_id).modal('show');
}

function get_description() {
var tmp_div = $('<div>').html($('#mark_description').val());
tmp_div.find('script').remove();
Expand Down
7 changes: 5 additions & 2 deletions web/marks/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
MarkSafeTag, MarkUnsafeTag, UnknownProblem
from marks.querysets import ListQuery
from marks.tags import TagsInfo
from marks.utils import UNSAFE_COLOR, SAFE_COLOR, STATUS_COLOR, MarkAccess
from marks.utils import UNSAFE_COLOR, SAFE_COLOR, STATUS_COLOR, MarkAccess, get_mark_comments
from reports.mea.wrapper import COMPARISON_FUNCTIONS, CONVERSION_FUNCTIONS
from reports.models import ReportSafe, ReportUnsafe, ReportUnknown
from users.utils import DEF_NUMBER_OF_ELEMENTS, ALL_ATTRS
Expand Down Expand Up @@ -317,7 +317,10 @@ def __get_values(self):
elif col == 'description' and len(mark_rep.mark.description) > 0:
val = mark_rep.mark.description
elif col == 'buttons':
val = (mark_rep.mark_id, mark_rep.type)
comments = []
if self.type == 'unsafe':
comments = get_mark_comments(mark_rep.mark.comments, self.user)
val = (mark_rep.mark_id, mark_rep.type, comments)
href = '%sedit?report_to_redirect=%s' % (
reverse('marks:mark', args=[self.type, mark_rep.mark_id]), self.report.pk
)
Expand Down
62 changes: 62 additions & 0 deletions web/marks/templates/marks/Comments.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{% comment "License" %}
% CVV is a continuous verification visualizer.
% Copyright (c) 2023 ISP RAS (http://www.ispras.ru)
% Ivannikov Institute for System Programming of the Russian Academy of Sciences
%
% Copyright (c) 2018 ISP RAS (http://www.ispras.ru)
% Ivannikov Institute for System Programming of the Russian Academy of Sciences
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
{% endcomment %}

{% load i18n %}

{% load static %}
<link href="{% static 'marks/css/comments.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'marks/js/comments.js' %}"></script>

<div class="ui gray segment fluid">
<h4 class="ui top left attached gray label">{% trans 'Comments' %}</h4>
<div id="comments_root_{{mark_id}}" class="ui minimal comments" style="max-width: 100%">
{% for comment in comments %}
<div id="comment_{{comment.3}}" class="comment{% if not comment.5 and show_short_comments_list %} collapsed{% endif %}">
<div class="content">
<a href="{% url 'users:show_profile' comment.0.id %}" class="author">{{comment.0.username}}</a>
<div class="metadata">
<span class="date">{{comment.1}}</span>
</div>
<div id="comment_text_{{comment.3}}" class="text">{{comment.2|safe}}</div>
<div class="actions" style="align-items: left">
<a class="reply" onclick="reply_comment('{{comment.3}}', '{{comment.0.username}}', '{{mark_id}}')">{% trans 'Reply' %}</a>
{% if comment.4 %}
<a class="edit" onclick="edit_comment({{comment.3}}, {{mark_id}})">{% trans 'Edit' %}</a>
<a class="delete" onclick="delete_comment({{comment.3}}, {{mark_id}})">{% trans 'Delete' %}</a>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% if comments|length > 3 and show_short_comments_list %}
<div id="show_comments_btn_{{mark_id}}" class="ui icon small basic compact button" onclick="show_comments({{mark_id}})">{% trans 'Show all' %}</div>
{% endif %}
<form class="ui reply form">
<div class="field">
<textarea id="new_comment_field_{{mark_id}}" placeholder="{% trans 'Comment' %}..." rows="2" style="resize:none; height: 100%" maxlength="512"></textarea>
</div>
<div class="ui green button small" onclick="new_comment({{mark_id}})">
<i class="icon edit"></i> {% trans 'Create comment' %}
</div>
</form>
</div>
</div>
<input id="edit_comment_id_{{mark_id}}" type="hidden" value="">
13 changes: 7 additions & 6 deletions web/marks/templates/marks/Mark.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@
</div>
{% else %}
{% if markdata.tags.table %}
<div class="ui horizontal segments">
<div class="ui pink segment">
<h4 class="header ui pink">{% trans 'Tags' %}</h4>
{% include 'marks/MarkTags.html' with tags=markdata.tags.table available=markdata.tags.available selected=markdata.tags.selected %}
</div>
<div class="ui pink segment">
<h4 class="ui top left attached pink label">{% trans 'Tags' %}</h4>
{% include 'marks/MarkTags.html' with tags=markdata.tags.table available=markdata.tags.available selected=markdata.tags.selected %}
</div>
{% endif %}
{% endif %}
{% if markdata.type == 'unsafe' %}
{% include 'marks/Comments.html' with comments=comments mark_id=mark.pk show_short_comments_list=True %}
{% endif %}
{% if markdata.type == 'unsafe' %}
<div class="ui styled accordion fluid">
<div class="title"><i class="file outline icon"></i>{% trans 'Converted error trace' %}</div>
Expand Down Expand Up @@ -90,7 +91,7 @@ <h4 class="header ui pink">{% trans 'Tags' %}</h4>
{% endif %}

<tr>
<td class="right aligned" width="15%">{% trans 'Status' %}</td>
<td class="right aligned" width="33%">{% trans 'Status' %}</td>
<td>
{% for s in markdata.statuses %}
{% if s.checked %}<strong style="color: {{ s.color }};">{{ s.title }}</strong>{% endif %}
Expand Down
4 changes: 4 additions & 0 deletions web/marks/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@
path('get_last_mark/', views.GetLastMark.as_view()),
path('get_converted_trace/<int:pk>/', views.GetConvertedTrace.as_view()),
path('check-unknown-mark/<int:pk>/', views.CheckUnknownMarkView.as_view()),

# Comments
path('create-comment/', views.CreateComment.as_view()),
path('delete-comment/', views.DeleteComment.as_view()),
]
10 changes: 10 additions & 0 deletions web/marks/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@
}


def get_mark_comments(comments, user):
results = []
counter = 0
for m in comments.order_by('-id'):
is_edit = m.author == user or user.is_staff
results.append((m.author, m.date, m.description, m.id, is_edit, counter < 3))
counter += 1
return results


class MarkAccess:

def __init__(self, user, mark=None, report=None):
Expand Down
58 changes: 56 additions & 2 deletions web/marks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from marks.Download import UploadMark, MarkArchiveGenerator, AllMarksGen, UploadAllMarks, PresetMarkFile
from marks.UnsafeUtils import decode_optimizations
from marks.models import MarkSafe, MarkUnsafe, MarkUnknown, MarkSafeHistory, MarkUnsafeHistory, MarkUnknownHistory, \
MarkUnsafeCompare, UnsafeTag, SafeTag, SafeTagAccess, UnsafeTagAccess, \
MarkUnsafeCompare, UnsafeTag, SafeTag, SafeTagAccess, UnsafeTagAccess, MarkUnsafeComment, \
MarkSafeReport, MarkUnsafeReport, MarkUnknownReport, MarkAssociationsChanges, ReportComponent
from marks.tables import MarkData, MarkChangesTable, MarkReportsTable, MarksList, AssociationChangesTable
from marks.tags import GetTagsData, GetParents, SaveTag, TagsInfo, CreateTagsFromFile, TagAccess
Expand Down Expand Up @@ -89,6 +89,10 @@ def get_context_data(self, **kwargs):
title += ': ' + m.comment
versions.append({'version': m.version, 'title': title})

comments = []
if self.kwargs['type'] == "unsafe":
comments = mutils.get_mark_comments(self.object.comments, self.request.user)

desc = {}
markdata = MarkData(self.kwargs['type'], mark_version=history_set.first())
edited_error_trace = None
Expand Down Expand Up @@ -125,7 +129,8 @@ def get_context_data(self, **kwargs):
'desc': desc,
'similarity': similarity,
'args': args, 'optimizations': optimizations,
'operators': ATTRIBUTES_OPERATORS
'operators': ATTRIBUTES_OPERATORS,
'comments': comments
}


Expand Down Expand Up @@ -699,6 +704,55 @@ def get_context_data(self, **kwargs):
return results


class CreateComment(LoggedCallMixin, Bview.JsonView):
def get_context_data(self, **kwargs):
description = self.request.POST['description']
mark_id = self.request.POST['mark_id']
comment_id = self.request.POST['comment_id']
context = {}
if comment_id:
# Edit comment
try:
mark_comment = MarkUnsafeComment.objects.get(id=comment_id)
if mark_comment.author == self.request.user or self.request.user.is_staff:
mark_comment.description = description
mark_comment.date = now()
mark_comment.save()
else:
raise BridgeException("You do not have an access to edit the selected comment")
except Exception as e:
logger.exception(e, stack_info=True)
else:
# Create new comment
mark = MarkUnsafe.objects.get(id=mark_id)
try:
mark_comment = MarkUnsafeComment.objects.create(
mark=mark, description=description,
author=self.request.user, date=now()
)
context['comment_id'] = mark_comment.id
context['user_name'] = self.request.user.username
context['user_id'] = self.request.user.id

except Exception as e:
logger.exception(e, stack_info=True)
return context


class DeleteComment(LoggedCallMixin, Bview.JsonView):
def get_context_data(self, **kwargs):
comment_id = self.request.POST['comment_id']
try:
mark_comment = MarkUnsafeComment.objects.get(id=comment_id)
if mark_comment.author == self.request.user or self.request.user.is_staff:
mark_comment.delete()
else:
raise BridgeException("You do not have an access to delete the selected comment")
except Exception as e:
logger.exception(e, stack_info=True)
return {}


class GetConvertedTrace(LoggedCallMixin, Bview.JsonDetailPostView):
model = ReportUnsafe

Expand Down
13 changes: 13 additions & 0 deletions web/reports/templates/reports/AssociatedMarksTable.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@
<span style="white-space: pre-wrap;">{{ v.value|safe }}</span>
{% elif v.column == 'buttons' %}
<div class="ui buttons">
{% if report_type == "unsafe" %}
<button onclick="show_comments_for_mark({{ v.value.0 }})" class="ui mini icon button violet basic note-popup" data-content="{% trans 'Show comments' %}"><i class="comment alternate icon"></i></button>
<div id="comments_{{ v.value.0 }}" class="ui modal">
{% include 'marks/Comments.html' with comments=v.value.2 mark_id=v.value.0 show_short_comments_list=True %}
<div class="actions">
<div class="ui grid">
<div class="eight wide column right aligned">
<button type="button" class="ui blue button small cancel">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
{% endif %}
<a class="ui mini icon button basic blue note-popup" href="{{ row_data.0.href }}" data-content="{% trans 'Show' %}"><i class="eye icon"></i></a>
<button id="inline_edit_mark_{{ v.value.0 }}" class="ui mini icon button teal basic note-popup" data-content="{% trans 'Adjust description' %}"><i class="edit icon"></i></button>
<a class="ui mini icon button teal note-popup" href="{{ v.href }}" data-content="{% trans 'Edit' %}"><i class="edit outline icon"></i></a>
Expand Down

0 comments on commit 1b3fde8

Please sign in to comment.