We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
If an error occurs on the python backend during tax report calculation, the app will hang forever
## Problem Definition

If a python backend error happens during the calculation of the tax report, then because the call to the backend is made in an asynchronous way we have no way of detecting that something went wrong and the application will always be stuck at the "Loading..." stage

## Task

Somehow catch all errors that may happen during the tax report or periodically check for errors. If an error happens, then make sure to stop the process and notify the user. 
If an error occurs on the python backend during tax report calculation, the app will hang forever
## Problem Definition

If a python backend error happens during the calculation of the tax report, then because the call to the backend is made in an asynchronous way we have no way of detecting that something went wrong and the application will always be stuck at the "Loading..." stage

## Task

Somehow catch all errors that may happen during the tax report or periodically check for errors. If an error happens, then make sure to stop the process and notify the user. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rotkehlchen/server.py`
Content:
```
1 # Based on https://github.com/fyears/electron-python-example
2 from __future__ import print_function
3 
4 import gevent
5 from gevent.event import Event
6 from gevent.lock import Semaphore
7 import signal
8 import zerorpc
9 import pickle
10 import traceback
11 
12 from rotkehlchen.errors import AuthenticationError, PermissionError
13 from rotkehlchen.args import app_args
14 from rotkehlchen.rotkehlchen import Rotkehlchen
15 from rotkehlchen.utils import pretty_json_dumps, process_result
16 from rotkehlchen.inquirer import get_fiat_usd_exchange_rates
17 
18 import logging
19 logger = logging.getLogger(__name__)
20 
21 
22 class RotkehlchenServer(object):
23     def __init__(self):
24         self.args = app_args()
25         self.rotkehlchen = Rotkehlchen(self.args)
26         self.stop_event = Event()
27         mainloop_greenlet = self.rotkehlchen.start()
28         mainloop_greenlet.link_exception(self.handle_killed_greenlets)
29         self.greenlets = [mainloop_greenlet]
30         self.task_lock = Semaphore()
31         self.task_id = 0
32         self.task_results = {}
33 
34     def new_task_id(self):
35         with self.task_lock:
36             task_id = self.task_id
37             self.task_id += 1
38         return task_id
39 
40     def write_task_result(self, task_id, result):
41         with self.task_lock:
42             self.task_results[task_id] = result
43 
44     def get_task_result(self, task_id):
45         with self.task_lock:
46             return self.task_results[task_id]
47 
48     def port(self):
49         return self.args.zerorpc_port
50 
51     def shutdown(self):
52         logger.debug('Shutdown initiated')
53         self.zerorpc.stop()
54         gevent.wait(self.greenlets)
55         self.rotkehlchen.shutdown()
56         print("Shutting down zerorpc server")
57         logger.debug('Shutdown completed')
58         logging.shutdown()
59 
60     def set_main_currency(self, currency_text):
61         self.rotkehlchen.set_main_currency(currency_text)
62 
63     def set_settings(self, settings):
64         result, message = self.rotkehlchen.set_settings(settings)
65         return {'result': result, 'message': message}
66 
67     def get_total_in_usd(self, balances):
68         total = 0
69         for _, entry in balances.items():
70             total += entry['usd_value']
71 
72         return total
73 
74     def handle_killed_greenlets(self, greenlet):
75         if greenlet.exception:
76             logger.error(
77                 'Greenlet for task {} dies with exception: {}.\n'
78                 'Exception Name: {}\nException Info: {}\nTraceback:\n {}'
79                 .format(
80                     greenlet.task_id,
81                     greenlet.exception,
82                     greenlet._exc_info[0],
83                     greenlet._exc_info[1],
84                     ''.join(traceback.format_tb(pickle.loads(greenlet._exc_info[2]))),
85                 ))
86             # also write an error for the task result
87             result = {
88                 'error': str(greenlet.exception)
89             }
90             self.write_task_result(greenlet.task_id, result)
91 
92     def _query_async(self, command, task_id, **kwargs):
93         result = getattr(self, command)(**kwargs)
94         self.write_task_result(task_id, result)
95 
96     def query_async(self, command, **kwargs):
97         task_id = self.new_task_id()
98         logger.debug("NEW TASK {} (kwargs:{}) with ID: {}".format(command, kwargs, task_id))
99         greenlet = gevent.spawn(
100             self._query_async,
101             command,
102             task_id,
103             **kwargs
104         )
105         greenlet.task_id = task_id
106         greenlet.link_exception(self.handle_killed_greenlets)
107         self.greenlets.append(greenlet)
108         return task_id
109 
110     def query_task_result(self, task_id):
111         logger.debug("Querying task result with task id {}".format(task_id))
112         with self.task_lock:
113             len1 = len(self.task_results)
114             ret = self.task_results.pop(int(task_id), None)
115             if not ret and len1 != len(self.task_results):
116                 logger.error("Popped None from results task but lost an entry")
117             if ret:
118                 logger.debug("Found response for task {}".format(task_id))
119         return ret
120 
121     def get_fiat_exchange_rates(self, currencies):
122         res = {'exchange_rates': get_fiat_usd_exchange_rates(currencies)}
123         return process_result(res)
124 
125     def get_settings(self):
126         return process_result(self.rotkehlchen.data.db.get_settings())
127 
128     def remove_exchange(self, name):
129         result, message = self.rotkehlchen.remove_exchange(name)
130         return {'result': result, 'message': message}
131 
132     def setup_exchange(self, name, api_key, api_secret):
133         result, message = self.rotkehlchen.setup_exchange(name, api_key, api_secret)
134         return {'result': result, 'message': message}
135 
136     def query_otctrades(self):
137         trades = self.rotkehlchen.data.get_external_trades()
138         return process_result(trades)
139 
140     def add_otctrade(self, data):
141         result, message = self.rotkehlchen.data.add_external_trade(data)
142         return {'result': result, 'message': message}
143 
144     def edit_otctrade(self, data):
145         result, message = self.rotkehlchen.data.edit_external_trade(data)
146         return {'result': result, 'message': message}
147 
148     def delete_otctrade(self, trade_id):
149         result, message = self.rotkehlchen.data.delete_external_trade(trade_id)
150         return {'result': result, 'message': message}
151 
152     def set_premium_credentials(self, api_key, api_secret):
153         result, empty_or_error = self.rotkehlchen.set_premium_credentials(api_key, api_secret)
154         return {'result': result, 'message': empty_or_error}
155 
156     def set_premium_option_sync(self, should_sync):
157         self.rotkehlchen.data.db.update_premium_sync(should_sync)
158         return True
159 
160     def query_exchange_balances(self, name):
161         res = {'name': name}
162         balances, msg = getattr(self.rotkehlchen, name).query_balances()
163         if balances is None:
164             res['error'] = msg
165         else:
166             res['balances'] = balances
167 
168         return process_result(res)
169 
170     def query_exchange_balances_async(self, name):
171         res = self.query_async('query_exchange_balances', name=name)
172         return {'task_id': res}
173 
174     def query_blockchain_balances(self):
175         result, empty_or_error = self.rotkehlchen.blockchain.query_balances()
176         return process_result({'result': result, 'message': empty_or_error})
177 
178     def query_blockchain_balances_async(self):
179         res = self.query_async('query_blockchain_balances')
180         return {'task_id': res}
181 
182     def query_fiat_balances(self):
183         res = self.rotkehlchen.query_fiat_balances()
184         return process_result(res)
185 
186     def set_fiat_balance(self, currency, balance):
187         result, message = self.rotkehlchen.data.set_fiat_balance(currency, balance)
188         return {'result': result, 'message': message}
189 
190     def query_trade_history(self, location, start_ts, end_ts):
191         start_ts = int(start_ts)
192         end_ts = int(end_ts)
193         if location == 'all':
194             return self.rotkehlchen.trades_historian.get_history(start_ts, end_ts)
195 
196         try:
197             exchange = getattr(self.rotkehlchen, location)
198         except AttributeError:
199             raise "Unknown location {} given".format(location)
200 
201         return exchange.query_trade_history(start_ts, end_ts, end_ts)
202 
203     def process_trade_history(self, start_ts, end_ts):
204         start_ts = int(start_ts)
205         end_ts = int(end_ts)
206         result, error_or_empty = self.rotkehlchen.process_history(start_ts, end_ts)
207         response = {'result': result, 'message': error_or_empty}
208         return process_result(response)
209 
210     def process_trade_history_async(self, start_ts, end_ts):
211         res = self.query_async('process_trade_history', start_ts=start_ts, end_ts=end_ts)
212         return {'task_id': res}
213 
214     def export_processed_history_csv(self, dirpath):
215         result, message = self.rotkehlchen.accountant.csvexporter.create_files(dirpath)
216         return {'result': result, 'message': message}
217 
218     def query_balances(self, save_data=False):
219         if isinstance(save_data, str) and (save_data == 'save' or save_data == 'True'):
220             save_data = True
221 
222         result = self.rotkehlchen.query_balances(save_data)
223         print(pretty_json_dumps(result))
224         return process_result(result)
225 
226     def query_balances_async(self, save_data=False):
227         res = self.query_async('query_balances')
228         return {'task_id': res}
229 
230     def query_last_balance_save_time(self):
231         res = self.rotkehlchen.data.db.get_last_balance_save_time()
232         return res
233 
234     def get_eth_tokens(self):
235         result = {
236             'all_eth_tokens': self.rotkehlchen.data.eth_tokens,
237             'owned_eth_tokens': self.rotkehlchen.blockchain.eth_tokens
238         }
239         return process_result(result)
240 
241     def add_owned_eth_tokens(self, tokens):
242         return self.rotkehlchen.add_owned_eth_tokens(tokens)
243 
244     def remove_owned_eth_tokens(self, tokens):
245         return self.rotkehlchen.remove_owned_eth_tokens(tokens)
246 
247     def add_blockchain_account(self, blockchain, account):
248         return self.rotkehlchen.add_blockchain_account(blockchain, account)
249 
250     def remove_blockchain_account(self, blockchain, account):
251         return self.rotkehlchen.remove_blockchain_account(blockchain, account)
252 
253     def get_ignored_assets(self):
254         result = {
255             'ignored_assets': self.rotkehlchen.data.db.get_ignored_assets()
256         }
257         return result
258 
259     def add_ignored_asset(self, asset):
260         result, message = self.rotkehlchen.data.add_ignored_asset(asset)
261         return {'result': result, 'message': message}
262 
263     def remove_ignored_asset(self, asset):
264         result, message = self.rotkehlchen.data.remove_ignored_asset(asset)
265         return {'result': result, 'message': message}
266 
267     def unlock_user(self, user, password, create_new, sync_approval, api_key, api_secret):
268         """Either unlock an existing user or create a new one"""
269         res = {'result': True, 'message': ''}
270 
271         assert isinstance(sync_approval, str), "sync_approval should be a string"
272         assert isinstance(api_key, str), "api_key should be a string"
273         assert isinstance(api_secret, str), "api_secret should be a string"
274 
275         valid_actions = ['unknown', 'yes', 'no']
276         valid_approve = isinstance(sync_approval, str) and sync_approval in valid_actions
277         if not valid_approve:
278             raise ValueError('Provided invalid value for sync_approval')
279 
280         if api_key != '' and create_new is False:
281             raise ValueError('Should not ever have api_key provided during a normal login')
282 
283         if api_key != '' and api_secret == '' or api_secret != '' and api_key == '':
284             raise ValueError('Must provide both or neither of api key/secret')
285 
286         try:
287             self.rotkehlchen.unlock_user(
288                 user,
289                 password,
290                 create_new,
291                 sync_approval,
292                 api_key,
293                 api_secret
294             )
295             res['exchanges'] = self.rotkehlchen.connected_exchanges
296             res['premium'] = True if hasattr(self.rotkehlchen, 'premium') else False
297             res['settings'] = self.rotkehlchen.data.db.get_settings()
298         except AuthenticationError as e:
299             res['result'] = False
300             res['message'] = str(e)
301         except PermissionError as e:
302             res['result'] = False
303             res['permission_needed'] = True
304             res['message'] = str(e)
305 
306         return res
307 
308     def echo(self, text):
309         return text
310 
311     def main(self):
312         gevent.hub.signal(signal.SIGINT, self.shutdown)
313         gevent.hub.signal(signal.SIGTERM, self.shutdown)
314         # self.zerorpc = zerorpc.Server(self, heartbeat=15)
315         self.zerorpc = zerorpc.Server(self)
316         addr = 'tcp://127.0.0.1:' + str(self.port())
317         self.zerorpc.bind(addr)
318         print('start running on {}'.format(addr))
319         self.zerorpc.run()
320
```
--- 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/rotkehlchen/server.py b/rotkehlchen/server.py
--- a/rotkehlchen/server.py
+++ b/rotkehlchen/server.py
@@ -1,21 +1,21 @@
 # Based on https://github.com/fyears/electron-python-example
 from __future__ import print_function
 
+import logging
+import signal
+import traceback
+
 import gevent
+import zerorpc
 from gevent.event import Event
 from gevent.lock import Semaphore
-import signal
-import zerorpc
-import pickle
-import traceback
 
-from rotkehlchen.errors import AuthenticationError, PermissionError
 from rotkehlchen.args import app_args
+from rotkehlchen.errors import AuthenticationError, PermissionError
+from rotkehlchen.inquirer import get_fiat_usd_exchange_rates
 from rotkehlchen.rotkehlchen import Rotkehlchen
 from rotkehlchen.utils import pretty_json_dumps, process_result
-from rotkehlchen.inquirer import get_fiat_usd_exchange_rates
 
-import logging
 logger = logging.getLogger(__name__)
 
 
@@ -72,22 +72,25 @@
         return total
 
     def handle_killed_greenlets(self, greenlet):
-        if greenlet.exception:
-            logger.error(
-                'Greenlet for task {} dies with exception: {}.\n'
-                'Exception Name: {}\nException Info: {}\nTraceback:\n {}'
-                .format(
-                    greenlet.task_id,
-                    greenlet.exception,
-                    greenlet._exc_info[0],
-                    greenlet._exc_info[1],
-                    ''.join(traceback.format_tb(pickle.loads(greenlet._exc_info[2]))),
-                ))
-            # also write an error for the task result
-            result = {
-                'error': str(greenlet.exception)
-            }
-            self.write_task_result(greenlet.task_id, result)
+        if not greenlet.exception:
+            logger.warning('handle_killed_greenlets without an exception')
+            return
+
+        logger.error(
+            'Greenlet for task {} dies with exception: {}.\n'
+            'Exception Name: {}\nException Info: {}\nTraceback:\n {}'
+            .format(
+                greenlet.task_id,
+                greenlet.exception,
+                greenlet.exc_info[0],
+                greenlet.exc_info[1],
+                ''.join(traceback.format_tb(greenlet.exc_info[2])),
+            ))
+        # also write an error for the task result
+        result = {
+            'error': str(greenlet.exception)
+        }
+        self.write_task_result(greenlet.task_id, result)
 
     def _query_async(self, command, task_id, **kwargs):
         result = getattr(self, command)(**kwargs)

{"golden_diff": "diff --git a/rotkehlchen/server.py b/rotkehlchen/server.py\n--- a/rotkehlchen/server.py\n+++ b/rotkehlchen/server.py\n@@ -1,21 +1,21 @@\n # Based on https://github.com/fyears/electron-python-example\n from __future__ import print_function\n \n+import logging\n+import signal\n+import traceback\n+\n import gevent\n+import zerorpc\n from gevent.event import Event\n from gevent.lock import Semaphore\n-import signal\n-import zerorpc\n-import pickle\n-import traceback\n \n-from rotkehlchen.errors import AuthenticationError, PermissionError\n from rotkehlchen.args import app_args\n+from rotkehlchen.errors import AuthenticationError, PermissionError\n+from rotkehlchen.inquirer import get_fiat_usd_exchange_rates\n from rotkehlchen.rotkehlchen import Rotkehlchen\n from rotkehlchen.utils import pretty_json_dumps, process_result\n-from rotkehlchen.inquirer import get_fiat_usd_exchange_rates\n \n-import logging\n logger = logging.getLogger(__name__)\n \n \n@@ -72,22 +72,25 @@\n         return total\n \n     def handle_killed_greenlets(self, greenlet):\n-        if greenlet.exception:\n-            logger.error(\n-                'Greenlet for task {} dies with exception: {}.\\n'\n-                'Exception Name: {}\\nException Info: {}\\nTraceback:\\n {}'\n-                .format(\n-                    greenlet.task_id,\n-                    greenlet.exception,\n-                    greenlet._exc_info[0],\n-                    greenlet._exc_info[1],\n-                    ''.join(traceback.format_tb(pickle.loads(greenlet._exc_info[2]))),\n-                ))\n-            # also write an error for the task result\n-            result = {\n-                'error': str(greenlet.exception)\n-            }\n-            self.write_task_result(greenlet.task_id, result)\n+        if not greenlet.exception:\n+            logger.warning('handle_killed_greenlets without an exception')\n+            return\n+\n+        logger.error(\n+            'Greenlet for task {} dies with exception: {}.\\n'\n+            'Exception Name: {}\\nException Info: {}\\nTraceback:\\n {}'\n+            .format(\n+                greenlet.task_id,\n+                greenlet.exception,\n+                greenlet.exc_info[0],\n+                greenlet.exc_info[1],\n+                ''.join(traceback.format_tb(greenlet.exc_info[2])),\n+            ))\n+        # also write an error for the task result\n+        result = {\n+            'error': str(greenlet.exception)\n+        }\n+        self.write_task_result(greenlet.task_id, result)\n \n     def _query_async(self, command, task_id, **kwargs):\n         result = getattr(self, command)(**kwargs)\n", "issue": "If an error occurs on the python backend during tax report calculation, the app will hang forever\n## Problem Definition\r\n\r\nIf a python backend error happens during the calculation of the tax report, then because the call to the backend is made in an asynchronous way we have no way of detecting that something went wrong and the application will always be stuck at the \"Loading...\" stage\r\n\r\n## Task\r\n\r\nSomehow catch all errors that may happen during the tax report or periodically check for errors. If an error happens, then make sure to stop the process and notify the user. \nIf an error occurs on the python backend during tax report calculation, the app will hang forever\n## Problem Definition\r\n\r\nIf a python backend error happens during the calculation of the tax report, then because the call to the backend is made in an asynchronous way we have no way of detecting that something went wrong and the application will always be stuck at the \"Loading...\" stage\r\n\r\n## Task\r\n\r\nSomehow catch all errors that may happen during the tax report or periodically check for errors. If an error happens, then make sure to stop the process and notify the user. \n", "before_files": [{"content": "# Based on https://github.com/fyears/electron-python-example\nfrom __future__ import print_function\n\nimport gevent\nfrom gevent.event import Event\nfrom gevent.lock import Semaphore\nimport signal\nimport zerorpc\nimport pickle\nimport traceback\n\nfrom rotkehlchen.errors import AuthenticationError, PermissionError\nfrom rotkehlchen.args import app_args\nfrom rotkehlchen.rotkehlchen import Rotkehlchen\nfrom rotkehlchen.utils import pretty_json_dumps, process_result\nfrom rotkehlchen.inquirer import get_fiat_usd_exchange_rates\n\nimport logging\nlogger = logging.getLogger(__name__)\n\n\nclass RotkehlchenServer(object):\n    def __init__(self):\n        self.args = app_args()\n        self.rotkehlchen = Rotkehlchen(self.args)\n        self.stop_event = Event()\n        mainloop_greenlet = self.rotkehlchen.start()\n        mainloop_greenlet.link_exception(self.handle_killed_greenlets)\n        self.greenlets = [mainloop_greenlet]\n        self.task_lock = Semaphore()\n        self.task_id = 0\n        self.task_results = {}\n\n    def new_task_id(self):\n        with self.task_lock:\n            task_id = self.task_id\n            self.task_id += 1\n        return task_id\n\n    def write_task_result(self, task_id, result):\n        with self.task_lock:\n            self.task_results[task_id] = result\n\n    def get_task_result(self, task_id):\n        with self.task_lock:\n            return self.task_results[task_id]\n\n    def port(self):\n        return self.args.zerorpc_port\n\n    def shutdown(self):\n        logger.debug('Shutdown initiated')\n        self.zerorpc.stop()\n        gevent.wait(self.greenlets)\n        self.rotkehlchen.shutdown()\n        print(\"Shutting down zerorpc server\")\n        logger.debug('Shutdown completed')\n        logging.shutdown()\n\n    def set_main_currency(self, currency_text):\n        self.rotkehlchen.set_main_currency(currency_text)\n\n    def set_settings(self, settings):\n        result, message = self.rotkehlchen.set_settings(settings)\n        return {'result': result, 'message': message}\n\n    def get_total_in_usd(self, balances):\n        total = 0\n        for _, entry in balances.items():\n            total += entry['usd_value']\n\n        return total\n\n    def handle_killed_greenlets(self, greenlet):\n        if greenlet.exception:\n            logger.error(\n                'Greenlet for task {} dies with exception: {}.\\n'\n                'Exception Name: {}\\nException Info: {}\\nTraceback:\\n {}'\n                .format(\n                    greenlet.task_id,\n                    greenlet.exception,\n                    greenlet._exc_info[0],\n                    greenlet._exc_info[1],\n                    ''.join(traceback.format_tb(pickle.loads(greenlet._exc_info[2]))),\n                ))\n            # also write an error for the task result\n            result = {\n                'error': str(greenlet.exception)\n            }\n            self.write_task_result(greenlet.task_id, result)\n\n    def _query_async(self, command, task_id, **kwargs):\n        result = getattr(self, command)(**kwargs)\n        self.write_task_result(task_id, result)\n\n    def query_async(self, command, **kwargs):\n        task_id = self.new_task_id()\n        logger.debug(\"NEW TASK {} (kwargs:{}) with ID: {}\".format(command, kwargs, task_id))\n        greenlet = gevent.spawn(\n            self._query_async,\n            command,\n            task_id,\n            **kwargs\n        )\n        greenlet.task_id = task_id\n        greenlet.link_exception(self.handle_killed_greenlets)\n        self.greenlets.append(greenlet)\n        return task_id\n\n    def query_task_result(self, task_id):\n        logger.debug(\"Querying task result with task id {}\".format(task_id))\n        with self.task_lock:\n            len1 = len(self.task_results)\n            ret = self.task_results.pop(int(task_id), None)\n            if not ret and len1 != len(self.task_results):\n                logger.error(\"Popped None from results task but lost an entry\")\n            if ret:\n                logger.debug(\"Found response for task {}\".format(task_id))\n        return ret\n\n    def get_fiat_exchange_rates(self, currencies):\n        res = {'exchange_rates': get_fiat_usd_exchange_rates(currencies)}\n        return process_result(res)\n\n    def get_settings(self):\n        return process_result(self.rotkehlchen.data.db.get_settings())\n\n    def remove_exchange(self, name):\n        result, message = self.rotkehlchen.remove_exchange(name)\n        return {'result': result, 'message': message}\n\n    def setup_exchange(self, name, api_key, api_secret):\n        result, message = self.rotkehlchen.setup_exchange(name, api_key, api_secret)\n        return {'result': result, 'message': message}\n\n    def query_otctrades(self):\n        trades = self.rotkehlchen.data.get_external_trades()\n        return process_result(trades)\n\n    def add_otctrade(self, data):\n        result, message = self.rotkehlchen.data.add_external_trade(data)\n        return {'result': result, 'message': message}\n\n    def edit_otctrade(self, data):\n        result, message = self.rotkehlchen.data.edit_external_trade(data)\n        return {'result': result, 'message': message}\n\n    def delete_otctrade(self, trade_id):\n        result, message = self.rotkehlchen.data.delete_external_trade(trade_id)\n        return {'result': result, 'message': message}\n\n    def set_premium_credentials(self, api_key, api_secret):\n        result, empty_or_error = self.rotkehlchen.set_premium_credentials(api_key, api_secret)\n        return {'result': result, 'message': empty_or_error}\n\n    def set_premium_option_sync(self, should_sync):\n        self.rotkehlchen.data.db.update_premium_sync(should_sync)\n        return True\n\n    def query_exchange_balances(self, name):\n        res = {'name': name}\n        balances, msg = getattr(self.rotkehlchen, name).query_balances()\n        if balances is None:\n            res['error'] = msg\n        else:\n            res['balances'] = balances\n\n        return process_result(res)\n\n    def query_exchange_balances_async(self, name):\n        res = self.query_async('query_exchange_balances', name=name)\n        return {'task_id': res}\n\n    def query_blockchain_balances(self):\n        result, empty_or_error = self.rotkehlchen.blockchain.query_balances()\n        return process_result({'result': result, 'message': empty_or_error})\n\n    def query_blockchain_balances_async(self):\n        res = self.query_async('query_blockchain_balances')\n        return {'task_id': res}\n\n    def query_fiat_balances(self):\n        res = self.rotkehlchen.query_fiat_balances()\n        return process_result(res)\n\n    def set_fiat_balance(self, currency, balance):\n        result, message = self.rotkehlchen.data.set_fiat_balance(currency, balance)\n        return {'result': result, 'message': message}\n\n    def query_trade_history(self, location, start_ts, end_ts):\n        start_ts = int(start_ts)\n        end_ts = int(end_ts)\n        if location == 'all':\n            return self.rotkehlchen.trades_historian.get_history(start_ts, end_ts)\n\n        try:\n            exchange = getattr(self.rotkehlchen, location)\n        except AttributeError:\n            raise \"Unknown location {} given\".format(location)\n\n        return exchange.query_trade_history(start_ts, end_ts, end_ts)\n\n    def process_trade_history(self, start_ts, end_ts):\n        start_ts = int(start_ts)\n        end_ts = int(end_ts)\n        result, error_or_empty = self.rotkehlchen.process_history(start_ts, end_ts)\n        response = {'result': result, 'message': error_or_empty}\n        return process_result(response)\n\n    def process_trade_history_async(self, start_ts, end_ts):\n        res = self.query_async('process_trade_history', start_ts=start_ts, end_ts=end_ts)\n        return {'task_id': res}\n\n    def export_processed_history_csv(self, dirpath):\n        result, message = self.rotkehlchen.accountant.csvexporter.create_files(dirpath)\n        return {'result': result, 'message': message}\n\n    def query_balances(self, save_data=False):\n        if isinstance(save_data, str) and (save_data == 'save' or save_data == 'True'):\n            save_data = True\n\n        result = self.rotkehlchen.query_balances(save_data)\n        print(pretty_json_dumps(result))\n        return process_result(result)\n\n    def query_balances_async(self, save_data=False):\n        res = self.query_async('query_balances')\n        return {'task_id': res}\n\n    def query_last_balance_save_time(self):\n        res = self.rotkehlchen.data.db.get_last_balance_save_time()\n        return res\n\n    def get_eth_tokens(self):\n        result = {\n            'all_eth_tokens': self.rotkehlchen.data.eth_tokens,\n            'owned_eth_tokens': self.rotkehlchen.blockchain.eth_tokens\n        }\n        return process_result(result)\n\n    def add_owned_eth_tokens(self, tokens):\n        return self.rotkehlchen.add_owned_eth_tokens(tokens)\n\n    def remove_owned_eth_tokens(self, tokens):\n        return self.rotkehlchen.remove_owned_eth_tokens(tokens)\n\n    def add_blockchain_account(self, blockchain, account):\n        return self.rotkehlchen.add_blockchain_account(blockchain, account)\n\n    def remove_blockchain_account(self, blockchain, account):\n        return self.rotkehlchen.remove_blockchain_account(blockchain, account)\n\n    def get_ignored_assets(self):\n        result = {\n            'ignored_assets': self.rotkehlchen.data.db.get_ignored_assets()\n        }\n        return result\n\n    def add_ignored_asset(self, asset):\n        result, message = self.rotkehlchen.data.add_ignored_asset(asset)\n        return {'result': result, 'message': message}\n\n    def remove_ignored_asset(self, asset):\n        result, message = self.rotkehlchen.data.remove_ignored_asset(asset)\n        return {'result': result, 'message': message}\n\n    def unlock_user(self, user, password, create_new, sync_approval, api_key, api_secret):\n        \"\"\"Either unlock an existing user or create a new one\"\"\"\n        res = {'result': True, 'message': ''}\n\n        assert isinstance(sync_approval, str), \"sync_approval should be a string\"\n        assert isinstance(api_key, str), \"api_key should be a string\"\n        assert isinstance(api_secret, str), \"api_secret should be a string\"\n\n        valid_actions = ['unknown', 'yes', 'no']\n        valid_approve = isinstance(sync_approval, str) and sync_approval in valid_actions\n        if not valid_approve:\n            raise ValueError('Provided invalid value for sync_approval')\n\n        if api_key != '' and create_new is False:\n            raise ValueError('Should not ever have api_key provided during a normal login')\n\n        if api_key != '' and api_secret == '' or api_secret != '' and api_key == '':\n            raise ValueError('Must provide both or neither of api key/secret')\n\n        try:\n            self.rotkehlchen.unlock_user(\n                user,\n                password,\n                create_new,\n                sync_approval,\n                api_key,\n                api_secret\n            )\n            res['exchanges'] = self.rotkehlchen.connected_exchanges\n            res['premium'] = True if hasattr(self.rotkehlchen, 'premium') else False\n            res['settings'] = self.rotkehlchen.data.db.get_settings()\n        except AuthenticationError as e:\n            res['result'] = False\n            res['message'] = str(e)\n        except PermissionError as e:\n            res['result'] = False\n            res['permission_needed'] = True\n            res['message'] = str(e)\n\n        return res\n\n    def echo(self, text):\n        return text\n\n    def main(self):\n        gevent.hub.signal(signal.SIGINT, self.shutdown)\n        gevent.hub.signal(signal.SIGTERM, self.shutdown)\n        # self.zerorpc = zerorpc.Server(self, heartbeat=15)\n        self.zerorpc = zerorpc.Server(self)\n        addr = 'tcp://127.0.0.1:' + str(self.port())\n        self.zerorpc.bind(addr)\n        print('start running on {}'.format(addr))\n        self.zerorpc.run()\n", "path": "rotkehlchen/server.py"}], "after_files": [{"content": "# Based on https://github.com/fyears/electron-python-example\nfrom __future__ import print_function\n\nimport logging\nimport signal\nimport traceback\n\nimport gevent\nimport zerorpc\nfrom gevent.event import Event\nfrom gevent.lock import Semaphore\n\nfrom rotkehlchen.args import app_args\nfrom rotkehlchen.errors import AuthenticationError, PermissionError\nfrom rotkehlchen.inquirer import get_fiat_usd_exchange_rates\nfrom rotkehlchen.rotkehlchen import Rotkehlchen\nfrom rotkehlchen.utils import pretty_json_dumps, process_result\n\nlogger = logging.getLogger(__name__)\n\n\nclass RotkehlchenServer(object):\n    def __init__(self):\n        self.args = app_args()\n        self.rotkehlchen = Rotkehlchen(self.args)\n        self.stop_event = Event()\n        mainloop_greenlet = self.rotkehlchen.start()\n        mainloop_greenlet.link_exception(self.handle_killed_greenlets)\n        self.greenlets = [mainloop_greenlet]\n        self.task_lock = Semaphore()\n        self.task_id = 0\n        self.task_results = {}\n\n    def new_task_id(self):\n        with self.task_lock:\n            task_id = self.task_id\n            self.task_id += 1\n        return task_id\n\n    def write_task_result(self, task_id, result):\n        with self.task_lock:\n            self.task_results[task_id] = result\n\n    def get_task_result(self, task_id):\n        with self.task_lock:\n            return self.task_results[task_id]\n\n    def port(self):\n        return self.args.zerorpc_port\n\n    def shutdown(self):\n        logger.debug('Shutdown initiated')\n        self.zerorpc.stop()\n        gevent.wait(self.greenlets)\n        self.rotkehlchen.shutdown()\n        print(\"Shutting down zerorpc server\")\n        logger.debug('Shutdown completed')\n        logging.shutdown()\n\n    def set_main_currency(self, currency_text):\n        self.rotkehlchen.set_main_currency(currency_text)\n\n    def set_settings(self, settings):\n        result, message = self.rotkehlchen.set_settings(settings)\n        return {'result': result, 'message': message}\n\n    def get_total_in_usd(self, balances):\n        total = 0\n        for _, entry in balances.items():\n            total += entry['usd_value']\n\n        return total\n\n    def handle_killed_greenlets(self, greenlet):\n        if not greenlet.exception:\n            logger.warning('handle_killed_greenlets without an exception')\n            return\n\n        logger.error(\n            'Greenlet for task {} dies with exception: {}.\\n'\n            'Exception Name: {}\\nException Info: {}\\nTraceback:\\n {}'\n            .format(\n                greenlet.task_id,\n                greenlet.exception,\n                greenlet.exc_info[0],\n                greenlet.exc_info[1],\n                ''.join(traceback.format_tb(greenlet.exc_info[2])),\n            ))\n        # also write an error for the task result\n        result = {\n            'error': str(greenlet.exception)\n        }\n        self.write_task_result(greenlet.task_id, result)\n\n    def _query_async(self, command, task_id, **kwargs):\n        result = getattr(self, command)(**kwargs)\n        self.write_task_result(task_id, result)\n\n    def query_async(self, command, **kwargs):\n        task_id = self.new_task_id()\n        logger.debug(\"NEW TASK {} (kwargs:{}) with ID: {}\".format(command, kwargs, task_id))\n        greenlet = gevent.spawn(\n            self._query_async,\n            command,\n            task_id,\n            **kwargs\n        )\n        greenlet.task_id = task_id\n        greenlet.link_exception(self.handle_killed_greenlets)\n        self.greenlets.append(greenlet)\n        return task_id\n\n    def query_task_result(self, task_id):\n        logger.debug(\"Querying task result with task id {}\".format(task_id))\n        with self.task_lock:\n            len1 = len(self.task_results)\n            ret = self.task_results.pop(int(task_id), None)\n            if not ret and len1 != len(self.task_results):\n                logger.error(\"Popped None from results task but lost an entry\")\n            if ret:\n                logger.debug(\"Found response for task {}\".format(task_id))\n        return ret\n\n    def get_fiat_exchange_rates(self, currencies):\n        res = {'exchange_rates': get_fiat_usd_exchange_rates(currencies)}\n        return process_result(res)\n\n    def get_settings(self):\n        return process_result(self.rotkehlchen.data.db.get_settings())\n\n    def remove_exchange(self, name):\n        result, message = self.rotkehlchen.remove_exchange(name)\n        return {'result': result, 'message': message}\n\n    def setup_exchange(self, name, api_key, api_secret):\n        result, message = self.rotkehlchen.setup_exchange(name, api_key, api_secret)\n        return {'result': result, 'message': message}\n\n    def query_otctrades(self):\n        trades = self.rotkehlchen.data.get_external_trades()\n        return process_result(trades)\n\n    def add_otctrade(self, data):\n        result, message = self.rotkehlchen.data.add_external_trade(data)\n        return {'result': result, 'message': message}\n\n    def edit_otctrade(self, data):\n        result, message = self.rotkehlchen.data.edit_external_trade(data)\n        return {'result': result, 'message': message}\n\n    def delete_otctrade(self, trade_id):\n        result, message = self.rotkehlchen.data.delete_external_trade(trade_id)\n        return {'result': result, 'message': message}\n\n    def set_premium_credentials(self, api_key, api_secret):\n        result, empty_or_error = self.rotkehlchen.set_premium_credentials(api_key, api_secret)\n        return {'result': result, 'message': empty_or_error}\n\n    def set_premium_option_sync(self, should_sync):\n        self.rotkehlchen.data.db.update_premium_sync(should_sync)\n        return True\n\n    def query_exchange_balances(self, name):\n        res = {'name': name}\n        balances, msg = getattr(self.rotkehlchen, name).query_balances()\n        if balances is None:\n            res['error'] = msg\n        else:\n            res['balances'] = balances\n\n        return process_result(res)\n\n    def query_exchange_balances_async(self, name):\n        res = self.query_async('query_exchange_balances', name=name)\n        return {'task_id': res}\n\n    def query_blockchain_balances(self):\n        result, empty_or_error = self.rotkehlchen.blockchain.query_balances()\n        return process_result({'result': result, 'message': empty_or_error})\n\n    def query_blockchain_balances_async(self):\n        res = self.query_async('query_blockchain_balances')\n        return {'task_id': res}\n\n    def query_fiat_balances(self):\n        res = self.rotkehlchen.query_fiat_balances()\n        return process_result(res)\n\n    def set_fiat_balance(self, currency, balance):\n        result, message = self.rotkehlchen.data.set_fiat_balance(currency, balance)\n        return {'result': result, 'message': message}\n\n    def query_trade_history(self, location, start_ts, end_ts):\n        start_ts = int(start_ts)\n        end_ts = int(end_ts)\n        if location == 'all':\n            return self.rotkehlchen.trades_historian.get_history(start_ts, end_ts)\n\n        try:\n            exchange = getattr(self.rotkehlchen, location)\n        except AttributeError:\n            raise \"Unknown location {} given\".format(location)\n\n        return exchange.query_trade_history(start_ts, end_ts, end_ts)\n\n    def process_trade_history(self, start_ts, end_ts):\n        start_ts = int(start_ts)\n        end_ts = int(end_ts)\n        result, error_or_empty = self.rotkehlchen.process_history(start_ts, end_ts)\n        response = {'result': result, 'message': error_or_empty}\n        return process_result(response)\n\n    def process_trade_history_async(self, start_ts, end_ts):\n        res = self.query_async('process_trade_history', start_ts=start_ts, end_ts=end_ts)\n        return {'task_id': res}\n\n    def export_processed_history_csv(self, dirpath):\n        result, message = self.rotkehlchen.accountant.csvexporter.create_files(dirpath)\n        return {'result': result, 'message': message}\n\n    def query_balances(self, save_data=False):\n        if isinstance(save_data, str) and (save_data == 'save' or save_data == 'True'):\n            save_data = True\n\n        result = self.rotkehlchen.query_balances(save_data)\n        print(pretty_json_dumps(result))\n        return process_result(result)\n\n    def query_balances_async(self, save_data=False):\n        res = self.query_async('query_balances')\n        return {'task_id': res}\n\n    def query_last_balance_save_time(self):\n        res = self.rotkehlchen.data.db.get_last_balance_save_time()\n        return res\n\n    def get_eth_tokens(self):\n        result = {\n            'all_eth_tokens': self.rotkehlchen.data.eth_tokens,\n            'owned_eth_tokens': self.rotkehlchen.blockchain.eth_tokens\n        }\n        return process_result(result)\n\n    def add_owned_eth_tokens(self, tokens):\n        return self.rotkehlchen.add_owned_eth_tokens(tokens)\n\n    def remove_owned_eth_tokens(self, tokens):\n        return self.rotkehlchen.remove_owned_eth_tokens(tokens)\n\n    def add_blockchain_account(self, blockchain, account):\n        return self.rotkehlchen.add_blockchain_account(blockchain, account)\n\n    def remove_blockchain_account(self, blockchain, account):\n        return self.rotkehlchen.remove_blockchain_account(blockchain, account)\n\n    def get_ignored_assets(self):\n        result = {\n            'ignored_assets': self.rotkehlchen.data.db.get_ignored_assets()\n        }\n        return result\n\n    def add_ignored_asset(self, asset):\n        result, message = self.rotkehlchen.data.add_ignored_asset(asset)\n        return {'result': result, 'message': message}\n\n    def remove_ignored_asset(self, asset):\n        result, message = self.rotkehlchen.data.remove_ignored_asset(asset)\n        return {'result': result, 'message': message}\n\n    def unlock_user(self, user, password, create_new, sync_approval, api_key, api_secret):\n        \"\"\"Either unlock an existing user or create a new one\"\"\"\n        res = {'result': True, 'message': ''}\n\n        assert isinstance(sync_approval, str), \"sync_approval should be a string\"\n        assert isinstance(api_key, str), \"api_key should be a string\"\n        assert isinstance(api_secret, str), \"api_secret should be a string\"\n\n        valid_actions = ['unknown', 'yes', 'no']\n        valid_approve = isinstance(sync_approval, str) and sync_approval in valid_actions\n        if not valid_approve:\n            raise ValueError('Provided invalid value for sync_approval')\n\n        if api_key != '' and create_new is False:\n            raise ValueError('Should not ever have api_key provided during a normal login')\n\n        if api_key != '' and api_secret == '' or api_secret != '' and api_key == '':\n            raise ValueError('Must provide both or neither of api key/secret')\n\n        try:\n            self.rotkehlchen.unlock_user(\n                user,\n                password,\n                create_new,\n                sync_approval,\n                api_key,\n                api_secret\n            )\n            res['exchanges'] = self.rotkehlchen.connected_exchanges\n            res['premium'] = True if hasattr(self.rotkehlchen, 'premium') else False\n            res['settings'] = self.rotkehlchen.data.db.get_settings()\n        except AuthenticationError as e:\n            res['result'] = False\n            res['message'] = str(e)\n        except PermissionError as e:\n            res['result'] = False\n            res['permission_needed'] = True\n            res['message'] = str(e)\n\n        return res\n\n    def echo(self, text):\n        return text\n\n    def main(self):\n        gevent.hub.signal(signal.SIGINT, self.shutdown)\n        gevent.hub.signal(signal.SIGTERM, self.shutdown)\n        # self.zerorpc = zerorpc.Server(self, heartbeat=15)\n        self.zerorpc = zerorpc.Server(self)\n        addr = 'tcp://127.0.0.1:' + str(self.port())\n        self.zerorpc.bind(addr)\n        print('start running on {}'.format(addr))\n        self.zerorpc.run()\n", "path": "rotkehlchen/server.py"}]}