From d9d2b2485925dacedc249d2a303d4fb2584e9e33 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:06:11 +0100 Subject: [PATCH 01/12] fix css resource path --- truthinquiry/templates/admin/npc.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/truthinquiry/templates/admin/npc.html b/truthinquiry/templates/admin/npc.html index f4a03b7..edb233f 100644 --- a/truthinquiry/templates/admin/npc.html +++ b/truthinquiry/templates/admin/npc.html @@ -3,7 +3,7 @@ NPC - + From 1862ef9274811b4dba8872461e15898cadd6cd84 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Fri, 24 Mar 2023 11:42:55 +0100 Subject: [PATCH 02/12] Remove debug calls to print() --- truthinquiry/logic/game_logic.py | 1 - truthinquiry/routes/routes_api.py | 3 --- truthinquiry/routes/routes_ui.py | 1 - 3 files changed, 5 deletions(-) diff --git a/truthinquiry/logic/game_logic.py b/truthinquiry/logic/game_logic.py index 1e2b126..2e7bcb8 100644 --- a/truthinquiry/logic/game_logic.py +++ b/truthinquiry/logic/game_logic.py @@ -132,7 +132,6 @@ class Game: :param npc_id: the id of the npc, to get the reactions from, must be in the current game :return: the reaction image as bytes """ - print(self.reaction_table) if npc_id not in self.reaction_table: return None trait_id = self.reaction_table[npc_id] diff --git a/truthinquiry/routes/routes_api.py b/truthinquiry/routes/routes_api.py index 25ed9ee..c3728a0 100644 --- a/truthinquiry/routes/routes_api.py +++ b/truthinquiry/routes/routes_api.py @@ -35,9 +35,6 @@ def create_game(): @routes_api.route("/getGameMembers", methods=["GET", "POST"]) def get_members(): game_id = flask.request.values.get("game_id") - print(50 * "#") - print(game_id) - print(50*"_") game = game_logic.get_game(game_id) if game is None: return {"error": 1, "msg": "this game doesn't exist"} diff --git a/truthinquiry/routes/routes_ui.py b/truthinquiry/routes/routes_ui.py index a1410fe..e2ffd18 100644 --- a/truthinquiry/routes/routes_ui.py +++ b/truthinquiry/routes/routes_ui.py @@ -34,7 +34,6 @@ def lobbyRedirect(): @routes_ui.route("/lobby/") def lobby(game_id): # rendered by the javascript client-side - print(game_id) if game_id is None: return flask.redirect("") return flask.render_template("lobby.html", gameid=game_id) From 55018c90bb4dd192f2c8ccad3c101e8ebb3fd7ad Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sat, 25 Mar 2023 20:16:48 +0100 Subject: [PATCH 03/12] add utility fucntions to query texts from locales --- truthinquiry/ext/database/models.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/truthinquiry/ext/database/models.py b/truthinquiry/ext/database/models.py index f47cb99..c872d9b 100644 --- a/truthinquiry/ext/database/models.py +++ b/truthinquiry/ext/database/models.py @@ -50,6 +50,27 @@ class Locale(Base): def __repr__(self) -> str: return self.__str__() + def get_texts(self, lang): + texts = [] + for text in self.TEXTS: + if text.LANG == lang: + texts.append(text) + return texts + + def get_text(self, lang, auto_create): + for text in self.TEXTS: + if text.LANG == lang: + return text + + if auto_create: + text = Text(None, None, lang, None) + self.TEXTS.append(text) + return text + else: + return None + + + class Place(Base): """ From ccac9c302cb642ffd46802cd6c02b70a918fc880 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sat, 25 Mar 2023 20:17:05 +0100 Subject: [PATCH 04/12] Connect npc view to backend --- truthinquiry/routes/routes_admin.py | 1 + truthinquiry/routes/routes_api_admin.py | 24 +++++++++++++++++++ truthinquiry/static/js/admin.js | 24 +++++++++++++++++++ truthinquiry/templates/admin/npc.html | 32 +++++++++++++++---------- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index a48ec4b..e20db2b 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -25,6 +25,7 @@ def npc(npc_id): npc_answers.append(answer_list) npc_dict = { + "id": npc_obj.NPC_ID, "name": npc_obj.LOCALE.TEXTS[0].TEXT, "img": npc_obj.NPC_ID, "answers": npc_answers, diff --git a/truthinquiry/routes/routes_api_admin.py b/truthinquiry/routes/routes_api_admin.py index 42a29db..87a052a 100644 --- a/truthinquiry/routes/routes_api_admin.py +++ b/truthinquiry/routes/routes_api_admin.py @@ -121,4 +121,28 @@ def set_places(): db.session.commit() + return {"error": 0} + +@routes_api_admin.route("/setNpc", methods=["GET", "POST"]) +def set_npc(): + input_lang = flask.request.json["lang"] + input_npc = flask.request.json["npc"] + + if input_npc["id"] == None: + npc_obj = Npc(None, None) + db.session.add(npc_obj) + else: + npc_obj = db.session.get(Npc, input_npc["id"]) + + npc_obj.LOCALE.get_text(input_lang, True).TEXT = input_npc["name"] + + for answer_type, input_answer_type in zip(npc_obj.ANSWERS, input_npc["allAnswers"]): + for text in answer_type.LOCALE.get_texts(input_lang): + db.session.delete(text) + for input_answer in input_answer_type["answers"]: + answer_type.LOCALE.TEXTS.append(Text(None, None, input_lang, input_answer["text"])) + + + db.session.commit() + return {"error": 0} \ No newline at end of file diff --git a/truthinquiry/static/js/admin.js b/truthinquiry/static/js/admin.js index 1923bd7..d8e30cb 100644 --- a/truthinquiry/static/js/admin.js +++ b/truthinquiry/static/js/admin.js @@ -92,3 +92,27 @@ function changeLang(){ } +//functions for npc.html + +function saveFormNpc(){ + let data = {} + + data["id"] = npc.querySelector("#npc_id").value; + data["name"] = npc.querySelector("#npc_name").value; + + let allAnswersJson = []; + data["allAnswers"] = allAnswersJson; + + for(let answerTypeNode of npc.querySelectorAll(".answerType")){ + let answersJson = []; + let answerTypeJson = {"answers": answersJson}; + allAnswersJson.push(answerTypeJson); + + for(let answerNode of answerTypeNode.querySelectorAll("input")){ + answersJson.push({"text": answerNode.value}) + } + } + + makeAPIRequest("admin/setNpc", {"npc": data, "lang": "FR"}, {"content": "json"}) +} + diff --git a/truthinquiry/templates/admin/npc.html b/truthinquiry/templates/admin/npc.html index edb233f..3f04ba7 100644 --- a/truthinquiry/templates/admin/npc.html +++ b/truthinquiry/templates/admin/npc.html @@ -4,26 +4,32 @@ NPC + go Back
-
- Npc name: - - -
- -
-

Answers:

- {%for answer_type in npc.get("answers") or []%} -
- {%for answer in answer_type%} - +
+
+ Npc name: + + + +
+ +
+

Answers:

+ {%for answer_type in npc.get("answers") or []%} +
+ {%for answer in answer_type%} + + {%endfor%} +
{%endfor%}
- {%endfor%}
+ + From 25e29d5c33923aa9d5fb4706094165cdf83b2ce7 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sat, 25 Mar 2023 20:33:45 +0100 Subject: [PATCH 05/12] refactor SQL relationship names --- truthinquiry/ext/database/models.py | 12 +++++----- truthinquiry/routes/routes_admin.py | 10 ++++----- truthinquiry/routes/routes_api_admin.py | 30 ++++++++++++------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/truthinquiry/ext/database/models.py b/truthinquiry/ext/database/models.py index c872d9b..c6fd4d0 100644 --- a/truthinquiry/ext/database/models.py +++ b/truthinquiry/ext/database/models.py @@ -80,7 +80,7 @@ class Place(Base): __tablename__ = 'T_PLACE' PLACE_ID = Column(Integer, primary_key=True, autoincrement=True, comment="ID of this place") NAME_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Place name") - LOCALE = relationship("Locale") + NAME_LOCALE = relationship("Locale") def __init__(self, PLACE_ID, NAME_LID): self.PLACE_ID = PLACE_ID @@ -101,7 +101,7 @@ class QuestionType(Base): __tablename__ = "T_QUESTION_TYPE" QUESTION_TYPE_ID = Column(Integer, default=0, primary_key=True, comment="ID of this question type.") TEXT_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Question text") - LOCALE = relationship("Locale") + TEXT_LOCALE = relationship("Locale") def __init__(self, QUESTION_TYPE_ID, TEXT_LID): self.QUESTION_TYPE_ID = QUESTION_TYPE_ID @@ -124,7 +124,7 @@ class Answer(Base): QUESTION_TYPE_ID = Column(Integer, ForeignKey("T_QUESTION_TYPE.QUESTION_TYPE_ID"), primary_key=True, comment="Question type ID") NPC_ID = Column(Integer, ForeignKey("T_NPC.NPC_ID"), primary_key=True, comment="ID of the NPC that will say this answer") TEXT_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Text of the answer") - LOCALE = relationship("Locale") + TEXT_LOCALE = relationship("Locale") NPC = relationship("Npc", backref="ANSWERS") def __init__(self, QUESTION_TYPE_ID, NPC_ID, TEXT_LID): @@ -149,7 +149,7 @@ class Npc(Base): NPC_ID = Column(Integer, autoincrement=True, primary_key=True, comment="ID of this Npc") NAME_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Name of this Npc") DEFAULT_IMG = Column(LargeBinary(length=2**24), comment="Binary data of the default image of this Npc") - LOCALE = relationship("Locale") + NAME_LOCALE = relationship("Locale") def __init__(self, NPC_ID, NAME_LID): self.NPC_ID = NPC_ID @@ -171,8 +171,8 @@ class Trait(Base): NAME_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Name of this trait") DESC_LID = Column(Integer, ForeignKey("T_LOCALE.LID"), comment="Description of this trait") - Name = relationship("Locale",foreign_keys=[NAME_LID]) - Desc = relationship("Locale",foreign_keys=[DESC_LID]) + NAME_LOCALE = relationship("Locale",foreign_keys=[NAME_LID]) + DESC_LOCALE = relationship("Locale",foreign_keys=[DESC_LID]) def __init__(self, TRAIT_ID, NAME_LID, DESC_LID): diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index e20db2b..0780201 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -9,7 +9,7 @@ routes_admin = flask.Blueprint("admin", __name__) @routes_admin.route("/") def index(): npcs_objs = db.session.query(Npc).all() - npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.LOCALE.TEXTS[0].TEXT} for npc_obj in npcs_objs] + npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.NAME_LOCALE.TEXTS[0].TEXT} for npc_obj in npcs_objs] return flask.render_template("admin/index.html", npcs=npcs_dicts) @routes_admin.route("/npc/") @@ -21,12 +21,12 @@ def npc(npc_id): npc_answers = [] for answer_type in npc_obj.ANSWERS: - answer_list = [answer.TEXT for answer in answer_type.LOCALE.TEXTS] + answer_list = [answer.TEXT for answer in answer_type.TEXT_LOCALE.TEXTS] npc_answers.append(answer_list) npc_dict = { "id": npc_obj.NPC_ID, - "name": npc_obj.LOCALE.TEXTS[0].TEXT, + "name": npc_obj.NAME_LOCALE.TEXTS[0].TEXT, "img": npc_obj.NPC_ID, "answers": npc_answers, } @@ -62,11 +62,11 @@ def questions(): @routes_admin.route("/places") def places(): places_objs = db.session.query(Place).all() - places_dicts = [{"id": place_obj.PLACE_ID, "name": place_obj.LOCALE.TEXTS[0].TEXT} for place_obj in places_objs] + places_dicts = [{"id": place_obj.PLACE_ID, "name": place_obj.NAME_LOCALE.TEXTS[0].TEXT} for place_obj in places_objs] return flask.render_template("admin/places.html", places=places_dicts) @routes_admin.route("/traits") def traits(): traits_objs = db.session.query(Trait).all() - traits_dicts = [{"id": trait_obj.TRAIT_ID, "name": trait_obj.Name.TEXTS[0].TEXT, "desc": trait_obj.Desc.TEXTS[0].TEXT} for trait_obj in traits_objs] + traits_dicts = [{"id": trait_obj.TRAIT_ID, "name": trait_obj.NAME_LOCALE.TEXTS[0].TEXT, "desc": trait_obj.DESC_LOCALE.TEXTS[0].TEXT} for trait_obj in traits_objs] return flask.render_template("admin/traits.html", traits=traits_dicts) diff --git a/truthinquiry/routes/routes_api_admin.py b/truthinquiry/routes/routes_api_admin.py index 87a052a..11523ef 100644 --- a/truthinquiry/routes/routes_api_admin.py +++ b/truthinquiry/routes/routes_api_admin.py @@ -57,10 +57,10 @@ def set_traits(): # modify db_trait = list(filter(lambda db_trait: db_trait.TRAIT_ID == int(input_trait["id"]), db_traits))[0] - db.session.delete(db_trait.Name.TEXTS[0]) - db.session.delete(db_trait.Desc.TEXTS[0]) - db_trait.Name.TEXTS = [Text(None, None, input_lang, input_trait["name"])] - db_trait.Desc.TEXTS = [Text(None, None, input_lang, input_trait["desc"])] + db.session.delete(db_trait.NAME_LOCALE.TEXTS[0]) + db.session.delete(db_trait.DESC_LOCALE.TEXTS[0]) + db_trait.NAME_LOCALE.TEXTS = [Text(None, None, input_lang, input_trait["name"])] + db_trait.DESC_LOCALE.TEXTS = [Text(None, None, input_lang, input_trait["desc"])] db.session.add(db_trait) modified_db_traits.append(db_trait) @@ -68,11 +68,11 @@ def set_traits(): # add new_trait = Trait(None, None, None) - new_trait.Name = Locale(None) - new_trait.Desc = Locale(None) + new_trait.NAME_LOCALE = Locale(None) + new_trait.DESC_LOCALE = Locale(None) - new_trait.Name.TEXTS.append(Text(None, None, input_lang, input_trait["name"])) - new_trait.Desc.TEXTS.append(Text(None, None, input_lang, input_trait["desc"])) + new_trait.NAME_LOCALE.TEXTS.append(Text(None, None, input_lang, input_trait["name"])) + new_trait.DESC_LOCALE.TEXTS.append(Text(None, None, input_lang, input_trait["desc"])) db.session.add(new_trait) @@ -99,9 +99,9 @@ def set_places(): # modify db_place = list(filter(lambda db_place: db_place.PLACE_ID == int(input_place["id"]), db_places))[0] - db.session.delete(db_place.LOCALE.TEXTS[0]) + db.session.delete(db_place.NAME_LOCALE.TEXTS[0]) - db_place.LOCALE.TEXTS = [Text(None, None, input_lang, input_place["name"])] + db_place.NAME_LOCALE.TEXTS = [Text(None, None, input_lang, input_place["name"])] db.session.add(db_place) modified_db_places.append(db_place) @@ -109,8 +109,8 @@ def set_places(): # add new_place = Place(None, None) - new_place.LOCALE = Locale(None) - new_place.LOCALE.TEXTS = [Text(None, None, input_lang, input_place["name"])] + new_place.NAME_LOCALE = Locale(None) + new_place.NAME_LOCALE.TEXTS = [Text(None, None, input_lang, input_place["name"])] db.session.add(new_place) @@ -134,13 +134,13 @@ def set_npc(): else: npc_obj = db.session.get(Npc, input_npc["id"]) - npc_obj.LOCALE.get_text(input_lang, True).TEXT = input_npc["name"] + npc_obj.NAME_LOCALE.get_text(input_lang, True).TEXT = input_npc["name"] for answer_type, input_answer_type in zip(npc_obj.ANSWERS, input_npc["allAnswers"]): - for text in answer_type.LOCALE.get_texts(input_lang): + for text in answer_type.TEXT_LOCALE.get_texts(input_lang): db.session.delete(text) for input_answer in input_answer_type["answers"]: - answer_type.LOCALE.TEXTS.append(Text(None, None, input_lang, input_answer["text"])) + answer_type.TEXT_LOCALE.TEXTS.append(Text(None, None, input_lang, input_answer["text"])) db.session.commit() From 135c91ad19512635db66f007588c972efb613a5f Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sat, 25 Mar 2023 20:38:58 +0100 Subject: [PATCH 06/12] use text utility methods when possible --- truthinquiry/ext/database/models.py | 2 +- truthinquiry/routes/routes_admin.py | 16 +++++++++++----- truthinquiry/routes/routes_api_admin.py | 6 +++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/truthinquiry/ext/database/models.py b/truthinquiry/ext/database/models.py index c6fd4d0..3535f5c 100644 --- a/truthinquiry/ext/database/models.py +++ b/truthinquiry/ext/database/models.py @@ -57,7 +57,7 @@ class Locale(Base): texts.append(text) return texts - def get_text(self, lang, auto_create): + def get_text(self, lang, auto_create=False): for text in self.TEXTS: if text.LANG == lang: return text diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index 0780201..4a03368 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -6,10 +6,12 @@ from truthinquiry.ext.database.fsa import db routes_admin = flask.Blueprint("admin", __name__) +DEFAULT_LANG = "FR" + @routes_admin.route("/") def index(): npcs_objs = db.session.query(Npc).all() - npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.NAME_LOCALE.TEXTS[0].TEXT} for npc_obj in npcs_objs] + npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.NAME_LOCALE.get_text(DEFAULT_LANG).TEXT} for npc_obj in npcs_objs] return flask.render_template("admin/index.html", npcs=npcs_dicts) @routes_admin.route("/npc/") @@ -26,7 +28,7 @@ def npc(npc_id): npc_dict = { "id": npc_obj.NPC_ID, - "name": npc_obj.NAME_LOCALE.TEXTS[0].TEXT, + "name": npc_obj.NAME_LOCALE.get_text(DEFAULT_LANG).TEXT, "img": npc_obj.NPC_ID, "answers": npc_answers, } @@ -35,7 +37,7 @@ def npc(npc_id): @routes_admin.route("/questions") def questions(): - lang = "FR" + lang = DEFAULT_LANG results = db.session.execute( select(QuestionType, Text) @@ -61,12 +63,16 @@ def questions(): @routes_admin.route("/places") def places(): + lang = DEFAULT_LANG + places_objs = db.session.query(Place).all() - places_dicts = [{"id": place_obj.PLACE_ID, "name": place_obj.NAME_LOCALE.TEXTS[0].TEXT} for place_obj in places_objs] + places_dicts = [{"id": place_obj.PLACE_ID, "name": place_obj.NAME_LOCALE.get_text(lang).TEXT} for place_obj in places_objs] return flask.render_template("admin/places.html", places=places_dicts) @routes_admin.route("/traits") def traits(): + lang = DEFAULT_LANG + traits_objs = db.session.query(Trait).all() - traits_dicts = [{"id": trait_obj.TRAIT_ID, "name": trait_obj.NAME_LOCALE.TEXTS[0].TEXT, "desc": trait_obj.DESC_LOCALE.TEXTS[0].TEXT} for trait_obj in traits_objs] + traits_dicts = [{"id": trait_obj.TRAIT_ID, "name": trait_obj.NAME_LOCALE.get_text(lang).TEXT, "desc": trait_obj.DESC_LOCALE.get_text(lang).TEXT} for trait_obj in traits_objs] return flask.render_template("admin/traits.html", traits=traits_dicts) diff --git a/truthinquiry/routes/routes_api_admin.py b/truthinquiry/routes/routes_api_admin.py index 11523ef..3b88cef 100644 --- a/truthinquiry/routes/routes_api_admin.py +++ b/truthinquiry/routes/routes_api_admin.py @@ -57,8 +57,8 @@ def set_traits(): # modify db_trait = list(filter(lambda db_trait: db_trait.TRAIT_ID == int(input_trait["id"]), db_traits))[0] - db.session.delete(db_trait.NAME_LOCALE.TEXTS[0]) - db.session.delete(db_trait.DESC_LOCALE.TEXTS[0]) + db.session.delete(db_trait.NAME_LOCALE.get_text(input_lang)) + db.session.delete(db_trait.DESC_LOCALE.get_text(input_lang)) db_trait.NAME_LOCALE.TEXTS = [Text(None, None, input_lang, input_trait["name"])] db_trait.DESC_LOCALE.TEXTS = [Text(None, None, input_lang, input_trait["desc"])] @@ -99,7 +99,7 @@ def set_places(): # modify db_place = list(filter(lambda db_place: db_place.PLACE_ID == int(input_place["id"]), db_places))[0] - db.session.delete(db_place.NAME_LOCALE.TEXTS[0]) + db.session.delete(db_place.NAME_LOCALE.get_text(input_lang)) db_place.NAME_LOCALE.TEXTS = [Text(None, None, input_lang, input_place["name"])] From ef5ed73c9ec498340266e5c8c7e9d3617b70330d Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sun, 26 Mar 2023 15:42:22 +0200 Subject: [PATCH 07/12] added REACTION_UUID field to Reaction --- truthinquiry/ext/database/models.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/truthinquiry/ext/database/models.py b/truthinquiry/ext/database/models.py index 3535f5c..c53d077 100644 --- a/truthinquiry/ext/database/models.py +++ b/truthinquiry/ext/database/models.py @@ -1,4 +1,6 @@ -from sqlalchemy import Column, Integer, VARCHAR, Text, LargeBinary, ForeignKey +import uuid + +from sqlalchemy import Column, Integer, VARCHAR, Text, LargeBinary, ForeignKey, UUID from sqlalchemy.orm import relationship, declarative_base Base = declarative_base() @@ -198,14 +200,16 @@ class Reaction(Base): IMG = Column(LargeBinary(length=2**24), comment="Binary data of the image associated to this npc and trait") NPC = relationship("Npc") TRAIT = relationship("Trait") + REACTION_UUID = Column(UUID, unique=True, comment="ID of this reaction") def __init__(self, REACTION_ID, NPC_ID, TRAIT_ID): self.REACTION_ID = REACTION_ID self.NPC_ID = NPC_ID self.TRAIT_ID = TRAIT_ID + self.REACTION_UUID = uuid.uuid4() def __str__(self) -> str: - return f"Reaction(REACTION_ID={self.REACTION_ID}, NPC_ID={self.NPC_ID}, TRAIT_ID={self.TRAIT_ID})" + return f"Reaction(REACTION_ID={self.REACTION_ID}, NPC_ID={self.NPC_ID}, TRAIT_ID={self.TRAIT_ID}, REACTION_UUID={self.REACTION_UUID})" def __repr__(self) -> str: return self.__str__() \ No newline at end of file From daeea9cfd7837c357ecca08385fd0ddd314509b8 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sun, 26 Mar 2023 16:12:10 +0200 Subject: [PATCH 08/12] implement api route /getReaction --- truthinquiry/routes/routes_api.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/truthinquiry/routes/routes_api.py b/truthinquiry/routes/routes_api.py index c3728a0..055ce50 100644 --- a/truthinquiry/routes/routes_api.py +++ b/truthinquiry/routes/routes_api.py @@ -1,7 +1,11 @@ import json -import json -import flask +import io +import flask +from sqlalchemy import select + +from truthinquiry.ext.database.models import * +from truthinquiry.ext.database.fsa import db from truthinquiry.ext.discord_bot import discord_bot from truthinquiry.ext.socketio import socket_io from truthinquiry.logic import game_logic @@ -154,6 +158,20 @@ def get_npc_reaction(): 'Content-Disposition', 'attachment', filename='reaction.png') return response +@routes_api.route("/getReaction", methods=["GET", "POST"]) +def get_reaction(): + input_uuid = flask.request.values.get("uuid") + results = db.session.execute(select(Reaction).where(Reaction.REACTION_UUID==uuid.UUID(input_uuid))) + + row = results.first() + if row == None: + return {"error": 1, "msg": "No such reaction"} + reaction_obj = row[0] + + return flask.send_file(io.BytesIO(reaction_obj.IMG), mimetype='image/png') + + + @routes_api.route("/gameProgress", methods=["GET", "POST"]) def game_progress(): From f3a27be97188604a0853a03fc27f57cb2e5c3b73 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Sun, 26 Mar 2023 16:22:20 +0200 Subject: [PATCH 09/12] do not use UUID type for compatibility with old MariaDB servers --- truthinquiry/ext/database/models.py | 4 ++-- truthinquiry/routes/routes_api.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/truthinquiry/ext/database/models.py b/truthinquiry/ext/database/models.py index c53d077..957a8be 100644 --- a/truthinquiry/ext/database/models.py +++ b/truthinquiry/ext/database/models.py @@ -1,6 +1,6 @@ import uuid -from sqlalchemy import Column, Integer, VARCHAR, Text, LargeBinary, ForeignKey, UUID +from sqlalchemy import Column, Integer, VARCHAR, Text, LargeBinary, ForeignKey from sqlalchemy.orm import relationship, declarative_base Base = declarative_base() @@ -200,7 +200,7 @@ class Reaction(Base): IMG = Column(LargeBinary(length=2**24), comment="Binary data of the image associated to this npc and trait") NPC = relationship("Npc") TRAIT = relationship("Trait") - REACTION_UUID = Column(UUID, unique=True, comment="ID of this reaction") + REACTION_UUID = Column(VARCHAR(255), unique=True, comment="ID of this reaction") def __init__(self, REACTION_ID, NPC_ID, TRAIT_ID): self.REACTION_ID = REACTION_ID diff --git a/truthinquiry/routes/routes_api.py b/truthinquiry/routes/routes_api.py index 055ce50..1a89169 100644 --- a/truthinquiry/routes/routes_api.py +++ b/truthinquiry/routes/routes_api.py @@ -161,7 +161,7 @@ def get_npc_reaction(): @routes_api.route("/getReaction", methods=["GET", "POST"]) def get_reaction(): input_uuid = flask.request.values.get("uuid") - results = db.session.execute(select(Reaction).where(Reaction.REACTION_UUID==uuid.UUID(input_uuid))) + results = db.session.execute(select(Reaction).where(Reaction.REACTION_UUID==input_uuid)) row = results.first() if row == None: From 17d39ceb91866f80d7c06e710acac35f0104ccde Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:57:41 +0200 Subject: [PATCH 10/12] add decorator to restrict admin endpoints --- truthinquiry/routes/routes_admin.py | 7 +++++++ truthinquiry/routes/routes_api_admin.py | 5 +++++ truthinquiry/utils.py | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 truthinquiry/utils.py diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index 4a03368..4572278 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -3,18 +3,22 @@ from sqlalchemy import select, or_ from truthinquiry.ext.database.models import * from truthinquiry.ext.database.fsa import db +from truthinquiry.utils import require_admin + routes_admin = flask.Blueprint("admin", __name__) DEFAULT_LANG = "FR" @routes_admin.route("/") +@require_admin(ui=True) def index(): npcs_objs = db.session.query(Npc).all() npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.NAME_LOCALE.get_text(DEFAULT_LANG).TEXT} for npc_obj in npcs_objs] return flask.render_template("admin/index.html", npcs=npcs_dicts) @routes_admin.route("/npc/") +@require_admin(ui=True) def npc(npc_id): if npc_id == "new": return flask.render_template("admin/npc.html", npc={}) @@ -36,6 +40,7 @@ def npc(npc_id): return flask.render_template("admin/npc.html", npc=npc_dict) @routes_admin.route("/questions") +@require_admin(ui=True) def questions(): lang = DEFAULT_LANG @@ -62,6 +67,7 @@ def questions(): return flask.render_template("admin/questions.html", questions=data, langs=["FR", "EN"]) @routes_admin.route("/places") +@require_admin(ui=True) def places(): lang = DEFAULT_LANG @@ -70,6 +76,7 @@ def places(): return flask.render_template("admin/places.html", places=places_dicts) @routes_admin.route("/traits") +@require_admin(ui=True) def traits(): lang = DEFAULT_LANG diff --git a/truthinquiry/routes/routes_api_admin.py b/truthinquiry/routes/routes_api_admin.py index 3b88cef..1caccbe 100644 --- a/truthinquiry/routes/routes_api_admin.py +++ b/truthinquiry/routes/routes_api_admin.py @@ -3,11 +3,13 @@ from sqlalchemy import select, delete, or_ from truthinquiry.ext.database.models import * from truthinquiry.ext.database.fsa import db +from truthinquiry.utils import require_admin routes_api_admin = flask.Blueprint("api_admin", __name__) @routes_api_admin.route("/setQuestions", methods=["GET", "POST"]) +@require_admin(api=True) def set_questions(): if not flask.request.json: return {"error": 1, "msg": "no json set"} @@ -44,6 +46,7 @@ def set_questions(): return {"error": 0} @routes_api_admin.route("/setTraits", methods=["GET", "POST"]) +@require_admin(api=True) def set_traits(): input_lang = flask.request.json["lang"] input_traits = flask.request.json["traits"] @@ -86,6 +89,7 @@ def set_traits(): return {"error": 0} @routes_api_admin.route("/setPlaces", methods=["GET", "POST"]) +@require_admin(api=True) def set_places(): input_lang = flask.request.json["lang"] input_places = flask.request.json["places"] @@ -124,6 +128,7 @@ def set_places(): return {"error": 0} @routes_api_admin.route("/setNpc", methods=["GET", "POST"]) +@require_admin(api=True) def set_npc(): input_lang = flask.request.json["lang"] input_npc = flask.request.json["npc"] diff --git a/truthinquiry/utils.py b/truthinquiry/utils.py new file mode 100644 index 0000000..9359137 --- /dev/null +++ b/truthinquiry/utils.py @@ -0,0 +1,20 @@ +from functools import wraps + +import flask + +def require_admin(*args, **kwargs): + def decorator(route): + @wraps(route) + def decorated_function(*route_args, **route_kwargs): + + if flask.session.get("admin"): + return route(*route_args, **route_kwargs) + elif kwargs.get("api"): + return {"error": 1, "msg": "Invalid authentication"} + elif kwargs.get("ui"): + return flask.redirect("/admin/auth") + else: + raise ValueError("Can't determine request type") + + return decorated_function + return decorator From 9433d625f9fbb6d1e140d9ce6c8b56858f3a48dc Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:12:23 +0200 Subject: [PATCH 11/12] add admin auth method --- .env.dist | 1 + truthinquiry/routes/routes_admin.py | 4 ++++ truthinquiry/routes/routes_api_admin.py | 11 +++++++++++ truthinquiry/templates/admin/auth.html | 5 +++++ 4 files changed, 21 insertions(+) create mode 100644 truthinquiry/templates/admin/auth.html diff --git a/.env.dist b/.env.dist index 32463fa..4320f4d 100644 --- a/.env.dist +++ b/.env.dist @@ -9,3 +9,4 @@ DB_PORT=3306 DB_USER="" DB_PASSWORD="" DB_DBNAME="" +ADMIN_PASSWORD="s0meV3ryL0ngP@sswOrd" \ No newline at end of file diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index 4572278..8b831a7 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -17,6 +17,10 @@ def index(): npcs_dicts = [{"id": npc_obj.NPC_ID, "name": npc_obj.NAME_LOCALE.get_text(DEFAULT_LANG).TEXT} for npc_obj in npcs_objs] return flask.render_template("admin/index.html", npcs=npcs_dicts) +@routes_admin.route("/auth") +def auth(): + return flask.render_template("admin/auth.html") + @routes_admin.route("/npc/") @require_admin(ui=True) def npc(npc_id): diff --git a/truthinquiry/routes/routes_api_admin.py b/truthinquiry/routes/routes_api_admin.py index 1caccbe..0e6e8ff 100644 --- a/truthinquiry/routes/routes_api_admin.py +++ b/truthinquiry/routes/routes_api_admin.py @@ -1,3 +1,5 @@ +import os + import flask from sqlalchemy import select, delete, or_ @@ -8,6 +10,15 @@ from truthinquiry.utils import require_admin routes_api_admin = flask.Blueprint("api_admin", __name__) +@routes_api_admin.route("/auth", methods=["GET", "POST"]) +def auth(): + password = flask.request.values.get("password") + if password == os.getenv("ADMIN_PASSWORD"): + flask.session["admin"] = True + return flask.redirect("/admin") + else: + return flask.redirect("/admin/auth?failed=1") + @routes_api_admin.route("/setQuestions", methods=["GET", "POST"]) @require_admin(api=True) def set_questions(): diff --git a/truthinquiry/templates/admin/auth.html b/truthinquiry/templates/admin/auth.html new file mode 100644 index 0000000..122bfa8 --- /dev/null +++ b/truthinquiry/templates/admin/auth.html @@ -0,0 +1,5 @@ +
+

Password :

+ + +
\ No newline at end of file From 5c62e0bd23cbc6cde5c01d1f0028a9b4930264a0 Mon Sep 17 00:00:00 2001 From: Thomas Rubini <74205383+ThomasRubini@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:19:36 +0200 Subject: [PATCH 12/12] Show message when password is invalid --- truthinquiry/routes/routes_admin.py | 3 ++- truthinquiry/templates/admin/auth.html | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/truthinquiry/routes/routes_admin.py b/truthinquiry/routes/routes_admin.py index 8b831a7..a4d23b7 100644 --- a/truthinquiry/routes/routes_admin.py +++ b/truthinquiry/routes/routes_admin.py @@ -19,7 +19,8 @@ def index(): @routes_admin.route("/auth") def auth(): - return flask.render_template("admin/auth.html") + input_failed = bool(flask.request.values.get("failed")) + return flask.render_template("admin/auth.html", failed=input_failed) @routes_admin.route("/npc/") @require_admin(ui=True) diff --git a/truthinquiry/templates/admin/auth.html b/truthinquiry/templates/admin/auth.html index 122bfa8..66dd733 100644 --- a/truthinquiry/templates/admin/auth.html +++ b/truthinquiry/templates/admin/auth.html @@ -1,3 +1,7 @@ +{% if failed %} +

Invalid password !

+{% endif %} +

Password :