diff --git a/.gitignore b/.gitignore index ead7ec4..c8552f5 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ settings.ini MANIFEST example.py nosetests +*.sqlite \ No newline at end of file diff --git a/IntraPy/IntraPy.py b/IntraPy/IntraPy.py index 888152b..ee7c130 100644 --- a/IntraPy/IntraPy.py +++ b/IntraPy/IntraPy.py @@ -20,8 +20,9 @@ import json import time import requests -from IntraPy.config import APP_UID, APP_SECRET, TOKEN_FILE - +import requests_cache +import sqlite3 +from IntraPy.config import APP_UID, APP_SECRET, TOKEN_FILE, CACHE_TYPE, DBNAME, TABLE_NAME class IntraPy: """ @@ -37,9 +38,22 @@ def __init__(self): raise EnvironmentError("APP_SECRET wasn't found in your settings.ini file.") if TOKEN_FILE == "None": raise EnvironmentError("TOKEN_FILE wasn't found in your settings.ini file.") + if CACHE_TYPE == "None": + print("CACHE_TYPE variable not found or set to None. No cache method will be used") + self.table_name = TABLE_NAME self.app_secret = APP_SECRET self.app_uid = APP_UID self.token_file = TOKEN_FILE + self.cache_type = CACHE_TYPE + self.db_name = DBNAME + # @todo: Customize the expire_after for rcache + if self.cache_type == "rcache": + requests_cache.install_cache(self.db_name, backend='sqlite', expire_after=180) + elif self.cache_type == "sqlite": + self.connection = sqlite3.connect(self.db_name) + self.cursor = self.connection.cursor() + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + self.table_name + ' (url text, endpoint text, response text, last_requested int, expires_after int, expires_at int)') + self.connection.commit() self.app_token = IntraPy.check_app_token(self) def api_request_new_token(self): @@ -50,7 +64,11 @@ def api_request_new_token(self): """ d = {'grant_type': 'client_credentials', 'client_id': self.app_uid, 'client_secret': self.app_secret} - r = requests.post("https://api.intra.42.fr/oauth/token", data=d) + if self.cache_type == "rcache": + with requests_cache.disabled(): + r = requests.post("https://api.intra.42.fr/oauth/token", data=d) + else: + r = requests.post("https://api.intra.42.fr/oauth/token", data=d) print("New access token requested") print(r.json()['access_token']) with open(self.token_file, "w") as file: @@ -66,8 +84,13 @@ def test_token(self): """ self.app_token = IntraPy.get_token_from_file(self) h = {'Authorization': 'Bearer ' + self.app_token} - r = requests.request("GET", - "https://api.intra.42.fr" + "/oauth/token/info", headers=h, allow_redirects=False) + if self.cache_type == "rcache": + with requests_cache.disabled(): + r = requests.request("GET", "https://api.intra.42.fr" + "/oauth/token/info", headers=h, allow_redirects=False) + else: + r = requests.request("GET", "https://api.intra.42.fr" + "/oauth/token/info", headers=h, allow_redirects=False) + + try: if r.json()['error'] == "invalid_request": return False @@ -154,9 +177,17 @@ def api_get_single(self, uri: str, methods="GET"): :return: Returns the response object returned by requests.request() """ + response = None h = {'Authorization': 'Bearer ' + self.app_token} - response = requests.request(methods, "https://api.intra.42.fr" + - uri, headers=h, allow_redirects=False) + if self.cache_type == "None": + with requests_cache.disabled(): + response = requests.request(methods, "https://api.intra.42.fr" + uri, headers=h, allow_redirects=False) + elif self.cache_type == "sqlite": + with requests_cache.disabled(): + response = requests.request(methods, "https://api.intra.42.fr" + uri, headers=h, allow_redirects=False) + self.cache_response(response, uri) + elif self.cache_type == "rcache": + response = requests.request(methods, "https://api.intra.42.fr" + uri, headers=h, allow_redirects=False) if response.status_code == 401: self.app_token = IntraPy.check_app_token(self) return IntraPy.api_get_single(self, uri, methods) @@ -165,6 +196,28 @@ def api_get_single(self, uri: str, methods="GET"): return self.api_get_single(uri, methods) return response + def cache_response(self, response, uri: str): + endpoint = uri.split('?')[0] + content = json.dumps(response.content.decode("utf-8")) + last_requested = str(round(time.time())) + expires_after = self.get_expire_time_seconds(endpoint) + expires_at = last_requested + expires_after + values = "'" + uri + "', '" + endpoint + "', '" + content + "', " + last_requested + ", " + expires_after + ", " + expires_at + self.cursor.execute("INSERT INTO " + self.table_name + " VALUES (" + values + ")") + self.connection.commit() + + # @todo: Change the ifs into a dict/list for get_expire_time + # @todo: Add /v2/campus and /v2/cursus in get_expire_time + def get_expire_time_seconds(self, endpoint: str): + if endpoint == "/oauth/token/info": + return "1" # 1 second + if endpoint == "/v2/accreditations": + return "86400" # 1 day + if endpoint == "/v2/achievements": + return "2629746" # 1 month + if endpoint == "/v2/titles": + return "2629746" # 1 month + def get_changeable_parameters(self, args): """ This will create and return the string with the parameter that will @@ -211,7 +264,11 @@ def get_token_expire_time_in_seconds(self): :return: Return the time in seconds of when the token will expire """ - response = self.api_get_single("/oauth/token/info") + if self.cache_type == "rcache": + with requests_cache.disabled(): + response = self.api_get_single("/oauth/token/info") + else: + response = self.api_get_single("/oauth/token/info") ret = json.loads(response.content) return str(ret["expires_in_seconds"]) @@ -221,7 +278,11 @@ def get_token_expire_time(self): :return: Returns a string formated in h:m:s """ - response = self.api_get_single("/oauth/token/info") + if self.cache_type == "rcache": + with requests_cache.disabled(): + response = self.api_get_single("/oauth/token/info") + else: + response = self.api_get_single("/oauth/token/info") ret = json.loads(response.content) m, s = divmod(ret["expires_in_seconds"], 60) h, m = divmod(m, 60) @@ -233,7 +294,11 @@ def get_token_creation_epoch(self): :return: Returns a string containing the epoch creation time """ - response = self.api_get_single("/oauth/token/info") + if self.cache_type == "rcache": + with requests_cache.disabled(): + response = self.api_get_single("/oauth/token/info") + else: + response = self.api_get_single("/oauth/token/info") ret = json.loads(response.content) return str(ret["created_at"]) @@ -243,6 +308,10 @@ def get_token_creation_date(self): :return: It will return a string in form of `YYYY-MM-DD hh-mm-ss` """ - response = self.api_get_single("/oauth/token/info") + if self.cache_type == "rcache": + with requests_cache.disabled(): + response = self.api_get_single("/oauth/token/info") + else: + response = self.api_get_single("/oauth/token/info") ret = json.loads(response.content) return str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ret["created_at"]))) diff --git a/IntraPy/accreditation_handler/accreditations.py b/IntraPy/accreditation_handler/accreditations.py index 7736c5f..fbbb20c 100644 --- a/IntraPy/accreditation_handler/accreditations.py +++ b/IntraPy/accreditation_handler/accreditations.py @@ -60,7 +60,7 @@ def get_accreditations_by_id(self, accreditation_id: int, pretty=False): :return: Returns a list in json form containing the requested accreditation """ - response = self.api_get_single("/v2/achievements/" + str(accreditation_id), "GET") + response = self.api_get_single("/v2/accreditations/" + str(accreditation_id), "GET") accreditation = json.loads(response.content) if pretty: return json.dumps(accreditation, indent=4, sort_keys=True) diff --git a/IntraPy/config.py b/IntraPy/config.py index 705afb8..387947e 100644 --- a/IntraPy/config.py +++ b/IntraPy/config.py @@ -24,6 +24,9 @@ APP_SECRET: the app secret given by 42 TOKEN_FILE: The filename you want your token to be stored in """ -APP_UID = config('APP_UID', cast=str, default = None) +APP_UID = config('APP_UID', cast=str, default=None) APP_SECRET = config('APP_SECRET', cast=str, default=None) -TOKEN_FILE = config('TOKEN_FILE', cast=str, default=None) +TOKEN_FILE = config('TOKEN_FILE', cast=str, default=".app_token") +CACHE_TYPE = config('CACHE_TYPE', cast=str, default="None") +DBNAME = config('DBNAME', cast=str, default="IntraPy.db") +TABLE_NAME = config('TABLE_NAME', cast=str, default="cache") diff --git a/IntraPy/database.py b/IntraPy/database.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 8c8d612..21959c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests==2.18.4 +requests-cache python-decouple==3.1 \ No newline at end of file diff --git a/settings.ini.example b/settings.ini.example index e8018f8..ced1d5a 100644 --- a/settings.ini.example +++ b/settings.ini.example @@ -1,4 +1,8 @@ [settings] APP_UID=8ngp6jutwr7ge667eqsnqqacppk7wkv2v6hk7rdtvbtzfu82krdeakg3x4rvcra3 APP_SECRET=m5ynvgudum3cmmxksxpkmw3cz96mabfpr9wddujbnwksu3jgqjqa5cux4z6hys2a -TOKEN_FILE=.app_token \ No newline at end of file +TOKEN_FILE=.app_token +CACHE_TYPE=None +; None for no caching (or remove variable), rcache to use request-cache, and sqlite to use sqlite. +DBNAME=IntraPy.db +TABLE_NAME=cache \ No newline at end of file diff --git a/setup.py b/setup.py index 2f9caee..09d1986 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ 'json', 'requests', 'python-decouple', + 'requests-cache', ], classifiers=[], )