From 37225532c86db1b91ee4e734e48f38e1a8f9d844 Mon Sep 17 00:00:00 2001 From: Katboi01 Date: Mon, 4 Sep 2023 14:20:35 +0200 Subject: [PATCH] KF3: Api additions and improvements. --- app.py | 18 +- {resources => endpoints}/KF3/friend.py | 7 +- {resources => endpoints}/KF3/friends.py | 11 +- endpoints/KF3/update.py | 27 ++ {resources => endpoints}/Kingdom/friend.py | 0 {resources => endpoints}/Kingdom/friends.py | 0 {resources => endpoints}/Kingdom/item.py | 0 {resources => endpoints}/Kingdom/items.py | 0 loaders/KF3/kf3db.py | 295 ++++++++++++++++-- .../KF3/{charaData.py => statCalculation.py} | 0 loaders/Kingdom/kemono.py | 25 ++ loaders/Kingdom/kingdomdb.py | 18 +- resources/KF3/endpoints.py | 0 resources/average.py | 36 --- resources/difference_last.py | 34 -- resources/minmax_last.py | 38 --- resources/shared.py | 13 - 17 files changed, 353 insertions(+), 169 deletions(-) rename {resources => endpoints}/KF3/friend.py (74%) rename {resources => endpoints}/KF3/friends.py (53%) create mode 100644 endpoints/KF3/update.py rename {resources => endpoints}/Kingdom/friend.py (100%) rename {resources => endpoints}/Kingdom/friends.py (100%) rename {resources => endpoints}/Kingdom/item.py (100%) rename {resources => endpoints}/Kingdom/items.py (100%) rename loaders/KF3/{charaData.py => statCalculation.py} (100%) delete mode 100644 resources/KF3/endpoints.py delete mode 100644 resources/average.py delete mode 100644 resources/difference_last.py delete mode 100644 resources/minmax_last.py delete mode 100644 resources/shared.py diff --git a/app.py b/app.py index 0c6dfb3..4802878 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,5 @@ from flask import Flask from flask_restful import Api - -from resources.KF3.friend import KF3_Friend -from resources.KF3.friends import KF3_Friends -from resources.Kingdom.friend import Kingdom_Friend -from resources.Kingdom.friends import Kingdom_Friends -from resources.Kingdom.item import Kingdom_Item -from resources.Kingdom.items import Kingdom_Items from loaders.KF3.kf3db import KF3DB from loaders.Kingdom.kingdomdb import KingdomDB @@ -14,17 +7,10 @@ app = Flask(__name__) app.config['JSON_AS_ASCII'] = False app.databases = {} -KF3DB(app) -KingdomDB(app) - api = Api(app) -api.add_resource(KF3_Friend, "/KF3/Friend/") -api.add_resource(KF3_Friends, "/KF3/Friends") -api.add_resource(Kingdom_Friend, "/Kingdom/Friend/") -api.add_resource(Kingdom_Friends, "/Kingdom/Friends") -api.add_resource(Kingdom_Item, "/Kingdom/Item/") -api.add_resource(Kingdom_Items, "/Kingdom/Items") +KF3DB(api) +KingdomDB(api) if __name__ == '__main__': app.run(host='127.0.0.1', port=8080, debug=True) \ No newline at end of file diff --git a/resources/KF3/friend.py b/endpoints/KF3/friend.py similarity index 74% rename from resources/KF3/friend.py rename to endpoints/KF3/friend.py index 26e2ec1..2f2eb8f 100644 --- a/resources/KF3/friend.py +++ b/endpoints/KF3/friend.py @@ -5,8 +5,9 @@ import json class KF3_Friend(Resource): def get(self, id:int): + db = app.databases["KF3"] if "wiki" in request.args: - result = app.databases["KF3"].get_chara_wiki(id) + result = db.get_chara_wiki(id) response = app.response_class( response=result, @@ -14,8 +15,8 @@ class KF3_Friend(Resource): mimetype='text/plain' ) else: - result = app.databases["KF3"].get_chara(id) - result = json.dumps(result) + result = db.get_chara(id) + result = json.dumps(result, ensure_ascii=False, indent=1) response = app.response_class( response=result, diff --git a/resources/KF3/friends.py b/endpoints/KF3/friends.py similarity index 53% rename from resources/KF3/friends.py rename to endpoints/KF3/friends.py index 345f8e2..e1afa45 100644 --- a/resources/KF3/friends.py +++ b/endpoints/KF3/friends.py @@ -1,15 +1,20 @@ import json from flask_restful import Resource from flask import current_app as app +from flask import request class KF3_Friends(Resource): def get(self): db = app.databases["KF3"] result = [] - for value in db.charaData.values(): - result.append({"id": value["id"], "name": value["nameEn"]}) + for value in db.processed_friends.values(): + result.append({"id": value["id"], "name": value["nameEn"], "startTime" : value["startTime"], "startTimeRaw" : value["startTimeRaw"]}) - result = sorted(result, key=lambda f: f["id"]) + sort_arg = request.args["sort"] if "sort" in request.args and request.args["sort"] in result[0] else "id" + if sort_arg == "startTime": + sort_arg = "startTimeRaw" + + result = sorted(result, key=lambda f: f[sort_arg]) response = app.response_class( response=json.dumps(result, ensure_ascii=False, indent=1), diff --git a/endpoints/KF3/update.py b/endpoints/KF3/update.py new file mode 100644 index 0000000..7f02730 --- /dev/null +++ b/endpoints/KF3/update.py @@ -0,0 +1,27 @@ +from flask_restful import Resource +from flask import current_app as app +from flask import request +import json + +class KF3_Update(Resource): + def post(self): + db = app.databases["KF3"] + try: + db.reload_data() + response = app.response_class( + response="update successful", + status=200, + mimetype='text/plain' + ) + except: + response = app.response_class( + response="update failed", + status=500, + mimetype='text/plain' + ) + + response.headers.add("Access-Control-Allow-Origin", "*") + return response + + def get(self): + return self.post() \ No newline at end of file diff --git a/resources/Kingdom/friend.py b/endpoints/Kingdom/friend.py similarity index 100% rename from resources/Kingdom/friend.py rename to endpoints/Kingdom/friend.py diff --git a/resources/Kingdom/friends.py b/endpoints/Kingdom/friends.py similarity index 100% rename from resources/Kingdom/friends.py rename to endpoints/Kingdom/friends.py diff --git a/resources/Kingdom/item.py b/endpoints/Kingdom/item.py similarity index 100% rename from resources/Kingdom/item.py rename to endpoints/Kingdom/item.py diff --git a/resources/Kingdom/items.py b/endpoints/Kingdom/items.py similarity index 100% rename from resources/Kingdom/items.py rename to endpoints/Kingdom/items.py diff --git a/loaders/KF3/kf3db.py b/loaders/KF3/kf3db.py index 87d1cc8..467820c 100644 --- a/loaders/KF3/kf3db.py +++ b/loaders/KF3/kf3db.py @@ -1,44 +1,55 @@ import gzip import json +import platform import UnityPy -from loaders.KF3.charaData import get_all_stats, fill_miracle_numbers +from datetime import datetime +from loaders.KF3.statCalculation import get_all_stats, fill_miracle_numbers +from endpoints.KF3.friend import KF3_Friend +from endpoints.KF3.friends import KF3_Friends +from endpoints.KF3.update import KF3_Update class KF3DB: processed_friends = {} - paramAbilities = {} - paramAbilities1 = {} - paramAbilities2 = {} - paramAlphaBases = {} - paramArts = {} - paramSpecialAttacks = {} - paramWaitActions = {} - charaData = {} - promoteData = {} - promotePresetData = {} - charaClothesData = {} - limitlevel_rising_status = {} - def __init__(self, app) -> None: + def __init__(self, api) -> None: + app = api.app if "KF3" in app.databases: del app.databases["KF3"] - self.parse_parameter() + self.reload_data() - with gzip.open("data/KF3/CHARA_DATA.d", mode="rt", encoding="utf-8") as file: + app.databases["KF3"] = self + api.add_resource(KF3_Friend, "/KF3/Friend/") + api.add_resource(KF3_Friends, "/KF3/Friends") + api.add_resource(KF3_Update, "/KF3/Update") + + def reload_data(self): + self.charaData = {} + self.promoteData = {} + self.promotePresetData = {} + self.charaClothesData = {} + self.limitlevel_rising_status = {} + + if platform.system() == "Windows": + path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\mst\\" + else: + path = "/media/USB2/Apache/Katworks/KF/assets/KF3/mst//" + + with gzip.open(path + "CHARA_DATA.d", mode="rt", encoding="utf-8") as file: for entry in json.loads(file.read()): self.charaData[entry["id"]] = entry - with gzip.open("data/KF3/CHARA_PROMOTE_DATA.d", mode="rt", encoding="utf-8") as file: + with gzip.open(path + "CHARA_PROMOTE_DATA.d", mode="rt", encoding="utf-8") as file: for entry in json.loads(file.read()): self.promoteData[entry["promoteId"]] = entry - with gzip.open("data/KF3/CHARA_PROMOTE_PRESET_DATA.d", mode="rt", encoding="utf-8") as file: + with gzip.open(path + "CHARA_PROMOTE_PRESET_DATA.d", mode="rt", encoding="utf-8") as file: for entry in json.loads(file.read()): if entry["promotePresetId"] not in self.promotePresetData: self.promotePresetData[entry["promotePresetId"]] = [] self.promotePresetData[entry["promotePresetId"]].append(entry) - with gzip.open("data/KF3/CHARA_CLOTHES_DATA.d", mode="rt", encoding="utf-8") as file: + with gzip.open(path + "CHARA_CLOTHES_DATA.d", mode="rt", encoding="utf-8") as file: for entry in json.loads(file.read()): if entry["clothesPresetId"] not in self.charaClothesData: self.charaClothesData[entry["clothesPresetId"]] = [] @@ -48,11 +59,24 @@ class KF3DB: for entry in json.loads(file.read()): self.limitlevel_rising_status[entry["patternId"]] = entry + self.parse_parameter() self.process_friends() - app.databases["KF3"] = self - + def parse_parameter(self): - paramAsset = UnityPy.load("data/KF3/assets/parameter.asset") + self.paramAbilities = {} + self.paramAbilities1 = {} + self.paramAbilities2 = {} + self.paramAlphaBases = {} + self.paramArts = {} + self.paramSpecialAttacks = {} + self.paramWaitActions = {} + + if platform.system() == "Windows": + path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\assets\\" + else: + path = "/media/USB2/Apache/Katworks/KF/assets/KF3/assets//" + + paramAsset = UnityPy.load(path + "parameter.asset") for obj in paramAsset.objects: data = obj.read() if data.name.split('_')[0] == "ParamAbility": @@ -122,10 +146,18 @@ class KF3DB: chara["promoteBonus"] = promote_bonus chara["costumeBonus"] = costume_bonus chara["attribute"] = charaData["attribute"] + chara["name"] = charaData["name"] chara["nameEn"] = charaData["nameEn"] + chara["nickname"] = charaData["nickname"] chara["rankLow"] = charaData["rankLow"] chara["rankHigh"] = charaData["rankHigh"] chara["castName"] = charaData["castName"] + chara["loginText"] = charaData["loginText"] + chara["flavorText"] = charaData["flavorText"] + chara["kizunaupText"] = charaData["kizunaupText"] + chara["greetingText"] = charaData["greetingText"] + chara["startTimeRaw"] = charaData["startTime"] + chara["startTime"] = datetime.fromtimestamp(charaData["startTime"]/1000.0).strftime('%A, %B %d, %Y') chara["wr"] = 4 if wrLocked else 5 chara["is_wr5"] = chara["wr"] == 5 @@ -158,6 +190,8 @@ class KF3DB: chara["wait_action"] = self.paramWaitActions[id] if id in self.paramWaitActions else None chara["special_attack"] = self.paramSpecialAttacks[id] if id in self.paramSpecialAttacks else None + chara["costumes"] = self.get_costumes(id, chara["rankLow"]) + return chara def get_chara(self, id : int): @@ -168,6 +202,8 @@ class KF3DB: def get_chara_wiki(self, id : int): chara = self.get_chara(id) + if chara is None: + return None lines = [] @@ -182,7 +218,7 @@ class KF3DB: f"[[Category:{stars_word} Star KF3 Friends]]", f"[[Category:Missing Content]]", f"[[Category:Needs Audio]]", - f"{{{{#vardefine:id|{str(chara['id'])}}}}}" + f"{{{{#vardefine:id|{str(chara['id']).zfill(4)}}}}}" ] if chara['has_party_dress']: @@ -200,7 +236,7 @@ class KF3DB: lines.append(f"|apprarity={{{{KF3{chara['rankLow']}Star}}}}") lines.append(f"|seiyuu={chara['castName']}") lines.append(f"|attribute={attribute_word} {{{{KF3{attribute_word}}}}}") - lines.append(f"|implemented={0}") + lines.append(f"|implemented={chara['startTime']} (App)") lines.append(f"|id={chara['id']}") lines.append(f"|wr5={'Yes' if chara['is_wr5'] else 'No'}") lines.append(f"|rainbowtrait={'Yes' if chara['has_rainbow_trait'] else 'No'}") @@ -226,6 +262,7 @@ class KF3DB: lines.append(f"|maxbeat={chara['stats_max']['beat']}%") lines.append(f"|maxaction={chara['stats_max']['act']}%") lines.append(f"|maxtry={chara['stats_max']['try']}%") + lines.append("") cards_str = "|flags=" for card in chara["cards"]: cards_str += " {{" @@ -271,6 +308,214 @@ class KF3DB: else: lines.append(f"|rainbowtrait=N/A") lines.append(f"|rainbowtraitskill=Not Implemented.") + + lines.append("") + lines.append(f"|bondup={chara['kizunaupText']}") + lines.append(f"|profile={chara['flavorText']}") + lines.append(f"|greeting={chara['loginText']}") + lines.append(f"|selfintro={chara['greetingText']}") + lines.append(f"|enselfintro=") + + cos_images = "|cos = " + ",".join([costume["image_name"] for costume in chara["costumes"]]) + ",end" + cos_obtain = "|cosobt = " + ",".join([costume["obtain"] for costume in chara["costumes"]]) + cos_names = "|cosname = " + ",".join([costume["name"] for costume in chara["costumes"]]) + + lines.append("") + lines.append(cos_images) + lines.append(cos_names) + lines.append(cos_obtain) + lines.append("}}") - return "\n".join(lines) \ No newline at end of file + return "\n".join(lines) + + def get_costumes(self, id : int, defaultRarity : int): + if id not in self.charaClothesData: + return [] + + output = [] + data = self.charaClothesData[id] + for costume in data: + cos = { + "image_name" : f"icon dressup {costume['id']}.png", + "obtain" : "Limited" + } + cosId = costume['clothesId'] + if cosId == 1: + cos["name"] = "Default" + cos["obtain"] = "Default" + + elif cosId == 2: + cos["name"] = "Personal Fashion" + cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" + + elif cosId == 3: + cos["name"] = "Tracksuit" + cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" + + elif cosId == 4: + cos["name"] = "Park Staff" + cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" + + elif cosId == 5: + cos["name"] = "Café Uniform" + cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" + + elif cosId == 6: + cos["name"] = "Special Outfit 1" + + elif cosId == 8: + cos["name"] = "Party Dress" + cos["obtain"] = "Increase Photo Pocket level to 12" + + elif cosId == 9: + cos["name"] = "School Uniform" + + elif cosId == 10: + cos["name"] = "Gym Clothes" + + elif cosId == 11: + cos["name"] = "Raincoat" + + elif cosId == 12: + cos["name"] = "Hanamaru Animal Swimsuit" + + elif cosId == 13: + cos["name"] = "Cafe Uniform (Green)" + cos["obtain"] = "Dojo reward" + + elif cosId == 14: + cos["name"] = "Cafe Uniform (Blue)" + cos["obtain"] = "Dojo reward" + + elif cosId == 15: + cos["name"] = "Cafe Uniform (Pink)" + cos["obtain"] = "Dojo reward" + + elif cosId == 16: + cos["name"] = "Orihime Costume" + + elif cosId == 17: + cos["name"] = "Arle's Outfit" + + elif cosId == 18: + cos["name"] = "Marine Nanoda! Swimsuit" + + elif cosId == 19: + cos["name"] = "Batten Japari Dan Swimsuit" + + elif cosId == 20: + cos["name"] = "Lifeguard Swimsuit" + + elif cosId == 21: + cos["name"] = "Harvest Festival Outfit" + + elif cosId == 22: + cos["name"] = "Halloween Costume" + + elif cosId == 23: + cos["name"] = "Classic Maid Uniform" + + elif cosId == 24: + cos["name"] = "Arle's Outfit" + + elif cosId == 25: + cos["name"] = "Swimsuit" + + elif cosId == 26: + cos["name"] = "Swimsuit" + + elif cosId == 27: + cos["name"] = "Swimsuit" + + elif cosId == 28: + cos["name"] = "Matching Clothes" + + elif cosId == 29: + cos["name"] = "Frilly Frilly PPP Bikini" + + elif cosId == 30: + cos["name"] = "Venus Flower Bikini" + + elif cosId == 31: + cos["name"] = "Christmas Dress" + + elif cosId == 32: + cos["name"] = "Chinese Shrine Maiden" + + elif cosId == 33: + cos["name"] = "Chinese Dress" + + elif cosId == 34: + cos["name"] = "Hanamaru Stage Costume" + + elif cosId == 35: + cos["name"] = "Camo Outfit" + + elif cosId == 36: + cos["name"] = "でびでび・ふりる" + + elif cosId == 37: + cos["name"] = "夜に紛れし闇の衣" + + elif cosId == 38: + cos["name"] = "Santa" + + elif cosId == 39: + cos["name"] = "Fluffy Pajamas" + + elif cosId == 40: + cos["name"] = "Japanese Armor" + + elif cosId == 41: + cos["name"] = "Miko Outfit" + + elif cosId == 42: + cos["name"] = "PPP Hoodie" + + elif cosId == 43: + cos["name"] = "Blue Tracksuit" + + elif cosId == 44: + cos["name"] = "Park Staff (beige)" + + elif cosId == 45: + cos["name"] = "Sailor School Uniform" + + elif cosId == 46: + cos["name"] = "Snow Knight Dress" + + elif cosId == 47: + cos["name"] = "Sparkling Tree Dress" + + elif cosId == 48: + cos["name"] = "Retro Detective" + + elif cosId == 49: + cos["name"] = "Steampunk Detective" + + elif cosId == 50: + cos["name"] = "鞠と花びらのはんなり和風服" + + elif cosId == 51: + cos["name"] = "Numazu Deep Sea Aquarium Clothes" + + elif cosId == 52: + cos["name"] = "たのしいことをお届け!" + + elif cosId == 53: + cos["name"] = "かわいい勝負ですよ!ワンピース水着" + + elif cosId == 54: + cos["name"] = "どきどき♡ずっきゅんビキニ" + + elif cosId == 55: + cos["name"]= "のんほいパークの服" + + else: + print(f"{costume['clothesId']}\t{costume['id']}\t{costume['name']}") + continue + + output.append(cos) + return output + diff --git a/loaders/KF3/charaData.py b/loaders/KF3/statCalculation.py similarity index 100% rename from loaders/KF3/charaData.py rename to loaders/KF3/statCalculation.py diff --git a/loaders/Kingdom/kemono.py b/loaders/Kingdom/kemono.py index 30da50f..1e64d5e 100644 --- a/loaders/Kingdom/kemono.py +++ b/loaders/Kingdom/kemono.py @@ -87,6 +87,31 @@ def calculate_stats(friend, awaken : int, level : int, trust_percent = 0, trustA return stats +def get_stat_growth(friend, awaken : int): + stats_array = friend["attrScriptParam"] + max_awaken = friend["maxAwaken"] + + awaken_step = max_awaken + 2 + + atk_ptr = 1 * awaken_step + satk_ptr = 2 * awaken_step + pdef_ptr = 3 * awaken_step + sdef_ptr = 4 * awaken_step + hp_ptr = 5 * awaken_step + speed_ptr = 6 * awaken_step + + stats = {} + i = awaken + stats["patk"] += stats_array[atk_ptr + 1 + i] + stats["satk"] += stats_array[satk_ptr + 1 + i] + stats["pdef"] += stats_array[pdef_ptr + 1 + i] + stats["sdef"] += stats_array[sdef_ptr + 1 + i] + stats["hp"] += stats_array[hp_ptr + 1 + i] + stats["speed"] += stats_array[speed_ptr + 1 + i] + + return stats + + def clean_skill_string(input_string): if input_string is None: return None diff --git a/loaders/Kingdom/kingdomdb.py b/loaders/Kingdom/kingdomdb.py index 30f5051..44cfcd9 100644 --- a/loaders/Kingdom/kingdomdb.py +++ b/loaders/Kingdom/kingdomdb.py @@ -3,6 +3,11 @@ import platform from loaders.Kingdom.numeric import numeric from loaders.Kingdom.kemono import clean_skill_string, calculate_stats, fill_miracle, get_awaken_materials, get_skill_materials +from endpoints.Kingdom.friend import Kingdom_Friend +from endpoints.Kingdom.friends import Kingdom_Friends +from endpoints.Kingdom.item import Kingdom_Item +from endpoints.Kingdom.items import Kingdom_Items + #element 1-orange 2-blue 3-green #showSkillType 1-control 2-guard 3-heal 4-support 5-assault 6-aoe @@ -10,7 +15,8 @@ class KingdomDB: processed_friends = {} item_stages = {} - def __init__(self, app) -> None: + def __init__(self, api) -> None: + app = api.app if "Kingdom" in app.databases: del app.databases["Kingdom"] @@ -45,6 +51,10 @@ class KingdomDB: self.process_stages() app.databases["Kingdom"] = self + api.add_resource(Kingdom_Friend, "/Kingdom/Friend/") + api.add_resource(Kingdom_Friends, "/Kingdom/Friends") + api.add_resource(Kingdom_Item, "/Kingdom/Item/") + api.add_resource(Kingdom_Items, "/Kingdom/Items") def process_friends(self): self.processed_friends = {} @@ -87,11 +97,15 @@ class KingdomDB: raise Exception() if bk_value["effectType1"] == 4: lb = self.kfk_en_kemono_WkPaDc.get(bk_value['effectParam1'][0] * 100) + if lb is None: + lb = {"name": None, "describe": None} limit_breaks.append({"name": lb["name"], "desc": clean_skill_string(lb["describe"])}) habits = [] for habit in friend["habitSn"]: hbt = self.kfk_en_kemono_WkPaDc.get(habit*100) + if hbt is None: + hbt = {"name": None, "describe": None} habits.append({"name":hbt["name"], "desc":hbt["describe"]}) for i in range (3): if len(habits) < i+1: @@ -103,6 +117,8 @@ class KingdomDB: awaken_id = friend_id * 100 + i awaken = self.kfk_kemono_waken.get(awaken_id) awaken1 = self.kfk_en_kemono_waken.get(awaken_id) + if awaken1 == None: + awaken1 = {"describe":"missing", "brilliance":"missing"} if awaken != None and awaken1 != None: awaken["en"] = awaken1 awakens.append(awaken) diff --git a/resources/KF3/endpoints.py b/resources/KF3/endpoints.py deleted file mode 100644 index e69de29..0000000 diff --git a/resources/average.py b/resources/average.py deleted file mode 100644 index 024f4c4..0000000 --- a/resources/average.py +++ /dev/null @@ -1,36 +0,0 @@ -import requests -from datetime import datetime -from flask_restful import Resource -from flask import jsonify, make_response -from resources.shared import API_URL, VALID_CURRENCIES_A, ERROR_CURRENCY_CODE, ERROR_DATE_FORMAT, ERROR_NO_DATA - -class AverageRate(Resource): - def get(self, date : str, currency : str): - """ - Returns average exchange rate for the specified currency and date. - - Weekends and holidays return code 404. - """ - if not currency.upper() in VALID_CURRENCIES_A: - return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400) - - try: - date_temp = datetime.strptime(date, "%Y-%m-%d") - - if date_temp.weekday() in [5,6]: - return make_response(jsonify({"error": ERROR_NO_DATA}), 404) - - #ensure correct date string format - date = date_temp.strftime("%Y-%m-%d") - except: - return make_response(jsonify({"error": ERROR_DATE_FORMAT}), 400) - - url = f"{API_URL}/exchangerates/rates/a/{currency}/{date}/" - response = requests.get(url) - if response.status_code == 200: - data = response.json() - return make_response(jsonify({"mid": data["rates"][0]["mid"]}), 200) - elif response.status_code == 404: - return make_response(jsonify({"error": ERROR_NO_DATA}), 404) - else: - return make_response(jsonify({"error": response.text}), 400) \ No newline at end of file diff --git a/resources/difference_last.py b/resources/difference_last.py deleted file mode 100644 index 82befd5..0000000 --- a/resources/difference_last.py +++ /dev/null @@ -1,34 +0,0 @@ -import requests -from flask_restful import Resource -from flask import jsonify, make_response -from resources.shared import API_URL, ERROR_CURRENCY_CODE, ERROR_DATE_RANGE, VALID_CURRENCIES_C - -class DifferenceLast(Resource): - def get(self, currency : str, num_days : int): - """ - Returns the highest difference between "ask" and "bid" values, and the day it occured. - - Weekends and holidays are skipped and do not count towards the 'num_days' limit." - """ - if not currency.upper() in VALID_CURRENCIES_C: - return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400) - - if not (num_days > 0 and num_days < 256): - return make_response(jsonify({"error": ERROR_DATE_RANGE}), 400) - - url = f"{API_URL}/exchangerates/rates/c/{currency}/last/{num_days}/" - response = requests.get(url) - if response.status_code == 200: - data = response.json() - result = {"diff_max": 0, "date":""} - for rate in data["rates"]: - #ensure correct precision - difference = round(rate["ask"] - rate["bid"],4) - #spread can be negative on rare occasions - if abs(difference) > abs(result["diff_max"]): - result["diff_max"] = difference - result["date"] = rate["effectiveDate"] - - return make_response(jsonify(result), 200) - else: - return make_response(jsonify({"error": response.text}), 400) \ No newline at end of file diff --git a/resources/minmax_last.py b/resources/minmax_last.py deleted file mode 100644 index 30edbb3..0000000 --- a/resources/minmax_last.py +++ /dev/null @@ -1,38 +0,0 @@ -import requests -from flask_restful import Resource -from flask import jsonify, make_response -from resources.shared import API_URL, ERROR_CURRENCY_CODE, ERROR_DATE_RANGE, VALID_CURRENCIES_A - -class MinMaxLast(Resource): - def get(self, currency : str, num_days : int): - """ - Returns minimum and maximum average exchange rate values, and the days they occured. - - Weekends and holidays are skipped and do not count towards the 'num_days' limit." - """ - if not currency.upper() in VALID_CURRENCIES_A: - return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400) - - if not (num_days > 0 and num_days < 256): - return make_response(jsonify({"error": ERROR_DATE_RANGE}), 400) - - url = f"{API_URL}/exchangerates/rates/a/{currency}/last/{num_days}/" - response = requests.get(url) - if response.status_code == 200: - data = response.json() - result = {"mid_min": data["rates"][0]["mid"], "date_min":"", "mid_max": data["rates"][0]["mid"], "date_max":""} - for rate in data["rates"]: - #if rates are equal, return the more recent one - #recent values are at the end of the list - - if rate["mid"] >= result["mid_max"]: - result["mid_max"] = rate["mid"] - result["date_max"] = rate["effectiveDate"] - - if rate["mid"] <= result["mid_min"]: - result["mid_min"] = rate["mid"] - result["date_min"] = rate["effectiveDate"] - - return make_response(jsonify(result), 200) - else: - return make_response(jsonify({"error": response.text}), 400) diff --git a/resources/shared.py b/resources/shared.py deleted file mode 100644 index e4475d9..0000000 --- a/resources/shared.py +++ /dev/null @@ -1,13 +0,0 @@ -# Root url for the NBP API -API_URL = "https://api.nbp.pl/api/" - -# List of currencies accepted by NBP as of 2023-04-21 -# https://nbp.pl/en/statistic-and-financial-reporting/rates/table-a/ -# https://nbp.pl/en/statistic-and-financial-reporting/rates/table-c/ -VALID_CURRENCIES_A = ["AUD","THB","BRL","BGN","CAD","CLP","CZK","DKK","EUR","HUF","HKD","UAH","ISK","INR","MYR","MXN","ILS","NZD","NOK","PHP","GBP","ZAR","RON","IDR","SGD","SEK","CHF","TRY","USD","KRW","JPY","CNY","XDR" ] -VALID_CURRENCIES_C = ["AUD","CAD","CZK","DKK","EUR","HUF","NOK","GBP","SEK","CHF","USD","JPY","XDR"] - -ERROR_CURRENCY_CODE = "Invalid currency code." -ERROR_DATE_FORMAT = "Incorrect date format, expected YYYY-MM-DD" -ERROR_DATE_RANGE = "Invalid currency code." -ERROR_NO_DATA = "Exchange rate data is not available for this date." \ No newline at end of file