Skip to content
Open
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
3 changes: 2 additions & 1 deletion source/app/blueprints/graphql/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def mutate(root, info, case_id, name=None, soc_id=None, classification_id=None,
permissions_check_current_user_has_some_case_access(case_id, [CaseAccessLevel.full_access])

case = cases_get_by_identifier(case_id)
previous_state_id = case.state_id

# If user tries to update the customer, check if the user has access to the new customer
if request.get('case_customer') and request.get('case_customer') != case.client_id:
Expand All @@ -197,5 +198,5 @@ def mutate(root, info, case_id, name=None, soc_id=None, classification_id=None,
updated_case = add_case_schema.load(request, instance=case, partial=True)
protagonists = request.get('protagonists')
tags = request.get('case_tags')
case = cases_update(case, updated_case, protagonists, tags)
case = cases_update(case, updated_case, protagonists, tags, previous_state_id=previous_state_id)
return CaseUpdate(case=case)
Original file line number Diff line number Diff line change
Expand Up @@ -394,14 +394,14 @@ <h5>Protagonist</h5>
</datalist>

<div class="modal-footer">
<button type="button" class="btn btn-outline-danger " onclick="remove_case('{{ data.case_id }}');"
<button type="button" class="btn btn-outline-danger case-info-readonly-action" onclick="remove_case('{{ data.case_id }}');"
id="delete_case_info">Delete case</button>

{% if not data.close_date %}
<button type="button" class="btn btn-outline-warning mr-auto" onclick="close_case('{{ data.case_id }}');"
<button type="button" class="btn btn-outline-warning mr-auto case-info-readonly-action" onclick="close_case('{{ data.case_id }}');"
id="close_case_info">Close case</button>
{% else %}
<button type="button" class="btn btn-success" onclick="reopen_case('{{ data.case_id }}');"
<button type="button" class="btn btn-success mr-auto case-info-readonly-action" onclick="reopen_case('{{ data.case_id }}');"
id="reopen_case_info">Reopen case</button>
{% endif %}
<button type="button" class="btn btn-outline-dark mr-2" style="display:none;" id="cancel_case_info" onclick="cancel_case_edit();">Cancel</button>
Expand Down
3 changes: 2 additions & 1 deletion source/app/blueprints/rest/manage/manage_cases_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def update_case_info(identifier):
case_schema = CaseSchema()
try:
case = cases_get_by_identifier(identifier)
previous_state_id = case.state_id

request_data = request.get_json()
# If user tries to update the customer, check if the user has access to the new customer
Expand All @@ -296,7 +297,7 @@ def update_case_info(identifier):

protagonists = request_data.get('protagonists')
tags = request_data.get('case_tags')
case = cases_update(case, updated_case, protagonists, tags)
case = cases_update(case, updated_case, protagonists, tags, previous_state_id=previous_state_id)
return response_success('Updated', data=case_schema.dump(case))
except ValidationError as e:
return response_error('Data error', e.messages)
Expand Down
3 changes: 2 additions & 1 deletion source/app/blueprints/rest/v2/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def update(self, identifier):

try:
case = cases_get_by_identifier(identifier)
previous_state_id = case.state_id

request_data = request.get_json()

Expand All @@ -151,7 +152,7 @@ def update(self, identifier):

protagonists = request_data.get('protagonists')
tags = request_data.get('case_tags')
case = cases_update(case, updated_case, protagonists, tags)
case = cases_update(case, updated_case, protagonists, tags, previous_state_id=previous_state_id)
result = self._schema.dump(case)
return response_api_success(result)
except ValidationError as e:
Expand Down
6 changes: 3 additions & 3 deletions source/app/business/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ def cases_delete(case_identifier):
raise BusinessProcessingError('Cannot delete the case. Please check server logs for additional informations')


def cases_update(case: Cases, updated_case, protagonists, tags) -> Cases:
def cases_update(case: Cases, updated_case, protagonists, tags, previous_state_id=None) -> Cases:
try:
closed_state_id = get_case_state_by_name('Closed').state_id
previous_case_state = case.state_id
previous_case_state = case.state_id if previous_state_id is None else previous_state_id
case_previous_reviewer_id = case.reviewer_id
db.session.commit()

Expand Down Expand Up @@ -198,7 +198,7 @@ def cases_update(case: Cases, updated_case, protagonists, tags) -> Cases:

elif previous_case_state == closed_state_id and updated_case.state_id != closed_state_id:
track_activity('case re-opened', caseid=case.case_id)
res = reopen_case(case.case_id)
res = reopen_case(case.case_id, updated_case.state_id)
if not res:
raise BusinessProcessingError('Tried to re-open an non-existing case')

Expand Down
7 changes: 5 additions & 2 deletions source/app/datamgmt/manage/manage_cases_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,18 @@ def map_alert_resolution_to_case_status(case_status_id):
return None


def reopen_case(case_id):
def reopen_case(case_id, state_id=None):
res = Cases.query.filter(
Cases.case_id == case_id
).first()

if res:
res.close_date = None

res.state_id = get_case_state_by_name('Open').state_id
if state_id is None:
state_id = get_case_state_by_name('Open').state_id

res.state_id = state_id

db.session.commit()
return res
Expand Down
66 changes: 66 additions & 0 deletions tests/tests_rest_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ def _get_case_with_identifier(response, identifier):
raise ValueError('Case not found')


def _get_case_state_identifier_by_name(subject, state_name):
response = subject.get('/manage/case-states/list').json()
for case_state in response['data']:
if case_state['state_name'].lower() == state_name.lower():
return case_state['state_id']

raise ValueError(f'Case state {state_name} not found')


def _get_non_closed_case_state_identifier(subject):
response = subject.get('/manage/case-states/list').json()
open_state_identifier = None

for case_state in response['data']:
state_name = case_state['state_name'].lower()
if state_name == 'open':
open_state_identifier = case_state['state_id']
if state_name not in ('closed', 'open'):
return case_state['state_id']

if open_state_identifier is not None:
return open_state_identifier

raise ValueError('No non-closed case state found')


class TestsRestCases(TestCase):

def setUp(self) -> None:
Expand Down Expand Up @@ -252,3 +278,43 @@ def test_close_case_should_set_closing_date(self):
identifier = self._subject.create_dummy_case()
response = self._subject.create(f'/manage/cases/close/{identifier}', {}).json()
self.assertIsNotNone(response['data']['close_date'])

def test_update_case_state_to_closed_should_set_closing_date(self):
identifier = self._subject.create_dummy_case()
closed_state_identifier = _get_case_state_identifier_by_name(self._subject, 'Closed')
response = self._subject.update(f'/api/v2/cases/{identifier}', {'state_id': closed_state_identifier}).json()

self.assertEqual(closed_state_identifier, response['state']['state_id'])
self.assertIsNotNone(response['close_date'])

def test_manage_update_case_state_to_closed_should_set_closing_date(self):
identifier = self._subject.create_dummy_case()
closed_state_identifier = _get_case_state_identifier_by_name(self._subject, 'Closed')
response = self._subject.create(
f'/manage/cases/update/{identifier}',
{'state_id': closed_state_identifier}
).json()

self.assertEqual(closed_state_identifier, response['data']['state_id'])
self.assertIsNotNone(response['data']['close_date'])

def test_update_case_state_to_non_closed_should_clear_closing_date(self):
identifier = self._subject.create_dummy_case()
reopened_state_identifier = _get_non_closed_case_state_identifier(self._subject)
self._subject.create(f'/manage/cases/close/{identifier}', {})
response = self._subject.update(f'/api/v2/cases/{identifier}', {'state_id': reopened_state_identifier}).json()

self.assertEqual(reopened_state_identifier, response['state']['state_id'])
self.assertIsNone(response['close_date'])

def test_manage_update_case_state_to_non_closed_should_clear_closing_date(self):
identifier = self._subject.create_dummy_case()
reopened_state_identifier = _get_non_closed_case_state_identifier(self._subject)
self._subject.create(f'/manage/cases/close/{identifier}', {})
response = self._subject.create(
f'/manage/cases/update/{identifier}',
{'state_id': reopened_state_identifier}
).json()

self.assertEqual(reopened_state_identifier, response['data']['state_id'])
self.assertIsNone(response['data']['close_date'])
71 changes: 61 additions & 10 deletions ui/src/pages/manage.cases.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function case_detail(id) {
}

/* Close case function */
function close_case(id) {
function confirm_close_case(id, on_confirm) {
swal({
title: "Are you sure?",
text: "Case ID " + id + " will be closed and will not appear in contexts anymore",
Expand All @@ -36,18 +36,24 @@ function close_case(id) {
confirmButtonText: 'Yes, close it!'
})
.then((willClose) => {
if (willClose) {
post_request_api(`/manage/cases/close/${id}`)
.done((data) => {
if (!refresh_case_table()) {
window.location.reload();
}
$('#modal_case_detail').modal('hide');
});
if (willClose && on_confirm) {
on_confirm();
}
});
}

function close_case(id) {
confirm_close_case(id, function () {
post_request_api(`/manage/cases/close/${id}`)
.done((data) => {
if (!refresh_case_table()) {
window.location.reload();
}
$('#modal_case_detail').modal('hide');
});
});
}

/* Reopen case function */
function reopen_case(id) {
post_request_api(`/manage/cases/reopen/${id}`)
Expand Down Expand Up @@ -107,6 +113,10 @@ function remove_case(id) {
function edit_case_info() {
$('#case_gen_info_content').hide();
$('#case_gen_info_edit').show();
if ($('#case_state').attr('data-initial-state-id') === undefined) {
$('#case_state').attr('data-initial-state-id', $('#case_state').val());
}
$('.case-info-readonly-action').hide();
$('#cancel_case_info').show();
$('#save_case_info').show();
$('#case_info').hide();
Expand All @@ -115,13 +125,54 @@ function edit_case_info() {
function cancel_case_edit() {
$('#case_gen_info_content').show();
$('#case_gen_info_edit').hide();
$('.case-info-readonly-action').show();
$('#cancel_case_info').hide();
$('#save_case_info').hide();
$('#case_info').show();
}

function get_closed_case_state_identifier() {
var closed_state_option = $('#case_state option').filter(function () {
return $(this).text().trim().toLowerCase() === 'closed';
}).first();

if (closed_state_option.length === 0) {
return NaN;
}

return parseInt(closed_state_option.val(), 10);
}

function save_case_edit(case_id) {
var previous_state_identifier = parseInt($('#case_state').attr('data-initial-state-id'), 10);
var selected_state_identifier = parseInt($('#case_state').val(), 10);
var closed_state_identifier = get_closed_case_state_identifier();

if (!Number.isNaN(previous_state_identifier) &&
!Number.isNaN(selected_state_identifier) &&
!Number.isNaN(closed_state_identifier)) {
var is_closing_case = previous_state_identifier !== closed_state_identifier &&
selected_state_identifier === closed_state_identifier;
var is_reopening_case = previous_state_identifier === closed_state_identifier &&
selected_state_identifier !== closed_state_identifier;

if (is_closing_case) {
confirm_close_case(case_id, function () {
submit_case_edit(case_id);
});
return;
}

if (is_reopening_case) {
submit_case_edit(case_id);
return;
}
}

submit_case_edit(case_id);
}

function submit_case_edit(case_id) {
var data_sent = $('form#form_update_case').serializeObject();
var map_protagonists = Object();

Expand Down Expand Up @@ -381,4 +432,4 @@ function remove_cases_access_user(user_id, cases, on_finish) {
}
});

}
}