diff --git a/alembic/versions/1f24c9c8efa5_store_the_user_who_closed_an_issue_in_.py b/alembic/versions/1f24c9c8efa5_store_the_user_who_closed_an_issue_in_.py new file mode 100644 index 0000000..29c963b --- /dev/null +++ b/alembic/versions/1f24c9c8efa5_store_the_user_who_closed_an_issue_in_.py @@ -0,0 +1,31 @@ +"""Store the user who closed an issue in the db + +Revision ID: 1f24c9c8efa5 +Revises: 6a3ed02ee160 +Create Date: 2018-12-04 13:02:57.101095 + +""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "1f24c9c8efa5" +down_revision = "6a3ed02ee160" + + +def upgrade(): + + op.add_column( + "issues", + sa.Column( + "closed_by_id", + sa.Integer, + sa.ForeignKey("users.id", onupdate="CASCADE"), + ), + ) + + +def downgrade(): + op.drop_column("issues", "closed_by_id") diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 4c529b1..68c7e1c 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -249,6 +249,7 @@ def api_new_issue(repo, username=None, namespace=None): "blocks": [], "close_status": null, "closed_at": null, + "closed_by": null, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -465,6 +466,7 @@ def api_view_issues(repo, username=None, namespace=None): "blocks": ["1"], "close_status": null, "closed_at": null, + "closed_by": null, "comments": [], "content": "asd", "custom_fields": [], diff --git a/pagure/api/user.py b/pagure/api/user.py index c8c1f4b..15ddbc8 100644 --- a/pagure/api/user.py +++ b/pagure/api/user.py @@ -286,6 +286,7 @@ def api_view_user_issues(username): "blocks": [], "close_status": null, "closed_at": null, + "closed_by": null, "comments": [], "content": "Test Issue", "custom_fields": [], @@ -314,6 +315,7 @@ def api_view_user_issues(username): "blocks": [], "close_status": null, "closed_at": null, + "closed_by": null, "comments": [], "content": "Test Issue", "custom_fields": [], diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 1e6d9f4..6f4252b 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -1244,6 +1244,11 @@ class Issue(BASE): sa.DateTime, nullable=False, default=datetime.datetime.utcnow ) closed_at = sa.Column(sa.DateTime, nullable=True) + closed_by_id = sa.Column( + sa.Integer, + sa.ForeignKey("users.id", onupdate="CASCADE"), + nullable=True, + ) project = relation( "Project", @@ -1279,6 +1284,13 @@ class Issue(BASE): viewonly=True, ) + closed_by = relation( + "User", + foreign_keys=[closed_by_id], + remote_side=[User.id], + backref="closed_issues", + ) + def __repr__(self): return "Issue(%s, project:%s, user:%s, title:%s)" % ( self.id, @@ -1436,6 +1448,9 @@ class Issue(BASE): "priority": self.priority, "milestone": self.milestone, "custom_fields": custom_fields, + "closed_by": self.closed_by.to_json(public=public) + if self.closed_by + else None, } comments = [] diff --git a/pagure/lib/query.py b/pagure/lib/query.py index ecf7434..529e5ef 100644 --- a/pagure/lib/query.py +++ b/pagure/lib/query.py @@ -2026,6 +2026,7 @@ def edit_issue( issue.status = status if status.lower() != "open": issue.closed_at = datetime.datetime.utcnow() + issue.closed_by_id = user_obj.id elif issue.close_status: issue.close_status = None close_status = Unspecified diff --git a/pagure/templates/issue.html b/pagure/templates/issue.html index 117c2c5..e3a01f8 100644 --- a/pagure/templates/issue.html +++ b/pagure/templates/issue.html @@ -90,8 +90,10 @@ {% endif %} {{ issue.closed_at |humanize }} + {% if issue.closed_by %} by - {{ issue.user.user }}. + {{ issue.closed_by.user }}. + {% endif %} Opened {{ issue.date_created |humanize }} diff --git a/tests/test_pagure_flask_api_issue.py b/tests/test_pagure_flask_api_issue.py index 8feb4cf..39290ea 100644 --- a/tests/test_pagure_flask_api_issue.py +++ b/tests/test_pagure_flask_api_issue.py @@ -40,6 +40,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], @@ -63,6 +64,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -86,6 +88,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -109,6 +112,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -132,6 +136,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -155,6 +160,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -178,6 +184,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -201,6 +208,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -224,6 +232,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -251,6 +260,7 @@ LCL_ISSUES = [ 'blocks': [], 'close_status': None, 'closed_at': None, + "closed_by": None, 'comments': [], 'content': 'Description', 'custom_fields': [], @@ -271,6 +281,7 @@ LCL_ISSUES = [ 'blocks': [], 'close_status': None, 'closed_at': None, + "closed_by": None, 'comments': [], 'content': 'Description', 'custom_fields': [], @@ -2337,6 +2348,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): 'blocks': [], 'close_status': None, 'closed_at': None, + 'closed_by': None, 'comments': [], 'content': 'Description', 'custom_fields': [], @@ -2410,6 +2422,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "date_created": "1431414800", "close_status": None, "closed_at": None, + "closed_by": None, "depends": [], "id": 1, "last_updated": "1431414800", @@ -2508,6 +2521,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "date_created": "1431414800", "close_status": None, "closed_at": None, + "closed_by": None, "depends": [], "id": 2, "last_updated": "1431414800", @@ -2541,6 +2555,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): "date_created": "1431414800", "close_status": None, "closed_at": None, + "closed_by": None, "depends": [], "id": 2, "last_updated": "1431414800", diff --git a/tests/test_pagure_flask_api_issue_create.py b/tests/test_pagure_flask_api_issue_create.py index b7722eb..8247033 100644 --- a/tests/test_pagure_flask_api_issue_create.py +++ b/tests/test_pagure_flask_api_issue_create.py @@ -152,6 +152,7 @@ class PagureFlaskApiIssueCreatetests(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -206,6 +207,7 @@ class PagureFlaskApiIssueCreatetests(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -260,6 +262,7 @@ class PagureFlaskApiIssueCreatetests(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], diff --git a/tests/test_pagure_flask_api_ui_private_repo.py b/tests/test_pagure_flask_api_ui_private_repo.py index 9ff217d..3e7c3cf 100644 --- a/tests/test_pagure_flask_api_ui_private_repo.py +++ b/tests/test_pagure_flask_api_ui_private_repo.py @@ -29,6 +29,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], @@ -52,6 +53,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -75,6 +77,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -98,6 +101,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -121,6 +125,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -144,6 +149,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -167,6 +173,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -190,6 +197,7 @@ FULL_ISSUE_LIST = [ "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2498,6 +2506,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2577,6 +2586,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], @@ -2600,6 +2610,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2672,6 +2683,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], @@ -2695,6 +2707,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2835,6 +2848,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], @@ -2858,6 +2872,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2948,6 +2963,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], @@ -2996,6 +3012,7 @@ class PagurePrivateRepotest(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "This issue needs attention", "custom_fields": [], diff --git a/tests/test_pagure_flask_api_user.py b/tests/test_pagure_flask_api_user.py index 86a75e1..8a898a8 100644 --- a/tests/test_pagure_flask_api_user.py +++ b/tests/test_pagure_flask_api_user.py @@ -1225,6 +1225,7 @@ class PagureFlaskApiUsertestissues(tests.Modeltests): "blocks": [], "close_status": None, "closed_at": None, + "closed_by": None, "comments": [], "content": "We should work on this", "custom_fields": [], diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py index ae2fa04..8a60fcf 100644 --- a/tests/test_pagure_flask_ui_issues.py +++ b/tests/test_pagure_flask_ui_issues.py @@ -3960,7 +3960,9 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertEqual(msg.title, 'Test issue') user = tests.FakeUser() - user.username = 'pingou' + user.username = 'foo' + msg = pagure.lib.query.add_user_to_project(self.session, repo, "foo", "pingou") + self.session.commit() with tests.user_set(self.app.application, user): output = self.app.get('/test/issue/1') self.assertEqual(output.status_code, 200) @@ -4002,6 +4004,13 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertTrue( '' in output_text) + self.assertIn( + ' Closed: Fixed\n' + ' just now\n' + ' \n' + ' by\n' + ' foo.\n', + output_text) def _set_up_for_reaction_test(self, private=False): tests.create_projects(self.session) diff --git a/tests/test_pagure_lib_git.py b/tests/test_pagure_lib_git.py index 69fe990..3299ee0 100644 --- a/tests/test_pagure_lib_git.py +++ b/tests/test_pagure_lib_git.py @@ -1437,7 +1437,7 @@ new file mode 100644 index 0000000..60f7480 --- /dev/null +++ b/456 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +{ + "assignee": null, + "blocks": [], @@ -1493,6 +1493,8 @@ index 0000000..60f7480 elif 'closed_at' in row: t = row.split(': ')[0] row = '%s: null,' % t + elif 'closed_by' in row: + continue elif row.startswith('index 00'): row = 'index 0000000..60f7480' elif row.startswith('+++ b/'): @@ -1535,8 +1537,7 @@ diff --git a/123 b/456 index 458821a..77674a8 --- a/123 +++ b/456 -@@ -3,13 +3,32 @@ - "blocks": [], +@@ -4,13 +4,32 @@ "close_status": null, "closed_at": null, - "comments": [], @@ -1593,6 +1594,8 @@ index 458821a..77674a8 elif 'closed_at' in row: t = row.split(': ')[0] row = '%s: null,' % t + elif 'closed_by' in row: + continue elif row.startswith('index'): row = 'index 458821a..77674a8' elif row.startswith('--- a/'):