We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
User tracking to report parsl version
User tracking as described in #34 tracks a range of script attributes and anonymous user information.
It would help to track the version of Parsl and python. This would help us understand:
1. Rate of upgrade to newer releases
2. Python version in use, and support requirements
3. Support requirements for older parsl releases.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsl/dataflow/usage_tracking/usage.py`
Content:
```
1 import uuid
2 import time
3 import hashlib
4 import os
5 import getpass
6 import json
7 import logging
8 import socket
9 
10 from parsl.dataflow.states import States
11 
12 logger = logging.getLogger(__name__)
13 
14 
15 class UsageTracker (object):
16     """Anonymized Usage Tracking for Parsl.
17 
18     Client for this is here : https://github.com/Parsl/parsl_tracking
19     This issue captures the discussion that went into functionality
20     implemented here : https://github.com/Parsl/parsl/issues/34
21 
22     """
23 
24     def __init__(self, dfk, ip='52.3.111.203', port=50077,
25                  domain_name='tracking.parsl-project.org'):
26         """Initialize usage tracking unless the user has opted-out.
27 
28         Tracks usage stats by inspecting the internal state of the dfk.
29 
30         Args:
31              - dfk (DFK object) : Data Flow Kernel object
32 
33         KWargs:
34              - ip (string) : IP address
35              - port (int) : Port number, Default:50077
36              - domain_name (string) : Domain name, will override IP
37                   Default: tracking.parsl-project.org
38         """
39         if domain_name:
40             try:
41                 self.UDP_IP = socket.gethostbyname(domain_name)
42             except Exception:
43                 logging.debug("Could not lookup domain_name, defaulting to 52.3.111.203")
44                 self.UDP_IP = ip
45         else:
46             self.UDP_IP = ip
47         self.UDP_PORT = port
48         self.dfk = dfk
49         self.config = self.dfk.config
50         self.uuid = str(uuid.uuid4())
51         self.test_mode, self.tracking_enabled = self.check_tracking_enabled()
52         logger.debug("Tracking status: {}".format(self.tracking_enabled))
53         logger.debug("Testing mode   : {}".format(self.test_mode))
54         self.initialized = False  # Once first message is sent this will be True
55 
56     def check_tracking_enabled(self):
57         """By default tracking is enabled.
58 
59         If Test mode is set via env variable PARSL_TESTING, a test flag is set
60 
61         Tracking is disabled if :
62             1. config["globals"]["usageTracking"] is set to False (Bool)
63             2. Environment variable PARSL_TRACKING is set to false (case insensitive)
64 
65         """
66         track = True   # By default we track usage
67         test = False  # By default we are not in testing mode
68 
69         testvar = str(os.environ.get("PARSL_TESTING", 'None')).lower()
70         if testvar == 'true':
71             test = True
72 
73         if self.config and self.config["globals"]["usageTracking"] is False:
74             track = False
75 
76         envvar = str(os.environ.get("PARSL_TRACKING", True)).lower()
77         if envvar == "false":
78             track = False
79 
80         return test, track
81 
82     def construct_start_message(self):
83         """Collect preliminary run info at the start of the DFK.
84 
85         Returns :
86               - Message dict dumped as json string, ready for UDP
87         """
88         uname = getpass.getuser().encode('latin1')
89         hashed_username = hashlib.sha256(uname).hexdigest()[0:10]
90         hname = socket.gethostname().encode('latin1')
91         hashed_hostname = hashlib.sha256(hname).hexdigest()[0:10]
92         message = {'uuid': self.uuid,
93                    'uname': hashed_username,
94                    'hname': hashed_hostname,
95                    'test': self.test_mode,
96                    'start': time.time()}
97 
98         return json.dumps(message)
99 
100     def construct_end_message(self):
101         """Collect the final run information at the time of DFK cleanup.
102 
103         Returns:
104              - Message dict dumped as json string, ready for UDP
105         """
106         app_count = self.dfk.task_count
107 
108         site_count = 0
109         if self.dfk._executors_managed:
110             site_count = len(self.dfk.config['sites'])
111 
112         failed_states = (States.failed, States.dep_fail)
113         app_fails = len([t for t in self.dfk.tasks if
114                          self.dfk.tasks[t]['status'] in failed_states])
115 
116         message = {'uuid': self.uuid,
117                    'end': time.time(),
118                    't_apps': app_count,
119                    'sites': site_count,
120                    'c_time': None,
121                    'failed': app_fails,
122                    'test': self.test_mode,
123                    }
124 
125         return json.dumps(message)
126 
127     def send_UDP_message(self, message):
128         """Send UDP message."""
129         if self.tracking_enabled:
130             try:
131                 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP
132                 x = sock.sendto(bytes(message, "utf-8"), (self.UDP_IP, self.UDP_PORT))
133                 sock.close()
134             except OSError:
135                 logger.debug("Unable to reach the network to send usage data")
136                 x = 0
137         else:
138             x = -1
139 
140         return x
141 
142     def send_message(self):
143         """Send message over UDP.
144 
145         If tracking is disables, the bytes_sent will always be set to -1
146 
147         Returns:
148             (bytes_sent, time_taken)
149         """
150         start = time.time()
151         message = None
152         if not self.initialized:
153             message = self.construct_start_message()
154             self.initialized = True
155         else:
156             message = self.construct_end_message()
157 
158         x = self.send_UDP_message(message)
159         end = time.time()
160 
161         return x, end - start
162 
163 
164 if __name__ == '__main__':
165 
166     from parsl import *
167 
168     workers = ThreadPoolExecutor(max_workers=4)
169     dfk = DataFlowKernel(executors=[workers])
170 
171     # ut = UsageTracker(dfk, ip='52.3.111.203')
172     ut = UsageTracker(dfk, domain_name='tracking.parsl-project.org')
173 
174     for i in range(0, 2):
175         x = ut.send_message()
176         print(x)
177
```
--- 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/parsl/dataflow/usage_tracking/usage.py b/parsl/dataflow/usage_tracking/usage.py
--- a/parsl/dataflow/usage_tracking/usage.py
+++ b/parsl/dataflow/usage_tracking/usage.py
@@ -6,8 +6,10 @@
 import json
 import logging
 import socket
+import sys
 
 from parsl.dataflow.states import States
+from parsl.version import VERSION as PARSL_VERSION
 
 logger = logging.getLogger(__name__)
 
@@ -48,6 +50,10 @@
         self.dfk = dfk
         self.config = self.dfk.config
         self.uuid = str(uuid.uuid4())
+        self.parsl_version = PARSL_VERSION
+        self.python_version = "{}.{}.{}".format(sys.version_info.major,
+                                                sys.version_info.minor,
+                                                sys.version_info.micro)
         self.test_mode, self.tracking_enabled = self.check_tracking_enabled()
         logger.debug("Tracking status: {}".format(self.tracking_enabled))
         logger.debug("Testing mode   : {}".format(self.test_mode))
@@ -93,6 +99,8 @@
                    'uname': hashed_username,
                    'hname': hashed_hostname,
                    'test': self.test_mode,
+                   'parsl_v': self.parsl_version,
+                   'python_v': self.python_version,
                    'start': time.time()}
 
         return json.dumps(message)

{"golden_diff": "diff --git a/parsl/dataflow/usage_tracking/usage.py b/parsl/dataflow/usage_tracking/usage.py\n--- a/parsl/dataflow/usage_tracking/usage.py\n+++ b/parsl/dataflow/usage_tracking/usage.py\n@@ -6,8 +6,10 @@\n import json\n import logging\n import socket\n+import sys\n \n from parsl.dataflow.states import States\n+from parsl.version import VERSION as PARSL_VERSION\n \n logger = logging.getLogger(__name__)\n \n@@ -48,6 +50,10 @@\n         self.dfk = dfk\n         self.config = self.dfk.config\n         self.uuid = str(uuid.uuid4())\n+        self.parsl_version = PARSL_VERSION\n+        self.python_version = \"{}.{}.{}\".format(sys.version_info.major,\n+                                                sys.version_info.minor,\n+                                                sys.version_info.micro)\n         self.test_mode, self.tracking_enabled = self.check_tracking_enabled()\n         logger.debug(\"Tracking status: {}\".format(self.tracking_enabled))\n         logger.debug(\"Testing mode   : {}\".format(self.test_mode))\n@@ -93,6 +99,8 @@\n                    'uname': hashed_username,\n                    'hname': hashed_hostname,\n                    'test': self.test_mode,\n+                   'parsl_v': self.parsl_version,\n+                   'python_v': self.python_version,\n                    'start': time.time()}\n \n         return json.dumps(message)\n", "issue": "User tracking to report parsl version\nUser tracking as described in #34 tracks a range of script attributes and anonymous user information.\r\nIt would help to track the version of Parsl and python. This would help us understand:\r\n1. Rate of upgrade to newer releases\r\n2. Python version in use, and support requirements\r\n3. Support requirements for older parsl releases.\r\n\n", "before_files": [{"content": "import uuid\nimport time\nimport hashlib\nimport os\nimport getpass\nimport json\nimport logging\nimport socket\n\nfrom parsl.dataflow.states import States\n\nlogger = logging.getLogger(__name__)\n\n\nclass UsageTracker (object):\n    \"\"\"Anonymized Usage Tracking for Parsl.\n\n    Client for this is here : https://github.com/Parsl/parsl_tracking\n    This issue captures the discussion that went into functionality\n    implemented here : https://github.com/Parsl/parsl/issues/34\n\n    \"\"\"\n\n    def __init__(self, dfk, ip='52.3.111.203', port=50077,\n                 domain_name='tracking.parsl-project.org'):\n        \"\"\"Initialize usage tracking unless the user has opted-out.\n\n        Tracks usage stats by inspecting the internal state of the dfk.\n\n        Args:\n             - dfk (DFK object) : Data Flow Kernel object\n\n        KWargs:\n             - ip (string) : IP address\n             - port (int) : Port number, Default:50077\n             - domain_name (string) : Domain name, will override IP\n                  Default: tracking.parsl-project.org\n        \"\"\"\n        if domain_name:\n            try:\n                self.UDP_IP = socket.gethostbyname(domain_name)\n            except Exception:\n                logging.debug(\"Could not lookup domain_name, defaulting to 52.3.111.203\")\n                self.UDP_IP = ip\n        else:\n            self.UDP_IP = ip\n        self.UDP_PORT = port\n        self.dfk = dfk\n        self.config = self.dfk.config\n        self.uuid = str(uuid.uuid4())\n        self.test_mode, self.tracking_enabled = self.check_tracking_enabled()\n        logger.debug(\"Tracking status: {}\".format(self.tracking_enabled))\n        logger.debug(\"Testing mode   : {}\".format(self.test_mode))\n        self.initialized = False  # Once first message is sent this will be True\n\n    def check_tracking_enabled(self):\n        \"\"\"By default tracking is enabled.\n\n        If Test mode is set via env variable PARSL_TESTING, a test flag is set\n\n        Tracking is disabled if :\n            1. config[\"globals\"][\"usageTracking\"] is set to False (Bool)\n            2. Environment variable PARSL_TRACKING is set to false (case insensitive)\n\n        \"\"\"\n        track = True   # By default we track usage\n        test = False  # By default we are not in testing mode\n\n        testvar = str(os.environ.get(\"PARSL_TESTING\", 'None')).lower()\n        if testvar == 'true':\n            test = True\n\n        if self.config and self.config[\"globals\"][\"usageTracking\"] is False:\n            track = False\n\n        envvar = str(os.environ.get(\"PARSL_TRACKING\", True)).lower()\n        if envvar == \"false\":\n            track = False\n\n        return test, track\n\n    def construct_start_message(self):\n        \"\"\"Collect preliminary run info at the start of the DFK.\n\n        Returns :\n              - Message dict dumped as json string, ready for UDP\n        \"\"\"\n        uname = getpass.getuser().encode('latin1')\n        hashed_username = hashlib.sha256(uname).hexdigest()[0:10]\n        hname = socket.gethostname().encode('latin1')\n        hashed_hostname = hashlib.sha256(hname).hexdigest()[0:10]\n        message = {'uuid': self.uuid,\n                   'uname': hashed_username,\n                   'hname': hashed_hostname,\n                   'test': self.test_mode,\n                   'start': time.time()}\n\n        return json.dumps(message)\n\n    def construct_end_message(self):\n        \"\"\"Collect the final run information at the time of DFK cleanup.\n\n        Returns:\n             - Message dict dumped as json string, ready for UDP\n        \"\"\"\n        app_count = self.dfk.task_count\n\n        site_count = 0\n        if self.dfk._executors_managed:\n            site_count = len(self.dfk.config['sites'])\n\n        failed_states = (States.failed, States.dep_fail)\n        app_fails = len([t for t in self.dfk.tasks if\n                         self.dfk.tasks[t]['status'] in failed_states])\n\n        message = {'uuid': self.uuid,\n                   'end': time.time(),\n                   't_apps': app_count,\n                   'sites': site_count,\n                   'c_time': None,\n                   'failed': app_fails,\n                   'test': self.test_mode,\n                   }\n\n        return json.dumps(message)\n\n    def send_UDP_message(self, message):\n        \"\"\"Send UDP message.\"\"\"\n        if self.tracking_enabled:\n            try:\n                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP\n                x = sock.sendto(bytes(message, \"utf-8\"), (self.UDP_IP, self.UDP_PORT))\n                sock.close()\n            except OSError:\n                logger.debug(\"Unable to reach the network to send usage data\")\n                x = 0\n        else:\n            x = -1\n\n        return x\n\n    def send_message(self):\n        \"\"\"Send message over UDP.\n\n        If tracking is disables, the bytes_sent will always be set to -1\n\n        Returns:\n            (bytes_sent, time_taken)\n        \"\"\"\n        start = time.time()\n        message = None\n        if not self.initialized:\n            message = self.construct_start_message()\n            self.initialized = True\n        else:\n            message = self.construct_end_message()\n\n        x = self.send_UDP_message(message)\n        end = time.time()\n\n        return x, end - start\n\n\nif __name__ == '__main__':\n\n    from parsl import *\n\n    workers = ThreadPoolExecutor(max_workers=4)\n    dfk = DataFlowKernel(executors=[workers])\n\n    # ut = UsageTracker(dfk, ip='52.3.111.203')\n    ut = UsageTracker(dfk, domain_name='tracking.parsl-project.org')\n\n    for i in range(0, 2):\n        x = ut.send_message()\n        print(x)\n", "path": "parsl/dataflow/usage_tracking/usage.py"}], "after_files": [{"content": "import uuid\nimport time\nimport hashlib\nimport os\nimport getpass\nimport json\nimport logging\nimport socket\nimport sys\n\nfrom parsl.dataflow.states import States\nfrom parsl.version import VERSION as PARSL_VERSION\n\nlogger = logging.getLogger(__name__)\n\n\nclass UsageTracker (object):\n    \"\"\"Anonymized Usage Tracking for Parsl.\n\n    Client for this is here : https://github.com/Parsl/parsl_tracking\n    This issue captures the discussion that went into functionality\n    implemented here : https://github.com/Parsl/parsl/issues/34\n\n    \"\"\"\n\n    def __init__(self, dfk, ip='52.3.111.203', port=50077,\n                 domain_name='tracking.parsl-project.org'):\n        \"\"\"Initialize usage tracking unless the user has opted-out.\n\n        Tracks usage stats by inspecting the internal state of the dfk.\n\n        Args:\n             - dfk (DFK object) : Data Flow Kernel object\n\n        KWargs:\n             - ip (string) : IP address\n             - port (int) : Port number, Default:50077\n             - domain_name (string) : Domain name, will override IP\n                  Default: tracking.parsl-project.org\n        \"\"\"\n        if domain_name:\n            try:\n                self.UDP_IP = socket.gethostbyname(domain_name)\n            except Exception:\n                logging.debug(\"Could not lookup domain_name, defaulting to 52.3.111.203\")\n                self.UDP_IP = ip\n        else:\n            self.UDP_IP = ip\n        self.UDP_PORT = port\n        self.dfk = dfk\n        self.config = self.dfk.config\n        self.uuid = str(uuid.uuid4())\n        self.parsl_version = PARSL_VERSION\n        self.python_version = \"{}.{}.{}\".format(sys.version_info.major,\n                                                sys.version_info.minor,\n                                                sys.version_info.micro)\n        self.test_mode, self.tracking_enabled = self.check_tracking_enabled()\n        logger.debug(\"Tracking status: {}\".format(self.tracking_enabled))\n        logger.debug(\"Testing mode   : {}\".format(self.test_mode))\n        self.initialized = False  # Once first message is sent this will be True\n\n    def check_tracking_enabled(self):\n        \"\"\"By default tracking is enabled.\n\n        If Test mode is set via env variable PARSL_TESTING, a test flag is set\n\n        Tracking is disabled if :\n            1. config[\"globals\"][\"usageTracking\"] is set to False (Bool)\n            2. Environment variable PARSL_TRACKING is set to false (case insensitive)\n\n        \"\"\"\n        track = True   # By default we track usage\n        test = False  # By default we are not in testing mode\n\n        testvar = str(os.environ.get(\"PARSL_TESTING\", 'None')).lower()\n        if testvar == 'true':\n            test = True\n\n        if self.config and self.config[\"globals\"][\"usageTracking\"] is False:\n            track = False\n\n        envvar = str(os.environ.get(\"PARSL_TRACKING\", True)).lower()\n        if envvar == \"false\":\n            track = False\n\n        return test, track\n\n    def construct_start_message(self):\n        \"\"\"Collect preliminary run info at the start of the DFK.\n\n        Returns :\n              - Message dict dumped as json string, ready for UDP\n        \"\"\"\n        uname = getpass.getuser().encode('latin1')\n        hashed_username = hashlib.sha256(uname).hexdigest()[0:10]\n        hname = socket.gethostname().encode('latin1')\n        hashed_hostname = hashlib.sha256(hname).hexdigest()[0:10]\n        message = {'uuid': self.uuid,\n                   'uname': hashed_username,\n                   'hname': hashed_hostname,\n                   'test': self.test_mode,\n                   'parsl_v': self.parsl_version,\n                   'python_v': self.python_version,\n                   'start': time.time()}\n\n        return json.dumps(message)\n\n    def construct_end_message(self):\n        \"\"\"Collect the final run information at the time of DFK cleanup.\n\n        Returns:\n             - Message dict dumped as json string, ready for UDP\n        \"\"\"\n        app_count = self.dfk.task_count\n\n        site_count = 0\n        if self.dfk._executors_managed:\n            site_count = len(self.dfk.config['sites'])\n\n        failed_states = (States.failed, States.dep_fail)\n        app_fails = len([t for t in self.dfk.tasks if\n                         self.dfk.tasks[t]['status'] in failed_states])\n\n        message = {'uuid': self.uuid,\n                   'end': time.time(),\n                   't_apps': app_count,\n                   'sites': site_count,\n                   'c_time': None,\n                   'failed': app_fails,\n                   'test': self.test_mode,\n                   }\n\n        return json.dumps(message)\n\n    def send_UDP_message(self, message):\n        \"\"\"Send UDP message.\"\"\"\n        if self.tracking_enabled:\n            try:\n                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP\n                x = sock.sendto(bytes(message, \"utf-8\"), (self.UDP_IP, self.UDP_PORT))\n                sock.close()\n            except OSError:\n                logger.debug(\"Unable to reach the network to send usage data\")\n                x = 0\n        else:\n            x = -1\n\n        return x\n\n    def send_message(self):\n        \"\"\"Send message over UDP.\n\n        If tracking is disables, the bytes_sent will always be set to -1\n\n        Returns:\n            (bytes_sent, time_taken)\n        \"\"\"\n        start = time.time()\n        message = None\n        if not self.initialized:\n            message = self.construct_start_message()\n            self.initialized = True\n        else:\n            message = self.construct_end_message()\n\n        x = self.send_UDP_message(message)\n        end = time.time()\n\n        return x, end - start\n\n\nif __name__ == '__main__':\n\n    from parsl import *\n\n    workers = ThreadPoolExecutor(max_workers=4)\n    dfk = DataFlowKernel(executors=[workers])\n\n    # ut = UsageTracker(dfk, ip='52.3.111.203')\n    ut = UsageTracker(dfk, domain_name='tracking.parsl-project.org')\n\n    for i in range(0, 2):\n        x = ut.send_message()\n        print(x)\n", "path": "parsl/dataflow/usage_tracking/usage.py"}]}