Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import os | |
| from dotenv import load_dotenv | |
| from PIL import Image | |
| import io | |
| import base64 | |
| import requests | |
| from datetime import datetime | |
| from pytz import timezone | |
| import numpy as np | |
| load_dotenv() | |
| # 初回訪問のメッセージ | |
| welcome_message = """ | |
| <h2>このサービスは初めてご利用ですか?</h2> | |
| <p>以下から選択してください。</p> | |
| """ | |
| # Googleフォームの送信URL | |
| apps_script_url = os.environ["APPS_SCRIPT_URL"] | |
| # ローカライズ用辞書 | |
| translations = { | |
| "en": { | |
| "welcome_message": "<h2>Is this your first time using this service?</h2><p>Please select below.</p>", | |
| "visit_choices": ["First time", "Returning"], | |
| "returning_message": "<h2>Thank you for returning!</h2><p>Click the button below to proceed.</p>", | |
| "proceed_button": "Proceed", | |
| "survey_title": "<h2>Please answer the survey</h2><p>Fill out the form below and submit.</p>", | |
| "form": { | |
| "source": "1. Where did you learn about this Space?", | |
| "country": "2. Which country or region do you live in?", | |
| "ai_usage": "3. Have you used AI to generate illustrations?", | |
| "ai_usage_choices": ["Select...", "Frequently", "Sometimes", "Never"], | |
| "drawing_experience": "4. Have you practiced traditional hand-drawn illustrations?", | |
| "drawing_experience_choices": ["Select...", "As a hobby", "For work", "Never"], | |
| "issues": "5. (Optional) Tell us about any challenges you've faced while practicing illustrations.", | |
| "interest": "6. (Optional) What interested you in this Space?", | |
| "contact_info": "7. (Optional) Provide your contact information (e.g., SNS, URL, email)", | |
| "contact_info_placeholder": "e.g., Twitter, portfolio URL, email", | |
| "submit_button": "Submit" | |
| } | |
| }, | |
| "ja": { | |
| "welcome_message": "<h2>このサービスは初めてご利用ですか?</h2><p>以下から選択してください。</p>", | |
| "visit_choices": ["初めて利用する", "2回目以降"], | |
| "returning_message": "<h2>再訪ありがとうございます!</h2><p>以下のボタンをクリックして進んでください。</p>", | |
| "proceed_button": "進む", | |
| "survey_title": "<h2>アンケートにご回答ください</h2><p>以下のフォームに入力して送信してください。</p>", | |
| "form": { | |
| "source": "1. このSpaceをどこで知りましたか?", | |
| "country": "2. お住まいの国や地域を教えてください。", | |
| "ai_usage": "3. 生成AIでイラスト生成をしたことがありますか?", | |
| "ai_usage_choices": ["選択してください...", "よくする", "ある", "ない"], | |
| "drawing_experience": "4. 生成AIではない従来の手描きイラストを練習した経験はありますか?", | |
| "drawing_experience_choices": ["選択してください...", "趣味で", "仕事で", "ない"], | |
| "issues": "5. (任意)イラストの練習中に困った経験があれば教えてください", | |
| "interest": "6. (任意)このSpaceに興味を持った理由を教えてください。", | |
| "contact_info": "7. (任意)連絡先(SNS、URL、メールアドレスなど)を教えてください", | |
| "contact_info_placeholder": "例: Twitterアカウント、ポートフォリオURL、メールアドレスなど", | |
| "submit_button": "送信" | |
| } | |
| }, | |
| "zh": { | |
| "welcome_message": "<h2>这是您第一次使用此服务吗?</h2><p>请从下面选择。</p>", | |
| "visit_choices": ["第一次", "再次访问"], | |
| "returning_message": "<h2>感谢您的再次访问!</h2><p>请点击下面的按钮继续。</p>", | |
| "proceed_button": "继续", | |
| "survey_title": "<h2>请回答问卷</h2><p>填写以下表格并提交。</p>", | |
| "form": { | |
| "source": "1. 您是从哪里得知此服务的?", | |
| "country": "2. 您居住的国家或地区是?", | |
| "ai_usage": "3. 您是否使用过AI生成插图?", | |
| "ai_usage_choices": ["请选择...", "经常", "偶尔", "从未"], | |
| "drawing_experience": "4. 您是否练习过传统手绘插图?", | |
| "drawing_experience_choices": ["请选择...", "作为爱好", "为了工作", "从未"], | |
| "issues": "5. (可选)在练习插图过程中遇到的挑战是什么?", | |
| "interest": "6. (可选)是什么让您对这个Space感兴趣?", | |
| "contact_info": "7. (可选)提供您的联系方式(如:SNS、网址、电子邮件等)", | |
| "contact_info_placeholder": "例如:Twitter、作品集网址、电子邮件", | |
| "submit_button": "提交" | |
| } | |
| } | |
| } | |
| # 言語選択に応じてローカライズ | |
| def localize(language): | |
| t = translations[language] | |
| return { | |
| "welcome_message": t["welcome_message"], | |
| "visit_choices": t["visit_choices"], | |
| "returning_message": t["returning_message"], | |
| "proceed_button": t["proceed_button"], | |
| "form_html": f""" | |
| <h2>{t['survey_title']}</h2> | |
| <form id="survey-form" action="{apps_script_url}" method="POST" target="hidden_iframe"> | |
| <label for="source">{t['form']['source']}</label><br> | |
| <input type="text" name="source" id="source" required><br><br> | |
| <label for="country">{t['form']['country']}</label><br> | |
| <input type="text" name="country" id="country" required><br><br> | |
| <label for="ai_usage">{t['form']['ai_usage']}</label><br> | |
| <select name="ai_usage" id="ai_usage" required> | |
| <option value="" selected disabled>{t['form']['ai_usage_choices'][0]}</option> | |
| {"".join([f'<option value="{choice}">{choice}</option>' for choice in t['form']['ai_usage_choices'][1:]])} | |
| </select><br><br> | |
| <label for="drawing_experience">{t['form']['drawing_experience']}</label><br> | |
| <select name="drawing_experience" id="drawing_experience" required> | |
| <option value="" selected disabled>{t['form']['drawing_experience_choices'][0]}</option> | |
| {"".join([f'<option value="{choice}">{choice}</option>' for choice in t['form']['drawing_experience_choices'][1:]])} | |
| </select><br><br> | |
| <label for="issues">{t['form']['issues']}</label><br> | |
| <textarea name="issues" id="issues"></textarea><br><br> | |
| <label for="interest">{t['form']['interest']}</label><br> | |
| <textarea name="interest" id="interest"></textarea><br><br> | |
| <label for="contact_info">{t['form']['contact_info']}</label><br> | |
| <input type="text" name="contact_info" id="contact_info" placeholder="{t['form']['contact_info_placeholder']}"><br><br> | |
| <button type="submit" id="submit-button">{t['form']['submit_button']}</button> | |
| </form> | |
| <iframe name="hidden_iframe" style="display:none;"></iframe> | |
| """ | |
| } | |
| # 初回訪問の選択肢に応じた処理 | |
| def handle_visit_choice(choice, language): | |
| if choice == localize(language)["visit_choices"][0]: | |
| return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False) | |
| else: | |
| return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True) | |
| # フォーム送信後の処理 | |
| def handle_form_submission(flag_value): | |
| print("form submitted") | |
| return gr.update(visible=False), gr.update(visible=True) | |
| # 進むボタンを押した後の処理 | |
| def handle_proceed(): | |
| print("more than once") | |
| return gr.update(visible=False), gr.update(visible=True) | |
| script = """ | |
| () => { | |
| function attachFormListener() { | |
| const form = document.getElementById("survey-form"); | |
| if (form) { | |
| function submitForm() { | |
| console.log('form submitted'); | |
| const flagInput = document.querySelector("#form_flag textarea"); | |
| flagInput.value = 'true'; | |
| flagInput.dispatchEvent(new Event('input')); | |
| } | |
| // イベントを削除 | |
| form.removeEventListener("submit", submitForm); | |
| // イベントを追加 | |
| form.addEventListener("submit", submitForm); | |
| } | |
| } | |
| // 初期ロード時にイベントリスナーを設定 | |
| attachFormListener(); | |
| // DOMが動的に更新された場合にもイベントリスナーを再設定 | |
| const observer = new MutationObserver(() => { | |
| attachFormListener(); | |
| }); | |
| observer.observe(document.body, { childList: true, subtree: true }); | |
| } | |
| """ | |
| # Google Apps ScriptのURL | |
| feedback_script_url = os.environ["FEEDBACK_SCRIPT_URL"] | |
| # 画像生成パラメータ | |
| def generate_image(mode, weight1, weight2): | |
| # ダミーの画像生成処理 | |
| image = Image.new("RGB", (256, 256), color=(int(255*weight1), int(255*weight2), int(255*weight1*weight2))) | |
| return image | |
| # Google Driveに保存する処理 | |
| def send_feedback(image, filename): | |
| # numpy.ndarray を PIL.Image に変換 | |
| if isinstance(image, np.ndarray): | |
| image = Image.fromarray(image) | |
| # 画像をBase64にエンコード | |
| buffered = io.BytesIO() | |
| image.save(buffered, format="PNG") | |
| img_str = base64.b64encode(buffered.getvalue()).decode() | |
| # Google Apps Scriptに送信 | |
| response = requests.post( | |
| feedback_script_url, | |
| json={"image": img_str, "filename": filename} | |
| ) | |
| if response.status_code == 200: | |
| return gr.update(value="Thank you for cooperation!/ご協力ありがとうございます!", interactive=False) | |
| else: | |
| return gr.update(value="Failed to send. Try again./送信に失敗しました。もう一度お試しください。", interactive=True) | |