Memory leak
I have this function volumeCounter() that is in a loop (in function looper())that executes every 10 sec. It is receiving data via api. After that it stores it into a sqlite database. When ran for a few hours, the ram will rise. It is especially noticeable when there are 100 threads or more of this function running (one thread for each market like BTC-ETH). After commenting it out, memory usage did not rise.
My question is, what inside this function is causing memory to rise.
def volumeCounter(self):
pass_to_table = api.get_market_history(self.market, 100)["result"] #This is in json format.
for i in pass_to_table:
id_i = i['Id']
time_i = i['TimeStamp'].replace("T", " ")
type_i = i['OrderType']
total = i['Total']
price = i["Price"]
self.cursor.execute(
"""INSERT OR IGNORE INTO {} (id, price, total, type,time) VALUES(?,?,?,?,?)""".format(
self.table_prices), (id_i, price, total, type_i, time_i))
self.connection.commit()
Function looper() that is looping volumeCounter(). It is executed in __init__() at start.
Script looks like this:
key = 'public_key'
secret = 'secret_key'
api = Bittrex(key, secret)
class CryptoScanner:
def __init__(self, market, interval, mailAdrr, mailPass, volume):
# Spremenljivke
self.market = market
self.timeInterval = interval
self.path = "ann{}/{}".format(interval, market)
self.path_2 = self.market.replace('-', '_')
self.database = "{}/{}.db".format(self.path, self.path_2)
self.mailAdrr = mailAdrr
self.mailPass = mailPass
self.sez_length = 0
self.volume = volume
self.scan_mode = False
# Table names
self.table_prices = '{}_prices'.format(self.path_2)
self.table_prices_sum = '{}_prices_sum'.format(self.path_2)
self.table_volume = '{}_volume'.format(self.path_2)
self.table_volume_change = '{}_volume_change'.format(self.path_2)
self.looper()
Then the class object is made for every market with multithreading.
for i in api.get_market_summaries()['result']:
if "BTC" in i['MarketName'] and i['BaseVolume'] >= lower_boundary and i['BaseVolume'] <= upper_boundary:
string = i['MarketName']
d["{}".format(string[4:])] = "{}".format(i['MarketName'])
volume["{}".format(string[4:])] = i['BaseVolume']
threads = []
for i in d:
t = threading.Thread(target=CryptoScanner, args=(d[i], interval_sec, mail, password, volume[i]))
threads.append(d)
t.start()
print("Created thread {}".format(d[i]))
Edit
Added bittrex api library details
class Bittrex(object):
"""
Used for requesting Bittrex with API key and API secret
"""
def __init__(self, api_key, api_secret):
self.api_key = str(api_key) if api_key is not None else ''
self.api_secret = str(api_secret) if api_secret is not None else ''
def api_query(self, method, options=None):
"""
Queries Bittrex with given method and options
:param method: Query method for getting info
:type method: str
:param options: Extra options for query
:type options: dict
:return: JSON response from Bittrex
:rtype : dict
"""
if not options:
options = {}
nonce = str(int(time.time() * 1000))
method_set = 'public'
if method in MARKET_SET:
method_set = 'market'
elif method in ACCOUNT_SET:
method_set = 'account'
request_url = (BASE_URL % method_set) + method + '?'
if method_set != 'public':
request_url += 'apikey=' + self.api_key + "&nonce=" + nonce + '&'
request_url += urlencode(options)
return requests.get(
request_url,
headers={"apisign": hmac.new(self.api_secret.encode(), request_url.encode(), hashlib.sha512).hexdigest()}
).json()
def get_market_history(self, market, count):
"""
Used to retrieve the latest trades that have occurred for a
specific market.
/market/getmarkethistory
:param market: String literal for the market (ex: BTC-LTC)
:type market: str
:param count: Number between 1-100 for the number of entries to return (default = 20)
:type count: int
:return: Market history in JSON
:rtype : dict
"""
return self.api_query('getmarkethistory', {'market': market, 'count': count})
Related
I am trying to compare the balance of address1 which I am printing out normally with the total supply of the tokens in address 2. In order to get the total supply I have to get the ABI of the contract and check the value of the total supply function.
For some reason I am unable to get the ABI of address 2 and it is not giving me any errors. The if statement at the end is just to check that one is less than the other
import pip._vendor.requests
from requests import get
API_KEY = "UX33KV3H6CNV4KEG3J2EWG6BY9E4WRVKKG"
address = "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"
address2 = "0xe0BB0D3DE8c10976511e5030cA403dBf4c25165B"
BASE_URL = "https://api.etherscan.io/api"
def make_api_url(module, action, address , **kwargs):
url = BASE_URL + f"?module={module}&action={action}&address={address}&apikey={API_KEY}"
for key, value in kwargs.items():
url+= f"&{key}={value}"
return url
def get_account_balance(address):
get_balance_url = make_api_url("account", "balance", address, tag="latest")
response = get(get_balance_url)
data = response.json()
value = (data["result"])
return value
balance = get_account_balance(address)
print(balance)
def get_abi(address2):
get_abi_url = make_api_url("contract", "getabi", address2)
response = get(get_abi_url)
data = response.json()["result"]
for tx in data:
supply = tx["totalSupply"]
print("Total Supply: ", supply)
# Checking L1 and L2 tokens in case L2 is more than balance of L1
if balance >= supply : print(True)
else : print(False)
I have been trying to perform a swap on Ropsten using the 0x API to get the best quote but every time I try to perform a swap the transaction fails without any feedback(at least from Etherscan) while the approve tx goes through fine.
I am probably missing something here, but I'm getting blind to keep staring at my code.
import requests
import json
import time
from web3 import Web3
walletAddress = 'mywalletaddress'
wpk = 'myprivatekey'
# Setup Ropsten provider
w3 = Web3(Web3.HTTPProvider('https://ropsten.infura.io/v3/'))
# Get the nonce to prevent from sending the transaction twice
nonce = w3.eth.getTransactionCount(walletAddress)
print('Nonce:', nonce)
# Store the generic ERC20 abi for later use
ABI = json.load(open('abi.json'))
# ROPSTEN ADDRESSES
WETH = Web3.toChecksumAddress('0xc778417e063141139fce010982780140aa0cd5ab')
DAI = Web3.toChecksumAddress('0xad6d458402f60fd3bd25163575031acdce07538d')
USDC = Web3.toChecksumAddress('0x07865c6e87b9f70255377e024ace6630c1eaa37f')
def calcAmount(initialAmount, contractAddress):
contract = w3.eth.contract(address=contractAddress, abi=ABI)
decimals = contract.functions.decimals().call()
amount = int(initialAmount * 10**decimals)
return amount
print(decimals, amount)
def getTokenSymbol(contractAddress):
contract = w3.eth.contract(address=contractAddress, abi=ABI)
tokenSymbol = contract.functions.symbol().call()
return tokenSymbol
print('Symbol:', tokenSymbol)
# Define query parameters
initialAmount = 100
slippage = '0.01' # 0.01 = 1% slippage
sellAmount = calcAmount(initialAmount, USDC)
print('Sell:', sellAmount)
def approve(token, spenderAddress, walletAddress, wpk):
token = Web3.toChecksumAddress(token)
contract = w3.eth.contract(address=token, abi=ABI)
spender = Web3.toChecksumAddress(spenderAddress)
max_amount = Web3.toWei(2**64 - 1, 'ether')
nonce = w3.eth.getTransactionCount(walletAddress)
tx = contract.functions.approve(spender, max_amount).buildTransaction({
'from': walletAddress,
'nonce': nonce
})
signed_tx = w3.eth.account.signTransaction(tx, wpk)
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
print("Approve", w3.toHex(tx_hash))
w3.eth.wait_for_transaction_receipt(tx_hash)
def getQuote(buyToken, slippage, sellToken, sellAmount):
# Make the query https://ropsten.api.0x.org/
query = 'https://ropsten.api.0x.org/swap/v1/quote?buyToken={}&slippage={}&sellToken={}&sellAmount={}'.format(buyToken, slippage, sellToken, sellAmount)
# Get the query and store the response in data
response = requests.get(query)
data = response.json()
# Print a prettiefied version of the response (optional)
print(json.dumps(data, indent=2))
# Convert the buyAmmount to integer
buyAmount = int(data['buyAmount'])
# Convert the buyAmount to a readable number
finalPrice = calcAmount(buyAmount, buyToken)
# Get the symbol of the 2 tokens
buyTokenSymbol = getTokenSymbol(buyToken)
sellTokenSymbol = getTokenSymbol(sellToken)
print(sellTokenSymbol, '->', buyTokenSymbol)
approve(data['sellTokenAddress'], data['allowanceTarget'], walletAddress, wpk)
tx = {
"from": walletAddress,
"to": Web3.toChecksumAddress(data['to']),
"data": data['data'],
"value": 0,
"gas": int(data['gas']),
"gasPrice": int(data['gasPrice']),
"nonce": nonce + 1,
"chainId": data['chainId']
}
# sign the transaction
signed_tx = w3.eth.account.sign_transaction(tx, wpk)
#
# send transaction
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
#
# get transaction hash
print(w3.toHex(tx_hash))
# Use only for testing the query
getQuote(DAI, slippage, USDC, sellAmount)
As you can see I am trying to swap USDC for DAI using the 0x API.
The first tx generated is approved
Etherscan approval tx
But the actual swap fails without any feedback
Etherscan swap tx
I honestly don't know what to try anymore...I've been digging both documentation and reading anything I could find on Stackoverflow without any luck.
Any help is greatly appreciate.
Gas Limit & Usage by Txn: 134,523 | 132,823 (98.74%)
Im 99,9% sure it is because ur running out of gas, try to increase it
Hoping to get some help on making calls to the Coinbase Pro API.
I created a key, noted my phasephrase, key and secret, and am running the below Python script. The response I get is "invalid signature".
On the CBPro documentation site, when I try running it with my credentials on this page, I get a "Sorry, you couldn't be authenticated with those credentails" message.
I've seen some sources that encode to base64 and have success, and others that don't, but neither works for me. What am I don't wrong?
Code:
import requests
import time
import base64
import json
url = "https://api.exchange.coinbase.com/accounts/account_id/transfers?limit=100"
key = "key"
secret = "secret"
passphrase = "pass"
timestamp = str(time.time())
headers = {
"Accept": "application/json",
"cb-access-key": key,
"cb-access-passphrase": passphrase,
"cb-access-sign": encodedData,
"cb-access-timestamp": timestamp
}
response = requests.request("GET", url, headers=headers)
print(response.text)
Signing a request is probably the worst part of the coinbase api.
Here is the documentation for it. some things to note:
signature is only good for 30 seconds so you have to hurry and copy / paste the encoded data and timestamp into the docs form.
the timestamp has to be the same value you use in the encodedData.
the non-pro api signs a little different so make sure you're on the right set of docs. This one I don't think works for pro.
If you're still having trouble authenticating your requests to coinbase here's an example of what i'm using. You might have to change a few things but does the job.
What you're looking for in this example are the HMACAuth & CoinbaseSession classes for your particular need.
# -*- coding: UTF-8 -*-
from base64 import b64encode, b64decode
from collections import namedtuple
from datetime import datetime, timezone
from hashlib import sha256
from hmac import HMAC
from json import loads, JSONDecodeError
from types import SimpleNamespace
from typing import Union, Generator
from requests import Session
from requests.adapters import HTTPAdapter
from requests.auth import AuthBase
from requests.exceptions import HTTPError
from requests.models import PreparedRequest
from requests.utils import to_native_string
from urllib3.util.retry import Retry
EXCHANGE: str = r"https://api.exchange.coinbase.com"
ACCOUNT = namedtuple(
"ACCOUNT",
(
"id",
"currency",
"balance",
"hold",
"available",
"profile_id",
"trading_enabled",
)
)
PRODUCT = namedtuple(
"PRODUCT",
(
"id",
"base_currency",
"quote_currency",
"base_min_size",
"base_max_size",
"quote_increment",
"base_increment",
"display_name",
"min_market_funds",
"max_market_funds",
"margin_enabled",
"fx_stablecoin",
"max_slippage_percentage",
"post_only",
"limit_only",
"cancel_only",
"trading_disabled",
"status",
"status_message",
"auction_mode",
)
)
def encode(value: Union[str, bytes]) -> bytes:
"""Encode the string `value` with UTF-8."""
if isinstance(value, str) is True:
value = value.encode("UTF-8")
return value
def decode(value: Union[bytes, str]) -> str:
"""Decode the bytes-like object `value` with UTF-8."""
if isinstance(value, bytes) is True:
value = value.decode("UTF-8")
return value
def req_time():
"""POSIX timestamp as float. Number of seconds since Unix Epoch in UTC."""
utc = get_utc()
return utc.timestamp()
def get_utc() -> datetime:
"""Construct a datetime object with UTC time zone info."""
return datetime.now(timezone.utc)
class TimeoutHTTPAdapter(HTTPAdapter):
"""Custom HTTP adapter with timeout capability."""
def __init__(self, *args, **kwargs):
self._timeout = kwargs.pop("timeout")
super(TimeoutHTTPAdapter, self).__init__(*args, **kwargs)
def send(self, request, **kwargs):
kwargs.update({"timeout": self._timeout})
return super(TimeoutHTTPAdapter, self).send(request, **kwargs)
class CoinbaseSession(Session):
"""Coinbase Session handle."""
_headers: dict = {
"Accept": "application/json",
"Content-Type": "application/json",
"Accept-Charset": "utf-8",
}
#staticmethod
def http_adapter(retries: int = 3, backoff: int = 1, timeout: int = 30):
return TimeoutHTTPAdapter(
max_retries=Retry(total=retries, backoff_factor=backoff),
timeout=timeout
)
def __init__(self):
super(CoinbaseSession, self).__init__()
self.headers.update(self._headers)
self.auth = HMACAuth()
self.mount("https://", self.http_adapter())
self.mount("http://", self.http_adapter())
class HMACAuth(AuthBase):
"""Requests signing handle."""
#staticmethod
def __pre_hash(timestamp: Union[str, int, float], request: PreparedRequest) -> bytes:
"""
Create the pre-hash string by concatenating the timestamp with
the request method, path_url and body if exists.
"""
message = f"{timestamp}{request.method.upper()}{request.path_url}"
body = request.body
if body is not None:
message = f"{message}{decode(body)}"
return encode(message)
#staticmethod
def __sign(message: bytes) -> bytes:
"""Create a sha256 HMAC and sign the required `message`."""
key = b64decode(encode(API.SECRET)) # be careful were you keep this!
hmac = HMAC(key=key, msg=message, digestmod=sha256).digest()
return b64encode(hmac)
def __call__(self, request: PreparedRequest):
timestamp = req_time()
message = self.__pre_hash(timestamp, request)
cb_access_sign = self.__sign(message)
request.headers.update(
{
to_native_string('CB-ACCESS-KEY'): API.KEY, # be careful where you keep this!
to_native_string('CB-ACCESS-SIGN'): cb_access_sign,
to_native_string('CB-ACCESS-TIMESTAMP'): str(timestamp),
to_native_string('CB-ACCESS-PASSPHRASE'): API.PASSPHRASE, # be careful where you keep this!
}
)
return request
class CoinbaseAPI(object):
"""Coinbase API handle."""
def __init__(self):
self._session = CoinbaseSession()
def request(self, **kwargs):
"""
Send HTTP requests to the Coinbase API.
Raises HTTPError if response is not 200.
"""
print(f"DEBUG: Requesting resource (url = {kwargs.get('url')})")
try:
results = self.__request(**kwargs)
except HTTPError as http_error:
print(f"ERROR: Resource not found!", f"Cause: {http_error}")
else:
print(f"DEBUG: Resource found (url = {kwargs.get('url')})")
return results
def __request(self, **kwargs):
"""
Send HTTP requests to the Coinbase API.
Raises HTTPError if response is not 200.
"""
method = getattr(self._session, kwargs.pop("method"))
response = method(**kwargs)
if response.status_code != 200:
response.raise_for_status()
else:
try:
results = loads(response.text)
except JSONDecodeError as json_error:
print("WARNING: Decoding JSON object failed!", f"Cause: {json_error}")
kwargs.update({"method": method.__name__})
return self.__request(**kwargs)
else:
return results
class Endpoints(object):
"""Coinbase server endpoints."""
_server = None
#staticmethod
def join(*path, **params) -> str:
"""
Construct the resource url by appending all `path`
items to base url and join `params` if any.
"""
url = "/".join(path)
if len(params) > 0:
params = [f"{key}={value}" for key, value in params.items()]
url = f"{url}?{'&'.join(params)}"
return url
class ExchangeEndpoints(Endpoints):
"""Coinbase exchange server endpoints."""
_server = EXCHANGE
def __init__(self):
self.products = self.join(self._server, "products")
self.accounts = self.join(self._server, "accounts")
class Exchange(CoinbaseAPI):
"""Coinbase exchange API client handle."""
_endpoints = ExchangeEndpoints()
def __init__(self):
super(Exchange, self).__init__()
def get_accounts(self) -> Generator:
"""Get a list of trading accounts from the profile of the API key."""
response = self._accounts()
for item in response:
yield ACCOUNT(**item)
def get_account(self, account_id: str) -> ACCOUNT:
"""
Information for a single account. Use this endpoint when you know the account_id.
API key must belong to the same profile as the account.
"""
response = self._accounts(account_id)
return ACCOUNT(**response)
def get_products(self, query: str = None) -> Generator:
"""
Gets a list of available currency pairs for trading.
:param query: `type` query parameter (unknown).
"""
response = self._products(type=query)
for item in response:
yield PRODUCT(**item)
def get_product(self, product_id: str) -> PRODUCT:
"""
Get information on a single product.
:param product_id: The `id` string of the product/currency pair (ex: BTC-USD).
"""
result = self._products(product_id)
return PRODUCT(**result)
def _accounts(self, *path, **params):
"""Access resources from the `accounts` endpoint of the exchange API."""
params = self.clean_params(**params)
_url = self._endpoints.join(
self._endpoints.accounts,
*path,
**params
)
return self.request(method="get", url=_url)
def _products(self, *path, **params):
"""Access resources from the `products` endpoint of the exchange API."""
params = self.clean_params(**params)
_url = self._endpoints.join(
self._endpoints.products,
*path,
**params
)
return self.request(method="get", url=_url)
def clean_params(self, **params) -> dict:
"""Clean `params` by removing None values."""
temp = dict()
for key, value in params.items():
if value is None:
continue
if isinstance(value, dict) is True:
value = self.clean_params(**value)
temp.update({key: value})
return temp
if __name__ == '__main__':
# we're using PBKDF2HMAC (with symmetrically derived encryption key)
# not included in this example
key_vault = KeyVault() # custom class for encrypting and storing secrets to keyring
key_vault.cypher.password(value="your_password", salt="salty_password")
api_credentials = loads(
key_vault.get_password("coinbase", "pro-coinbase-api")
)
# accounts:
ADA: str = "your_crypto_account_id" # ex: 8b9806a4-7395-11ec-9b1a-f02f74d9105d
API = SimpleNamespace(
NAME="pro-coinbase-api",
VERSION="2021-08-27",
KEY=api_credentials.get("key"),
PASSPHRASE=api_credentials.get("passphrase"),
SECRET=api_credentials.get("secret"),
) # not the best example but does the job as long as you don't invite hackers in your PC :)
api = Exchange()
accounts = api.get_accounts()
for account in accounts:
print(account)
account = api.get_account(account_id=ADA)
print(account)
products = api.get_products()
for product in products:
print(product)
product = api.get_product("ATOM-EUR")
print(product)
I have problem with pagination. If i try this with just a list of dict it works ok, problem starts when i try use model from my DB.I have about 10k users in my sql alchemy database. User is dbModel:
class UserModel(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
I want to show users from 1 to 100, from 100 to 200 etc. I'm trying to use this tutorial : https://aviaryan.com/blog/gsoc/paginated-apis-flask
Here is classmethod to return model into dict:
#classmethod
def return_all(cls):
def to_json(x):
return {
'id': x.id,
'username': x.username,
'password': x.password
}
return {'users': list(map(lambda x: to_json(x), UserModel.query.all()))}
Here is the pagination def:
def get_paginated_list(klass, url, start, limit):
start = int(start)
limit = int(limit)
# check if page exists
results = klass.return_all()
count = (len(results))
if count < start or limit < 0:
abort(404)
# make response
obj = {}
obj['start'] = start
obj['limit'] = limit
obj['count'] = count
# make URLs
# make previous url
if start == 1:
obj['previous'] = ''
else:
start_copy = max(1, start - limit)
limit_copy = start - 1
obj['previous'] = url + '?start=%d&limit=%d' % (start_copy, limit_copy)
# make next url
if start + limit > count:
obj['next'] = ''
else:
start_copy = start + limit
obj['next'] = url + '?start=%d&limit=%d' % (start_copy, limit)
# finally extract result according to bounds
obj['results'] = results[(start - 1):(start - 1 + limit)]
return obj
My api resource code is:
class AllUsers(Resource):
def get(self):
jsonify(get_paginated_list(
UserModel,
'users/page',
start=request.args.get('start', 1),
limit=request.args.get('limit', 100)
))
The problem i get is when i try get users from link http://127.0.0.1:5000/users/page?start=1&limit=100
TypeError: unhashable type: 'slice'
How to solve this thing? Or how can i show results as i want?
Probably bug is here:
obj['results'] = results[(start - 1):(start - 1 + limit)]
results is not a list, function return_all returns dict like {'users': []}
so try something like this:
obj['results'] = results['users'][(start - 1):(start - 1 + limit)]
I am trying to use the threading and storing the result on a threads using a single session. And this is working fine most of the time except a few scenarios where my whole application gets, and I am not able to figure out the reason for that.
My application is getting blocked on notification.save() in __filter_notifications_by_status_and_request_type method. notification.save() is saving the data in to the DB.
I am not able to figure out is this a DB issue or a threading or locking issue.
I am using the flask app, which I am hitting using passenger_wsgi via apache. After my application gets blocked, my server stop taking the further request.
DB python library used = SqlAlchemy
class Inference:
##
# #brief initializer of the Inference Handler
#
# #param kwargs keywords Arguments
#
# #return None
def __init__(self, **kwargs):
""" Calling the inference from here and get the result """
if((kwargs.has_key('IRISRequest'))):
self.iris_request = kwargs['IRISRequest']
self.racerx_inference_config = Config.get_racerx_inference_config()
self.thread_lock = threading.Lock()
##
# #brief Call the Infernce
#
# #return Inference Object
def get_inference_object(self):
log.info("get_inference_object is called")
inference_map = {}
inference_map['inference'] = {}
if self.iris_request.system == "athena":
url_to_notification_map = Config.get_url_to_notification_map()
for notification_id, urls in url_to_notification_map.iteritems():
inference_map['inference'][notification_id] = any(url in string.lower(self.iris_request.url) for url in urls)
title_to_notification_map = Config.get_title_to_notification_map()
if self.iris_request.context.has_key('title') :
for notification_id, titles in title_to_notification_map.iteritems():
if not inference_map['inference'].has_key(notification_id) or inference_map['inference'][notification_id] == False:
inference_map['inference'][notification_id] = any(title in string.lower(self.iris_request.context['title']) for title in titles)
return inference_map
##
# #brief
#
# #return the list of the notification required from the reference
def get_notification_name_list(self):
inference_object = self.get_inference_object()
return [y for y in inference_object['inference'] if inference_object['inference'][y] == True]
##
# #brief collect notifications from the various sources
#
# #return notification objects
def get_notifications(self):
if(len(self.iris_request.notification_name_list) > 0):
self.notification_name_list = self.iris_request.notification_name_list # List of Notifciation List is provided by the client
else:
self.notification_name_list = self.get_notification_name_list() # Get Notification Name List from the Inference
string_translations = {}
for notification_name in self.notification_name_list:
config = Config.get_config(notification_name)
nt = {}
nt['message'] = self.__get_message_from_template(config.message_map)
nt['subject'] = self.__get_message_from_template(config.subject_map)
nt['short_message'] = self.__get_message_from_template(config.short_message_map)
nt['impact_summary'] = self.__get_message_from_template(config.impact_summary_map)
action_string_map = {}
for h in config.action_summary_map:
if h.has_key('string_id'):
action_string_map[h['string_id']] = self.__get_message_from_template(h)
nt['action_summary_list'] = action_string_map
help_strings_map = {}
for h in config.help_content:
if h.has_key('string_id'):
help_strings_map[h['string_id']] = self.__get_message_from_template(h)
nt['help_content_strings'] = help_strings_map
string_translations[notification_name] = nt
notifications_map = {}
log.info("starting the thread pool for getting the notifications data")
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
future_to_notification_name = dict((executor.submit(self.fetch_notifications_by_notification_name, notification_name, string_translations), notification_name)
for notification_name in self.notification_name_list)
log.info("end of threadpool")
log.info("start processing the data produced by the thread pool")
for future in concurrent.futures.as_completed(future_to_notification_name):
notification_name = future_to_notification_name[future]
if future.exception() is not None:
raise Exception("Error occured while fetching the data for notification: "+notification_name+", error: "+str(future.exception()))
if len(future.result()) > 0:
notifications_map[notification_name] = future.result()
log.info("end processing the data produced by the thread pool")
self.iris_request.session.commit()
log.info("Commited the DB session for the notifications")
return notifications_map
###
# #brief This function collect the notifications for the specified notification type, by making object model call
#
# #input notification_name : Type of the notification to be fetched
# #input string_translations : List of string translations
# #input notification_map : Map of notifications, collected notifications will be pushed to this map
def fetch_notifications_by_notification_name (self, notification_name, string_translations):
log.info("fetch_notifications_by_notification_name is called")
object_model = ObjectModel(IRISRequest = self.iris_request, NotificationName = notification_name, StringMap = string_translations[notification_name])
notifications = object_model.get_iris_notification_objects()
filtered_notifications = self.__filter_notifications_by_status_and_request_type(notifications)
if len(filtered_notifications) > 0:
return filtered_notifications
else:
return []
###
# #brief This function filter the notification based on status, i.e. of notification is expired, snoozed or dismissed
# and also based on request type
#
# #input notifications: List of notifications
#
# #return Filtered notification list
def __filter_notifications_by_status_and_request_type(self, notifications):
log.info("__filter_notifications_by_status_and_request_type is called")
filtered_notifications = []
for notification in notifications:
keep_notification = True
# Extracting read status of notifications and storing new notifications
log.info("Acquiring the lock on thread, to save the data into DB")
self.thread_lock.acquire()
notification.save()
self.thread_lock.release()
log.info("Releasing the lock after saving the data into DB")
# Filtering inactive notifications, i.e dismissed notifications
if notification.is_active == False:
keep_notification = False
# Filtering expired notifications, if validity = -1 then notification will never expire
if notification.validity != -1 and (datetime.date.today() - notification.creation_date).days > notification.validity:
keep_notification = False
# Filtering out the snoozed notifications
if notification.snooze_date != None and (datetime.datetime.today() - notification.snooze_date).days <= notification.snooze_duration:
keep_notification = False
# Filtering out unread notification when request type is FETCH_READ
if self.iris_request.notifcation_fetch_type == Constants.FETCH_TYPE_READ and notification.is_read == False:
keep_notification = False
# Filtering out read notification when request type is FETCH_UNREAD
if self.iris_request.notifcation_fetch_type == Constants.FETCH_TYPE_UNREAD and notification.is_read == True:
keep_notification = False
if keep_notification == True:
filtered_notifications.append(notification)
return filtered_notifications
I was using the lock in given manner
self.thread_lock.acquire()
notification.save()
self.thread_lock.release()
when notification.save() is throwing an exception, then system will unable to release the thread.
it could be easily fixed by proper error handling.
self.thread_lock.acquire()
try:
notification.save()
except Exception as e:
log.error("unable to store info in DB")
finally:
self.thread_lock.release()