We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Issue data cntrb_id null for closed issues 
Cntrb_id is stated to be "The ID of the person that closed the issue" and even for closed issues I am seeing nulls 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `augur/tasks/github/events/tasks.py`
Content:
```
1 import time
2 import logging
3 import traceback
4 
5 from augur.tasks.init.celery_app import celery_app as celery
6 from augur.tasks.init.celery_app import AugurCoreRepoCollectionTask
7 from augur.application.db.data_parse import *
8 from augur.tasks.github.util.github_paginator import GithubPaginator, hit_api
9 from augur.tasks.github.util.github_task_session import GithubTaskManifest
10 from augur.application.db.session import DatabaseSession
11 from augur.tasks.github.util.util import get_owner_repo
12 from augur.tasks.util.worker_util import remove_duplicate_dicts
13 from augur.application.db.models import PullRequest, Message, PullRequestReview, PullRequestLabel, PullRequestReviewer, PullRequestEvent, PullRequestMeta, PullRequestAssignee, PullRequestReviewMessageRef, Issue, IssueEvent, IssueLabel, IssueAssignee, PullRequestMessageRef, IssueMessageRef, Contributor, Repo
14 from augur.application.db.util import execute_session_query
15 
16 platform_id = 1
17 
18 @celery.task(base=AugurCoreRepoCollectionTask)
19 def collect_events(repo_git: str):
20 
21     logger = logging.getLogger(collect_events.__name__)
22     
23     with GithubTaskManifest(logger) as manifest:
24 
25         augur_db = manifest.augur_db
26 
27         try:
28             
29             query = augur_db.session.query(Repo).filter(Repo.repo_git == repo_git)
30             repo_obj = execute_session_query(query, 'one')
31             repo_id = repo_obj.repo_id
32 
33             owner, repo = get_owner_repo(repo_git)
34 
35             logger.info(f"Collecting Github events for {owner}/{repo}")
36 
37             url = f"https://api.github.com/repos/{owner}/{repo}/issues/events"
38 
39             event_data = retrieve_all_event_data(repo_git, logger, manifest.key_auth)
40 
41             if event_data:
42             
43                 process_events(event_data, f"{owner}/{repo}: Event task", repo_id, logger, manifest.augur_db)
44 
45             else:
46                 logger.info(f"{owner}/{repo} has no events")
47         except Exception as e:
48             logger.error(f"Could not collect events for {repo_git}\n Reason: {e} \n Traceback: {''.join(traceback.format_exception(None, e, e.__traceback__))}")
49 
50 
51 def retrieve_all_event_data(repo_git: str, logger, key_auth):
52 
53     owner, repo = get_owner_repo(repo_git)
54 
55     logger.info(f"Collecting Github events for {owner}/{repo}")
56 
57     url = f"https://api.github.com/repos/{owner}/{repo}/issues/events"
58         
59     # returns an iterable of all issues at this url (this essentially means you can treat the issues variable as a list of the issues)
60     events = GithubPaginator(url, key_auth, logger)
61 
62 
63     num_pages = events.get_num_pages()
64     all_data = []
65     for page_data, page in events.iter_pages():
66 
67         if page_data is None:
68             return all_data
69             
70         elif len(page_data) == 0:
71             logger.debug(f"{repo.capitalize()} Events Page {page} contains no data...returning")
72             logger.info(f"Events Page {page} of {num_pages}")
73             return all_data
74 
75         logger.info(f"{repo} Events Page {page} of {num_pages}")
76 
77         all_data += page_data
78 
79     return all_data        
80 
81 def process_events(events, task_name, repo_id, logger, augur_db):
82     
83     tool_source = "Github events task"
84     tool_version = "2.0"
85     data_source = "Github API"
86    
87     pr_event_dicts = []
88     issue_event_dicts = []
89     contributors = []
90 
91 
92     # create mapping from issue url to issue id of current issues
93     issue_url_to_id_map = {}
94     issues = augur_db.session.query(Issue).filter(Issue.repo_id == repo_id).all()
95     for issue in issues:
96         issue_url_to_id_map[issue.issue_url] = issue.issue_id
97 
98     # create mapping from pr url to pr id of current pull requests
99     pr_url_to_id_map = {}
100     prs = augur_db.session.query(PullRequest).filter(PullRequest.repo_id == repo_id).all()
101     for pr in prs:
102         pr_url_to_id_map[pr.pr_url] = pr.pull_request_id
103 
104     not_mapable_event_count = 0
105     event_len = len(events)
106     for event in events:
107 
108         event, contributor = process_github_event_contributors(logger, event, tool_source, tool_version, data_source)
109 
110         # event_mapping_data is the pr or issue data needed to relate the event to an issue or pr
111         event_mapping_data = event["issue"]
112 
113         if event_mapping_data is None:
114             not_mapable_event_count += 1
115             continue
116 
117         pull_request = event_mapping_data.get('pull_request', None)
118         if pull_request:
119             pr_url = pull_request["url"]
120 
121             try:
122                 pull_request_id = pr_url_to_id_map[pr_url]
123 
124                 # query = augur_db.session.query(PullRequest).filter(PullRequest.pr_url == pr_url)
125                 # related_pr = execute_session_query(query, 'one')
126             except KeyError:
127                 logger.info(f"{task_name}: Could not find related pr")
128                 logger.info(f"{task_name}: We were searching for: {pr_url}")
129                 logger.info(f"{task_name}: Skipping")
130                 continue
131 
132             pr_event_dicts.append(
133                 extract_pr_event_data(event, pull_request_id, platform_id, repo_id,
134                                     tool_source, tool_version, data_source)
135             )
136 
137         else:
138             issue_url = event_mapping_data["url"]
139 
140             try:
141                 issue_id = issue_url_to_id_map[issue_url]
142                 # query = augur_db.session.query(Issue).filter(Issue.issue_url == issue_url)
143                 # related_issue = execute_session_query(query, 'one')
144             except KeyError:
145                 logger.info(f"{task_name}: Could not find related pr")
146                 logger.info(f"{task_name}: We were searching for: {issue_url}")
147                 logger.info(f"{task_name}: Skipping")
148                 continue
149 
150             issue_event_dicts.append(
151                 extract_issue_event_data(event, issue_id, platform_id, repo_id,
152                                         tool_source, tool_version, data_source)
153             )
154         
155         # add contributor to list after porcessing the event, 
156         # so if it fails processing for some reason the contributor is not inserted
157         # NOTE: contributor is none when there is no contributor data on the event
158         if contributor:
159             contributors.append(contributor)
160 
161     # remove contributors that were found in the data more than once
162     contributors = remove_duplicate_dicts(contributors)
163 
164     augur_db.insert_data(contributors, Contributor, ["cntrb_id"])
165 
166     issue_events_len = len(issue_event_dicts)
167     pr_events_len = len(pr_event_dicts)
168     if event_len != (issue_events_len + pr_events_len):
169 
170         unassigned_events = event_len - issue_events_len - pr_events_len
171 
172         logger.error(f"{task_name}: {event_len} events were processed, but {pr_events_len} pr events were found and related to a pr, and {issue_events_len} issue events were found and related to an issue. {not_mapable_event_count} events were not related to a pr or issue due to the api returning insufficient data. For some reason {unassigned_events} events were not able to be processed even when the api returned sufficient data. This is usually because pull requests or issues have not been collected, and the events are skipped because they cannot be related to a pr or issue")
173 
174     logger.info(f"{task_name}: Inserting {len(pr_event_dicts)} pr events and {len(issue_event_dicts)} issue events")
175 
176     # TODO: Could replace this with "id" but it isn't stored on the table for some reason
177     pr_event_natural_keys = ["node_id"]
178     augur_db.insert_data(pr_event_dicts, PullRequestEvent, pr_event_natural_keys)
179 
180     issue_event_natural_keys = ["issue_id", "issue_event_src_id"]
181     augur_db.insert_data(issue_event_dicts, IssueEvent, issue_event_natural_keys)
182 
183 
184 # TODO: Should we skip an event if there is no contributor to resolve it o
185 def process_github_event_contributors(logger, event, tool_source, tool_version, data_source):
186 
187     if event["actor"]:
188 
189         event_cntrb = extract_needed_contributor_data(event["actor"], tool_source, tool_version, data_source)
190         event["cntrb_id"] = event_cntrb["cntrb_id"]
191 
192     else:
193         event["cntrb_id"] = None
194         return event, None
195     
196     return event, event_cntrb
197
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/augur/tasks/github/events/tasks.py b/augur/tasks/github/events/tasks.py
--- a/augur/tasks/github/events/tasks.py
+++ b/augur/tasks/github/events/tasks.py
@@ -1,6 +1,7 @@
 import time
 import logging
 import traceback
+import sqlalchemy as s
 
 from augur.tasks.init.celery_app import celery_app as celery
 from augur.tasks.init.celery_app import AugurCoreRepoCollectionTask
@@ -180,6 +181,7 @@
     issue_event_natural_keys = ["issue_id", "issue_event_src_id"]
     augur_db.insert_data(issue_event_dicts, IssueEvent, issue_event_natural_keys)
 
+    update_issue_closed_cntrbs_from_events(augur_db.engine, repo_id)
 
 # TODO: Should we skip an event if there is no contributor to resolve it o
 def process_github_event_contributors(logger, event, tool_source, tool_version, data_source):
@@ -194,3 +196,30 @@
         return event, None
     
     return event, event_cntrb
+
+
+def update_issue_closed_cntrbs_from_events(engine, repo_id):
+
+    get_ranked_issues = s.text(f"""
+        WITH RankedIssues AS (
+            SELECT repo_id, issue_id, cntrb_id, 
+                ROW_NUMBER() OVER(PARTITION BY issue_id ORDER BY created_at DESC) AS rn
+            FROM issue_events 
+            WHERE "action" = 'closed'
+        )
+                                            
+        SELECT issue_id, cntrb_id from RankedIssues where rn=1 and repo_id={repo_id}
+    """)
+    result = engine.execute(get_ranked_issues).fetchall()
+
+    update_data = [{'issue_id': row['issue_id'], 'cntrb_id': row['cntrb_id'], 'repo_id': repo_id} for row in result]
+    with engine.connect() as connection:
+        update_stmt = s.text("""
+            UPDATE issues
+            SET cntrb_id = :cntrb_id
+            WHERE issue_id = :issue_id
+            AND repo_id = :repo_id
+        """)
+        connection.execute(update_stmt, update_data)
+
+

{"golden_diff": "diff --git a/augur/tasks/github/events/tasks.py b/augur/tasks/github/events/tasks.py\n--- a/augur/tasks/github/events/tasks.py\n+++ b/augur/tasks/github/events/tasks.py\n@@ -1,6 +1,7 @@\n import time\n import logging\n import traceback\n+import sqlalchemy as s\n \n from augur.tasks.init.celery_app import celery_app as celery\n from augur.tasks.init.celery_app import AugurCoreRepoCollectionTask\n@@ -180,6 +181,7 @@\n     issue_event_natural_keys = [\"issue_id\", \"issue_event_src_id\"]\n     augur_db.insert_data(issue_event_dicts, IssueEvent, issue_event_natural_keys)\n \n+    update_issue_closed_cntrbs_from_events(augur_db.engine, repo_id)\n \n # TODO: Should we skip an event if there is no contributor to resolve it o\n def process_github_event_contributors(logger, event, tool_source, tool_version, data_source):\n@@ -194,3 +196,30 @@\n         return event, None\n     \n     return event, event_cntrb\n+\n+\n+def update_issue_closed_cntrbs_from_events(engine, repo_id):\n+\n+    get_ranked_issues = s.text(f\"\"\"\n+        WITH RankedIssues AS (\n+            SELECT repo_id, issue_id, cntrb_id, \n+                ROW_NUMBER() OVER(PARTITION BY issue_id ORDER BY created_at DESC) AS rn\n+            FROM issue_events \n+            WHERE \"action\" = 'closed'\n+        )\n+                                            \n+        SELECT issue_id, cntrb_id from RankedIssues where rn=1 and repo_id={repo_id}\n+    \"\"\")\n+    result = engine.execute(get_ranked_issues).fetchall()\n+\n+    update_data = [{'issue_id': row['issue_id'], 'cntrb_id': row['cntrb_id'], 'repo_id': repo_id} for row in result]\n+    with engine.connect() as connection:\n+        update_stmt = s.text(\"\"\"\n+            UPDATE issues\n+            SET cntrb_id = :cntrb_id\n+            WHERE issue_id = :issue_id\n+            AND repo_id = :repo_id\n+        \"\"\")\n+        connection.execute(update_stmt, update_data)\n+\n+\n", "issue": "Issue data cntrb_id null for closed issues \nCntrb_id is stated to be \"The ID of the person that closed the issue\" and even for closed issues I am seeing nulls \n", "before_files": [{"content": "import time\nimport logging\nimport traceback\n\nfrom augur.tasks.init.celery_app import celery_app as celery\nfrom augur.tasks.init.celery_app import AugurCoreRepoCollectionTask\nfrom augur.application.db.data_parse import *\nfrom augur.tasks.github.util.github_paginator import GithubPaginator, hit_api\nfrom augur.tasks.github.util.github_task_session import GithubTaskManifest\nfrom augur.application.db.session import DatabaseSession\nfrom augur.tasks.github.util.util import get_owner_repo\nfrom augur.tasks.util.worker_util import remove_duplicate_dicts\nfrom augur.application.db.models import PullRequest, Message, PullRequestReview, PullRequestLabel, PullRequestReviewer, PullRequestEvent, PullRequestMeta, PullRequestAssignee, PullRequestReviewMessageRef, Issue, IssueEvent, IssueLabel, IssueAssignee, PullRequestMessageRef, IssueMessageRef, Contributor, Repo\nfrom augur.application.db.util import execute_session_query\n\nplatform_id = 1\n\n@celery.task(base=AugurCoreRepoCollectionTask)\ndef collect_events(repo_git: str):\n\n    logger = logging.getLogger(collect_events.__name__)\n    \n    with GithubTaskManifest(logger) as manifest:\n\n        augur_db = manifest.augur_db\n\n        try:\n            \n            query = augur_db.session.query(Repo).filter(Repo.repo_git == repo_git)\n            repo_obj = execute_session_query(query, 'one')\n            repo_id = repo_obj.repo_id\n\n            owner, repo = get_owner_repo(repo_git)\n\n            logger.info(f\"Collecting Github events for {owner}/{repo}\")\n\n            url = f\"https://api.github.com/repos/{owner}/{repo}/issues/events\"\n\n            event_data = retrieve_all_event_data(repo_git, logger, manifest.key_auth)\n\n            if event_data:\n            \n                process_events(event_data, f\"{owner}/{repo}: Event task\", repo_id, logger, manifest.augur_db)\n\n            else:\n                logger.info(f\"{owner}/{repo} has no events\")\n        except Exception as e:\n            logger.error(f\"Could not collect events for {repo_git}\\n Reason: {e} \\n Traceback: {''.join(traceback.format_exception(None, e, e.__traceback__))}\")\n\n\ndef retrieve_all_event_data(repo_git: str, logger, key_auth):\n\n    owner, repo = get_owner_repo(repo_git)\n\n    logger.info(f\"Collecting Github events for {owner}/{repo}\")\n\n    url = f\"https://api.github.com/repos/{owner}/{repo}/issues/events\"\n        \n    # returns an iterable of all issues at this url (this essentially means you can treat the issues variable as a list of the issues)\n    events = GithubPaginator(url, key_auth, logger)\n\n\n    num_pages = events.get_num_pages()\n    all_data = []\n    for page_data, page in events.iter_pages():\n\n        if page_data is None:\n            return all_data\n            \n        elif len(page_data) == 0:\n            logger.debug(f\"{repo.capitalize()} Events Page {page} contains no data...returning\")\n            logger.info(f\"Events Page {page} of {num_pages}\")\n            return all_data\n\n        logger.info(f\"{repo} Events Page {page} of {num_pages}\")\n\n        all_data += page_data\n\n    return all_data        \n\ndef process_events(events, task_name, repo_id, logger, augur_db):\n    \n    tool_source = \"Github events task\"\n    tool_version = \"2.0\"\n    data_source = \"Github API\"\n   \n    pr_event_dicts = []\n    issue_event_dicts = []\n    contributors = []\n\n\n    # create mapping from issue url to issue id of current issues\n    issue_url_to_id_map = {}\n    issues = augur_db.session.query(Issue).filter(Issue.repo_id == repo_id).all()\n    for issue in issues:\n        issue_url_to_id_map[issue.issue_url] = issue.issue_id\n\n    # create mapping from pr url to pr id of current pull requests\n    pr_url_to_id_map = {}\n    prs = augur_db.session.query(PullRequest).filter(PullRequest.repo_id == repo_id).all()\n    for pr in prs:\n        pr_url_to_id_map[pr.pr_url] = pr.pull_request_id\n\n    not_mapable_event_count = 0\n    event_len = len(events)\n    for event in events:\n\n        event, contributor = process_github_event_contributors(logger, event, tool_source, tool_version, data_source)\n\n        # event_mapping_data is the pr or issue data needed to relate the event to an issue or pr\n        event_mapping_data = event[\"issue\"]\n\n        if event_mapping_data is None:\n            not_mapable_event_count += 1\n            continue\n\n        pull_request = event_mapping_data.get('pull_request', None)\n        if pull_request:\n            pr_url = pull_request[\"url\"]\n\n            try:\n                pull_request_id = pr_url_to_id_map[pr_url]\n\n                # query = augur_db.session.query(PullRequest).filter(PullRequest.pr_url == pr_url)\n                # related_pr = execute_session_query(query, 'one')\n            except KeyError:\n                logger.info(f\"{task_name}: Could not find related pr\")\n                logger.info(f\"{task_name}: We were searching for: {pr_url}\")\n                logger.info(f\"{task_name}: Skipping\")\n                continue\n\n            pr_event_dicts.append(\n                extract_pr_event_data(event, pull_request_id, platform_id, repo_id,\n                                    tool_source, tool_version, data_source)\n            )\n\n        else:\n            issue_url = event_mapping_data[\"url\"]\n\n            try:\n                issue_id = issue_url_to_id_map[issue_url]\n                # query = augur_db.session.query(Issue).filter(Issue.issue_url == issue_url)\n                # related_issue = execute_session_query(query, 'one')\n            except KeyError:\n                logger.info(f\"{task_name}: Could not find related pr\")\n                logger.info(f\"{task_name}: We were searching for: {issue_url}\")\n                logger.info(f\"{task_name}: Skipping\")\n                continue\n\n            issue_event_dicts.append(\n                extract_issue_event_data(event, issue_id, platform_id, repo_id,\n                                        tool_source, tool_version, data_source)\n            )\n        \n        # add contributor to list after porcessing the event, \n        # so if it fails processing for some reason the contributor is not inserted\n        # NOTE: contributor is none when there is no contributor data on the event\n        if contributor:\n            contributors.append(contributor)\n\n    # remove contributors that were found in the data more than once\n    contributors = remove_duplicate_dicts(contributors)\n\n    augur_db.insert_data(contributors, Contributor, [\"cntrb_id\"])\n\n    issue_events_len = len(issue_event_dicts)\n    pr_events_len = len(pr_event_dicts)\n    if event_len != (issue_events_len + pr_events_len):\n\n        unassigned_events = event_len - issue_events_len - pr_events_len\n\n        logger.error(f\"{task_name}: {event_len} events were processed, but {pr_events_len} pr events were found and related to a pr, and {issue_events_len} issue events were found and related to an issue. {not_mapable_event_count} events were not related to a pr or issue due to the api returning insufficient data. For some reason {unassigned_events} events were not able to be processed even when the api returned sufficient data. This is usually because pull requests or issues have not been collected, and the events are skipped because they cannot be related to a pr or issue\")\n\n    logger.info(f\"{task_name}: Inserting {len(pr_event_dicts)} pr events and {len(issue_event_dicts)} issue events\")\n\n    # TODO: Could replace this with \"id\" but it isn't stored on the table for some reason\n    pr_event_natural_keys = [\"node_id\"]\n    augur_db.insert_data(pr_event_dicts, PullRequestEvent, pr_event_natural_keys)\n\n    issue_event_natural_keys = [\"issue_id\", \"issue_event_src_id\"]\n    augur_db.insert_data(issue_event_dicts, IssueEvent, issue_event_natural_keys)\n\n\n# TODO: Should we skip an event if there is no contributor to resolve it o\ndef process_github_event_contributors(logger, event, tool_source, tool_version, data_source):\n\n    if event[\"actor\"]:\n\n        event_cntrb = extract_needed_contributor_data(event[\"actor\"], tool_source, tool_version, data_source)\n        event[\"cntrb_id\"] = event_cntrb[\"cntrb_id\"]\n\n    else:\n        event[\"cntrb_id\"] = None\n        return event, None\n    \n    return event, event_cntrb\n", "path": "augur/tasks/github/events/tasks.py"}], "after_files": [{"content": "import time\nimport logging\nimport traceback\nimport sqlalchemy as s\n\nfrom augur.tasks.init.celery_app import celery_app as celery\nfrom augur.tasks.init.celery_app import AugurCoreRepoCollectionTask\nfrom augur.application.db.data_parse import *\nfrom augur.tasks.github.util.github_paginator import GithubPaginator, hit_api\nfrom augur.tasks.github.util.github_task_session import GithubTaskManifest\nfrom augur.application.db.session import DatabaseSession\nfrom augur.tasks.github.util.util import get_owner_repo\nfrom augur.tasks.util.worker_util import remove_duplicate_dicts\nfrom augur.application.db.models import PullRequest, Message, PullRequestReview, PullRequestLabel, PullRequestReviewer, PullRequestEvent, PullRequestMeta, PullRequestAssignee, PullRequestReviewMessageRef, Issue, IssueEvent, IssueLabel, IssueAssignee, PullRequestMessageRef, IssueMessageRef, Contributor, Repo\nfrom augur.application.db.util import execute_session_query\n\nplatform_id = 1\n\n@celery.task(base=AugurCoreRepoCollectionTask)\ndef collect_events(repo_git: str):\n\n    logger = logging.getLogger(collect_events.__name__)\n    \n    with GithubTaskManifest(logger) as manifest:\n\n        augur_db = manifest.augur_db\n\n        try:\n            \n            query = augur_db.session.query(Repo).filter(Repo.repo_git == repo_git)\n            repo_obj = execute_session_query(query, 'one')\n            repo_id = repo_obj.repo_id\n\n            owner, repo = get_owner_repo(repo_git)\n\n            logger.info(f\"Collecting Github events for {owner}/{repo}\")\n\n            url = f\"https://api.github.com/repos/{owner}/{repo}/issues/events\"\n\n            event_data = retrieve_all_event_data(repo_git, logger, manifest.key_auth)\n\n            if event_data:\n            \n                process_events(event_data, f\"{owner}/{repo}: Event task\", repo_id, logger, manifest.augur_db)\n\n            else:\n                logger.info(f\"{owner}/{repo} has no events\")\n        except Exception as e:\n            logger.error(f\"Could not collect events for {repo_git}\\n Reason: {e} \\n Traceback: {''.join(traceback.format_exception(None, e, e.__traceback__))}\")\n\n\ndef retrieve_all_event_data(repo_git: str, logger, key_auth):\n\n    owner, repo = get_owner_repo(repo_git)\n\n    logger.info(f\"Collecting Github events for {owner}/{repo}\")\n\n    url = f\"https://api.github.com/repos/{owner}/{repo}/issues/events\"\n        \n    # returns an iterable of all issues at this url (this essentially means you can treat the issues variable as a list of the issues)\n    events = GithubPaginator(url, key_auth, logger)\n\n\n    num_pages = events.get_num_pages()\n    all_data = []\n    for page_data, page in events.iter_pages():\n\n        if page_data is None:\n            return all_data\n            \n        elif len(page_data) == 0:\n            logger.debug(f\"{repo.capitalize()} Events Page {page} contains no data...returning\")\n            logger.info(f\"Events Page {page} of {num_pages}\")\n            return all_data\n\n        logger.info(f\"{repo} Events Page {page} of {num_pages}\")\n\n        all_data += page_data\n\n    return all_data        \n\ndef process_events(events, task_name, repo_id, logger, augur_db):\n    \n    tool_source = \"Github events task\"\n    tool_version = \"2.0\"\n    data_source = \"Github API\"\n   \n    pr_event_dicts = []\n    issue_event_dicts = []\n    contributors = []\n\n\n    # create mapping from issue url to issue id of current issues\n    issue_url_to_id_map = {}\n    issues = augur_db.session.query(Issue).filter(Issue.repo_id == repo_id).all()\n    for issue in issues:\n        issue_url_to_id_map[issue.issue_url] = issue.issue_id\n\n    # create mapping from pr url to pr id of current pull requests\n    pr_url_to_id_map = {}\n    prs = augur_db.session.query(PullRequest).filter(PullRequest.repo_id == repo_id).all()\n    for pr in prs:\n        pr_url_to_id_map[pr.pr_url] = pr.pull_request_id\n\n    not_mapable_event_count = 0\n    event_len = len(events)\n    for event in events:\n\n        event, contributor = process_github_event_contributors(logger, event, tool_source, tool_version, data_source)\n\n        # event_mapping_data is the pr or issue data needed to relate the event to an issue or pr\n        event_mapping_data = event[\"issue\"]\n\n        if event_mapping_data is None:\n            not_mapable_event_count += 1\n            continue\n\n        pull_request = event_mapping_data.get('pull_request', None)\n        if pull_request:\n            pr_url = pull_request[\"url\"]\n\n            try:\n                pull_request_id = pr_url_to_id_map[pr_url]\n\n                # query = augur_db.session.query(PullRequest).filter(PullRequest.pr_url == pr_url)\n                # related_pr = execute_session_query(query, 'one')\n            except KeyError:\n                logger.info(f\"{task_name}: Could not find related pr\")\n                logger.info(f\"{task_name}: We were searching for: {pr_url}\")\n                logger.info(f\"{task_name}: Skipping\")\n                continue\n\n            pr_event_dicts.append(\n                extract_pr_event_data(event, pull_request_id, platform_id, repo_id,\n                                    tool_source, tool_version, data_source)\n            )\n\n        else:\n            issue_url = event_mapping_data[\"url\"]\n\n            try:\n                issue_id = issue_url_to_id_map[issue_url]\n                # query = augur_db.session.query(Issue).filter(Issue.issue_url == issue_url)\n                # related_issue = execute_session_query(query, 'one')\n            except KeyError:\n                logger.info(f\"{task_name}: Could not find related pr\")\n                logger.info(f\"{task_name}: We were searching for: {issue_url}\")\n                logger.info(f\"{task_name}: Skipping\")\n                continue\n\n            issue_event_dicts.append(\n                extract_issue_event_data(event, issue_id, platform_id, repo_id,\n                                        tool_source, tool_version, data_source)\n            )\n        \n        # add contributor to list after porcessing the event, \n        # so if it fails processing for some reason the contributor is not inserted\n        # NOTE: contributor is none when there is no contributor data on the event\n        if contributor:\n            contributors.append(contributor)\n\n    # remove contributors that were found in the data more than once\n    contributors = remove_duplicate_dicts(contributors)\n\n    augur_db.insert_data(contributors, Contributor, [\"cntrb_id\"])\n\n    issue_events_len = len(issue_event_dicts)\n    pr_events_len = len(pr_event_dicts)\n    if event_len != (issue_events_len + pr_events_len):\n\n        unassigned_events = event_len - issue_events_len - pr_events_len\n\n        logger.error(f\"{task_name}: {event_len} events were processed, but {pr_events_len} pr events were found and related to a pr, and {issue_events_len} issue events were found and related to an issue. {not_mapable_event_count} events were not related to a pr or issue due to the api returning insufficient data. For some reason {unassigned_events} events were not able to be processed even when the api returned sufficient data. This is usually because pull requests or issues have not been collected, and the events are skipped because they cannot be related to a pr or issue\")\n\n    logger.info(f\"{task_name}: Inserting {len(pr_event_dicts)} pr events and {len(issue_event_dicts)} issue events\")\n\n    # TODO: Could replace this with \"id\" but it isn't stored on the table for some reason\n    pr_event_natural_keys = [\"node_id\"]\n    augur_db.insert_data(pr_event_dicts, PullRequestEvent, pr_event_natural_keys)\n\n    issue_event_natural_keys = [\"issue_id\", \"issue_event_src_id\"]\n    augur_db.insert_data(issue_event_dicts, IssueEvent, issue_event_natural_keys)\n\n    update_issue_closed_cntrbs_from_events(augur_db.engine, repo_id)\n\n# TODO: Should we skip an event if there is no contributor to resolve it o\ndef process_github_event_contributors(logger, event, tool_source, tool_version, data_source):\n\n    if event[\"actor\"]:\n\n        event_cntrb = extract_needed_contributor_data(event[\"actor\"], tool_source, tool_version, data_source)\n        event[\"cntrb_id\"] = event_cntrb[\"cntrb_id\"]\n\n    else:\n        event[\"cntrb_id\"] = None\n        return event, None\n    \n    return event, event_cntrb\n\n\ndef update_issue_closed_cntrbs_from_events(engine, repo_id):\n\n    get_ranked_issues = s.text(f\"\"\"\n        WITH RankedIssues AS (\n            SELECT repo_id, issue_id, cntrb_id, \n                ROW_NUMBER() OVER(PARTITION BY issue_id ORDER BY created_at DESC) AS rn\n            FROM issue_events \n            WHERE \"action\" = 'closed'\n        )\n                                            \n        SELECT issue_id, cntrb_id from RankedIssues where rn=1 and repo_id={repo_id}\n    \"\"\")\n    result = engine.execute(get_ranked_issues).fetchall()\n\n    update_data = [{'issue_id': row['issue_id'], 'cntrb_id': row['cntrb_id'], 'repo_id': repo_id} for row in result]\n    with engine.connect() as connection:\n        update_stmt = s.text(\"\"\"\n            UPDATE issues\n            SET cntrb_id = :cntrb_id\n            WHERE issue_id = :issue_id\n            AND repo_id = :repo_id\n        \"\"\")\n        connection.execute(update_stmt, update_data)\n\n\n", "path": "augur/tasks/github/events/tasks.py"}]}