From ded552f69820697c8a68b7eba3a9b30763511a87 Mon Sep 17 00:00:00 2001 From: Fuchs0101 Date: Mon, 2 Dec 2024 21:14:09 +0900 Subject: [PATCH 01/32] setup development environment with Docker Compose --- backend/Dockerfile | 9 ++++++++ backend/requirements.txt | 3 +++ compose.yml | 45 ++++++++++++++++++++++++++++++++++++++++ frontend/Dockerfile | 8 +++++++ frontend/package.json | 3 +++ 5 files changed, 68 insertions(+) create mode 100644 backend/Dockerfile create mode 100644 backend/requirements.txt create mode 100644 compose.yml create mode 100644 frontend/Dockerfile create mode 100644 frontend/package.json diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..fb25ab0 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.10 + +WORKDIR /app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..a23a866 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.2.5 +boto3==1.26.145 +gunicorn==20.1.0 \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..17fdc1b --- /dev/null +++ b/compose.yml @@ -0,0 +1,45 @@ +services: + frontend: + build: ./frontend + container_name: frontend + ports: + - "3000:3000" + volumes: + - ./frontend:/app + environment: + - NODE_ENV=development + + backend: + build: ./backend + container_name: backend + ports: + - "5000:5000" + volumes: + - ./backend:/app + environment: + - FLASK_ENV=development + + dynamodb: + image: amazon/dynamodb-local + container_name: dynamodb + ports: + - "8000:8000" + volumes: + - dynamodb_data:/home/dynamodblocal/data + + minio: + image: minio/minio + container_name: minio + ports: + - "9000:9000" + - "9001:9001" + environment: + - MINIO_ROOT_USER=miniominio + - MINIO_ROOT_PASSWORD=miniominio + command: server --console-address ":9001" /data + volumes: + - minio_data:/data + +volumes: + dynamodb_data: + minio_data: \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..223797c --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,8 @@ +FROM node:18 + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..544b7b4 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file From e0d2c4541f2eca5eff0257f811de004acd6ff2a2 Mon Sep 17 00:00:00 2001 From: Sairon <138011409+Yukkurisiteikitai@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:26:39 +0900 Subject: [PATCH 02/32] refactor/display-Set --- frontend/game.js | 188 ++++++++++++++++++++++++++++++++++++++++++++ frontend/index.html | 51 ++++++++++++ frontend/style.css | 60 ++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 frontend/game.js create mode 100644 frontend/index.html create mode 100644 frontend/style.css diff --git a/frontend/game.js b/frontend/game.js new file mode 100644 index 0000000..2801f3b --- /dev/null +++ b/frontend/game.js @@ -0,0 +1,188 @@ +document.addEventListener("DOMContentLoaded", () => { + const homeScreen = document.getElementById("home"); + const quizScreen = document.getElementById("quiz"); + const resultScreen = document.getElementById("result"); + const menuScreen = document.getElementById("menu"); + + const startButton = document.getElementById("start-button"); + const createButton = document.getElementById("create-button"); + const playButton = document.getElementById("play-button"); + + const options = document.querySelectorAll(".selects"); + const nextButton = document.querySelector(".next-button"); + const quitButton = document.querySelector(".quit-button"); + + + + const statusText = document.querySelector(".status"); + const answerText = document.querySelector(".answer"); + + // クイズデータ + + + questions = [ + { + "question": "halejfalwjfwfioa", + "options": [ + "hewaj;foewifj;w", + "jfoiea;jfiwfo", + "hoieajf;fjwf", + "hioefj;aefiofe" + ], + "answer": "hfeafjaweo;fiw" + }, + { + "question": "別の質問例", + "options": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"], + "answer": "正解" + } + ] + + function reqListener(){ + console.log(this.responseText); + } + + function LoadRequest(){ + console.log("hello"); + + const req = new XMLHttpRequest(); + // req.addEventListener("load",reqListener); + req.open("GET","http://127.0.0.1:2342/info/2") + // req.send(); + req.responseType = "json"; + req.onload = () => { + if(req.readyState == 4 && req.status == 200){ + const date = req.response; + console.log(date); + return date; + }else{ + console.log(`Error: ${req.status}`); + } + }; + return "Not Found"; + } + + + question = {} + + fetch('http://127.0.0.1:2342/info/2') + .then(res => res.json()) + .then(data => { + console.log(data[0]); + const question = data[0]; + const d = document.createElement('h1'); + d.innerText=question["questiondate"]; + document.body.appendChild(d) + }) + + +displayLoadRequest + //display + function displayLoadRequest(display) + { + /** + * That option discplayis + * @param display (string)第1引数のコメント + * [home],["quiz"],["menu"],["result"] + */ + quizScreen.classList.add("d-none"); + resultScreen.classList.add("d-none"); + menuScreen.classList.add("d-none"); + homeScreen.classList.add("d-none"); + if (display == "home") + { + homeScreen.classList.remove("d-none"); + } + else if (display == "quiz") + { + quizScreen.classList.remove("d-none"); + } + else if (display == "menu") + { + menuScreen.classList.remove("d-none"); + } + else if (display == "result") + { + resultScreen.classList.remove("d-none"); + } + } + + // 正解データ + const correctAnswer = "スイッチ"; + + // クイズ開始 + startButton.addEventListener("click", () => { + /* + homeScreen.classList.add("d-none"); + quizScreen.classList.remove("d-none"); + */ + displayLoadRequest("menu"); + }); + + + + // 回答選択 + options.forEach(option => { + option.addEventListener("click", (event) => { + const selectedAnswer = event.target.textContent; + + // 判定処理 + if (selectedAnswer === correctAnswer) + { + statusText.textContent = "正解"; + statusText.style.color = "#28a745"; + } + else + { + statusText.textContent = "不正解"; + statusText.style.color = "#dc3545"; + } + + answerText.textContent = correctAnswer; // 正解を表示 + + // 結果画面へ移動 + quizScreen.classList.add("d-none"); + resultScreen.classList.remove("d-none"); + }); + }); + + + function ScreenSet(switch_screen,screen) + { + console.log(screen,switch_screen); + if(switch_screen == true) + { + + screen.classList.remove("d-none"); + } + else if(switch_screen == false) + { + screen.classList.add("d-none"); + } + else + { + console.error("ScreenDon't set switch_screen"); + } + console.log(screen,switch_screen); + + } + + // 次の問題へ(ここでは単純にホーム画面に戻る処理) + nextButton.addEventListener("click", () => { + // resultScreen.classList.add("d-none"); + // menuScreen.classList.add("d-none"); + // homeScreen.classList.remove("d-none"); + ScreenSet(false,resultScreen); + ScreenSet(false,menuScreen); + ScreenSet(true,homeScreen); + }); + + // やめる(ホーム画面に戻る) + quitButton.addEventListener("click", () => { + resultScreen.classList.add("d-none"); + homeScreen.classList.remove("d-none"); + }); + + + }); + diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..afdbfae --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,51 @@ + + + + + + クイズアプリ + + + + + +
+

ミニラン

+ +
+ + + + + +
+

クイズ

+

任天堂・・

+
+ + + + +
+
+ + +
+

正解

+

スイッチ

+
+ + +
+
+ + + + diff --git a/frontend/style.css b/frontend/style.css new file mode 100644 index 0000000..2c90a81 --- /dev/null +++ b/frontend/style.css @@ -0,0 +1,60 @@ +body { + font-family: 'Arial', sans-serif; + background-color: #f8f9fa; + margin: 0; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + } + + .container { + max-width: 500px; + } + + .logo { + font-size: 2.5rem; + font-weight: bold; + margin-bottom: 20px; + } + + .title { + font-size: 1.8rem; + margin-bottom: 10px; + } + + .question { + font-size: 1.5rem; + margin: 20px 0; + } + + .question.h2{ + color: black; + } + + .options .btn { + width: 100%; + margin: 5px 0; + } + + .status { + font-size: 1.2rem; + color: #28a745; + } + + .answer { + font-size: 2rem; + font-weight: bold; + margin: 20px 0; + } + + .actions .btn { + width: 100%; + margin: 5px 0; + } + + .d-none { + display: none; + } + \ No newline at end of file From c295a6b98389a81928eecdc521495389e0232271 Mon Sep 17 00:00:00 2001 From: Sairon <138011409+Yukkurisiteikitai@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:15:12 +0900 Subject: [PATCH 03/32] set GameMenu&GameKind --- frontend/game.js | 122 ++++++++++++++++++++++++-------------------- frontend/index.html | 14 +++-- 2 files changed, 78 insertions(+), 58 deletions(-) diff --git a/frontend/game.js b/frontend/game.js index 2801f3b..1389ae4 100644 --- a/frontend/game.js +++ b/frontend/game.js @@ -2,23 +2,29 @@ document.addEventListener("DOMContentLoaded", () => { const homeScreen = document.getElementById("home"); const quizScreen = document.getElementById("quiz"); const resultScreen = document.getElementById("result"); - const menuScreen = document.getElementById("menu"); + const gameModeScreen = document.getElementById("game-mode"); + const selectGameScreen = document.getElementById("SelctGameKind"); + const startButton = document.getElementById("start-button"); const createButton = document.getElementById("create-button"); const playButton = document.getElementById("play-button"); + + const options = document.querySelectorAll(".selects"); const nextButton = document.querySelector(".next-button"); const quitButton = document.querySelector(".quit-button"); + const gameKindOptions = document.querySelector(".gamekind-option"); + // const gameModeOptions = document.querySelector(".gamemodes-option"); const statusText = document.querySelector(".status"); const answerText = document.querySelector(".answer"); - // クイズデータ - + // ゲームデータ + let gameMode = "play"; questions = [ { @@ -38,56 +44,22 @@ document.addEventListener("DOMContentLoaded", () => { } ] - function reqListener(){ - console.log(this.responseText); - } - - function LoadRequest(){ - console.log("hello"); - - const req = new XMLHttpRequest(); - // req.addEventListener("load",reqListener); - req.open("GET","http://127.0.0.1:2342/info/2") - // req.send(); - req.responseType = "json"; - req.onload = () => { - if(req.readyState == 4 && req.status == 200){ - const date = req.response; - console.log(date); - return date; - }else{ - console.log(`Error: ${req.status}`); - } - }; - return "Not Found"; - } - - question = {} - - fetch('http://127.0.0.1:2342/info/2') - .then(res => res.json()) - .then(data => { - console.log(data[0]); - const question = data[0]; - const d = document.createElement('h1'); - d.innerText=question["questiondate"]; - document.body.appendChild(d) - }) - + -displayLoadRequest - //display + /** + * That option discplayis + * @param display (string)この引数にhome,quiz,result,game_select,game_modeなどをstringで入れると、 + * その名前のdisplayを表示する + */ function displayLoadRequest(display) { - /** - * That option discplayis - * @param display (string)第1引数のコメント - * [home],["quiz"],["menu"],["result"] - */ + + gameModeScreen.classList.add("d-none"); + selectGameScreen.classList.add("d-none"); quizScreen.classList.add("d-none"); resultScreen.classList.add("d-none"); - menuScreen.classList.add("d-none"); + // menuScreen.classList.add("d-none"); homeScreen.classList.add("d-none"); if (display == "home") { @@ -97,9 +69,13 @@ displayLoadRequest { quizScreen.classList.remove("d-none"); } - else if (display == "menu") + else if (display == "game_select") { - menuScreen.classList.remove("d-none"); + selectGameScreen.classList.remove("d-none"); + } + else if (display == "game_mode") + { + gameModeScreen.classList.remove("d-none"); } else if (display == "result") { @@ -107,18 +83,53 @@ displayLoadRequest } } + displayLoadRequest("home"); // 正解データ const correctAnswer = "スイッチ"; - - // クイズ開始 + + // ゲームモードへ行く startButton.addEventListener("click", () => { /* homeScreen.classList.add("d-none"); quizScreen.classList.remove("d-none"); */ - displayLoadRequest("menu"); + console.log("start"); + displayLoadRequest("game_mode"); + }); + + playButton.addEventListener("click",()=>{ + displayLoadRequest("game_select"); }); + //gameMode + options.forEach(option => { + option.addEventListener("click", (event) => { + const selectedAnswer = event.target.textContent; + + // 判定処理 + if (selectedAnswer === correctAnswer) + { + statusText.textContent = "正解"; + statusText.style.color = "#28a745"; + } + else + { + statusText.textContent = "不正解"; + statusText.style.color = "#dc3545"; + } + + answerText.textContent = correctAnswer; // 正解を表示 + + // 結果画面へ移動 + /* + quizScreen.classList.add("d-none"); + resultScreen.classList.remove("d-none"); + */ + displayLoadRequest("result"); + }); + }); + + // 回答選択 @@ -141,8 +152,11 @@ displayLoadRequest answerText.textContent = correctAnswer; // 正解を表示 // 結果画面へ移動 + /* quizScreen.classList.add("d-none"); resultScreen.classList.remove("d-none"); + */ + displayLoadRequest("result"); }); }); @@ -172,9 +186,7 @@ displayLoadRequest // resultScreen.classList.add("d-none"); // menuScreen.classList.add("d-none"); // homeScreen.classList.remove("d-none"); - ScreenSet(false,resultScreen); - ScreenSet(false,menuScreen); - ScreenSet(true,homeScreen); + displayLoadRequest("home"); }); // やめる(ホーム画面に戻る) diff --git a/frontend/index.html b/frontend/index.html index afdbfae..af266c7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -14,9 +14,17 @@

ミニラン

- - - -
-

クイズ

-

任天堂・・

-
- - - - + + + +
+ + +
+

間違い探し

+
+

05/??

+ + + + +
+ + +
+

クイズ

+

任天堂・・

+
+ + + + +
+
+ + +
+

制限時間は10秒!5回まで答えられるよ!

+
+

残り時間: 10

+

解答回数: 0/5

+ +
+ + +
+ + + + +
+
+ + + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+
+

間違い探し

+
+ 画像1を選択 + 画像2を選択 +
+
+ +
+

クイズ

+
+ +
- -
-

正解

-

スイッチ

-
- -
- + + + + + + + diff --git a/make.css b/make.css new file mode 100644 index 0000000..5ca278a --- /dev/null +++ b/make.css @@ -0,0 +1,98 @@ +/* 画面全体の中央揃え */ +body { + font-family: Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; + background-color: #f0f0f0; +} + +#quiz-container { + text-align: center; + color: #333; + width: 100%; + max-width: 500px; /* 適切な幅を指定 */ +} + +#info-text { + font-size: 18px; + font-weight: bold; + margin-bottom: 20px; + color: #ff4081; +} + +button { + padding: 10px 20px; + margin: 10px 0; /* 垂直方向の間隔を調整 */ + font-size: 16px; + cursor: pointer; + border-radius: 5px; + border: none; + transition: background-color 0.3s ease; +} + +#start-button { + background-color: #ff4081; + color: white; +} + +.answer-button { + background-color: #4caf50; + color: white; +} + +button.disabled { + background-color: #ccc; + cursor: not-allowed; +} + +#answer-input { + padding: 10px; + font-size: 16px; + border-radius: 5px; + border: 1px solid #ddd; + margin-top: 10px; + width: calc(100% - 40px); /* 入力フィールドの幅調整 */ + box-sizing: border-box; + display: none; +} + +#submit-answer-button { + background-color: #00bcd4; /* 水色 */ + color: white; + padding: 10px 20px; + font-size: 16px; + border-radius: 5px; + border: none; + cursor: pointer; + margin-top: 10px; +} + +button.disabled, +#answer-input.disabled { + background-color: #ccc; /* 灰色 */ + cursor: not-allowed; + color: #666; +} + +#next-button { + background-color: #ff9800; /* オレンジ */ + color: white; + margin-top: 20px; +} + +#result-message { + font-size: 100px; + font-weight: bold; + margin: 20px; +} + +.correct { + color: blue; +} + +.incorrect { + color: red; +} \ No newline at end of file diff --git a/play.css b/play.css new file mode 100644 index 0000000..e69de29 diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 0000000..e69de29 From e5690cb9e5261c33932fc238026f796476c95170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E8=A5=BF=E5=93=B2=E5=8F=B8?= Date: Sat, 7 Dec 2024 13:54:20 +0900 Subject: [PATCH 22/32] create_intro --- backend/controller/game_controller.py | 29 +++++++++++++++++++++++---- backend/engine/dynamo_client.py | 12 ++++++++--- backend/router.py | 10 +++++++++ compose.yml | 2 +- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/backend/controller/game_controller.py b/backend/controller/game_controller.py index a5d2730..59e85d2 100644 --- a/backend/controller/game_controller.py +++ b/backend/controller/game_controller.py @@ -12,12 +12,17 @@ def get_game_random(self): 'game': response })) + def create_quiz(self): + # 必要な処理をここに記述 + return {"message": "Intro created successfully"} + def create_quiz(self): # リクエストデータを取得 data = request.get_json() question = data['questions'] selects = data['selects'] - answer = data['answer'] + answer = data['answer_idx'] + id = data['id'] # データベースを見て次のidを取得する # バリデーションを実装 if not data or 'quiz_name' not in data or 'questions' not in data: @@ -42,9 +47,20 @@ def create_quiz(self): def create_intro(self): data = request.get_json() - if not data or 'intro_name' not in data or 'intro_text' not in data: - return make_response(jsonify({'code': 400, 'message': 'Invalid data'}), 400) - + music_title_answer = data['music_title_answer'] + music_data = data['music_data']#musicのデータ本体 + id = data['id'] # データベースを見て次のidを取得する + + # バリデーションを実装 + #if not data or 'intro_name' not in data or 'intro_text' not in data: + # return make_response(jsonify({'code': 400, 'message': 'Invalid data'}), 400) + + if not isinstance(music_title_answer, str) or not music_title_answer.strip(): + return make_response(jsonify({'code': 400, 'message': 'Invalid music_title_answer'}), 400) + if not music_data: + return make_response(jsonify({'code': 400, 'message': 'Invalid music_data'}), 400) + + # DBに保存 try: if self.db_client.create_intro(data): return make_response(jsonify({'code': 200, 'message': 'Intro created successfully'}), 200) @@ -52,3 +68,8 @@ def create_intro(self): return make_response(jsonify({'code': 500, 'message': 'Failed to create intro'}), 500) except Exception as e: return make_response(jsonify({'code': 500, 'message': str(e)}), 500) + + def create_diffshot(self): + data = request.get_json() + music_title_answer = data['music_title_answer'] + music_data = data['music_data'] \ No newline at end of file diff --git a/backend/engine/dynamo_client.py b/backend/engine/dynamo_client.py index b4544a4..271aad0 100644 --- a/backend/engine/dynamo_client.py +++ b/backend/engine/dynamo_client.py @@ -28,9 +28,15 @@ def create_quiz(self, data: dict) -> None: ''' client = boto3.client('dynamodb') # ここで良いかは不明 response = client.put_item(TableName=self.table_name, Item={ - 'quiz_name': {'S': data['quiz_name']}, # quiz_nameではなくidか 'questions': {'S': data['questions']}, 'selects': {'L': [{'S': s} for s in data['selects']]}, - 'answer': {'N': str(data['answer'])} + 'answer_idx': {'N': str(data['answer'])}, + 'likes': {'N': '0'} }) - return True if response['ResponseMetadata']['HTTPStatusCode'] == 200 else False \ No newline at end of file + return True if response['ResponseMetadata']['HTTPStatusCode'] == 200 else False + + def create_intro(self, data: dict) -> None: + '''対象のテーブルにイントロデータを登録するメソッド + ''' + client = boto3.client('dynamodb') + \ No newline at end of file diff --git a/backend/router.py b/backend/router.py index 679179c..ba9b227 100644 --- a/backend/router.py +++ b/backend/router.py @@ -26,6 +26,16 @@ def api_play_random(): def api_create_quiz(): return game_controller.create_quiz() +@router.route("/api/create/intro", methods=['POST']) +@logger.http_request_logging +def api_create_intro(): + return game_controller.create_intro() + +@router.route("/api/create/diffshot", methods=['POST']) +@logger.http_request_logging +def api_create_diffshot(): + return game_controller.create_diffshot() + @router.after_request def after_request(response): # response.headers.add('Access-Control-Allow-Origin', '*') diff --git a/compose.yml b/compose.yml index 3cae6f6..5d99fb4 100644 --- a/compose.yml +++ b/compose.yml @@ -15,7 +15,7 @@ services: build: ./backend container_name: backend ports: - - "5001:5001" + - "5001:5000" volumes: - ./backend:/app env_file: From 856a18055ec3cf2611241921d8404e2e08e2824b Mon Sep 17 00:00:00 2001 From: nozo120 Date: Sat, 7 Dec 2024 14:24:56 +0900 Subject: [PATCH 23/32] =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AE=E5=A4=89=E6=9B=B4=E3=82=92?= =?UTF-8?q?=E5=8A=A0=E3=81=88=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 2 +- index2.html | 9 +- merged_index.html | 51 ++++------- merged_script.js | 224 ++++++++++++++++++++++++++++++++++++++++++++++ merged_style.css | 118 ++++++++++++++++++++++++ style2.css | 1 + 6 files changed, 367 insertions(+), 38 deletions(-) create mode 100644 merged_script.js create mode 100644 merged_style.css diff --git a/index.html b/index.html index 63cb66c..a4af0a7 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ -
+

制限時間は10秒!5回まで答えられるよ!

残り時間: 10

diff --git a/index2.html b/index2.html index 44a0389..46e7271 100644 --- a/index2.html +++ b/index2.html @@ -5,7 +5,10 @@ イントロドン作成 - + + + +
@@ -30,8 +33,10 @@

イントロドン作成

- + + + diff --git a/merged_index.html b/merged_index.html index aec47ae..c43f825 100644 --- a/merged_index.html +++ b/merged_index.html @@ -4,20 +4,18 @@ イントロドン - - イントロドン作成 - + -
@@ -111,15 +109,26 @@

間違い探し

クイズ

- +
+

質問

+ + +

選択肢

+ + + + + +

回答

+ +
-
- +
diff --git a/frontend/style.css b/frontend/style.css index 2c90a81..4f32952 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -57,4 +57,104 @@ body { .d-none { display: none; } - \ No newline at end of file + + + body { + font-family: Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; + background-color: #f0f0f0; +} + +#quiz-container { + text-align: center; + color: #333; + width: 100%; + max-width: 500px; /* 適切な幅を指定 */ +} + +#info-text { + font-size: 18px; + font-weight: bold; + margin-bottom: 20px; + color: #ff4081; +} + +button { + width: 120px; + height: 45px; + padding: 10px 20px; + margin: 10px 0; /* 垂直方向の間隔を調整 */ + font-size: 16px; + cursor: pointer; + border-radius: 5px; + border: none; + transition: background-color 0.3s ease; +} + +#start-button { + background-color: #ff4081; + color: white; +} + +.answer-button { + background-color: #4caf50; + color: white; +} + +button.disabled { + background-color: #ccc; + cursor: not-allowed; +} + +#answer-input { + padding: 10px; + font-size: 16px; + border-radius: 5px; + border: 1px solid #ddd; + margin-top: 10px; + width: calc(100% - 40px); /* 入力フィールドの幅調整 */ + box-sizing: border-box; + display: none; +} + +#submit-answer-button { + background-color: #00bcd4; /* 水色 */ + color: white; + padding: 10px 20px; + font-size: 16px; + border-radius: 5px; + border: none; + cursor: pointer; + margin-top: 10px; +} + +button.disabled, +#answer-input.disabled { + background-color: #ccc; /* 灰色 */ + cursor: not-allowed; + color: #666; +} + +#next-button { + background-color: #ff9800; /* オレンジ */ + color: white; + margin-top: 20px; +} + +#result-message { + font-size: 100px; + font-weight: bold; + margin: 20px; +} + +.correct { + color: blue; +} + +.incorrect { + color: red; +} From 90984cb756e3d93203df3e2ff5ddf9e1f0c34e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E8=A5=BF=E5=93=B2=E5=8F=B8?= Date: Sat, 7 Dec 2024 16:13:25 +0900 Subject: [PATCH 26/32] create_quiz --- backend/controller/game_controller.py | 72 ++++++++++++++++++++------- backend/engine/dynamo_client.py | 18 ++++++- backend/router.py | 22 ++++++-- 3 files changed, 89 insertions(+), 23 deletions(-) diff --git a/backend/controller/game_controller.py b/backend/controller/game_controller.py index 59e85d2..3474ae4 100644 --- a/backend/controller/game_controller.py +++ b/backend/controller/game_controller.py @@ -1,5 +1,10 @@ from flask import make_response, jsonify, request from engine.dynamo_client import DynamoClient +import hashlib +#from werkzeug.utils import secure_filename +#from dynamo_client import save_file_metadata +#from s3_client import upload_to_s3 +#from dotenv import load_dotenv class GameController: def __init__(self): @@ -12,22 +17,16 @@ def get_game_random(self): 'game': response })) - def create_quiz(self): - # 必要な処理をここに記述 - return {"message": "Intro created successfully"} - def create_quiz(self): # リクエストデータを取得 data = request.get_json() - question = data['questions'] + question = data['question'] selects = data['selects'] - answer = data['answer_idx'] - id = data['id'] # データベースを見て次のidを取得する + answer = data['answer'] + id = 1 # データベースを見て次のidを取得する + data['id'] = id # バリデーションを実装 - if not data or 'quiz_name' not in data or 'questions' not in data: - return make_response(jsonify({'code': 400, 'message': 'Invalid data'}), 400) - if not isinstance(question, str) or not question.strip(): return make_response(jsonify({'code': 400, 'message': 'Invalid question'}), 400) if not isinstance(selects, list) or len(selects) != 4: @@ -48,13 +47,32 @@ def create_quiz(self): def create_intro(self): data = request.get_json() music_title_answer = data['music_title_answer'] - music_data = data['music_data']#musicのデータ本体 - id = data['id'] # データベースを見て次のidを取得する + #music_data = data['music_data'] # musicのデータ本体 + id = 1 # データベースを見て次のidを取得する + data['id'] = id + + if 'file' not in request.files: + return jsonify({"error": "No file part"}), 400 + + file = request.files['file'] + if file.filename == '': + return jsonify({"error": "No selected file"}), 400 + # ファイル名を安全にする + filename = secure_filename(file.filename) + # ファイル名のハッシュを生成 + file_hash = hashlib.sha256(filename.encode('utf-8')).hexdigest() + + try: + # S3にファイルをアップロード + upload_to_s3(file, file_hash, file.content_type) + # DynamoDBにメタデータを保存 + save_file_metadata(file_hash, filename) + return jsonify({"message": "File uploaded successfully", "file_hash": file_hash}), 200 + except Exception as e: + return jsonify({"error": str(e)}), 500 - # バリデーションを実装 - #if not data or 'intro_name' not in data or 'intro_text' not in data: - # return make_response(jsonify({'code': 400, 'message': 'Invalid data'}), 400) + # バリデーションを実装 if not isinstance(music_title_answer, str) or not music_title_answer.strip(): return make_response(jsonify({'code': 400, 'message': 'Invalid music_title_answer'}), 400) if not music_data: @@ -71,5 +89,25 @@ def create_intro(self): def create_diffshot(self): data = request.get_json() - music_title_answer = data['music_title_answer'] - music_data = data['music_data'] \ No newline at end of file + picture_data = data['picture_data'] + picture_data_answer = data['picture_data_answer'] + answer_points = data['answer_points'] + id = 1 # データベースを見て次のidを取得する + data['id'] = id + + # バリデーションを実装 + if not isinstance(picture_data, str) or not music_title_answer.strip(): + return make_response(jsonify({'code': 400, 'message': 'Invalid music_title_answer'}), 400) + if not music_data: + return make_response(jsonify({'code': 400, 'message': 'Invalid music_data'}), 400) + + + # DBに保存 + try: + if self.db_client.create_diffshot(data): + return make_response(jsonify({'code': 200, 'message': 'Diffshot created successfully'}), 200) + else: + return make_response(jsonify({'code': 500, 'message': 'Failed to create diffshot'}), 500) + except Exception as e: + return make_response(jsonify({'code': 500, 'message': str(e)}), 500) + \ No newline at end of file diff --git a/backend/engine/dynamo_client.py b/backend/engine/dynamo_client.py index 271aad0..aab9846 100644 --- a/backend/engine/dynamo_client.py +++ b/backend/engine/dynamo_client.py @@ -6,6 +6,9 @@ class DynamoClient: def __init__(self, table_name: str) -> None: self.db = boto3.client('dynamodb', endpoint_url=os.getenv("DYNAMO_ENDPOINT")) self.table_name = table_name + #self.db = boto3.resource('dynamodb', region_name=REGION_NAME) + #self.table_name = table_name or DYNAMODB_TABLE + #self.table = self.db.Table(self.table_name) def get_all(self) -> list: '''対象のテーブルからレコード全件を取得するメソッド @@ -28,7 +31,8 @@ def create_quiz(self, data: dict) -> None: ''' client = boto3.client('dynamodb') # ここで良いかは不明 response = client.put_item(TableName=self.table_name, Item={ - 'questions': {'S': data['questions']}, + 'id': {'N': str(data['id'])}, + 'question': {'S': data['question']}, 'selects': {'L': [{'S': s} for s in data['selects']]}, 'answer_idx': {'N': str(data['answer'])}, 'likes': {'N': '0'} @@ -39,4 +43,16 @@ def create_intro(self, data: dict) -> None: '''対象のテーブルにイントロデータを登録するメソッド ''' client = boto3.client('dynamodb') + response = client.put_item(TableName=self.table_name, Item={ + 'id': {'N': str(data['id'])}, + 'music_title_answer': {'S': data['music_title_answer']}, + 'music_data': {'B': data['music_data']}, + 'likes': {'N': '0'} + }) + + def create_diffshot(self, data: dict) -> None: + '''対象のテーブルにディフショットデータを登録するメソッド + ''' + client = boto3.client('dynamodb') + \ No newline at end of file diff --git a/backend/router.py b/backend/router.py index ba9b227..88abe63 100644 --- a/backend/router.py +++ b/backend/router.py @@ -1,11 +1,23 @@ from flask import Blueprint -from controller import game_controller +from controller.game_controller import GameController from logging import config from json import load import logger +import boto3 + +## クライアントの初期化 +#s3_client = boto3.client( +# "s3", +# aws_access_key_id=S3_ACCESS_KEY, +# aws_secret_access_key=S3_SECRET_KEY, +# endpoint_url=S3_ENDPOINT, # MinIOの場合 +#) +#dynamodb = boto3.resource("dynamodb", region_name=REGION_NAME) +#dynamodb_table = dynamodb.Table(DYNAMODB_TABLE) # Generate Router Instance router = Blueprint('router', __name__) +controller = GameController() # Read Logging Configuration with open("./config/logging.json", "r", encoding="utf-8") as f: @@ -19,22 +31,22 @@ def hello_world(): @router.route("/api/play/random", methods=['GET']) @logger.http_request_logging def api_play_random(): - return game_controller.get_game_random() + return controller.get_game_random() @router.route("/api/create/quiz", methods=['POST']) @logger.http_request_logging def api_create_quiz(): - return game_controller.create_quiz() + return controller.create_quiz() @router.route("/api/create/intro", methods=['POST']) @logger.http_request_logging def api_create_intro(): - return game_controller.create_intro() + return controller.create_intro() @router.route("/api/create/diffshot", methods=['POST']) @logger.http_request_logging def api_create_diffshot(): - return game_controller.create_diffshot() + return controller.create_diffshot() @router.after_request def after_request(response): From 547dc385597e73934a62ee33ef4360163b2c7d0a Mon Sep 17 00:00:00 2001 From: Fuchs0101 Date: Sat, 7 Dec 2024 16:17:30 +0900 Subject: [PATCH 27/32] =?UTF-8?q?DynamoDB=E3=81=A8=E3=81=AE=E7=B9=8B?= =?UTF-8?q?=E3=81=8E=E3=81=93=E3=81=BF=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- backend/controller/game_controller.py | 2 +- backend/engine/dynamo_client.py | 8 ++++---- backend/router.py | 7 ++++--- compose.yml | 4 +--- frontend/Dockerfile | 10 ++-------- 6 files changed, 14 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 0b6b20a..cb52990 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # log files -backend/logs +*.log.* +*.log # environment informations and credentials *.env diff --git a/backend/controller/game_controller.py b/backend/controller/game_controller.py index 00db5ef..24ecda9 100644 --- a/backend/controller/game_controller.py +++ b/backend/controller/game_controller.py @@ -3,7 +3,7 @@ class GameController: def __init__(self): - self.db_client = DynamoClient(table_name="game_table") + self.db_client = DynamoClient(table_name="minirun_game_table") def get_game_random(self): response = self.db_client.get_random() diff --git a/backend/engine/dynamo_client.py b/backend/engine/dynamo_client.py index 624e13f..2b7a0b8 100644 --- a/backend/engine/dynamo_client.py +++ b/backend/engine/dynamo_client.py @@ -4,7 +4,7 @@ class DynamoClient: def __init__(self, table_name: str) -> None: - self.db = boto3.client('dynamodb', endpoint_url=os.getenv("DYNAMO_ENDPOINT")) + self.db = boto3.client('dynamodb') self.table_name = table_name def get_all(self) -> list: @@ -18,7 +18,7 @@ def get_random(self) -> None: '''対象のテーブルからランダムに1件レコードを取得するメソッド ''' response_all = self.db.scan(TableName=self.table_name) - response_len = response_all['Count'] - random_key = random.randrange(response_len) + valid_keys = [item['id']['N'] for item in response_all['Items']] + random_key = random.choice(valid_keys) response = self.db.get_item(TableName=self.table_name, Key={"id": {"N": str(random_key)}}) - return response.get('Item', []) + return response.get('Item') diff --git a/backend/router.py b/backend/router.py index 679179c..9a4aaab 100644 --- a/backend/router.py +++ b/backend/router.py @@ -1,11 +1,12 @@ from flask import Blueprint -from controller import game_controller +from controller.game_controller import GameController from logging import config from json import load import logger # Generate Router Instance router = Blueprint('router', __name__) +controller = GameController() # Read Logging Configuration with open("./config/logging.json", "r", encoding="utf-8") as f: @@ -19,12 +20,12 @@ def hello_world(): @router.route("/api/play/random", methods=['GET']) @logger.http_request_logging def api_play_random(): - return game_controller.get_game_random() + return controller.get_game_random() @router.route("/api/create/quiz", methods=['POST']) @logger.http_request_logging def api_create_quiz(): - return game_controller.create_quiz() + return controller.create_quiz() @router.after_request def after_request(response): diff --git a/compose.yml b/compose.yml index b6a7552..22f3440 100644 --- a/compose.yml +++ b/compose.yml @@ -3,11 +3,9 @@ services: build: ./frontend container_name: frontend ports: - - "3000:3000" + - "8080:80" volumes: - ./frontend:/app - environment: - - NODE_ENV=development depends_on: - backend diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 223797c..6e8acef 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,8 +1,2 @@ -FROM node:18 - -WORKDIR /app - -COPY package*.json ./ -RUN npm install - -COPY . . +FROM nginx:alpine +COPY . /usr/share/nginx/html From dd61c715de570523ca37a3acea0293dee1c07265 Mon Sep 17 00:00:00 2001 From: nozo120 Date: Sat, 7 Dec 2024 16:18:51 +0900 Subject: [PATCH 28/32] =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AE=E5=A4=89=E6=9B=B4=E3=82=92?= =?UTF-8?q?=E5=8A=A0=E3=81=88=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index2.html | 2 +- index3.html | 32 ++++++++++++++++++++++++++++++++ merged_index.html | 9 +++++++++ merged_script.js | 38 ++++++++++++++++++++++++++++++++++---- script3.js | 22 ++++++++++++++++++++++ style3.css | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 index3.html create mode 100644 script3.js create mode 100644 style3.css diff --git a/index2.html b/index2.html index 46e7271..1a9188c 100644 --- a/index2.html +++ b/index2.html @@ -8,7 +8,7 @@ - +
diff --git a/index3.html b/index3.html new file mode 100644 index 0000000..5c57f6b --- /dev/null +++ b/index3.html @@ -0,0 +1,32 @@ + + + + + + ボタンで画面遷移 + + + +
+

ボタンで画面遷移

+
+ + + +
+
+ + + +
+
+ + + + diff --git a/merged_index.html b/merged_index.html index c43f825..604a7ef 100644 --- a/merged_index.html +++ b/merged_index.html @@ -11,6 +11,15 @@ + +
+ -
- - -
- - - - -
-
- - - - diff --git a/index2.html b/index2.html deleted file mode 100644 index 1a9188c..0000000 --- a/index2.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - イントロドン作成 - - - - - - -
-

イントロドン作成

- - - - - -

- - - - - -

- - - - - - -
- - - - - - - - diff --git a/script.js b/script.js deleted file mode 100644 index d0dec9c..0000000 --- a/script.js +++ /dev/null @@ -1,147 +0,0 @@ - -document.addEventListener('DOMContentLoaded', function () { - const startButton = document.getElementById('start-button'); - const submitButton = document.getElementById('submit-answer-button'); - const resultMessage = document.getElementById('result-message'); - const nextButton = document.getElementById('next-button'); - const answerInput = document.getElementById('answer-input'); - const timeLeftElement = document.getElementById('time-left'); - const answerCountElement = document.getElementById('answer-count'); - const maxAnswerCountElement = document.getElementById('max-answer-count'); - const infoText = document.getElementById('info-text'); - const button1 = document.getElementById('button1'); - const button2 = document.getElementById('button2'); - - const audio = new Audio('your-music-file.mp3'); - let timer; - let timeRemaining = 10; - let answerCount = 0; - const maxAnswerCount = 5; - let correctAnswer = "曲名"; - let quizFinished = false; - let answerEnabled = false; - - // 初期設定 - maxAnswerCountElement.textContent = maxAnswerCount; - answerInput.style.display = 'none'; - submitButton.style.display = 'none'; - submitButton.disabled = true; - - - startButton.addEventListener('click', function () { - audio.play(); - startButton.disabled = true; - startTimer(); - }); - - function startTimer() { - timer = setInterval(function () { - timeRemaining--; - timeLeftElement.textContent = timeRemaining; - - if (timeRemaining <= 0) { - clearInterval(timer); - endQuiz(false); - } - }, 1000); - } - - function resetButtons() { - button1.disabled = false; - button2.disabled = false; - button1.classList.remove('disabled'); - button2.classList.remove('disabled'); - button1.style.backgroundColor = '#4caf50'; - button2.style.backgroundColor = '#4caf50'; - } - - button1.addEventListener('click', function () { - enableAnswer(); - button1.disabled = true; - button2.classList.add('disabled'); - button2.style.backgroundColor = '#ccc'; - }); - - button2.addEventListener('click', function () { - enableAnswer(); - button2.disabled = true; - button1.classList.add('disabled'); - button1.style.backgroundColor = '#ccc'; - }); - - function enableAnswer() { - answerEnabled = true; - answerInput.style.display = 'block'; - submitButton.style.display = 'block'; - submitButton.disabled = false; - } - - submitButton.addEventListener('click', function () { - if (quizFinished || !answerEnabled) return; - - const userAnswer = answerInput.value.trim(); - answerCount++; - answerCountElement.textContent = answerCount; - - if (userAnswer.toLowerCase() === correctAnswer.toLowerCase()) { - endQuiz(true); - } else if (answerCount >= maxAnswerCount) { - endQuiz(false); - } else { - resultMessage.style.display = 'block'; - resultMessage.textContent = "×"; - resultMessage.className = 'incorrect'; - resetButtons(); // ボタンの状態をリセット - } - }); - - nextButton.addEventListener('click', function () { - window.location.href = 'next-page.html'; - }); - - function endQuiz(isCorrect) { - clearInterval(timer); - quizFinished = true; - - resultMessage.style.display = 'block'; - nextButton.style.display = 'block'; - - if (isCorrect) { - resultMessage.textContent = "〇"; - resultMessage.className = 'correct'; - } else { - resultMessage.textContent = "×"; - resultMessage.className = 'incorrect'; - } - - disableAllInputs(); - - // 解答入力と解答完了ボタンを無効化 - answerInput.disabled = true; - submitButton.disabled = true; - submitButton.classList.add('disabled'); - } - -function disableAllInputs() { - document.querySelectorAll('.answer-button').forEach(button => { - button.disabled = true; - button.classList.add('disabled'); - }); - - // 解答入力フィールドと解答完了ボタンを無効化 - answerInput.disabled = true; - submitButton.disabled = true; - submitButton.classList.add('disabled'); -} - -function enableAllInputs() { - document.querySelectorAll('.answer-button').forEach(button => { - button.disabled = false; - button.classList.remove('disabled'); - }); - // 解答完了ボタンも有効化 - submitButton.disabled = false; - submitButton.classList.remove('disabled'); - answerInput.disabled = false; -} -}) \ No newline at end of file diff --git a/script2.js b/script2.js deleted file mode 100644 index 0cfa32d..0000000 --- a/script2.js +++ /dev/null @@ -1,61 +0,0 @@ -document.getElementById("create-button").addEventListener("click", function () { - const audioFile = document.getElementById("audio-upload").files[0]; - const answer = document.getElementById("answer-input").value; - - // 音声ファイルがない場合 - if (!audioFile) { - alert("音声ファイルを入力してください!"); - return; - } - - // 答えがない場合 - if (!answer) { - alert("答えを入力してください!"); - return; - } - - - // フォームデータの作成 - const formData = new FormData(); - formData.append("audio", audioFile); - formData.append("answer", answer); - - // データをバックエンドに送信 - fetch("YOUR_BACKEND_URL", { - method: "POST", - body: formData - }) - - - .then((response) => { - if (response.ok) { - document.getElementById("status-message").style.display = "block"; - } else { - alert("エラーが発生しました。再試行してください。"); - } - }) - .catch((error) => { - console.error("通信エラー:", error); - alert("通信エラーが発生しました。"); - }); -document.getElementById("audio-upload").addEventListener("change", toggleCreateButton); -document.getElementById("answer-input").addEventListener("input", toggleCreateButton); - -function toggleCreateButton() { - const audioFile = document.getElementById("audio-upload").files[0]; - const answer = document.getElementById("answer-input").value; - - const createButton = document.getElementById("create-button"); - - // 音声ファイルと答えが両方入力されている場合はボタンを有効化 - if (audioFile && answer) { - createButton.disabled = false; - } else { - createButton.disabled = true; - } -} - -// 初期状態でボタンを無効化しておく -toggleCreateButton(); - -}); From ce2fcb781e214961fd337d057ba6cf7515f84ade Mon Sep 17 00:00:00 2001 From: nozo120 Date: Sat, 7 Dec 2024 17:40:47 +0900 Subject: [PATCH 31/32] =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=83=87?= =?UTF-8?q?=E3=82=B0=E3=83=AC=E3=82=92=E8=A7=A3=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- merged_index.html | 6 +----- merged_script.js | 33 ++++++--------------------------- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/merged_index.html b/merged_index.html index 604a7ef..63b5dda 100644 --- a/merged_index.html +++ b/merged_index.html @@ -12,11 +12,7 @@ -
-
-
-

クイズ

-
-
-

質問

- - -

選択肢

- - - - - -

回答

- -
-
+
+

クイズ作成

+
+
+ + +
+
+ + + + + +
+
+ + +
+ +
+ @@ -132,7 +136,7 @@

クイズ

- + diff --git a/index.html b/index.html new file mode 100644 index 0000000..a4af0a7 --- /dev/null +++ b/index.html @@ -0,0 +1,30 @@ + + + + + + + イントロドン + + + +
+

制限時間は10秒!5回まで答えられるよ!

+
+

残り時間: 10

+

解答回数: 0/5

+ +
+ + +
+ + + + +
+
+ + + +