Commit ·
8df1b32
1
Parent(s): cca40b2
refactor: Update file upload and URL extraction tabs for improved clarity and functionality
Browse files- Makefile +2 -2
- tests/e2e/features/file_upload.feature +14 -13
- tests/e2e/features/text_management.feature +13 -7
- tests/e2e/features/url_extraction.feature +13 -6
- tests/e2e/steps/audio_generation_steps.py +2 -2
- tests/e2e/steps/common_steps.py +13 -8
- tests/e2e/steps/file_upload_steps.py +49 -6
- tests/e2e/steps/script_generation_steps.py +2 -2
- tests/e2e/steps/text_management_steps.py +138 -25
- tests/e2e/steps/url_extraction_steps.py +18 -0
- yomitalk/app.py +58 -29
Makefile
CHANGED
|
@@ -165,12 +165,12 @@ pre-commit-run-ci: setup-lint
|
|
| 165 |
# Run all tests
|
| 166 |
test: venv
|
| 167 |
@echo "Running tests..."
|
| 168 |
-
$(VENV_PYTHON) -m pytest tests/ --durations=10
|
| 169 |
|
| 170 |
# Run unit tests only
|
| 171 |
test-unit: venv
|
| 172 |
@echo "Running unit tests..."
|
| 173 |
-
$(VENV_PYTHON) -m pytest tests/unit/ --durations=10
|
| 174 |
|
| 175 |
# Run E2E tests only
|
| 176 |
test-e2e: venv
|
|
|
|
| 165 |
# Run all tests
|
| 166 |
test: venv
|
| 167 |
@echo "Running tests..."
|
| 168 |
+
$(VENV_PYTHON) -m pytest tests/ --durations=10
|
| 169 |
|
| 170 |
# Run unit tests only
|
| 171 |
test-unit: venv
|
| 172 |
@echo "Running unit tests..."
|
| 173 |
+
$(VENV_PYTHON) -m pytest tests/unit/ --durations=10
|
| 174 |
|
| 175 |
# Run E2E tests only
|
| 176 |
test-e2e: venv
|
tests/e2e/features/file_upload.feature
CHANGED
|
@@ -6,18 +6,18 @@ Feature: File Upload Functionality
|
|
| 6 |
Background:
|
| 7 |
Given the application is running
|
| 8 |
|
| 9 |
-
Scenario: Uploading a PDF file
|
| 10 |
Given the user has accessed the application page
|
| 11 |
-
When the user
|
| 12 |
-
And the user
|
| 13 |
Then text should be extracted
|
| 14 |
And the file input should be cleared
|
| 15 |
And the "トーク原稿を生成" button should be active
|
| 16 |
|
| 17 |
-
Scenario: Uploading a text file
|
| 18 |
Given the user has accessed the application page
|
| 19 |
-
When the user
|
| 20 |
-
And the user
|
| 21 |
Then text should be extracted
|
| 22 |
And the file input should be cleared
|
| 23 |
And the "トーク原稿を生成" button should be active
|
|
@@ -25,27 +25,28 @@ Feature: File Upload Functionality
|
|
| 25 |
Scenario: File extraction appends to existing text with separator
|
| 26 |
Given the user has accessed the application page
|
| 27 |
And the user has entered "Existing content" into the extracted text area
|
| 28 |
-
When the user
|
| 29 |
-
And the user
|
| 30 |
Then text should be extracted with source separator
|
| 31 |
And the extracted text area contains "Existing content"
|
| 32 |
And the extracted text area contains source information for "sample_text.txt"
|
|
|
|
| 33 |
|
| 34 |
Scenario: File extraction without automatic separator
|
| 35 |
Given the user has accessed the application page
|
| 36 |
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 37 |
And the user has entered "Existing content" into the extracted text area
|
| 38 |
-
When the user
|
| 39 |
-
And the user
|
| 40 |
Then text should be extracted without separator
|
| 41 |
And the extracted text area contains "Existing content"
|
|
|
|
| 42 |
|
| 43 |
Scenario: Multiple file extractions accumulate content
|
| 44 |
Given the user has accessed the application page
|
| 45 |
-
When the user
|
| 46 |
-
And the user
|
| 47 |
And the user uploads a text file "another_file.txt"
|
| 48 |
-
And the user clicks the "ファイルからテキストを抽出" button
|
| 49 |
Then the extracted text area contains content from both files
|
| 50 |
And the extracted text area contains source information for "sample_text.txt"
|
| 51 |
And the extracted text area contains source information for "another_file.txt"
|
|
|
|
| 6 |
Background:
|
| 7 |
Given the application is running
|
| 8 |
|
| 9 |
+
Scenario: Uploading a PDF file in file upload tab
|
| 10 |
Given the user has accessed the application page
|
| 11 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 12 |
+
And the user uploads a PDF file "sample_paper.pdf"
|
| 13 |
Then text should be extracted
|
| 14 |
And the file input should be cleared
|
| 15 |
And the "トーク原稿を生成" button should be active
|
| 16 |
|
| 17 |
+
Scenario: Uploading a text file in file upload tab
|
| 18 |
Given the user has accessed the application page
|
| 19 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 20 |
+
And the user uploads a text file "sample_text.txt"
|
| 21 |
Then text should be extracted
|
| 22 |
And the file input should be cleared
|
| 23 |
And the "トーク原稿を生成" button should be active
|
|
|
|
| 25 |
Scenario: File extraction appends to existing text with separator
|
| 26 |
Given the user has accessed the application page
|
| 27 |
And the user has entered "Existing content" into the extracted text area
|
| 28 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 29 |
+
And the user uploads a text file "sample_text.txt"
|
| 30 |
Then text should be extracted with source separator
|
| 31 |
And the extracted text area contains "Existing content"
|
| 32 |
And the extracted text area contains source information for "sample_text.txt"
|
| 33 |
+
And the file input should be cleared
|
| 34 |
|
| 35 |
Scenario: File extraction without automatic separator
|
| 36 |
Given the user has accessed the application page
|
| 37 |
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 38 |
And the user has entered "Existing content" into the extracted text area
|
| 39 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 40 |
+
And the user uploads a text file "sample_text.txt"
|
| 41 |
Then text should be extracted without separator
|
| 42 |
And the extracted text area contains "Existing content"
|
| 43 |
+
And the file input should be cleared
|
| 44 |
|
| 45 |
Scenario: Multiple file extractions accumulate content
|
| 46 |
Given the user has accessed the application page
|
| 47 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 48 |
+
And the user uploads a text file "sample_text.txt"
|
| 49 |
And the user uploads a text file "another_file.txt"
|
|
|
|
| 50 |
Then the extracted text area contains content from both files
|
| 51 |
And the extracted text area contains source information for "sample_text.txt"
|
| 52 |
And the extracted text area contains source information for "another_file.txt"
|
tests/e2e/features/text_management.feature
CHANGED
|
@@ -20,20 +20,26 @@ Feature: Text Management Functionality
|
|
| 20 |
When the user checks the "追加時に自動で区切りを挿入" checkbox
|
| 21 |
Then the automatic separator is enabled
|
| 22 |
|
| 23 |
-
Scenario:
|
| 24 |
Given the user has accessed the application page
|
| 25 |
-
When the user
|
| 26 |
-
And the user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
And the user enters "https://example.com" into the URL input field
|
| 28 |
And the user clicks the "URLからテキストを抽出" button
|
| 29 |
-
Then the extracted text area contains content from
|
| 30 |
-
And the extracted text area contains source information for "sample_text.txt"
|
| 31 |
And the extracted text area contains source information for "https://example.com"
|
| 32 |
|
| 33 |
Scenario: Manual text input preserved during extractions
|
| 34 |
Given the user has accessed the application page
|
| 35 |
And the user has entered "Manual input content" into the extracted text area
|
| 36 |
-
When the user
|
| 37 |
-
And the user
|
| 38 |
Then the extracted text area contains "Manual input content"
|
| 39 |
And the extracted text area contains content from the file
|
|
|
|
| 20 |
When the user checks the "追加時に自動で区切りを挿入" checkbox
|
| 21 |
Then the automatic separator is enabled
|
| 22 |
|
| 23 |
+
Scenario: File upload tab enables automatic extraction
|
| 24 |
Given the user has accessed the application page
|
| 25 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 26 |
+
And the user uploads a text file "sample_text.txt"
|
| 27 |
+
Then the extracted text area contains content from the file
|
| 28 |
+
And the extracted text area contains source information for "sample_text.txt"
|
| 29 |
+
And the file input should be cleared
|
| 30 |
+
|
| 31 |
+
Scenario: URL extraction tab requires extraction button
|
| 32 |
+
Given the user has accessed the application page
|
| 33 |
+
When the user clicks on the "Webページ抽出" tab
|
| 34 |
And the user enters "https://example.com" into the URL input field
|
| 35 |
And the user clicks the "URLからテキストを抽出" button
|
| 36 |
+
Then the extracted text area contains content from the URL
|
|
|
|
| 37 |
And the extracted text area contains source information for "https://example.com"
|
| 38 |
|
| 39 |
Scenario: Manual text input preserved during extractions
|
| 40 |
Given the user has accessed the application page
|
| 41 |
And the user has entered "Manual input content" into the extracted text area
|
| 42 |
+
When the user clicks on the "ファイルアップロード" tab
|
| 43 |
+
And the user uploads a text file "sample_text.txt"
|
| 44 |
Then the extracted text area contains "Manual input content"
|
| 45 |
And the extracted text area contains content from the file
|
tests/e2e/features/url_extraction.feature
CHANGED
|
@@ -9,34 +9,40 @@ Feature: URL extraction functionality
|
|
| 9 |
|
| 10 |
Scenario: Extract text from a valid URL
|
| 11 |
Given the user has accessed the application page
|
| 12 |
-
When the user
|
|
|
|
| 13 |
And the user clicks the "URLからテキストを抽出" button
|
| 14 |
Then the extracted text area shows content
|
| 15 |
And the "トーク原稿を生成" button is enabled
|
| 16 |
|
| 17 |
Scenario: Enter an invalid URL
|
| 18 |
Given the user has accessed the application page
|
| 19 |
-
When the user
|
|
|
|
| 20 |
And the user clicks the "URLからテキストを抽出" button
|
| 21 |
Then the extracted text area shows an error message
|
| 22 |
And the "トーク原稿を生成" button remains disabled
|
| 23 |
|
| 24 |
Scenario: Extract text from GitHub README URL
|
| 25 |
Given the user has accessed the application page
|
| 26 |
-
When the user
|
|
|
|
| 27 |
And the user clicks the "URLからテキストを抽出" button
|
| 28 |
Then the extracted text area shows GitHub README content
|
| 29 |
|
| 30 |
Scenario: Click extract button with empty URL field
|
|
|
|
| 31 |
Given the user has accessed the application page
|
| 32 |
-
When the user
|
|
|
|
| 33 |
And the user clicks the "URLからテキストを抽出" button
|
| 34 |
Then the extracted text area shows an error message
|
| 35 |
|
| 36 |
Scenario: URL extraction appends to existing text with separator
|
| 37 |
Given the user has accessed the application page
|
| 38 |
And the user has entered "Existing content" into the extracted text area
|
| 39 |
-
When the user
|
|
|
|
| 40 |
And the user clicks the "URLからテキストを抽出" button
|
| 41 |
Then the extracted text area shows content with source separator
|
| 42 |
And the extracted text area contains "Existing content"
|
|
@@ -46,7 +52,8 @@ Feature: URL extraction functionality
|
|
| 46 |
Given the user has accessed the application page
|
| 47 |
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 48 |
And the user has entered "Existing content" into the extracted text area
|
| 49 |
-
When the user
|
|
|
|
| 50 |
And the user clicks the "URLからテキストを抽出" button
|
| 51 |
Then the extracted text area shows appended content without separator
|
| 52 |
And the extracted text area contains "Existing content"
|
|
|
|
| 9 |
|
| 10 |
Scenario: Extract text from a valid URL
|
| 11 |
Given the user has accessed the application page
|
| 12 |
+
When the user clicks on the "Webページ抽出" tab
|
| 13 |
+
And the user enters "https://example.com" into the URL input field
|
| 14 |
And the user clicks the "URLからテキストを抽出" button
|
| 15 |
Then the extracted text area shows content
|
| 16 |
And the "トーク原稿を生成" button is enabled
|
| 17 |
|
| 18 |
Scenario: Enter an invalid URL
|
| 19 |
Given the user has accessed the application page
|
| 20 |
+
When the user clicks on the "Webページ抽出" tab
|
| 21 |
+
And the user enters "invalid-url" into the URL input field
|
| 22 |
And the user clicks the "URLからテキストを抽出" button
|
| 23 |
Then the extracted text area shows an error message
|
| 24 |
And the "トーク原稿を生成" button remains disabled
|
| 25 |
|
| 26 |
Scenario: Extract text from GitHub README URL
|
| 27 |
Given the user has accessed the application page
|
| 28 |
+
When the user clicks on the "Webページ抽出" tab
|
| 29 |
+
And the user enters a GitHub README URL into the URL input field
|
| 30 |
And the user clicks the "URLからテキストを抽出" button
|
| 31 |
Then the extracted text area shows GitHub README content
|
| 32 |
|
| 33 |
Scenario: Click extract button with empty URL field
|
| 34 |
+
Given the application is running
|
| 35 |
Given the user has accessed the application page
|
| 36 |
+
When the user clicks on the "Webページ抽出" tab
|
| 37 |
+
And the user leaves the URL input field empty
|
| 38 |
And the user clicks the "URLからテキストを抽出" button
|
| 39 |
Then the extracted text area shows an error message
|
| 40 |
|
| 41 |
Scenario: URL extraction appends to existing text with separator
|
| 42 |
Given the user has accessed the application page
|
| 43 |
And the user has entered "Existing content" into the extracted text area
|
| 44 |
+
When the user clicks on the "Webページ抽出" tab
|
| 45 |
+
And the user enters "https://example.com" into the URL input field
|
| 46 |
And the user clicks the "URLからテキストを抽出" button
|
| 47 |
Then the extracted text area shows content with source separator
|
| 48 |
And the extracted text area contains "Existing content"
|
|
|
|
| 52 |
Given the user has accessed the application page
|
| 53 |
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 54 |
And the user has entered "Existing content" into the extracted text area
|
| 55 |
+
When the user clicks on the "Webページ抽出" tab
|
| 56 |
+
And the user enters "https://example.com" into the URL input field
|
| 57 |
And the user clicks the "URLからテキストを抽出" button
|
| 58 |
Then the extracted text area shows appended content without separator
|
| 59 |
And the extracted text area contains "Existing content"
|
tests/e2e/steps/audio_generation_steps.py
CHANGED
|
@@ -16,8 +16,8 @@ def podcast_script_is_generated(page: Page):
|
|
| 16 |
page: Playwright page object
|
| 17 |
"""
|
| 18 |
|
| 19 |
-
# Enter test text in the
|
| 20 |
-
text_area = page.locator(
|
| 21 |
test_text = """
|
| 22 |
機械学習の最新研究によれば、大規模言語モデルは自然言語処理タスクにおいて
|
| 23 |
人間に匹敵する性能を発揮することが可能になっています。
|
|
|
|
| 16 |
page: Playwright page object
|
| 17 |
"""
|
| 18 |
|
| 19 |
+
# Enter test text in the extracted text area
|
| 20 |
+
text_area = page.locator('textarea[placeholder*="ファイルをアップロードするか"]')
|
| 21 |
test_text = """
|
| 22 |
機械学習の最新研究によれば、大規模言語モデルは自然言語処理タスクにおいて
|
| 23 |
人間に匹敵する性能を発揮することが可能になっています。
|
tests/e2e/steps/common_steps.py
CHANGED
|
@@ -74,17 +74,22 @@ def user_has_accessed_application_page(page: Page, app_environment):
|
|
| 74 |
heading = page.locator('text="トーク原稿の生成"')
|
| 75 |
expect(heading).to_be_visible(timeout=10000)
|
| 76 |
|
| 77 |
-
# Verify
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
file_input = page.locator('input[type="file"]')
|
| 79 |
expect(file_input).to_be_attached()
|
| 80 |
|
| 81 |
-
# Verify
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
extract_button = page.locator('button:has-text("URLからテキストを抽出")')
|
| 87 |
-
expect(extract_button).to_be_visible()
|
| 88 |
|
| 89 |
logger.info("All main UI elements are visible and accessible")
|
| 90 |
|
|
|
|
| 74 |
heading = page.locator('text="トーク原稿の生成"')
|
| 75 |
expect(heading).to_be_visible(timeout=10000)
|
| 76 |
|
| 77 |
+
# Verify tabs are present
|
| 78 |
+
file_upload_tab = page.get_by_role("tab", name="ファイルアップロード")
|
| 79 |
+
expect(file_upload_tab).to_be_visible()
|
| 80 |
+
|
| 81 |
+
web_extraction_tab = page.get_by_role("tab", name="Webページ抽出")
|
| 82 |
+
expect(web_extraction_tab).to_be_visible()
|
| 83 |
+
|
| 84 |
+
# Verify file input element is available (may be hidden initially due to tabs)
|
| 85 |
file_input = page.locator('input[type="file"]')
|
| 86 |
expect(file_input).to_be_attached()
|
| 87 |
|
| 88 |
+
# Verify extracted text area is visible
|
| 89 |
+
extracted_text_area = page.locator(
|
| 90 |
+
'textarea[placeholder*="ファイルをアップロードするか"]'
|
| 91 |
+
)
|
| 92 |
+
expect(extracted_text_area).to_be_visible()
|
|
|
|
|
|
|
| 93 |
|
| 94 |
logger.info("All main UI elements are visible and accessible")
|
| 95 |
|
tests/e2e/steps/file_upload_steps.py
CHANGED
|
@@ -116,19 +116,62 @@ def user_clicks_file_extract_button(page: Page):
|
|
| 116 |
logger.info("File extraction button clicked successfully")
|
| 117 |
|
| 118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
@then("the file input should be cleared")
|
| 120 |
def file_input_should_be_cleared(page: Page):
|
| 121 |
-
"""The file input should be cleared after
|
| 122 |
from tests.utils.logger import test_logger as logger
|
| 123 |
|
| 124 |
logger.info("Checking if file input is cleared")
|
| 125 |
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
-
#
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
|
| 133 |
logger.info("File input is cleared as expected")
|
| 134 |
|
|
|
|
| 116 |
logger.info("File extraction button clicked successfully")
|
| 117 |
|
| 118 |
|
| 119 |
+
@when('the user clicks on the "ファイルアップロード" tab')
|
| 120 |
+
def user_clicks_file_upload_tab(page: Page):
|
| 121 |
+
"""The user clicks on the file upload tab."""
|
| 122 |
+
from tests.utils.logger import test_logger as logger
|
| 123 |
+
|
| 124 |
+
logger.info("Clicking file upload tab")
|
| 125 |
+
|
| 126 |
+
# Look for the tab using different possible selectors
|
| 127 |
+
tab_selectors = [
|
| 128 |
+
'button[role="tab"]:has-text("ファイルアップロード")',
|
| 129 |
+
'.tab-nav button:has-text("ファイルアップロード")',
|
| 130 |
+
'button:has-text("ファイルアップロード")',
|
| 131 |
+
'*[data-testid*="tab"]:has-text("ファイルアップロード")',
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
tab_clicked = False
|
| 135 |
+
for selector in tab_selectors:
|
| 136 |
+
tab = page.locator(selector)
|
| 137 |
+
if tab.count() > 0 and tab.first.is_visible():
|
| 138 |
+
tab.first.click()
|
| 139 |
+
tab_clicked = True
|
| 140 |
+
break
|
| 141 |
+
|
| 142 |
+
if not tab_clicked:
|
| 143 |
+
# Fallback: look for any clickable element with the text
|
| 144 |
+
tab = page.get_by_text("ファイルアップロード")
|
| 145 |
+
if tab.count() > 0:
|
| 146 |
+
tab.first.click()
|
| 147 |
+
tab_clicked = True
|
| 148 |
+
|
| 149 |
+
if not tab_clicked:
|
| 150 |
+
raise Exception("File upload tab not found")
|
| 151 |
+
|
| 152 |
+
page.wait_for_timeout(1000)
|
| 153 |
+
logger.info("File upload tab clicked successfully")
|
| 154 |
+
|
| 155 |
+
|
| 156 |
@then("the file input should be cleared")
|
| 157 |
def file_input_should_be_cleared(page: Page):
|
| 158 |
+
"""The file input should be cleared after upload."""
|
| 159 |
from tests.utils.logger import test_logger as logger
|
| 160 |
|
| 161 |
logger.info("Checking if file input is cleared")
|
| 162 |
|
| 163 |
+
# Look for file input element
|
| 164 |
+
file_input = page.locator('input[type="file"]')
|
| 165 |
+
expect(file_input).to_be_visible()
|
| 166 |
+
|
| 167 |
+
# Check if file input has no value (cleared)
|
| 168 |
+
input_value = file_input.input_value()
|
| 169 |
+
logger.info(f"File input value: '{input_value}'")
|
| 170 |
|
| 171 |
+
# File input should be empty after automatic extraction
|
| 172 |
+
assert (
|
| 173 |
+
input_value == "" or input_value is None
|
| 174 |
+
), "File input should be cleared after extraction"
|
| 175 |
|
| 176 |
logger.info("File input is cleared as expected")
|
| 177 |
|
tests/e2e/steps/script_generation_steps.py
CHANGED
|
@@ -18,8 +18,8 @@ def text_is_entered(page: Page):
|
|
| 18 |
page: Playwright page object
|
| 19 |
"""
|
| 20 |
|
| 21 |
-
# Enter test text in the
|
| 22 |
-
text_area = page.locator(
|
| 23 |
test_text = """
|
| 24 |
機械学習の最新研究によれば、大規模言語モデルは自然言語処理タスクにおいて
|
| 25 |
人間に匹敵する性能を発揮することが可能になっています。
|
|
|
|
| 18 |
page: Playwright page object
|
| 19 |
"""
|
| 20 |
|
| 21 |
+
# Enter test text in the extracted text area
|
| 22 |
+
text_area = page.locator('textarea[placeholder*="ファイルをアップロードするか"]')
|
| 23 |
test_text = """
|
| 24 |
機械学習の最新研究によれば、大規模言語モデルは自然言語処理タスクにおいて
|
| 25 |
人間に匹敵する性能を発揮することが可能になっています。
|
tests/e2e/steps/text_management_steps.py
CHANGED
|
@@ -138,6 +138,76 @@ def user_clicks_clear_text_button(page: Page):
|
|
| 138 |
logger.info("Clear text button clicked successfully")
|
| 139 |
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
@when('the user clicks the "ファイルからテキストを抽出" button')
|
| 142 |
def user_clicks_file_extract_button(page: Page):
|
| 143 |
"""The user clicks the file text extraction button."""
|
|
@@ -271,34 +341,21 @@ def text_area_contains_source_info(page: Page, source: str):
|
|
| 271 |
|
| 272 |
@then("the file input should be cleared")
|
| 273 |
def file_input_should_be_cleared(page: Page):
|
| 274 |
-
"""The file input should be cleared after
|
| 275 |
logger.info("Checking if file input is cleared")
|
| 276 |
|
| 277 |
-
#
|
| 278 |
file_input = page.locator('input[type="file"]')
|
| 279 |
-
expect(file_input).to_be_attached()
|
| 280 |
-
|
| 281 |
-
#
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
#
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
expect(file_input).to_be_visible(timeout=3000)
|
| 290 |
-
input_value = file_input.input_value()
|
| 291 |
-
logger.info(f"File input value: '{input_value}'")
|
| 292 |
-
assert (
|
| 293 |
-
input_value == ""
|
| 294 |
-
), f"Expected empty file input, but found: '{input_value}'"
|
| 295 |
-
except AssertionError:
|
| 296 |
-
# 非表示の場合でも、要素は存在し、クリアされた状態であることを確認
|
| 297 |
-
logger.info(
|
| 298 |
-
"File input is hidden after extraction, which is expected Gradio behavior"
|
| 299 |
-
)
|
| 300 |
-
# 代わりに、新しいファイルアップロードが可能であることを確認
|
| 301 |
-
expect(file_input).to_be_attached()
|
| 302 |
|
| 303 |
logger.info("File input is cleared as expected")
|
| 304 |
|
|
@@ -633,6 +690,62 @@ def text_area_contains_existing_content(page: Page):
|
|
| 633 |
logger.info("Text area contains 'Existing content' as expected")
|
| 634 |
|
| 635 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 636 |
@then('the extracted text area contains "Manual input content"')
|
| 637 |
def text_area_contains_manual_input_content(page: Page):
|
| 638 |
"""The extracted text area contains 'Manual input content'."""
|
|
|
|
| 138 |
logger.info("Clear text button clicked successfully")
|
| 139 |
|
| 140 |
|
| 141 |
+
@when('the user clicks on the "ファイルアップロード" tab')
|
| 142 |
+
def user_clicks_file_upload_tab(page: Page):
|
| 143 |
+
"""The user clicks on the file upload tab."""
|
| 144 |
+
logger.info("Clicking file upload tab")
|
| 145 |
+
|
| 146 |
+
# Look for the tab using different possible selectors
|
| 147 |
+
tab_selectors = [
|
| 148 |
+
'button[role="tab"]:has-text("ファイルアップロード")',
|
| 149 |
+
'.tab-nav button:has-text("ファイルアップロード")',
|
| 150 |
+
'button:has-text("ファイルアップロード")',
|
| 151 |
+
'*[data-testid*="tab"]:has-text("ファイルアップロード")',
|
| 152 |
+
]
|
| 153 |
+
|
| 154 |
+
tab_clicked = False
|
| 155 |
+
for selector in tab_selectors:
|
| 156 |
+
tab = page.locator(selector)
|
| 157 |
+
if tab.count() > 0 and tab.first.is_visible():
|
| 158 |
+
tab.first.click()
|
| 159 |
+
tab_clicked = True
|
| 160 |
+
break
|
| 161 |
+
|
| 162 |
+
if not tab_clicked:
|
| 163 |
+
# Fallback: look for any clickable element with the text
|
| 164 |
+
tab = page.get_by_text("ファイルアップロード")
|
| 165 |
+
if tab.count() > 0:
|
| 166 |
+
tab.first.click()
|
| 167 |
+
tab_clicked = True
|
| 168 |
+
|
| 169 |
+
if not tab_clicked:
|
| 170 |
+
raise Exception("File upload tab not found")
|
| 171 |
+
|
| 172 |
+
time.sleep(1)
|
| 173 |
+
logger.info("File upload tab clicked successfully")
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
@when('the user clicks on the "Webページ抽出" tab')
|
| 177 |
+
def user_clicks_web_extraction_tab(page: Page):
|
| 178 |
+
"""The user clicks on the web page extraction tab."""
|
| 179 |
+
logger.info("Clicking web page extraction tab")
|
| 180 |
+
|
| 181 |
+
# Look for the tab using different possible selectors
|
| 182 |
+
tab_selectors = [
|
| 183 |
+
'button[role="tab"]:has-text("Webページ抽出")',
|
| 184 |
+
'.tab-nav button:has-text("Webページ抽出")',
|
| 185 |
+
'button:has-text("Webページ抽出")',
|
| 186 |
+
'*[data-testid*="tab"]:has-text("Webページ抽出")',
|
| 187 |
+
]
|
| 188 |
+
|
| 189 |
+
tab_clicked = False
|
| 190 |
+
for selector in tab_selectors:
|
| 191 |
+
tab = page.locator(selector)
|
| 192 |
+
if tab.count() > 0 and tab.first.is_visible():
|
| 193 |
+
tab.first.click()
|
| 194 |
+
tab_clicked = True
|
| 195 |
+
break
|
| 196 |
+
|
| 197 |
+
if not tab_clicked:
|
| 198 |
+
# Fallback: look for any clickable element with the text
|
| 199 |
+
tab = page.get_by_text("Webページ抽出")
|
| 200 |
+
if tab.count() > 0:
|
| 201 |
+
tab.first.click()
|
| 202 |
+
tab_clicked = True
|
| 203 |
+
|
| 204 |
+
if not tab_clicked:
|
| 205 |
+
raise Exception("Web page extraction tab not found")
|
| 206 |
+
|
| 207 |
+
time.sleep(1)
|
| 208 |
+
logger.info("Web page extraction tab clicked successfully")
|
| 209 |
+
|
| 210 |
+
|
| 211 |
@when('the user clicks the "ファイルからテキストを抽出" button')
|
| 212 |
def user_clicks_file_extract_button(page: Page):
|
| 213 |
"""The user clicks the file text extraction button."""
|
|
|
|
| 341 |
|
| 342 |
@then("the file input should be cleared")
|
| 343 |
def file_input_should_be_cleared(page: Page):
|
| 344 |
+
"""The file input should be cleared after upload."""
|
| 345 |
logger.info("Checking if file input is cleared")
|
| 346 |
|
| 347 |
+
# Look for file input element
|
| 348 |
file_input = page.locator('input[type="file"]')
|
| 349 |
+
expect(file_input).to_be_attached() # Check element exists, not visibility
|
| 350 |
+
|
| 351 |
+
# Check if file input has no value (cleared)
|
| 352 |
+
input_value = file_input.input_value()
|
| 353 |
+
logger.info(f"File input value: '{input_value}'")
|
| 354 |
+
|
| 355 |
+
# File input should be empty after automatic extraction
|
| 356 |
+
assert (
|
| 357 |
+
input_value == "" or input_value is None
|
| 358 |
+
), "File input should be cleared after extraction"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
|
| 360 |
logger.info("File input is cleared as expected")
|
| 361 |
|
|
|
|
| 690 |
logger.info("Text area contains 'Existing content' as expected")
|
| 691 |
|
| 692 |
|
| 693 |
+
@then("the file upload area should be visible")
|
| 694 |
+
def file_upload_area_should_be_visible(page: Page):
|
| 695 |
+
"""The file upload area should be visible."""
|
| 696 |
+
logger.info("Checking if file upload area is visible")
|
| 697 |
+
|
| 698 |
+
file_upload = page.locator('input[type="file"]')
|
| 699 |
+
expect(file_upload).to_be_visible()
|
| 700 |
+
logger.info("File upload area is visible")
|
| 701 |
+
|
| 702 |
+
|
| 703 |
+
@then("the file upload area should be hidden")
|
| 704 |
+
def file_upload_area_should_be_hidden(page: Page):
|
| 705 |
+
"""The file upload area should be hidden."""
|
| 706 |
+
logger.info("Checking if file upload area is hidden")
|
| 707 |
+
|
| 708 |
+
file_upload = page.locator('input[type="file"]')
|
| 709 |
+
expect(file_upload).to_be_hidden()
|
| 710 |
+
logger.info("File upload area is hidden")
|
| 711 |
+
|
| 712 |
+
|
| 713 |
+
@then("the URL input area should be visible")
|
| 714 |
+
def url_input_area_should_be_visible(page: Page):
|
| 715 |
+
"""The URL input area should be visible."""
|
| 716 |
+
logger.info("Checking if URL input area is visible")
|
| 717 |
+
|
| 718 |
+
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 719 |
+
expect(url_input).to_be_visible()
|
| 720 |
+
logger.info("URL input area is visible")
|
| 721 |
+
|
| 722 |
+
|
| 723 |
+
@then("the URL input area should be hidden")
|
| 724 |
+
def url_input_area_should_be_hidden(page: Page):
|
| 725 |
+
"""The URL input area should be hidden."""
|
| 726 |
+
logger.info("Checking if URL input area is hidden")
|
| 727 |
+
|
| 728 |
+
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 729 |
+
expect(url_input).to_be_hidden()
|
| 730 |
+
logger.info("URL input area is hidden")
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
@then("the extracted text area contains content from the URL")
|
| 734 |
+
def text_area_contains_url_content(page: Page):
|
| 735 |
+
"""The extracted text area contains content from the URL."""
|
| 736 |
+
logger.info("Checking if text area contains URL content")
|
| 737 |
+
|
| 738 |
+
text_area = page.locator(
|
| 739 |
+
'textarea[placeholder*="ファイルをアップロードするか、URLを入力するか"]'
|
| 740 |
+
)
|
| 741 |
+
expect(text_area).to_be_visible()
|
| 742 |
+
|
| 743 |
+
text_content = text_area.input_value()
|
| 744 |
+
assert len(text_content.strip()) > 0, "Text area should contain URL content"
|
| 745 |
+
|
| 746 |
+
logger.info("Text area contains URL content as expected")
|
| 747 |
+
|
| 748 |
+
|
| 749 |
@then('the extracted text area contains "Manual input content"')
|
| 750 |
def text_area_contains_manual_input_content(page: Page):
|
| 751 |
"""The extracted text area contains 'Manual input content'."""
|
tests/e2e/steps/url_extraction_steps.py
CHANGED
|
@@ -13,6 +13,12 @@ def user_enters_url(page: Page, url: str):
|
|
| 13 |
"""The user enters a URL into the URL input field."""
|
| 14 |
logger.info(f"Entering URL: {url}")
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
# URL入力フィールドを見つけて入力
|
| 17 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 18 |
expect(url_input).to_be_visible()
|
|
@@ -29,6 +35,12 @@ def user_enters_github_readme_url(page: Page):
|
|
| 29 |
)
|
| 30 |
logger.info(f"Entering GitHub README URL: {github_readme_url}")
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 33 |
expect(url_input).to_be_visible()
|
| 34 |
url_input.fill(github_readme_url)
|
|
@@ -41,6 +53,12 @@ def user_leaves_url_field_empty(page: Page):
|
|
| 41 |
"""The user leaves the URL input field empty."""
|
| 42 |
logger.info("Leaving URL field empty")
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
# URL入力フィールドが存在することを確認(何も入力しない)
|
| 45 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 46 |
expect(url_input).to_be_visible()
|
|
|
|
| 13 |
"""The user enters a URL into the URL input field."""
|
| 14 |
logger.info(f"Entering URL: {url}")
|
| 15 |
|
| 16 |
+
# Make sure the Web page extraction tab is active first
|
| 17 |
+
web_tab = page.get_by_role("tab", name="Webページ抽出")
|
| 18 |
+
if web_tab.is_visible():
|
| 19 |
+
web_tab.click()
|
| 20 |
+
time.sleep(1)
|
| 21 |
+
|
| 22 |
# URL入力フィールドを見つけて入力
|
| 23 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 24 |
expect(url_input).to_be_visible()
|
|
|
|
| 35 |
)
|
| 36 |
logger.info(f"Entering GitHub README URL: {github_readme_url}")
|
| 37 |
|
| 38 |
+
# Make sure the Web page extraction tab is active first
|
| 39 |
+
web_tab = page.get_by_role("tab", name="Webページ抽出")
|
| 40 |
+
if web_tab.is_visible():
|
| 41 |
+
web_tab.click()
|
| 42 |
+
time.sleep(1)
|
| 43 |
+
|
| 44 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 45 |
expect(url_input).to_be_visible()
|
| 46 |
url_input.fill(github_readme_url)
|
|
|
|
| 53 |
"""The user leaves the URL input field empty."""
|
| 54 |
logger.info("Leaving URL field empty")
|
| 55 |
|
| 56 |
+
# Make sure the Web page extraction tab is active first
|
| 57 |
+
web_tab = page.get_by_role("tab", name="Webページ抽出")
|
| 58 |
+
if web_tab.is_visible():
|
| 59 |
+
web_tab.click()
|
| 60 |
+
time.sleep(1)
|
| 61 |
+
|
| 62 |
# URL入力フィールドが存在することを確認(何も入力しない)
|
| 63 |
url_input = page.locator('textarea[placeholder="https://example.com/page"]')
|
| 64 |
expect(url_input).to_be_visible()
|
yomitalk/app.py
CHANGED
|
@@ -335,6 +335,10 @@ class PaperPodcastApp:
|
|
| 335 |
session_id = request.session_hash
|
| 336 |
return UserSession(session_id)
|
| 337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
def set_openai_api_key(self, api_key: str, user_session: UserSession):
|
| 339 |
"""Set the OpenAI API key for the specific user session."""
|
| 340 |
if not api_key or api_key.strip() == "":
|
|
@@ -500,6 +504,35 @@ class PaperPodcastApp:
|
|
| 500 |
logger.error(error_msg)
|
| 501 |
return f"Error: {str(e)}", user_session
|
| 502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
def generate_podcast_audio_streaming(
|
| 504 |
self, text: str, user_session: UserSession, progress=gr.Progress()
|
| 505 |
):
|
|
@@ -832,25 +865,20 @@ class PaperPodcastApp:
|
|
| 832 |
# サポートしているファイル形式の拡張子を取得
|
| 833 |
supported_extensions = ContentExtractor.SUPPORTED_EXTENSIONS
|
| 834 |
|
| 835 |
-
#
|
| 836 |
-
gr.Markdown(
|
| 837 |
-
|
| 838 |
-
)
|
| 839 |
-
with
|
| 840 |
-
with gr.
|
| 841 |
file_input = gr.File(
|
| 842 |
file_types=supported_extensions,
|
| 843 |
type="filepath",
|
| 844 |
label=f"ファイルをアップロード({', '.join(supported_extensions)})",
|
| 845 |
height=120,
|
| 846 |
)
|
| 847 |
-
file_extract_btn = gr.Button(
|
| 848 |
-
"ファイルからテキストを抽出",
|
| 849 |
-
variant="secondary",
|
| 850 |
-
size="lg",
|
| 851 |
-
)
|
| 852 |
|
| 853 |
-
with gr.
|
| 854 |
url_input = gr.Textbox(
|
| 855 |
placeholder="https://example.com/page",
|
| 856 |
label="WebページのURLを入力",
|
|
@@ -858,35 +886,29 @@ class PaperPodcastApp:
|
|
| 858 |
lines=2,
|
| 859 |
)
|
| 860 |
url_extract_btn = gr.Button(
|
| 861 |
-
"URLからテキストを抽出", variant="
|
| 862 |
)
|
| 863 |
|
| 864 |
-
#
|
| 865 |
with gr.Row(equal_height=True):
|
| 866 |
-
with gr.Column(scale=
|
| 867 |
auto_separator_checkbox = gr.Checkbox(
|
| 868 |
label="テキスト抽出時に区切りを自動挿入",
|
| 869 |
value=True,
|
| 870 |
info="ファイル名やURLの情報を含む区切り線を自動挿入します",
|
| 871 |
)
|
| 872 |
-
with gr.Column(scale=1
|
| 873 |
-
clear_text_btn = gr.
|
| 874 |
-
|
| 875 |
-
value="テキストをクリア",
|
| 876 |
-
variant="secondary",
|
| 877 |
-
size="lg",
|
| 878 |
)
|
| 879 |
|
| 880 |
-
#
|
| 881 |
extracted_text = gr.Textbox(
|
| 882 |
label="解説対象テキスト(トークの元ネタ)",
|
| 883 |
-
placeholder="ファイルをアップロードするか、URLを入力するか、直接ここにテキストを
|
| 884 |
lines=10,
|
| 885 |
)
|
| 886 |
|
| 887 |
-
# ClearButtonにextracted_textを設定
|
| 888 |
-
clear_text_btn.add([extracted_text])
|
| 889 |
-
|
| 890 |
with gr.Column(variant="panel"):
|
| 891 |
gr.Markdown("### プロンプト設定")
|
| 892 |
document_type_radio = gr.Radio(
|
|
@@ -1054,9 +1076,16 @@ class PaperPodcastApp:
|
|
| 1054 |
)
|
| 1055 |
|
| 1056 |
# Set up event handlers
|
| 1057 |
-
#
|
| 1058 |
-
|
| 1059 |
-
fn=self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1060 |
inputs=[
|
| 1061 |
file_input,
|
| 1062 |
extracted_text,
|
|
|
|
| 335 |
session_id = request.session_hash
|
| 336 |
return UserSession(session_id)
|
| 337 |
|
| 338 |
+
def clear_extracted_text(self) -> str:
|
| 339 |
+
"""Clear the extracted text area."""
|
| 340 |
+
return ""
|
| 341 |
+
|
| 342 |
def set_openai_api_key(self, api_key: str, user_session: UserSession):
|
| 343 |
"""Set the OpenAI API key for the specific user session."""
|
| 344 |
if not api_key or api_key.strip() == "":
|
|
|
|
| 504 |
logger.error(error_msg)
|
| 505 |
return f"Error: {str(e)}", user_session
|
| 506 |
|
| 507 |
+
def extract_file_text_auto(
|
| 508 |
+
self,
|
| 509 |
+
file_obj,
|
| 510 |
+
existing_text: str,
|
| 511 |
+
add_separator: bool,
|
| 512 |
+
user_session: UserSession,
|
| 513 |
+
) -> Tuple[None, str, UserSession]:
|
| 514 |
+
"""Extract text from uploaded file automatically (for file upload mode)."""
|
| 515 |
+
if file_obj is None:
|
| 516 |
+
logger.warning("No file provided for automatic extraction")
|
| 517 |
+
return None, existing_text, user_session
|
| 518 |
+
|
| 519 |
+
# Extract new text from file
|
| 520 |
+
new_text = ContentExtractor.extract_text(file_obj)
|
| 521 |
+
|
| 522 |
+
# Get source name from file
|
| 523 |
+
source_name = ContentExtractor.get_source_name_from_file(file_obj)
|
| 524 |
+
|
| 525 |
+
# Append to existing text with source information
|
| 526 |
+
combined_text = ContentExtractor.append_text_with_source(
|
| 527 |
+
existing_text, new_text, source_name, add_separator
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
logger.debug(
|
| 531 |
+
f"Auto file text extraction completed for session {user_session.session_id}"
|
| 532 |
+
)
|
| 533 |
+
# Return None for file_input to clear it after extraction
|
| 534 |
+
return None, combined_text, user_session
|
| 535 |
+
|
| 536 |
def generate_podcast_audio_streaming(
|
| 537 |
self, text: str, user_session: UserSession, progress=gr.Progress()
|
| 538 |
):
|
|
|
|
| 865 |
# サポートしているファイル形式の拡張子を取得
|
| 866 |
supported_extensions = ContentExtractor.SUPPORTED_EXTENSIONS
|
| 867 |
|
| 868 |
+
# Content extraction tabs
|
| 869 |
+
gr.Markdown("### 解説対象テキストの作成")
|
| 870 |
+
|
| 871 |
+
extraction_tabs = gr.Tabs()
|
| 872 |
+
with extraction_tabs:
|
| 873 |
+
with gr.TabItem("ファイルアップロード"):
|
| 874 |
file_input = gr.File(
|
| 875 |
file_types=supported_extensions,
|
| 876 |
type="filepath",
|
| 877 |
label=f"ファイルをアップロード({', '.join(supported_extensions)})",
|
| 878 |
height=120,
|
| 879 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 880 |
|
| 881 |
+
with gr.TabItem("Webページ抽出"):
|
| 882 |
url_input = gr.Textbox(
|
| 883 |
placeholder="https://example.com/page",
|
| 884 |
label="WebページのURLを入力",
|
|
|
|
| 886 |
lines=2,
|
| 887 |
)
|
| 888 |
url_extract_btn = gr.Button(
|
| 889 |
+
"URLからテキストを抽出", variant="primary", size="lg"
|
| 890 |
)
|
| 891 |
|
| 892 |
+
# Auto separator checkbox and clear button in the same row
|
| 893 |
with gr.Row(equal_height=True):
|
| 894 |
+
with gr.Column(scale=3):
|
| 895 |
auto_separator_checkbox = gr.Checkbox(
|
| 896 |
label="テキスト抽出時に区切りを自動挿入",
|
| 897 |
value=True,
|
| 898 |
info="ファイル名やURLの情報を含む区切り線を自動挿入します",
|
| 899 |
)
|
| 900 |
+
with gr.Column(scale=1):
|
| 901 |
+
clear_text_btn = gr.Button(
|
| 902 |
+
"テキストをクリア", variant="secondary", size="sm"
|
|
|
|
|
|
|
|
|
|
| 903 |
)
|
| 904 |
|
| 905 |
+
# Extracted text display
|
| 906 |
extracted_text = gr.Textbox(
|
| 907 |
label="解説対象テキスト(トークの元ネタ)",
|
| 908 |
+
placeholder="ファイルをアップロードするか、URLを入力するか、直接ここにテキストを入力してください...",
|
| 909 |
lines=10,
|
| 910 |
)
|
| 911 |
|
|
|
|
|
|
|
|
|
|
| 912 |
with gr.Column(variant="panel"):
|
| 913 |
gr.Markdown("### プロンプト設定")
|
| 914 |
document_type_radio = gr.Radio(
|
|
|
|
| 1076 |
)
|
| 1077 |
|
| 1078 |
# Set up event handlers
|
| 1079 |
+
# Clear text button
|
| 1080 |
+
clear_text_btn.click(
|
| 1081 |
+
fn=self.clear_extracted_text,
|
| 1082 |
+
outputs=[extracted_text],
|
| 1083 |
+
queue=False,
|
| 1084 |
+
)
|
| 1085 |
+
|
| 1086 |
+
# Auto file extraction when file is uploaded (file upload mode)
|
| 1087 |
+
file_input.change(
|
| 1088 |
+
fn=self.extract_file_text_auto,
|
| 1089 |
inputs=[
|
| 1090 |
file_input,
|
| 1091 |
extracted_text,
|