import datetime, os, pathlib, time from fastapi import APIRouter, Body, Depends, Header, HTTPException, Path, Query, Response, status from fastapi.responses import FileResponse, StreamingResponse from pydantic import BaseModel, EmailStr, Field from typing import Dict, List, Optional, Set, Union from app.lib_general import common_route_params, Common_Route_Params from app.log import log, logging, logger_reset from app.config import settings from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, redis_lookup_id_random from app.routers.api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template from app.methods.event_file_methods import create_event_file_obj, handle_delete_event_file, load_event_file_obj # , update_event_file_obj from app.methods.hosted_file_methods import load_hosted_file_obj, handle_delete_hosted_file from app.models.event_file_models import Event_File_Base from app.models.response_models import Resp_Body_Base, mk_resp router = APIRouter() @router.post('/event/file', response_model=Resp_Body_Base) async def post_event_file_obj( event_file_obj: Event_File_Base, x_account_id: str = Header(...), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'event_file' event_file_obj_data_dict = event_file_obj.dict(by_alias=False, exclude_unset=True) result = post_obj_template( obj_type = obj_type, data = event_file_obj_data_dict, return_obj = True, by_alias = True, exclude_unset = True, ) return result # ### BEGIN ### API Event File ### create_from_hosted_file() ### # Updated 2022-08-18 @router.post('/event/file/from_hosted_file/{hosted_file_id}', response_model=Resp_Body_Base) async def create_from_hosted_file( event_file_obj: Event_File_Base, hosted_file_id: str = Path(min_length=11, max_length=22), inc_hosted_file: bool = False, return_obj: bool = True, commons: Common_Route_Params = Depends(common_route_params), ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING # time.sleep(2.5) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING if load_hosted_file_obj_result := load_hosted_file_obj( hosted_file_id = hosted_file_id, ): pass else: return mk_resp(data=False, status_code=400, response=commons.response, status_message='The event session was not created. Check the field names and data types.') hosted_file_obj = load_hosted_file_obj_result event_file_data = {} event_file_data['hosted_file_id_random'] = hosted_file_id event_file_data['for_type'] = event_file_obj.for_type # event_file_data['for_id'] = event_file_obj.for_id event_file_data['for_id_random'] = event_file_obj.for_id_random # event_file_data['event_id'] = event_file_obj.event_id event_file_data['event_id_random'] = event_file_obj.event_id_random # event_file_data['event_session_id'] = event_file_obj.event_session_id event_file_data['event_session_id_random'] = event_file_obj.event_session_id_random # event_file_data['event_presentation_id'] = event_file_obj.event_presentation_id event_file_data['event_presentation_id_random'] = event_file_obj.event_presentation_id_random # event_file_data['event_presenter_id'] = event_file_obj.event_presenter_id event_file_data['event_presenter_id_random'] = event_file_obj.event_presenter_id_random # event_file_data['event_location_id'] = event_file_obj.event_location_id event_file_data['event_location_id_random'] = event_file_obj.event_location_id_random # event_file_data['event_track_id'] = event_file_obj.event_track_id event_file_data['event_track_id_random'] = event_file_obj.event_track_id_random if event_file_obj.filename: event_file_data['filename'] = event_file_obj.filename else: event_file_data['filename'] = hosted_file_obj.filename if event_file_obj.extension: event_file_data['extension'] = event_file_obj.extension else: event_file_data['extension'] = hosted_file_obj.extension event_file_data['open_in_os'] = event_file_obj.open_in_os event_file_data['internal_use'] = event_file_obj.internal_use # event_file_data['public_use'] = hosted_file_obj.public_use # event_file_data['lu_file_purpose_id'] = hosted_file_obj.lu_file_purpose_id # event_file_data['file_purpose'] = hosted_file_obj.file_purpose # event_file_data['public'] = hosted_file_obj.public # event_file_data['hide'] = hosted_file_obj.hide event_file_data['enable'] = True # hosted_file_obj.enable log.debug(event_file_data) try: event_file_obj = Event_File_Base(**event_file_data) except ValidationError as e: log.error(e.json()) return False log.debug(event_file_obj) create_event_file_obj_result = create_event_file_obj(event_file_obj_new=event_file_obj) log.debug(create_event_file_obj_result) if isinstance(create_event_file_obj_result, int): event_file_id = create_event_file_obj_result log.info(f'New Event File created. Event File ID = {event_file_id}') if return_obj: if load_event_file_obj_result := load_event_file_obj( event_file_id = event_file_id, inc_hosted_file = inc_hosted_file, model_as_dict = True, ): data = load_event_file_obj_result else: data = False else: event_file_id = event_file_id event_file_id_random = get_id_random(record_id=event_file_id, table_name='event_file') data = {} data['event_file_id'] = event_file_id data['event_file_id_random'] = event_file_id_random log.debug(data) return mk_resp(data=data, response=commons.response, status_message='The event file was created.') else: return mk_resp(data=False, status_code=400, response=commons.response, status_message='The result from trying to create an event file was unexpected.') # ### END ### API Event File ### create_from_hosted_file() ### @router.patch('/event/file/{obj_id}', response_model=Resp_Body_Base) async def patch_event_file_obj( obj_id: str = Path(min_length=11, max_length=22), obj: Event_File_Base = None, x_account_id: Optional[str] = Header(..., ), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'event_file' obj_data_dict = obj.dict(by_alias=False, exclude_unset=True) obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=obj_type) obj_data_dict['id_random'] = obj_id result = patch_obj_template( obj_type=obj_type, data=obj_data_dict, obj_id=obj_id, return_obj=True, by_alias=True, exclude_unset=True, ) return result # ### BEGIN ### API Event File ### get_event_file_lookup() ### # Updated 2021-10-06 @router.get('/event/file/lookup', response_model=Resp_Body_Base) async def event_file_lookup( hosted_file_id: str = Query(..., min_length=11, max_length=22), for_type: str = Query(..., min_length=5, max_length=15), for_id: str = Query(..., min_length=11, max_length=22), inc_hosted_file: bool = False, x_account_id: str = Header(...), by_alias: bool = True, exclude_unset: bool = True, response: Response = Response, ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if hosted_file_id := redis_lookup_id_random(record_id_random=hosted_file_id, table_name='hosted_file'): pass else: return mk_resp(data=False, status_code=404, status_message=f'Hosted File ID was not found. Hosted File ID: {hosted_file_id}', response=response) # Not Found if for_id := redis_lookup_id_random(record_id_random=for_id, table_name=for_type): pass else: return mk_resp(data=False, status_code=404, status_message=f'For ID was not found. For Type: {for_type}, For ID: {for_id}', response=response) # Not Found data = {} data['hosted_file_id'] = hosted_file_id data['for_type'] = for_type data['for_id'] = for_id log.debug(data) sql = f""" SELECT id AS 'event_file_id', id_random AS 'event_file_id_random' FROM `event_file` AS `event_file` WHERE `event_file`.hosted_file_id = :hosted_file_id AND `event_file`.for_type = :for_type AND `event_file`.for_id = :for_id """ log.debug(sql) if event_file_obj_result := sql_select(data=data, sql=sql): log.debug(event_file_obj_result) event_file_id = event_file_obj_result.get('event_file_id', None) if event_file_obj := load_event_file_obj( event_file_id = event_file_id, inc_hosted_file = inc_hosted_file, enabled = 'all', # NOTE: This should not be hardcoded model_as_dict = False, ): event_file_dict = event_file_obj.dict(by_alias = by_alias, exclude_unset=exclude_unset) else: return mk_resp(data=False, status_code=400, response=response) # Bad Request else: log.debug(event_file_obj_result) return mk_resp(data=None, status_code=404, status_message=f'An Event File was not found. Hosted File ID: {hosted_file_id}, For Type: {for_type}, For ID: {for_id}', response=response) # Not Found return mk_resp(data=event_file_dict, response=response) # ### END ### API Event File ### get_event_file_lookup() ### # ### BEGIN ### API Event File ### download_event_file() ### # Updated 2021-11-23 @router.get('/event/file/{event_file_id}/download', response_model=Resp_Body_Base) async def download_event_file( event_file_id: str = Path(min_length=11, max_length=22), filename: str = Query(None, min_length=4, max_length=255), streaming: bool = False, commons: Common_Route_Params = Depends(common_route_params), ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING # time.sleep(5.5) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING # ### SECTION ### Secondary data validation if event_file_id := redis_lookup_id_random(record_id_random=event_file_id, table_name='event_file'): pass else: return mk_resp(data=None, status_code=404, response=commons.response, status_message='The event_file ID was invalid or not found.') hosted_files_path = settings.FILES_PATH['hosted_files_root'] log.info(f'Hosted Files Path: {hosted_files_path}') if event_file_obj := load_event_file_obj( event_file_id = event_file_id, inc_hosted_file = True, ): pass else: return mk_resp(data=False, status_code=400, response=commons.response) # Bad Request if not filename: filename = event_file_obj.filename log.info(f'Filename: {filename}') # dir_path = event_file_obj.hosted_file.directory_path subdir_path = event_file_obj.hosted_file.subdirectory_path hash_sha256 = event_file_obj.hosted_file.hash_sha256 if not hash_sha256: log.error(f'The hash_sha256 value was not found in the database record. Event File ID: {event_file_id}') return mk_resp(data=None, status_code=404, response=commons.response, status_message=f'The hash_sha256 value was not found in the database record. Event File ID: {event_file_id}') # Not Found hash_filename = hash_sha256+'.file' if subdir_path: full_subdirectory_path = os.path.join(hosted_files_path, subdir_path) else: full_subdirectory_path = hosted_files_path log.debug(full_subdirectory_path) pathlib.Path(full_subdirectory_path).mkdir(parents=True, exist_ok=True) file_path_w_subdir = os.path.join(full_subdirectory_path, hash_filename) log.info(f'Full file path with subdirectory: {file_path_w_subdir}') if os.path.exists(file_path_w_subdir): log.info('Hosted file found on server.') if streaming: log.warning('Streaming!!!') def iterfile(): # with open(file_path_w_subdir, mode="rb") as file_like: # yield from file_like # return StreamingResponse(iterfile(), media_type='video/mp4') else: return FileResponse(file_path_w_subdir, filename=filename) else: log.error(f'The hosted file was not found on the server. Hash: {hash_sha256}') return mk_resp(data=None, status_code=404, response=commons.response, status_message=f'The hosted file was not found on the server. Hash: {hash_sha256}') # Not Found # ### END ### API Event File ### download_event_file() ### # ### BEGIN ### API Event File ### get_event_file_obj() ### # Updated 2021-10-21 @router.get('/event/file/{event_file_id}', response_model=Resp_Body_Base) async def get_event_file_obj( event_file_id: str = Path(min_length=11, max_length=22), inc_hosted_file: bool = False, enabled: str = 'enabled', # enabled, disabled, all; For now this covers any included objects or object lists hidden: str = 'not_hidden', # hidden, not_hidden, all x_account_id: str = Header(...), by_alias: bool = True, exclude_unset: bool = True, response: Response = Response, ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if event_file_id := redis_lookup_id_random(record_id_random=event_file_id, table_name='event_file'): pass else: return mk_resp(data=None, status_code=404, response=response) if event_file_obj := load_event_file_obj( event_file_id = event_file_id, enabled = enabled, # hidden = hidden, inc_hosted_file = inc_hosted_file, model_as_dict = False, ): event_file_dict = event_file_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) pass else: return mk_resp(data=False, status_code=400, response=response) # Bad Request return mk_resp(data=event_file_dict, response=response) #return mk_resp(data=event_file_obj) # ### END ### API Event File ### get_event_file_obj() ### # # ### BEGIN ### API Event File ### delete_event_file_obj() ### # # NOTE: This should be replaced by v2 below!!! 2022-08-18 # # Updated 2021-11-02 # @router.delete('/event/file/{event_file_id}', response_model=Resp_Body_Base) # async def delete_event_file_obj( # event_file_id: str = Path(min_length=11, max_length=22), # hosted_file_id: str = Query(..., min_length=11, max_length=22), # for_type: str = Query(..., min_length=1, max_length=25), # for_id: str = Query(..., min_length=11, max_length=22), # delete_hosted_file: bool = False, # Not currently used # commons: Common_Route_Params = Depends(common_route_params), # ): # log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL # log.debug(locals()) # if event_file_id := redis_lookup_id_random(record_id_random=event_file_id, table_name='event_file'): pass # else: return mk_resp(data=None, status_code=404, response=commons.response) # if hosted_file_id := redis_lookup_id_random(record_id_random=hosted_file_id, table_name='hosted_file'): pass # else: return mk_resp(data=None, status_code=404, response=response) # if for_id := redis_lookup_id_random(record_id_random=for_id, table_name=for_type): pass # else: return mk_resp(data=None, status_code=404, response=commons.response) # if event_file_delete_result := sql_delete(table_name='event_file', record_id=event_file_id): # log.info(f'Deleted Event File ID: {event_file_id}') # else: return mk_resp(data=False, status_code=500, response=commons.response) # Internal Server Error # sql = f""" # DELETE FROM hosted_file_link # WHERE hosted_file_id = :hosted_file_id # AND link_to_type = :for_type # AND link_to_id = :for_id # """ # hosted_file_link_data = {} # hosted_file_link_data['hosted_file_id'] = hosted_file_id # hosted_file_link_data['for_type'] = for_type # hosted_file_link_data['for_id'] = for_id # log.debug(hosted_file_link_data) # if event_file_delete_result := sql_delete(sql=sql, data=hosted_file_link_data): # log.info(f'Deleted Hosted File Link. Hosted File ID: {hosted_file_id}, Link To Type: {for_type}, Link To ID: {for_id}') # else: return mk_resp(data=False, status_code=500, response=commons.response) # Internal Server Error # return mk_resp(data=True, response=commons.response) # # ### END ### API Event File ### delete_event_file_obj() ### # ### BEGIN ### API Event File ### delete_event_file_obj_v2() ### # Updated 2022-08-18 @router.delete('/event/file/{event_file_id}/v2', response_model=Resp_Body_Base) async def delete_event_file_obj_v2( event_file_id: str = Path(min_length=11, max_length=22), delete_hosted_file: bool = False, # Not currently used rm_orphan: bool = False, commons: Common_Route_Params = Depends(common_route_params), ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if event_file_id := redis_lookup_id_random(record_id_random=event_file_id, table_name='event_file'): pass else: return mk_resp(data=None, status_code=404, response=commons.response, status_message=f'The Event File ID was invalid or not found. event_file_id={event_file_id}') if event_file_obj := load_event_file_obj( event_file_id = event_file_id, ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL # event_file_dict = event_file_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) log.info('Event File loaded') log.debug(event_file_obj) pass else: log.error('Something went wrong while trying to load the event file.') return mk_resp(data=False, status_code=400, response=response) # Bad Request # try: # log.debug(event_file_obj.hosted_file_id) # except e: # log.exception(e) # log.debug(event_file_obj.for_type) # log.debug(event_file_obj.for_id) hosted_file_id = event_file_obj.hosted_file_id for_type = event_file_obj.for_type for_id = event_file_obj.for_id link_to_type = for_type link_to_id = for_id log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(f'Event File ID: {event_file_id}') if handle_delete_event_file(event_file_id=event_file_id): if delete_hosted_file: pass else: return mk_resp(data=True, response=commons.response) else: log.error('Something went wrong while trying to delete the event file record') return mk_resp(data=False, status_code=500, response=commons.response, status_message=f'Something went wrong while trying to delete the event file record. Event File ID: {event_file_id}') # Internal Server Error # ### SECTION ### Handle the deletion of records and file if hosted_file_delete_result := handle_delete_hosted_file(account_id=commons.x_account_id, hosted_file_id=hosted_file_id, link_to_type=link_to_type, link_to_id=link_to_id, rm_orphan=rm_orphan): return mk_resp(data=True, response=commons.response, status_message='The hosted file link was deleted. Not an orphan file.') elif hosted_file_delete_result is None: log.warning(f'The file and or hosted file record may have already been deleted. Hosted File ID: {hosted_file_id}') return mk_resp(data=None, status_code=404, response=commons.response, status_message='The file and or hosted file record may have already been deleted.') # Not Found (maybe sort of...) else: log.error(f'Something may have gone wrong while trying to delete the hosted file from the server or the hosted_file record.') return mk_resp(data=False, status_code=400, response=commons.response, status_message='Something may have gone wrong while trying to delete the hosted file from the server or the hosted_file record.') # Bad Request # if event_file_delete_result := sql_delete(table_name='event_file', record_id=event_file_id): # log.info(f'Deleted Event File ID: {event_file_id}') # else: return mk_resp(data=False, status_code=500, response=commons.response) # Internal Server Error # return mk_resp(data=True, response=commons.response) # sql = f""" # DELETE FROM hosted_file_link # WHERE hosted_file_id = :hosted_file_id # AND link_to_type = :for_type # AND link_to_id = :for_id # """ # hosted_file_link_data = {} # hosted_file_link_data['hosted_file_id'] = hosted_file_id # hosted_file_link_data['for_type'] = for_type # hosted_file_link_data['for_id'] = for_id # log.debug(hosted_file_link_data) # if event_file_delete_result := sql_delete(sql=sql, data=hosted_file_link_data): # log.info(f'Deleted Hosted File Link. Hosted File ID: {hosted_file_id}, Link To Type: {for_type}, Link To ID: {for_id}') # else: return mk_resp(data=False, status_code=500, response=commons.response) # Internal Server Error # return mk_resp(data=True, response=commons.response) # ### END ### API Event File ### delete_event_file_obj_v2() ###