CoolFace
Apppublic

malepati/custom_template_working

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
export_utils.py246 linesDownload Raw Back to utils
1# import json2# import os3# import aiohttp4# from typing import Literal5# import uuid6# from fastapi import HTTPException7# from pathvalidate import sanitize_filename8 9# from models.pptx_models import PptxPresentationModel10# from models.presentation_and_path import PresentationAndPath11# from services.pptx_presentation_creator import PptxPresentationCreator12# from services.temp_file_service import TEMP_FILE_SERVICE13# from utils.asset_directory_utils import get_exports_directory14# import uuid15 16 17# async def export_presentation(18#     presentation_id: uuid.UUID, title: str, export_as: Literal["pptx", "pdf"]19# ) -> PresentationAndPath:20#     if export_as == "pptx":21 22#         # Get the converted PPTX model from the Next.js service23#         async with aiohttp.ClientSession() as session:24#             async with session.get(25#                 f"http://localhost/api/presentation_to_pptx_model?id={presentation_id}"26#             ) as response:27#                 if response.status != 200:28#                     error_text = await response.text()29#                     print(f"Failed to get PPTX model: {error_text}")30#                     raise HTTPException(31#                         status_code=500,32#                         detail="Failed to convert presentation to PPTX model",33#                     )34#                 pptx_model_data = await response.json()35 36#         # Create PPTX file using the converted model37#         pptx_model = PptxPresentationModel(**pptx_model_data)38#         temp_dir = TEMP_FILE_SERVICE.create_temp_dir()39#         pptx_creator = PptxPresentationCreator(pptx_model, temp_dir)40#         await pptx_creator.create_ppt()41 42#         export_directory = get_exports_directory()43#         pptx_path = os.path.join(44#             export_directory,45#             f"{sanitize_filename(title or str(uuid.uuid4()))}.pptx",46#         )47#         pptx_creator.save(pptx_path)48 49#         return PresentationAndPath(50#             presentation_id=presentation_id,51#             path=pptx_path,52#         )53#     else:54#         async with aiohttp.ClientSession() as session:55#             async with session.post(56#                 "http://localhost/api/export-as-pdf",57#                 json={58#                     "id": str(presentation_id),59#                     "title": sanitize_filename(title or str(uuid.uuid4())),60#                 },61#             ) as response:62#                 response_json = await response.json()63 64#         return PresentationAndPath(65#             presentation_id=presentation_id,66#             path=response_json["path"],67#         )68 69 70 71 72import json73import os74import aiohttp75from typing import Literal, Optional76import uuid77from fastapi import HTTPException78from pathvalidate import sanitize_filename79 80from models.pptx_models import PptxPresentationModel81from models.presentation_and_path import PresentationAndPath82from services.pptx_presentation_creator import PptxPresentationCreator83from services.temp_file_service import TEMP_FILE_SERVICE84from utils.asset_directory_utils import get_exports_directory85import uuid86 87 88async def export_presentation(89    presentation_id: uuid.UUID, title: str, export_as: Literal["pptx", "pdf"], auth_token: Optional[str] = None90) -> PresentationAndPath:91    if export_as == "pptx":92 93        # Get the converted PPTX model from the Next.js service94        headers = {}95        if auth_token:96            headers["Authorization"] = auth_token97 98        async with aiohttp.ClientSession() as session:99            async with session.get(100                f"http://localhost:3000/api/presentation_to_pptx_model?id={presentation_id}",101                headers=headers102            ) as response:103                if response.status != 200:104                    error_text = await response.text()105                    print(f"Failed to get PPTX model: {error_text}")106                    raise HTTPException(107                        status_code=500,108                        detail="Failed to convert presentation to PPTX model",109                    )110                pptx_model_data = await response.json()111 112        # Create PPTX file using the converted model113        pptx_model = PptxPresentationModel(**pptx_model_data)114 115        # HACK: Fetch slides from DB to verify if we missed any native charts (since Next.js scraper misses them)116        # We need a new session here or pass one in. Since this function doesn't take session, we must create one or use a context manager if possible.117        # However, making a new session cleanly is hard without dependency injection.118        # ALTERNATIVE: Use the API to fetch raw content? No.119        # Let's use the local `database.py` imports if available, but better: 120        # The presentation object usually passed to export might be better. 121        # But here we only have ID.122        123        # Let's try to fetch the slides via the existing Next.js API? No, Next.js API returns what it sees.124        # We must connect to DB.125        # from services.database import get_async_session126        # from models.sql.slide import SlideModel127        # from sqlalchemy import select128        # from models.pptx_models import (129        #     PptxChartModel, 130        #     PptxPositionModel, 131        #     PptxChartDataPointModel,132        #     PptxPictureBoxModel,133        #     PptxTextBoxModel,134        #     PptxAutoShapeBoxModel135        # )136        137        # # Helper to run async session manually138        # async for session in get_async_session():139        #     db_slides = await session.scalars(select(SlideModel).where(SlideModel.presentation == presentation_id).order_by(SlideModel.index))140        #     db_slides_list = db_slides.all()141        #     142        #     # Inject charts143        #     for i, db_slide in enumerate(db_slides_list):144        #         if i < len(pptx_model.slides):145        #             content = db_slide.content146        #             if isinstance(content, dict) and content.get("__chart_data__"):147        #                 chart_data_dict = content["__chart_data__"]148        #                 149        #                 # Create Chart Model150        #                 # Position: Default to bottom right or center-right.151        #                 # Using 50% width on right side.152        #                 # chart_shape = PptxChartModel(153        #                 #      chart_type=chart_data_dict.get("type", "BAR").upper(),154        #                 #      title=chart_data_dict.get("title", "Chart"),155        #                 #      data=[PptxChartDataPointModel(label=d["label"], value=d["value"]) for d in chart_data_dict.get("data", [])],156        #                 #      position=PptxPositionModel(157        #                 #          left=int(1280 * 0.05), # 5% margin158        #                 #          top=int(720 * 0.2),  # Below title159        #                 #          width=int(1280 * 0.9), # Full width minus margin160        #                 #          height=int(720 * 0.7) # large area161        #                 #      )162        #                 # )163        #                 # pptx_model.slides[i].shapes.append(chart_shape)164        #     break # Consume only one session165 166        # --- DYNAMIC LAYOUT PATCH ---167        # Resize content to full width if image is missing or a placeholder168        SLIDE_WIDTH = 1280169        MARGIN = 60 # Standard margin170 171        for slide in pptx_model.slides:172            image_shapes_indices = []173            content_shapes = []174 175            # Identify shapes176            for idx, shape in enumerate(slide.shapes):177                if isinstance(shape, PptxPictureBoxModel):178                    # Check if placeholder or empty179                    if not shape.picture.path or "placeholder.jpg" in shape.picture.path:180                        image_shapes_indices.append(idx)181                    # Note: We treat existing real images as "keeping the layout"182                elif isinstance(shape, (PptxTextBoxModel, PptxAutoShapeBoxModel)):183                    # Identify body content (heuristic: not the title)184                    # Titles usually have top < 120 (roughly)185                    if shape.position.top > 120:186                        content_shapes.append(shape)187 188            # If we found placeholders, remove them189            if image_shapes_indices:190                # Remove in reverse order191                for idx in sorted(image_shapes_indices, reverse=True):192                    slide.shapes.pop(idx)193            194            # Re-check if any images remain on the slide195            has_real_images = any(isinstance(s, PptxPictureBoxModel) for s in slide.shapes)196            has_charts = any(isinstance(s, PptxChartModel) for s in slide.shapes)197 198            # SAFETY CHECK: Only resize content if we *actually* removed a placeholder/empty image.199            # This prevents destroying layouts that were designed to be text-only (e.g. 2-column text)200            # which naturally have no images but shouldn't be merged into one big block.201            if image_shapes_indices and not has_real_images and not has_charts and content_shapes:202                # print(f"Resizing content for slide with no images")203                for shape in content_shapes:204                    # Make full width with margins205                    shape.position.left = MARGIN206                    shape.position.width = SLIDE_WIDTH - (MARGIN * 2)207                    # We don't touch top/height to preserve vertical flow208        # ----------------------------209 210        temp_dir = TEMP_FILE_SERVICE.create_temp_dir()211        pptx_creator = PptxPresentationCreator(pptx_model, temp_dir)212        await pptx_creator.create_ppt()213 214        export_directory = get_exports_directory()215        pptx_path = os.path.join(216            export_directory,217            f"{sanitize_filename(title or str(uuid.uuid4()))}.pptx",218        )219        pptx_creator.save(pptx_path)220 221        return PresentationAndPath(222            presentation_id=presentation_id,223            path=pptx_path,224        )225    else:226        # Send Authorization header if present227        headers = {}228        if auth_token:229            headers["Authorization"] = auth_token230 231        async with aiohttp.ClientSession() as session:232            async with session.post(233                "http://localhost:3000/api/export-as-pdf",234                headers=headers,235                json={236                    "id": str(presentation_id),237                    "title": sanitize_filename(title or str(uuid.uuid4())),238                },239            ) as response:240                response_json = await response.json()241 242        return PresentationAndPath(243            presentation_id=presentation_id,244            path=response_json["path"],245        )246