diff --git a/components/frontend/src/api/report.js b/components/frontend/src/api/report.js index 21cdd421ba..b52db57898 100644 --- a/components/frontend/src/api/report.js +++ b/components/frontend/src/api/report.js @@ -34,7 +34,10 @@ function get_changelog(nr_changes, uuids) { if (Object.keys(uuids).includes("subject_uuid")) { return fetch_server_api('get', `changelog/report/${uuids.report_uuid}/subject/${uuids.subject_uuid}/${nr_changes}`) } - return fetch_server_api('get', `changelog/report/${uuids.report_uuid}/${nr_changes}`) + if (Object.keys(uuids).includes("report_uuid")) { + return fetch_server_api('get', `changelog/report/${uuids.report_uuid}/${nr_changes}`) + } + return fetch_server_api('get', `changelog/${nr_changes}`) } export { diff --git a/components/frontend/src/changelog/ChangeLog.js b/components/frontend/src/changelog/ChangeLog.js index 7b62318f13..9610c4f9ac 100644 --- a/components/frontend/src/changelog/ChangeLog.js +++ b/components/frontend/src/changelog/ChangeLog.js @@ -12,7 +12,10 @@ function fetch_changelog(uuids, nrChanges, setChanges) { } export function ChangeLog(props) { - let scope = "Changes in this report"; + let scope = "Changes in this instance of Quality-time"; + if (props.report_uuid) { + scope = "Changes in this report" + } if (props.subject_uuid) { scope = "Changes to this subject"; } @@ -25,7 +28,10 @@ export function ChangeLog(props) { const [nrChanges, setNrChanges] = useState(10); const [changes, setChanges] = useState([]); useEffect(() => { - let uuids = { report_uuid: props.report.report_uuid }; + let uuids = {}; + if (props.report_uuid) { + uuids.report_uuid = props.report_uuid; + } if (props.subject_uuid) { uuids.subject_uuid = props.subject_uuid; } @@ -36,7 +42,7 @@ export function ChangeLog(props) { uuids.source_uuid = props.source_uuid; } fetch_changelog(uuids, nrChanges, setChanges) - }, [props.report.report_uuid, props.subject_uuid, props.metric_uuid, props.source_uuid, props.report.timestamp, nrChanges]); + }, [props.report_uuid, props.subject_uuid, props.metric_uuid, props.source_uuid, props.timestamp, nrChanges]); let rows = []; changes.forEach((change) => rows.push(
Comment with url https://google.com
'."""), self.report["delta"]) @@ -250,12 +254,11 @@ def test_post_comment_with_link(self, request): def test_post_position_first(self, request): """Test that a metric can be moved to the top of the list.""" request.json = dict(position="first") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid2", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID2, "position", self.database)) self.database.reports.insert.assert_called_once_with(self.report) + self.assertEqual([METRIC_ID2, METRIC_ID], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) self.assertEqual( - ["metric_uuid2", "metric_uuid"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) - self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid2", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID2, description="John changed the position of metric 'name2' of subject 'Subject' in report " "'Report' from '1' to '0'."), self.report["delta"]) @@ -263,12 +266,11 @@ def test_post_position_first(self, request): def test_post_position_last(self, request): """Test that a metric can be moved to the bottom of the list.""" request.json = dict(position="last") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID, "position", self.database)) self.database.reports.insert.assert_called_once_with(self.report) + self.assertEqual([METRIC_ID2, METRIC_ID], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) self.assertEqual( - ["metric_uuid2", "metric_uuid"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) - self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, description="John changed the position of metric 'name' of subject 'Subject' in report " "'Report' from '0' to '1'."), self.report["delta"]) @@ -276,12 +278,11 @@ def test_post_position_last(self, request): def test_post_position_previous(self, request): """Test that a metric can be moved up.""" request.json = dict(position="previous") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid2", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID2, "position", self.database)) self.database.reports.insert.assert_called_once_with(self.report) + self.assertEqual([METRIC_ID2, METRIC_ID], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) self.assertEqual( - ["metric_uuid2", "metric_uuid"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) - self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid2", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID2, description="John changed the position of metric 'name2' of subject 'Subject' in report " "'Report' from '1' to '0'."), self.report["delta"]) @@ -289,12 +290,11 @@ def test_post_position_previous(self, request): def test_post_position_next(self, request): """Test that a metric can be moved down.""" request.json = dict(position="next") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID, "position", self.database)) self.database.reports.insert.assert_called_once_with(self.report) + self.assertEqual([METRIC_ID2, METRIC_ID], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) self.assertEqual( - ["metric_uuid2", "metric_uuid"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) - self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, description="John changed the position of metric 'name' of subject 'Subject' in report " "'Report' from '0' to '1'."), self.report["delta"]) @@ -302,18 +302,16 @@ def test_post_position_next(self, request): def test_post_position_first_previous(self, request): """Test that moving the first metric up does nothing.""" request.json = dict(position="previous") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID, "position", self.database)) self.database.reports.insert.assert_not_called() - self.assertEqual( - ["metric_uuid", "metric_uuid2"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) + self.assertEqual([METRIC_ID, METRIC_ID2], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) def test_post_position_last_next(self, request): """Test that moving the last metric down does nothing.""" request.json = dict(position="next") - self.assertEqual(dict(ok=True), post_metric_attribute("report_uuid", "metric_uuid2", "position", self.database)) + self.assertEqual(dict(ok=True), post_metric_attribute(REPORT_ID, METRIC_ID2, "position", self.database)) self.database.reports.insert.assert_not_called() - self.assertEqual( - ["metric_uuid", "metric_uuid2"], list(self.report["subjects"]["subject_uuid"]["metrics"].keys())) + self.assertEqual([METRIC_ID, METRIC_ID2], list(self.report["subjects"][SUBJECT_ID]["metrics"].keys())) @patch("bottle.request") @@ -322,13 +320,13 @@ class PostSourceAttributeTest(unittest.TestCase): def setUp(self): self.report = dict( - _id="report_uuid", title="Report", - subjects=dict( - subject_uuid=dict( + _id=REPORT_ID, title="Report", + subjects={ + SUBJECT_ID: dict( name="Subject", - metrics=dict( - metric_uuid=dict( - name="Metric", type="type", sources=dict(source_uuid=dict(name="Source", type="type"))))))) + metrics={ + METRIC_ID: dict( + name="Metric", type="type", sources={SOURCE_ID: dict(name="Source", type="type")})})}) self.database = Mock() self.database.reports.find_one.return_value = self.report self.database.sessions.find_one.return_value = dict(user="Jenny") @@ -338,11 +336,10 @@ def setUp(self): def test_name(self, request): """Test that the source name can be changed.""" request.json = dict(name="New source name") - self.assertEqual(dict(ok=True), post_source_attribute("report_uuid", "source_uuid", "name", self.database)) + self.assertEqual(dict(ok=True), post_source_attribute(REPORT_ID, SOURCE_ID, "name", self.database)) self.database.reports.insert.assert_called_once_with(self.report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", - source_uuid="source_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, source_uuid=SOURCE_ID, description="Jenny changed the name of source 'Source' of metric 'Metric' of subject 'Subject' in " "report 'Report' from 'Source' to 'New source name'."), self.report["delta"]) @@ -350,11 +347,10 @@ def test_name(self, request): def test_post_source_type(self, request): """Test that the source type can be changed.""" request.json = dict(type="new_type") - self.assertEqual(dict(ok=True), post_source_attribute("report_uuid", "source_uuid", "type", self.database)) + self.assertEqual(dict(ok=True), post_source_attribute(REPORT_ID, SOURCE_ID, "type", self.database)) self.database.reports.insert.assert_called_once_with(self.report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", - source_uuid="source_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, source_uuid=SOURCE_ID, description="Jenny changed the type of source 'Source' of metric 'Metric' of subject 'Subject' in " "report 'Report' from 'type' to 'new_type'."), self.report["delta"]) @@ -366,14 +362,14 @@ class PostSourceParameterTest(unittest.TestCase): def setUp(self): self.report = dict( - _id="report_uuid", title="Report", - subjects=dict( - subject_uuid=dict( + _id=REPORT_ID, title="Report", + subjects={ + SUBJECT_ID: dict( name="Subject", - metrics=dict( - metric_uuid=dict( + metrics={ + METRIC_ID: dict( name="Metric", type="type", - sources=dict(source_uuid=dict(name="Source", type="type", parameters=dict()))))))) + sources={SOURCE_ID: dict(name="Source", type="type", parameters=dict())})})}) self.database = Mock() self.database.sessions.find_one.return_value = dict(user="Jenny") self.database.reports.find_one.return_value = self.report @@ -385,15 +381,14 @@ def test_url(self, mock_get, request): """Test that the source url can be changed and that the availability is checked.""" mock_get.return_value = MagicMock(status_code=123, reason='A good reason') request.json = dict(url="https://url") - response = post_source_parameter("report_uuid", "source_uuid", "url", self.database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", self.database) self.assertTrue(response['ok']) self.assertEqual(response['availability'], [{"status_code": 123, "reason": 'A good reason', - 'source_uuid': 'source_uuid', "parameter_key": 'url'}]) + 'source_uuid': SOURCE_ID, "parameter_key": 'url'}]) self.database.reports.insert.assert_called_once_with(self.report) mock_get.assert_called_once_with('https://url', auth=None) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", - source_uuid="source_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, source_uuid=SOURCE_ID, description="Jenny changed the url of source 'Source' of metric 'Metric' of subject 'Subject' in " "report 'Report' from '' to 'https://url'."), self.report["delta"]) @@ -403,14 +398,13 @@ def test_url_http_error(self, mock_get, request): """Test that the error is reported if a request exception occurs, while checking connection of a url.""" mock_get.side_effect = requests.exceptions.RequestException request.json = dict(url="https://url") - response = post_source_parameter("report_uuid", "source_uuid", "url", self.database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", self.database) self.assertTrue(response['ok']) self.assertEqual(response['availability'], [{"status_code": -1, "reason": 'Unknown error', - 'source_uuid': 'source_uuid', "parameter_key": 'url'}]) + 'source_uuid': SOURCE_ID, "parameter_key": 'url'}]) self.database.reports.insert.assert_called_once_with(self.report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", - source_uuid="source_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, source_uuid=SOURCE_ID, description="Jenny changed the url of source 'Source' of metric 'Metric' of subject 'Subject' in " "report 'Report' from '' to 'https://url'."), self.report["delta"]) @@ -421,15 +415,16 @@ def test_url_with_user(self, mock_get, request): database = self.database database.datamodels.find_one.return_value = dict(_id="id", sources=dict(type=dict(parameters=dict(url=dict( type="url"), username=dict(type="string"), password=dict(type="pwd"))))) - srcs = database.reports.find_one.return_value['subjects']['subject_uuid']['metrics']['metric_uuid']['sources'] - srcs['source_uuid']['parameters']['username'] = 'un' - srcs['source_uuid']['parameters']['password'] = 'pwd' + srcs = database.reports.find_one.return_value['subjects'][SUBJECT_ID]['metrics'][METRIC_ID]['sources'] + srcs[SOURCE_ID]['parameters']['username'] = 'un' + srcs[SOURCE_ID]['parameters']['password'] = 'pwd' mock_get.return_value = MagicMock(status_code=123, reason='A good reason') request.json = dict(url="https://url") - response = post_source_parameter("report_uuid", "source_uuid", "url", database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", database) self.assertTrue(response['ok']) - self.assertEqual(response['availability'], [{"status_code": 123, "reason": 'A good reason', - 'source_uuid': 'source_uuid', "parameter_key": 'url'}]) + self.assertEqual( + response['availability'], + [{"status_code": 123, "reason": 'A good reason', 'source_uuid': SOURCE_ID, "parameter_key": 'url'}]) self.database.reports.insert.assert_called_once_with(self.report) mock_get.assert_called_once_with('https://url', auth=('un', 'pwd')) @@ -441,7 +436,7 @@ def test_url_no_url_type(self, mock_get, request): type="string"), username=dict(type="string"), password=dict(type="pwd"))))) mock_get.return_value = MagicMock(status_code=123, reason='A good reason') request.json = dict(url="unimportant") - response = post_source_parameter("report_uuid", "source_uuid", "url", database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", database) self.assertEqual(response, dict(ok=True)) self.database.reports.insert.assert_called_once_with(self.report) mock_get.assert_not_called() @@ -452,7 +447,7 @@ def test_empty_url(self, request): database.datamodels.find_one.return_value = dict(_id="id", sources=dict(type=dict(parameters=dict(url=dict( type="url"), username=dict(type="string"), password=dict(type="pwd"))))) request.json = dict(url="") - response = post_source_parameter("report_uuid", "source_uuid", "url", database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", database) self.assertEqual(response, dict(ok=True)) self.database.reports.insert.assert_called_once_with(self.report) @@ -464,12 +459,13 @@ def test_url_with_token(self, mock_get, request): type="url"), username=dict(type="string"), private_token=dict(type="pwd"))))) mock_get.return_value = MagicMock(status_code=123, reason='A good reason') request.json = dict(url="https://url") - srcs = database.reports.find_one.return_value['subjects']['subject_uuid']['metrics']['metric_uuid']['sources'] - srcs['source_uuid']['parameters']['private_token'] = 'xxx' - response = post_source_parameter("report_uuid", "source_uuid", "url", database) + srcs = database.reports.find_one.return_value['subjects'][SUBJECT_ID]['metrics'][METRIC_ID]['sources'] + srcs[SOURCE_ID]['parameters']['private_token'] = 'xxx' + response = post_source_parameter(REPORT_ID, SOURCE_ID, "url", database) self.assertTrue(response['ok']) - self.assertEqual(response['availability'], [{"status_code": 123, "reason": 'A good reason', - 'source_uuid': 'source_uuid', "parameter_key": 'url'}]) + self.assertEqual( + response['availability'], + [{"status_code": 123, "reason": 'A good reason', 'source_uuid': SOURCE_ID, "parameter_key": 'url'}]) self.database.reports.insert.assert_called_once_with(self.report) mock_get.assert_called_once_with('https://url', auth=('xxx', '')) @@ -483,23 +479,22 @@ def test_urls_connection_on_update_other_field(self, mock_get, request): password=dict(type="password"))))) mock_get.side_effect = [MagicMock(status_code=123, reason='A good reason')] request.json = dict(password="changed") - srcs = database.reports.find_one.return_value['subjects']['subject_uuid']['metrics']['metric_uuid']['sources'] - srcs['source_uuid']['parameters']['url'] = "https://url" - response = post_source_parameter("report_uuid", "source_uuid", "password", database) + sources = database.reports.find_one.return_value['subjects'][SUBJECT_ID]['metrics'][METRIC_ID]['sources'] + sources[SOURCE_ID]['parameters']['url'] = "https://url" + response = post_source_parameter(REPORT_ID, SOURCE_ID, "password", database) self.assertTrue(response['ok']) self.assertEqual(response['availability'], [{"status_code": 123, "reason": 'A good reason', - 'source_uuid': 'source_uuid', "parameter_key": 'url'}]) + 'source_uuid': SOURCE_ID, "parameter_key": 'url'}]) self.database.reports.insert.assert_called_once_with(self.report) def test_password(self, request): """Test that the password can be changed and is not logged.""" request.json = dict(url="unimportant", password="secret") - response = post_source_parameter("report_uuid", "source_uuid", "password", self.database) + response = post_source_parameter(REPORT_ID, SOURCE_ID, "password", self.database) self.assertTrue(response['ok']) self.database.reports.insert.assert_called_once_with(self.report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", - source_uuid="source_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, source_uuid=SOURCE_ID, description="Jenny changed the password of source 'Source' of metric 'Metric' of subject 'Subject' in " "report 'Report' from '' to '******'."), self.report["delta"]) @@ -519,19 +514,19 @@ def setUp(self): def test_add_source(self): """Test that a new source is added.""" report = dict( - _id="report_uuid", title="Report", - subjects=dict( - subject_uuid=dict( + _id=REPORT_ID, title="Report", + subjects={ + SUBJECT_ID: dict( name="Subject", - metrics=dict( - metric_uuid=dict( + metrics={ + METRIC_ID: dict( name=None, type="metric_type", addition="sum", target="0", near_target="10", - debt_target=None, accept_debt=False, tags=[], sources=dict()))))) + debt_target=None, accept_debt=False, tags=[], sources=dict())})}) self.database.reports.find_one.return_value = report - self.assertEqual(dict(ok=True), post_source_new("report_uuid", "metric_uuid", self.database)) + self.assertEqual(dict(ok=True), post_source_new(REPORT_ID, METRIC_ID, self.database)) self.database.reports.insert.assert_called_once_with(report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, description="Jenny added a new source to metric 'Metric Type' of subject 'Subject' in report " "'Report'."), report["delta"]) @@ -539,18 +534,18 @@ def test_add_source(self): def test_delete_source(self): """Test that the source can be deleted.""" report = dict( - _id="report_uuid", title="Report", - subjects=dict( - subject_uuid=dict( + _id=REPORT_ID, title="Report", + subjects={ + SUBJECT_ID: dict( name="Subject", - metrics=dict( - metric_uuid=dict( - type="type", name="Metric", sources=dict(source_uuid=dict(name="Source"))))))) + metrics={ + METRIC_ID: dict( + type="type", name="Metric", sources={SOURCE_ID: dict(name="Source")})})}) self.database.reports.find_one.return_value = report - self.assertEqual(dict(ok=True), delete_source("report_uuid", "source_uuid", self.database)) + self.assertEqual(dict(ok=True), delete_source(REPORT_ID, SOURCE_ID, self.database)) self.database.reports.insert.assert_called_once_with(report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", metric_uuid="metric_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, metric_uuid=METRIC_ID, description="Jenny deleted the source 'Source' from metric 'Metric' of subject 'Subject' in report " "'Report'."), report["delta"]) @@ -570,38 +565,37 @@ def setUp(self): def test_add_metric(self): """Test that a metric can be added.""" - report = dict( - _id="report_uuid", title="Report", subjects=dict(subject_uuid=dict(name="Subject", metrics=dict()))) + report = dict(_id=REPORT_ID, title="Report", subjects={SUBJECT_ID: dict(name="Subject", metrics=dict())}) self.database.reports.find_one.return_value = report - self.assertEqual(dict(ok=True), post_metric_new("report_uuid", "subject_uuid", self.database)) + self.assertEqual(dict(ok=True), post_metric_new(REPORT_ID, SUBJECT_ID, self.database)) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, description="Jenny added a new metric to subject 'Subject' in report 'Report'."), report["delta"]) def test_get_metrics(self): """Test that the metrics can be retrieved and deleted reports are skipped.""" report = dict( - _id="id", report_uuid="report_uuid", - subjects=dict(subject_uuid=dict(metrics=dict(metric_uuid=dict(type="metric_type", tags=[]))))) + _id="id", report_uuid=REPORT_ID, + subjects={SUBJECT_ID: dict(metrics={METRIC_ID: dict(type="metric_type", tags=[])})}) self.database.reports_overviews.find_one.return_value = dict(_id="id", title="Reports", subtitle="") - self.database.reports.distinct.return_value = ["report_uuid", "deleted_report"] + self.database.reports.distinct.return_value = [REPORT_ID, "deleted_report"] self.database.reports.find_one.side_effect = [report, dict(deleted=True)] self.database.measurements.find.return_value = [dict( - _id="id", metric_uuid="metric_uuid", status="red", - sources=[dict(source_uuid="source_uuid", parse_error=None, connection_error=None, value="42")])] - self.assertEqual(dict(metric_uuid=dict(type="metric_type", tags=[])), get_metrics(self.database)) + _id="id", metric_uuid=METRIC_ID, status="red", + sources=[dict(source_uuid=SOURCE_ID, parse_error=None, connection_error=None, value="42")])] + self.assertEqual({METRIC_ID: dict(type="metric_type", tags=[])}, get_metrics(self.database)) def test_delete_metric(self): """Test that the metric can be deleted.""" report = dict( - _id="report_uuid", title="Report", - subjects=dict(subject_uuid=dict(name="Subject", metrics=dict(metric_uuid=dict(name="Metric"))))) + _id=REPORT_ID, title="Report", + subjects={SUBJECT_ID: dict(name="Subject", metrics={METRIC_ID: dict(name="Metric")})}) self.database.reports.find_one.return_value = report - self.assertEqual(dict(ok=True), delete_metric("report_uuid", "metric_uuid", self.database)) + self.assertEqual(dict(ok=True), delete_metric(REPORT_ID, METRIC_ID, self.database)) self.database.reports.insert.assert_called_once_with(report) self.assertEqual( - dict(report_uuid="report_uuid", subject_uuid="subject_uuid", + dict(report_uuid=REPORT_ID, subject_uuid=SUBJECT_ID, description=f"Jenny deleted metric 'Metric' from subject 'Subject' in report 'Report'."), report["delta"]) @@ -612,7 +606,7 @@ class SubjectTest(unittest.TestCase): def setUp(self): self.database = Mock() self.database.sessions.find_one.return_value = dict(user="Jenny") - self.report = dict(title="Report", subjects=dict(subject_uuid=dict(name="ABC"))) + self.report = dict(title="Report", subjects={SUBJECT_ID: dict(name="ABC")}) self.database.reports.find_one.return_value = self.report self.database.datamodels.find_one.return_value = dict() @@ -620,16 +614,16 @@ def test_add_subject(self): """Test that a subject can be added.""" self.database.datamodels.find_one.return_value = dict( _id="", subjects=dict(subject_type=dict(name="Subject", description=""))) - self.assertEqual(dict(ok=True), post_new_subject("report_uuid", self.database)) + self.assertEqual(dict(ok=True), post_new_subject(REPORT_ID, self.database)) self.assertEqual( - dict(report_uuid="report_uuid", description="Jenny created a new subject in report 'Report'."), + dict(report_uuid=REPORT_ID, description="Jenny created a new subject in report 'Report'."), self.report["delta"]) def test_delete_subject(self): """Test that a subject can be deleted.""" - self.assertEqual(dict(ok=True), delete_subject("report_uuid", "subject_uuid", self.database)) + self.assertEqual(dict(ok=True), delete_subject(REPORT_ID, SUBJECT_ID, self.database)) self.assertEqual( - dict(report_uuid="report_uuid", description="Jenny deleted the subject 'ABC' from report 'Report'."), + dict(report_uuid=REPORT_ID, description="Jenny deleted the subject 'ABC' from report 'Report'."), self.report["delta"]) @@ -646,7 +640,7 @@ def test_add_report(self): self.database.reports.insert.assert_called_once() inserted = self.database.reports.insert.call_args_list[0][0][0] self.assertEqual("New report", inserted["title"]) - self.assertEqual("Jenny created this report.", inserted["delta"]["description"]) + self.assertEqual("Jenny created a new report.", inserted["delta"]["description"]) def test_get_report(self): """Test that a report can be retrieved.""" @@ -655,28 +649,31 @@ def test_get_report(self): self.database.reports_overviews.find_one.return_value = dict(_id="id", title="Reports", subtitle="") self.database.measurements.find.return_value = [ dict( - _id="id", metric_uuid="metric_uuid", status="red", - sources=[dict(source_uuid="source_uuid", parse_error=None, connection_error=None, value="42")])] - self.database.reports.distinct.return_value = ["report_uuid"] + _id="id", metric_uuid=METRIC_ID, status="red", + sources=[dict(source_uuid=SOURCE_ID, parse_error=None, connection_error=None, value="42")])] + self.database.reports.distinct.return_value = [REPORT_ID] report = dict( - _id="id", report_uuid="report_uuid", - subjects=dict( - subject_uuid=dict( - metrics=dict( - metric_uuid=dict( + _id="id", report_uuid=REPORT_ID, + subjects={ + SUBJECT_ID: dict( + metrics={ + METRIC_ID: dict( type="metric_type", addition="sum", target="0", near_target="10", debt_target="0", - accept_debt=False, tags=["a"]))))) + accept_debt=False, tags=["a"])})}) self.database.reports.find_one.return_value = report report["summary"] = dict(red=0, green=0, yellow=0, grey=0, white=1) - report["summary_by_subject"] = dict(subject_uuid=dict(red=0, green=0, yellow=0, grey=0, white=1)) + report["summary_by_subject"] = {SUBJECT_ID: dict(red=0, green=0, yellow=0, grey=0, white=1)} report["summary_by_tag"] = {} self.assertEqual(dict(_id="id", title="Reports", subtitle="", reports=[report]), get_reports(self.database)) def test_delete_report(self): """Test that the report can be deleted.""" - report = dict(_id="1", report_uuid="report_uuid", title="Report") + self.database.datamodels.find_one.return_value = dict(_id="id") + report = dict(_id="1", report_uuid=REPORT_ID, title="Report") self.database.reports.find_one.return_value = report - self.assertEqual(dict(ok=True), delete_report("report_uuid", self.database)) + self.assertEqual(dict(ok=True), delete_report(REPORT_ID, self.database)) + inserted = self.database.reports.insert.call_args_list[0][0][0] + self.assertEqual("Jenny deleted the report 'Report'.", inserted["delta"]["description"]) @patch("bottle.request") def test_post_reports_attribute(self, request): @@ -693,21 +690,21 @@ def test_get_tag_report(self, request): _id="id", metrics=dict(metric_type=dict(default_scale="count"))) self.database.reports.find_one.return_value = None self.database.measurements.find.return_value = [] - self.database.reports.distinct.return_value = ["report_uuid"] + self.database.reports.distinct.return_value = [REPORT_ID] self.database.reports.find_one.return_value = dict( - _id="id", report_uuid="report_uuid", - subjects=dict( - subject_without_metrics=dict(metrics=dict()), - subject_uuid=dict( + _id="id", report_uuid=REPORT_ID, + subjects={ + "subject_without_metrics": dict(metrics=dict()), + SUBJECT_ID: dict( metrics=dict( metric_with_tag=dict(type="metric_type", tags=["tag"]), - metric_without_tag=dict(type="metric_type", tags=["other tag"]))))) + metric_without_tag=dict(type="metric_type", tags=["other tag"])))}) self.assertEqual( dict( summary=dict(red=0, green=0, yellow=0, grey=0, white=1), summary_by_tag=dict(tag=dict(red=0, green=0, yellow=0, grey=0, white=1)), - summary_by_subject=dict(subject_uuid=dict(red=0, green=0, yellow=0, grey=0, white=1)), + summary_by_subject={SUBJECT_ID: dict(red=0, green=0, yellow=0, grey=0, white=1)}, title='Report for tag "tag"', subtitle="Note: tag reports are read-only", report_uuid="tag-tag", - timestamp=date_time, subjects=dict( - subject_uuid=dict(metrics=dict(metric_with_tag=dict(type="metric_type", tags=["tag"]))))), + timestamp=date_time, subjects={ + SUBJECT_ID: dict(metrics=dict(metric_with_tag=dict(type="metric_type", tags=["tag"])))}), get_tag_report("tag", self.database)) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ecaade7058..36b5f68e48 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add keep-alive messages to the server-sent events stream so it does not time out when there are no new measurements for a while. Fixes [#787](https://github.com/ICTU/quality-time/issues/787). +### Added + +- In addition to a changelog per report, also keep a changelog for the reports overview. Closes [#746](https://github.com/ICTU/quality-time/issues/746). + ## [0.17.0] - [2019-11-10] ### Fixed