diff --git a/app/main.py b/app/main.py index 31017ff..ce16c09 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from . import config from app.log import log, logging # Import the routers here first: -from app.routers import api_crud, api, importing, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, event, event_badge, event_badge_template, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, site, site_domain, user, websockets, e_cvent, c_idaa, e_impexium +from app.routers import api_crud, api, importing, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, event, event_badge, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, site, site_domain, user, websockets, e_cvent, c_idaa, e_impexium from app.db_sql import db @@ -132,6 +132,11 @@ app.include_router( # prefix='/event/badge/template', tags=['Event Badge Template'], ) +app.include_router( + event_device.router, + # prefix='/event/device', + tags=['Event Device'], +) app.include_router( event_exhibit.router, # prefix='/event/exhibit', diff --git a/app/methods/event_device_methods.py b/app/methods/event_device_methods.py new file mode 100644 index 0000000..828a465 --- /dev/null +++ b/app/methods/event_device_methods.py @@ -0,0 +1,126 @@ +import datetime + +from typing import Dict, List, Optional, Set, Union +from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator + +from app.db_sql import redis_lookup_id_random, sql_enable_part, sql_insert, sql_limit_offset_part, sql_select, sql_update +from app.lib_general import log, logging, logger_reset + +from app.methods.event_cfg_methods import load_event_cfg_obj +from app.methods.event_location_methods import load_event_location_obj + +from app.models.common_field_schema import default_num_bytes +from app.models.event_cfg_models import Event_Cfg_Base +from app.models.event_device_models import Event_Device_Base +from app.models.event_location_models import Event_Location_Base + + +# ### BEGIN ### API Event Device Methods ### load_event_device_obj() ### +# Updated 2022-03-09 +@logger_reset +def load_event_device_obj( + event_device_id: int, + enabled: str = 'enabled', # enabled, disabled, all + limit: int = 100, + offset: int = 0, + by_alias: bool = True, + exclude_unset: bool = True, + model_as_dict: bool = False, + inc_event_cfg: bool = False, + inc_event_location: bool = False, + ) -> Event_Device_Base|dict|bool: + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + if event_device_rec := sql_select(table_name='v_event_device', record_id=event_device_id): pass + else: return False + + log.debug(event_device_rec) + + try: + event_device_obj = Event_Device_Base(**event_device_rec) + except ValidationError as e: + log.error(e.json()) + return False + log.debug(event_device_obj) + + # Updated 2022-03-09 + if inc_event_cfg: + log.info('Need to include event configuration...') + event_id = event_device_rec.get('event_id', None) + log.debug(event_id) + if event_cfg_result := load_event_cfg_obj( + event_id = event_id, + ): + event_device_obj.event_cfg = event_cfg_result + else: event_device_obj.event_cfg = {} # None + + # Updated 2022-03-09 + if inc_event_location: + log.info('Need to include event location...') + event_location_id = event_device_rec.get('event_location_id', None) + log.debug(event_location_id) + if event_location_result := load_event_location_obj( + event_location_id = event_location_id, + ): + event_device_obj.event_location = event_location_result + else: event_device_obj.event_location = {} # None + + if model_as_dict: + return event_device_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) # pylint: disable=no-member + else: + return event_device_obj +# ### END ### API Event Device Methods ### load_event_device_obj() ### + + +# ### BEGIN ### API Event Device Methods ### get_event_device_rec_list() ### +# Updated 2022-03-09 +@logger_reset +def get_event_device_rec_list( + for_type: str, # 'account' is a special case + for_id: str, + enabled: str = 'enabled', # enabled, disabled, all + limit: int = 100, + offset: int = 0, + ) -> list|bool: + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + if for_id := redis_lookup_id_random(record_id_random=for_id, table_name=for_type): pass + else: return False + + if for_type == 'account': + sql_for_type_id = f'`event_device`.account_id = :for_id' + elif for_type == 'event': + sql_for_type_id = f'`event_device`.event_id = :for_id' + elif for_type == 'event_location': + sql_for_type_id = f'`event_device`.event_location_id = :for_id' + else: return False + + data = {} + data['for_type'] = for_type + data['for_id'] = for_id + + sql_enabled, data['enable'] = sql_enable_part(table_name='event_device', enabled=enabled) # Reasonably safe return str and bool + sql_limit = sql_limit_offset_part(limit=limit, offset=offset) # Reasonably safe return str + + sql = f""" + SELECT `event_device`.id AS 'event_device_id', `event_device`.id_random AS 'event_device_id_random' + FROM `v_event_device` AS `event_device` + WHERE + {sql_for_type_id} + {sql_enabled} + ORDER BY `event_device`.created_on DESC, `event_device`.updated_on DESC + {sql_limit}; + """ + log.debug(sql) + + if event_device_rec_li_result := sql_select(data=data, sql=sql, as_list=True): + event_device_rec_li = event_device_rec_li_result + else: # [] or False + event_device_rec_li = event_device_rec_li_result + + log.debug(event_device_rec_li_result) + + return event_device_rec_li +# ### END ### API Event Device Methods ### get_event_device_rec_list() ### diff --git a/app/methods/event_file_methods.py b/app/methods/event_file_methods.py index 32a8be6..1ef5d8e 100644 --- a/app/methods/event_file_methods.py +++ b/app/methods/event_file_methods.py @@ -5,7 +5,7 @@ from fastapi import File, UploadFile from typing import Dict, List, Optional, Set, Union from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator -from app.db_sql import get_id_random, redis_lookup_id_random, sql_insert, sql_select, sql_update +from app.db_sql import get_id_random, redis_lookup_id_random, sql_enable_part, sql_insert, sql_limit_offset_part, sql_select, sql_update from app.lib_general import log, logging from app.methods.hosted_file_methods import load_hosted_file_obj diff --git a/app/methods/event_location_methods.py b/app/methods/event_location_methods.py index 24fc1d6..b63fa39 100644 --- a/app/methods/event_location_methods.py +++ b/app/methods/event_location_methods.py @@ -4,7 +4,7 @@ import datetime from typing import Dict, List, Optional, Set, Union from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator -from app.db_sql import redis_lookup_id_random, sql_insert, sql_select, sql_update +from app.db_sql import redis_lookup_id_random, sql_enable_part, sql_insert, sql_limit_offset_part, sql_select, sql_update from app.lib_general import log, logging, logger_reset # from app.methods.event_methods import load_event_obj diff --git a/app/methods/event_methods.py b/app/methods/event_methods.py index 4d68cf7..bd42c22 100644 --- a/app/methods/event_methods.py +++ b/app/methods/event_methods.py @@ -1,4 +1,3 @@ -from __future__ import annotations import datetime from typing import Dict, List, Optional, Set, Union @@ -164,11 +163,9 @@ def load_event_obj( if inc_event_abstract_list: pass if inc_event_badge_list: pass - # Updated 2021-06-30 + # Updated 2022-03-09 if inc_event_cfg: log.info('Need to include event configuration...') - # event_id = event_rec.get('event_id', None) - # log.debug(event_id) if event_cfg_result := load_event_cfg_obj( event_id = event_id, inc_event_registration_cfg = inc_event_registration_cfg, @@ -177,7 +174,7 @@ def load_event_obj( model_as_dict = model_as_dict, ): event_obj.event_cfg = event_cfg_result - else: event_obj.event_cfg = None + else: event_obj.event_cfg = {} # None if inc_event_device_list: pass if inc_event_exhibit_list: pass diff --git a/app/models/event_device_models.py b/app/models/event_device_models.py new file mode 100644 index 0000000..c4331c9 --- /dev/null +++ b/app/models/event_device_models.py @@ -0,0 +1,124 @@ +import datetime, hashlib, logging, os, pytz, redis, secrets + +from typing import Dict, List, Optional, Set, Union +from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator + +from app.db_sql import redis_lookup_id_random +from app.lib_general import log, logging + +from app.models.common_field_schema import base_fields, default_num_bytes +from app.models.event_cfg_models import Event_Cfg_Base +from app.models.event_location_models import Event_Location_Base + +# ### BEGIN ### API Event Device Models ### Event_Device_Base() ### +class Event_Device_Base(BaseModel): + log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + id_random: Optional[str] = Field( + **base_fields['event_device_id_random'], + alias = 'event_device_id_random', + default_factory = lambda:secrets.token_urlsafe(default_num_bytes), + ) + id: Optional[int] = Field( + alias = 'event_device_id' + ) + account_id_random: Optional[str] + account_id: Optional[int] + + event_id_random: Optional[str] + event_id: Optional[int] + + event_location_id_random: Optional[str] + event_location_id: Optional[int] + + code: Optional[str] + + name: Optional[str] + description: Optional[str] + + api_secret_key: Optional[str] + + host_file_cache_path: Optional[str] # Path for hash file cache only + host_file_temp_path: Optional[str] # Path for copied and renamed temporary files from hash file cache directory + recording_path: Optional[str] # Path to save recordings + + enable: Optional[bool] + + # hide: Optional[bool] + # priority: Optional[bool] + # sort: Optional[int] + # group: Optional[str] + + notes: Optional[str] + + created_on: Optional[datetime.datetime] = None + updated_on: Optional[datetime.datetime] = None + + # Including convenience data + # This is only for convenience. Probably going to keep unless it causes a problem. + + # Including JSON data + # other_json: Optional[Json] + # meta_json: Optional[Json] + + # Including other related objects + event_cfg: Optional[Event_Cfg_Base] + event_location: Optional[Event_Location_Base] + + _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) + + #@validator('event_device_id_random', always=True) + def event_device_id_random_copy(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if values['id_random']: + return values['id_random'] + return None + + @validator('id', always=True) + def event_device_id_lookup(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='event_device') + return None + + @validator('account_id', always=True) + def account_id_lookup(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('event_id', always=True) + def event_id_lookup(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('event_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='event') + return None + + @validator('event_location_id', always=True) + def event_location_id_lookup(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('event_location_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='event_location') + return None + + class Config: + underscore_attrs_are_private = True + allow_population_by_field_name = True + fields = base_fields +# ### END ### API Event Device Models ### Event_Device_Base() ### diff --git a/app/models/event_file_models.py b/app/models/event_file_models.py index 7c419e0..155b775 100644 --- a/app/models/event_file_models.py +++ b/app/models/event_file_models.py @@ -71,7 +71,6 @@ class Event_File_Base(BaseModel): enable_to: Optional[datetime.datetime] = None hide: Optional[bool] - priority: Optional[bool] sort: Optional[int] group: Optional[str] # Same or similar as file_purpose? diff --git a/app/models/hosted_file_models.py b/app/models/hosted_file_models.py index 0a909d6..81a55d6 100644 --- a/app/models/hosted_file_models.py +++ b/app/models/hosted_file_models.py @@ -49,12 +49,12 @@ class Hosted_File_Base(BaseModel): copy_timer: Optional[str] # This will probably only be populated on upload results saved: Optional[str] # This will probably only be populated on upload results - #metadata: Optional[str] + # metadata: Optional[str] - #hide: Optional[int] - #priority: Optional[int] - #sort: Optional[int] - #group: Optional[str] + # hide: Optional[bool] + # priority: Optional[bool] + # sort: Optional[int] + # group: Optional[str] notes: Optional[str] created_on: Optional[datetime.datetime] = None diff --git a/app/routers/event_device.py b/app/routers/event_device.py new file mode 100644 index 0000000..9a3b2b0 --- /dev/null +++ b/app/routers/event_device.py @@ -0,0 +1,101 @@ +import datetime, pytz, time +from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status +from pydantic import BaseModel, EmailStr, Field +from typing import Dict, List, Optional, Set, Union + +from app.lib_general import log, logging, common_route_params, Common_Route_Params +from app.config import settings +from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, get_id_random, 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_device_methods import get_event_device_rec_list, load_event_device_obj + +from app.models.common_field_schema import default_num_bytes +from app.models.event_device_models import Event_Device_Base +from app.models.response_models import Resp_Body_Base, mk_resp + + +router = APIRouter() + + +# ### BEGIN ### API Event Device ### get_event_device_obj() ### +# Updated 2022-03-09 +@router.get('/event/device/{event_device_id}', response_model=Resp_Body_Base) +async def get_event_device_obj( + event_device_id: str = Query(..., min_length=11, max_length=22), + + inc_event_cfg: bool = False, + inc_event_location: bool = False, + + commons: Common_Route_Params = Depends(common_route_params), + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + # ### SECTION ### Secondary data validation + if event_device_id := redis_lookup_id_random(record_id_random=event_device_id, table_name='event_device'): pass + else: return mk_resp(data=None, status_code=404, response=commons.response) + + if event_device_rec_result := load_event_device_obj( + event_device_id = event_device_id, + limit = commons.limit, + enabled = commons.enabled, + inc_event_cfg = inc_event_cfg, + inc_event_location = inc_event_location, + ): + log.info('Loading successful. Returning result') + return mk_resp(data=event_device_rec_result, response=commons.response) + elif isinstance(event_device_rec_result, list) or event_device_rec_result is None: # Empty list or None + log.info('No results') + return mk_resp(data=None, status_code=404, response=commons.response) # Not Found + else: + log.warning('Likely bad request') + return mk_resp(data=False, status_code=400, response=commons.response) # Bad Request +# ### END ### API Event Device ### get_event_device_obj() ### + + +# ### BEGIN ### API Event Device ### get_event_obj_device_list() ### +# Updated 2022-03-09 +@router.get('/event/{event_id}/device/list', response_model=Resp_Body_Base) +async def get_event_obj_device_list( + event_id: str = Query(..., min_length=11, max_length=22), + inc_event_cfg: bool = False, + inc_event_location: 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_id := redis_lookup_id_random(record_id_random=event_id, table_name='event'): pass + else: return mk_resp(data=None, status_code=404, response=commons.response) + + # Updated 2022-03-09 + if event_device_rec_list_result := get_event_device_rec_list( + for_type = 'event', + for_id = event_id, + enabled = commons.enabled, + limit = commons.limit, + offset = commons.offset, + ): + event_device_result_list = [] + for event_device_rec in event_device_rec_list_result: + if load_event_device_result := load_event_device_obj( + event_device_id = event_device_rec.get('event_device_id', None), + enabled = commons.enabled, + inc_event_cfg = inc_event_cfg, + inc_event_location = inc_event_location, + ): + event_device_result_list.append(load_event_device_result) + else: + event_device_result_list.append(None) + response_data = event_device_result_list + return mk_resp(data=response_data, response=commons.response) + elif isinstance(event_device_rec_list_result, list) or event_device_rec_list_result is None: # Empty list or None + log.info('No results') + return mk_resp(data=None, status_code=404, response=commons.response) # Not Found + else: + log.warning('Likely bad request') + return mk_resp(data=False, status_code=400, response=commons.response) # Bad Request +# ### END ### API Event Device ### get_event_obj_device_list() ### \ No newline at end of file diff --git a/app/routers/person.py b/app/routers/person.py index 87abdc3..0f2c18a 100644 --- a/app/routers/person.py +++ b/app/routers/person.py @@ -396,6 +396,7 @@ async def get_person_obj( @router.get('/account/{account_id}/person/list', response_model=Resp_Body_Base) async def get_account_obj_person_list( account_id: str = Query(..., min_length=11, max_length=22), + inc_address: bool = False, inc_contact: bool = False, # inc_membership_group_list: bool = False, # The list of all membership groups a person is a part of @@ -404,6 +405,7 @@ async def get_account_obj_person_list( # inc_order: bool = False, # inc_organization: bool = False, inc_user: bool = False, + commons: Common_Route_Params = Depends(common_route_params), ): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL