def burst_pdf(**kwargs): """ A task that creates a sub-folder next to a PDF file and extracts all pages from the PDF as PNG files into the sub-folder. """ from flask_app import app from filesystem_manager import get_abs_path, get_burst_path, get_file_data from filesystem_manager import delete_dir, make_dirs, path_exists from filesystem_sync import delete_folder from imagemagick import imagemagick_burst_pdf from models import Folder from util import get_file_extension (src,) = _extract_parameters(["src"], **kwargs) burst_folder_rel = get_burst_path(src) # Ensure src is a PDF if get_file_extension(src) not in app.config["PDF_FILE_TYPES"]: app.log.warn("Cannot burst non-PDF file: " + src) return # See if the burst folder already exists (in the database and on disk) db_folder = app.data_engine.get_folder(folder_path=burst_folder_rel) if db_folder is not None and db_folder.status == Folder.STATUS_ACTIVE: # Wipe the folder, old images, data, and uncache the old images delete_folder(db_folder, None, app.data_engine, None, app.log) deleted_ids = app.data_engine.list_image_ids(db_folder) for image_id in deleted_ids: app.image_engine._uncache_image_id(image_id) # See if the burst folder already exists (just on disk) if path_exists(burst_folder_rel, require_directory=True): # Wipe the folder and old images delete_dir(burst_folder_rel, recursive=True) # Create the burst folder and burst pdf_data = get_file_data(src) if pdf_data is not None: make_dirs(burst_folder_rel) burst_folder_abs = get_abs_path(burst_folder_rel) if not imagemagick_burst_pdf(pdf_data, burst_folder_abs, app.config["PDF_BURST_DPI"]): app.log.warn("Failed to burst PDF: " + src) else: app.log.warn("Cannot burst PDF, file not found: " + src)
def auto_sync_existing_file(rel_path, data_manager, task_manager, anon_history=True, burst_pdf='auto', _db_session=None): """ Returns the database record for an image file that is known to exist, creating a new record or un-deleting an old record if required, and always returning a value. This method creates anonymous image history entries when anon_history is True. If the current user should be recorded against an action, the caller should set anon_history to False and manually add a history record. The bursting of PDF files is also initiated here. By default, a PDF file will be burst if no burst folder already exists. Setting burst_pdf to False disables this, or setting burst_pdf to True will force it to be burst again. Raises a DoesNotExistError if the image path is in fact invalid. Raises a SecurityError if the image path is outside of IMAGES_BASE_DIR. Raises a DBError if the database record cannot be created. """ db_own = (_db_session is None) db_session = _db_session or data_manager.db_get_session() db_error = False try: # Get (or create) db record for the file on_create = on_image_db_create_anon_history if anon_history \ else on_image_db_create db_image = data_manager.get_or_create_image( rel_path, on_create, _db_session=db_session ) if not db_image: # Not expected raise DBError('Failed to add image to database: ' + rel_path) # Burst PDF if we need to # TODO This would be better in on_image_db_create if we can get a task_manager without # importing the one from flask_app. Needs to be compatible with the task server. if burst_pdf and app.config['PDF_BURST_TO_PNG']: can_burst = get_file_extension(rel_path) in app.config['PDF_FILE_TYPES'] if can_burst: if burst_pdf == 'auto': burst_pdf = not path_exists( get_burst_path(rel_path), require_directory=True ) if burst_pdf: burst_pdf_file(rel_path, task_manager) if db_image.status == Image.STATUS_ACTIVE: # The normal case return db_image else: # We need to undelete the database record db_image.status = Image.STATUS_ACTIVE if anon_history: on_image_db_create_anon_history(db_image) else: on_image_db_create(db_image) # Check whether the file's folder needs to be undeleted too if db_image.folder.status == Folder.STATUS_DELETED: auto_sync_existing_folder( db_image.folder.path, data_manager, _db_session=db_session ) return db_image except: db_error = True raise finally: if db_own: try: if db_error: db_session.rollback() else: db_session.commit() finally: db_session.close()