Spaces:
Paused
Paused
Upload folder using huggingface_hub
Browse files- app.py +32 -4
- verify_slides_logic.py +102 -0
app.py
CHANGED
|
@@ -122,7 +122,15 @@ def setup_mkslides():
|
|
| 122 |
# Install dependencies and mkslides
|
| 123 |
subprocess.run(["pip", "install", "./external/mkslides"])
|
| 124 |
else:
|
| 125 |
-
print("mkslides already present.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
setup_mkslides()
|
| 128 |
|
|
@@ -689,6 +697,16 @@ def get_reports_in_branch(repo_full_name, branch_name, filter_type=None):
|
|
| 689 |
|
| 690 |
# Method 1: Check user_experience_reports directory
|
| 691 |
reports = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
try:
|
| 693 |
contents = repo.get_contents("user_experience_reports", ref=branch_name)
|
| 694 |
for content_file in contents:
|
|
@@ -730,6 +748,7 @@ def get_reports_in_branch(repo_full_name, branch_name, filter_type=None):
|
|
| 730 |
# Highest priority: specific report.md and slides.md in user_experience_reports
|
| 731 |
if filter_type == "report" and p_lower == "user_experience_reports/report.md": score -= 1000
|
| 732 |
if filter_type == "slides" and p_lower == "user_experience_reports/slides.md": score -= 1000
|
|
|
|
| 733 |
|
| 734 |
# High priority: other files in user_experience_reports
|
| 735 |
if "user_experience_reports" in p_lower: score -= 100
|
|
@@ -819,7 +838,12 @@ def render_slides(repo_full_name, branch_name, report_path):
|
|
| 819 |
# Method 1: Check for multi-file slides folder
|
| 820 |
# We check this first if the report_path is in user_experience_reports or if it's default
|
| 821 |
if "user_experience_reports" in report_path:
|
| 822 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
try:
|
| 824 |
folder_contents = repo.get_contents(slides_folder, ref=branch_name)
|
| 825 |
if isinstance(folder_contents, list):
|
|
@@ -892,8 +916,12 @@ def render_slides(repo_full_name, branch_name, report_path):
|
|
| 892 |
|
| 893 |
if os.path.exists(f"{output_dir}/index.html"):
|
| 894 |
# Return IFrame pointing to the generated site.
|
| 895 |
-
#
|
| 896 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 897 |
else:
|
| 898 |
return "Failed to render slides."
|
| 899 |
|
|
|
|
| 122 |
# Install dependencies and mkslides
|
| 123 |
subprocess.run(["pip", "install", "./external/mkslides"])
|
| 124 |
else:
|
| 125 |
+
print("mkslides directory already present. Ensuring it is installed...")
|
| 126 |
+
subprocess.run(["pip", "install", "./external/mkslides"])
|
| 127 |
+
|
| 128 |
+
# Verify installation
|
| 129 |
+
res = subprocess.run(["which", "mkslides"], capture_output=True, text=True)
|
| 130 |
+
if res.returncode == 0:
|
| 131 |
+
print(f"mkslides verified at: {res.stdout.strip()}")
|
| 132 |
+
else:
|
| 133 |
+
print("WARNING: mkslides CLI not found in PATH after installation.")
|
| 134 |
|
| 135 |
setup_mkslides()
|
| 136 |
|
|
|
|
| 697 |
|
| 698 |
# Method 1: Check user_experience_reports directory
|
| 699 |
reports = []
|
| 700 |
+
|
| 701 |
+
# Check for merged slides folder first if we are looking for slides
|
| 702 |
+
if filter_type == "slides":
|
| 703 |
+
try:
|
| 704 |
+
repo.get_contents("user_experience_reports/slides", ref=branch_name)
|
| 705 |
+
reports.append("user_experience_reports/slides")
|
| 706 |
+
add_log("Detected 'user_experience_reports/slides' directory. Added as merged presentation option.")
|
| 707 |
+
except:
|
| 708 |
+
pass
|
| 709 |
+
|
| 710 |
try:
|
| 711 |
contents = repo.get_contents("user_experience_reports", ref=branch_name)
|
| 712 |
for content_file in contents:
|
|
|
|
| 748 |
# Highest priority: specific report.md and slides.md in user_experience_reports
|
| 749 |
if filter_type == "report" and p_lower == "user_experience_reports/report.md": score -= 1000
|
| 750 |
if filter_type == "slides" and p_lower == "user_experience_reports/slides.md": score -= 1000
|
| 751 |
+
if filter_type == "slides" and p_lower == "user_experience_reports/slides": score -= 2000
|
| 752 |
|
| 753 |
# High priority: other files in user_experience_reports
|
| 754 |
if "user_experience_reports" in p_lower: score -= 100
|
|
|
|
| 838 |
# Method 1: Check for multi-file slides folder
|
| 839 |
# We check this first if the report_path is in user_experience_reports or if it's default
|
| 840 |
if "user_experience_reports" in report_path:
|
| 841 |
+
# If the user selected the folder itself or a file within a folder that has a slides subfolder
|
| 842 |
+
if report_path.endswith("/slides") or report_path.endswith("/slides/"):
|
| 843 |
+
slides_folder = report_path
|
| 844 |
+
else:
|
| 845 |
+
slides_folder = "user_experience_reports/slides"
|
| 846 |
+
|
| 847 |
try:
|
| 848 |
folder_contents = repo.get_contents(slides_folder, ref=branch_name)
|
| 849 |
if isinstance(folder_contents, list):
|
|
|
|
| 916 |
|
| 917 |
if os.path.exists(f"{output_dir}/index.html"):
|
| 918 |
# Return IFrame pointing to the generated site.
|
| 919 |
+
# Use a path relative to the current working directory for Gradio's /file endpoint
|
| 920 |
+
rel_path = os.path.relpath(f"{output_dir}/index.html", os.getcwd())
|
| 921 |
+
add_log(f"Slides rendered. Serving from relative path: {rel_path}")
|
| 922 |
+
|
| 923 |
+
# Use 'file={rel_path}' which is more robust in HF Spaces than '/file={abs_path}'
|
| 924 |
+
return f'<iframe src="file={rel_path}" width="100%" height="600px" frameborder="0"></iframe>'
|
| 925 |
else:
|
| 926 |
return "Failed to render slides."
|
| 927 |
|
verify_slides_logic.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import unittest
|
| 3 |
+
from unittest.mock import MagicMock, patch
|
| 4 |
+
|
| 5 |
+
# Mock Gradio and other imports that might fail or are not needed for logic test
|
| 6 |
+
import sys
|
| 7 |
+
sys.modules['gradio'] = MagicMock()
|
| 8 |
+
sys.modules['github'] = MagicMock()
|
| 9 |
+
sys.modules['openai'] = MagicMock()
|
| 10 |
+
sys.modules['chevron'] = MagicMock()
|
| 11 |
+
|
| 12 |
+
# Import the functions from app.py
|
| 13 |
+
# We need to handle the global variables in app.py
|
| 14 |
+
with patch('app.gh', MagicMock()):
|
| 15 |
+
import app
|
| 16 |
+
|
| 17 |
+
class TestSlidesLogic(unittest.TestCase):
|
| 18 |
+
def setUp(self):
|
| 19 |
+
self.repo_name = "test/repo"
|
| 20 |
+
self.branch_name = "main"
|
| 21 |
+
app.gh = MagicMock()
|
| 22 |
+
self.mock_repo = MagicMock()
|
| 23 |
+
app.gh.get_repo.return_value = self.mock_repo
|
| 24 |
+
|
| 25 |
+
def test_get_reports_in_branch_with_slides_folder(self):
|
| 26 |
+
# Mock successful detection of slides folder
|
| 27 |
+
def get_contents_mock(path, ref=None):
|
| 28 |
+
if path == "user_experience_reports/slides":
|
| 29 |
+
return MagicMock() # directory exists
|
| 30 |
+
if path == "user_experience_reports":
|
| 31 |
+
mock_file = MagicMock()
|
| 32 |
+
mock_file.name = "report.md"
|
| 33 |
+
return [mock_file]
|
| 34 |
+
raise Exception("404 Not Found")
|
| 35 |
+
|
| 36 |
+
self.mock_repo.get_contents.side_effect = get_contents_mock
|
| 37 |
+
|
| 38 |
+
# Mock tree for recursive scan
|
| 39 |
+
mock_tree_element = MagicMock()
|
| 40 |
+
mock_tree_element.type = "blob"
|
| 41 |
+
mock_tree_element.path = "user_experience_reports/slides/01_intro.md"
|
| 42 |
+
self.mock_repo.get_git_tree.return_value.tree = [mock_tree_element]
|
| 43 |
+
|
| 44 |
+
reports = app.get_reports_in_branch(self.repo_name, self.branch_name, filter_type="slides")
|
| 45 |
+
|
| 46 |
+
self.assertIn("user_experience_reports/slides", reports)
|
| 47 |
+
# Should be at the top due to score -2000
|
| 48 |
+
self.assertEqual(reports[0], "user_experience_reports/slides")
|
| 49 |
+
|
| 50 |
+
@patch('subprocess.run')
|
| 51 |
+
@patch('os.makedirs')
|
| 52 |
+
@patch('os.path.exists')
|
| 53 |
+
@patch('shutil.rmtree')
|
| 54 |
+
@patch('builtins.open', new_callable=unittest.mock.mock_open)
|
| 55 |
+
def test_render_slides_merging(self, mock_open, mock_rmtree, mock_exists, mock_makedirs, mock_run):
|
| 56 |
+
# Mock exists to say index.html is created
|
| 57 |
+
mock_exists.side_effect = lambda p: True if "index.html" in p else False
|
| 58 |
+
|
| 59 |
+
# Mock slides folder contents
|
| 60 |
+
slide1 = MagicMock()
|
| 61 |
+
slide1.name = "01_slide.md"
|
| 62 |
+
slide1.path = "user_experience_reports/slides/01_slide.md"
|
| 63 |
+
|
| 64 |
+
slide2 = MagicMock()
|
| 65 |
+
slide2.name = "02_slide.md"
|
| 66 |
+
slide2.path = "user_experience_reports/slides/02_slide.md"
|
| 67 |
+
|
| 68 |
+
def get_contents_mock(path, ref=None):
|
| 69 |
+
if path == "user_experience_reports/slides":
|
| 70 |
+
return [slide1, slide2]
|
| 71 |
+
if "01_slide.md" in path:
|
| 72 |
+
m = MagicMock()
|
| 73 |
+
m.decoded_content = b"Slide 1 Content"
|
| 74 |
+
return m
|
| 75 |
+
if "02_slide.md" in path:
|
| 76 |
+
m = MagicMock()
|
| 77 |
+
m.decoded_content = b"Slide 2 Content"
|
| 78 |
+
return m
|
| 79 |
+
return MagicMock()
|
| 80 |
+
|
| 81 |
+
self.mock_repo.get_contents.side_effect = get_contents_mock
|
| 82 |
+
|
| 83 |
+
# We also need to mock add_log to avoid errors if it tries to print
|
| 84 |
+
with patch('app.add_log', MagicMock()):
|
| 85 |
+
result = app.render_slides(self.repo_name, self.branch_name, "user_experience_reports/slides")
|
| 86 |
+
|
| 87 |
+
# Verify merging logic
|
| 88 |
+
mock_open.assert_any_call(unittest.mock.ANY, "w")
|
| 89 |
+
handle = mock_open()
|
| 90 |
+
|
| 91 |
+
# Check if Slide 1 and Slide 2 are merged with ---
|
| 92 |
+
written_content = "".join(call.args[0] for call in handle.write.call_args_list)
|
| 93 |
+
self.assertIn("Slide 1 Content", written_content)
|
| 94 |
+
self.assertIn("Slide 2 Content", written_content)
|
| 95 |
+
self.assertIn("\n\n---\n\n", written_content)
|
| 96 |
+
|
| 97 |
+
# Verify IFrame URL is relative
|
| 98 |
+
self.assertIn('src="file=slides_site_', result)
|
| 99 |
+
self.assertNotIn('src="/file=', result)
|
| 100 |
+
|
| 101 |
+
if __name__ == "__main__":
|
| 102 |
+
unittest.main()
|