diff --git a/cumulus_lambda_functions/cumulus_auth_crud/__init__.py b/cumulus_lambda_functions/catalya_archive_trigger/__init__.py similarity index 100% rename from cumulus_lambda_functions/cumulus_auth_crud/__init__.py rename to cumulus_lambda_functions/catalya_archive_trigger/__init__.py diff --git a/cumulus_lambda_functions/catalya_archive_trigger/catalya_archive_trigger.py b/cumulus_lambda_functions/catalya_archive_trigger/catalya_archive_trigger.py new file mode 100644 index 00000000..ca3e23e9 --- /dev/null +++ b/cumulus_lambda_functions/catalya_archive_trigger/catalya_archive_trigger.py @@ -0,0 +1,318 @@ +import os +import json +from urllib.parse import urlparse +import posixpath +import requests +from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers +from mdps_ds_lib.lib.utils.json_validator import JsonValidator +from pystac import Item, Catalog, Collection + +from mdps_ds_lib.lib.aws.aws_param_store import AwsParamStore +from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 + +from cumulus_lambda_functions.daac_archiver.services.maap_api_client import MaapApiClient +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + + +class CatalyaArchiveTrigger: + HYSDS_MET_SCHEMA = { + 'type': 'object', + "username": "sshah", + "algorithm_name": "cardamom-noaa-downloader_2", + "algorithm_version": "1.0.0", + + 'required': ['username', 'algorithm_name', 'algorithm_version'], + 'properties': { + 'username': {'type': 'string'}, + 'algorithm_name': {'type': 'string'}, + 'algorithm_version': {'type': 'string'}, + } + } + @staticmethod + def join_s3_url(base_url: str, relative_path: str) -> str: + """ + Join a base S3 URL with a relative path, properly handling '.', '..', and multiple levels. + + Examples: + join_s3_url('s3://bucket/a/b/c/d', '../../data/abc.json') -> 's3://bucket/a/b/data/abc.json' + join_s3_url('s3://bucket/a/b/c/d', './file.json') -> 's3://bucket/a/b/c/file.json' + join_s3_url('s3://bucket/a/b/c/d', '../../../file.json') -> 's3://bucket/a/file.json' + + :param base_url: Base S3 URL (e.g., 's3://bucket/path/to/dir') or local path + :param relative_path: Relative path to join (e.g., '../../data/file.json', './file.json') + :return: Joined and normalized S3 URL or local path + """ + if base_url.startswith('s3://'): + # Parse the S3 URL + parsed = urlparse(base_url) + bucket = parsed.netloc + path = parsed.path + + # Use posixpath.join to combine paths, then normpath to resolve .. and . + joined_path = posixpath.join(path, relative_path) + normalized_path = posixpath.normpath(joined_path) + + # Reconstruct the S3 URL + return f's3://{bucket}{normalized_path}' + else: + # For local paths, use os.path + joined_path = os.path.join(base_url, relative_path) + return os.path.normpath(joined_path) + + def __init__(self): + self.__s3 = AwsS3() + self.__ssm = AwsParamStore() + self.__uds_api_creds_key = os.getenv('UDS_API_CREDS', '') + + def retrieve_all_stac_items(self, stac_catalog: dict, catalog_s3_url: str): + catalog_dir = os.path.dirname(catalog_s3_url) + LOGGER.debug(f"catalog S3 Dir: {catalog_dir}") + + catalog = Catalog.from_dict(stac_catalog) + LOGGER.info(f"Successfully parsed STAC catalog: {catalog.id}") + + # Extract STAC Items from the catalog (including items from collections) + item_links = [] + + # Get all child links from catalog + all_links = [k for k in catalog.get_links() if k.rel in ['item', 'child', 'collection'] and not k.target.startswith('http')] + LOGGER.info(f"Found {len(all_links)} total eligible links in catalog") + for each in all_links: + if os.path.isabs(each.target): + continue + each.target = self.join_s3_url(catalog_dir, each.target) + + for link in all_links: + # Check if link exists locally + b, p = self.__s3.split_s3_url(link.target) + if not self.__s3.exists(b, p): + LOGGER.warning(f"Local link file not found: {link.target}") + continue + + # Handle different link types + if link.rel == 'item': + # Direct item link + item_links.append(link.target) + LOGGER.info(f"Found item link: {link.target}") + elif link.rel == 'child' or link.rel == 'collection': + # Collection link - read collection and extract items + try: + collection_content = self.__s3.set_s3_url(link.target).read_small_txt_file() + collection_dict = json.loads(collection_content) + collection = Collection.from_dict(collection_dict) + collection_item_links = list(collection.get_item_links()) + collection_folder = os.path.dirname(link.target) + temp_item_links = [k.target for k in collection.get_links() if + k.rel in ['item'] and not k.target.startswith('http')] + for each in temp_item_links: + if os.path.isabs(each): + item_links.append(each) + else: + item_links.append(self.join_s3_url(collection_folder, each)) + LOGGER.info( + f"Found collection '{collection.id}' with {len(collection_item_links)} items: {link.target}") + except Exception as e: + LOGGER.warning(f"Failed to process collection link '{link.target}': {str(e)}") + continue + else: + # Other link types - log and ignore + LOGGER.info(f"Ignoring link of type '{link.rel}': {link.target}") + return list(set(item_links)) + + def retrieve_user_n_alg_name_version(self, catalog_s3_url): + b, p = self.__s3.split_s3_url(os.path.dirname(catalog_s3_url)) + s3_files = [k for k in self.__s3.get_child_s3_files(b, f'{p}/', lambda x: x['Key'].endswith('met.json'))] + if len(s3_files) < 1: + raise ValueError(f'missing file to find username + algorithm name & version') + errors = {} + for each_s3_file in s3_files: + hysds_metadata = json.loads(self.__s3.set_s3_url(f's3://{b}/{each_s3_file[0]}').read_small_txt_file()) + result = JsonValidator(self.HYSDS_MET_SCHEMA).validate(hysds_metadata) + if result is not None: + errors[each_s3_file[0]] = result + return { + 'username': hysds_metadata['username'], + 'algorithm_name': hysds_metadata['algorithm_name'], + 'algorithm_version': hysds_metadata['algorithm_version'], + } + raise ValueError(f'unable to find HySDS Metadata file {errors}') + + def retrieve_items(self, item_urls: list): + """ + Process a list of STAC item S3 URLs by downloading, parsing, and updating asset URLs. + + :param item_urls: List of S3 URLs (as strings or link objects with .target attribute) pointing to STAC item JSON files + :return: Dictionary mapping S3 URL to processed STAC item dictionary + """ + processed_items = {} + + for item_url_obj in item_urls: + # Handle both string URLs and link objects + item_s3_url = item_url_obj.target if hasattr(item_url_obj, 'target') else item_url_obj + + try: + LOGGER.info(f'Processing STAC item: {item_s3_url}') + + # Download and parse STAC item + item_content = self.__s3.set_s3_url(item_s3_url).read_small_txt_file() + item_dict = json.loads(item_content) + + # Convert to pystac Item object + stac_item = Item.from_dict(item_dict) + if stac_item.collection_id is None or stac_item.collection_id == '': + LOGGER.error(f'Missing collection_id for {item_s3_url}, skipping') + continue + + granule_id = stac_item.id + LOGGER.debug(f'Downloaded STAC item: {granule_id}') + + # Convert relative asset URLs to absolute S3 URLs and verify they exist + parsed_item_url = urlparse(item_s3_url) + item_bucket = parsed_item_url.netloc + item_path_parts = parsed_item_url.path.rsplit('/', 1) + item_base_path = item_path_parts[0] if len(item_path_parts) > 1 else '' + + s3_base_path = f's3://{item_bucket}{item_base_path}' + for asset_key, asset in stac_item.assets.items(): + asset_href = asset.href + + # If href is relative, convert to absolute S3 URL + if not asset_href.startswith('s3://') and not asset_href.startswith('http'): + # Remove leading ./ or / + absolute_s3_url = self.join_s3_url(s3_base_path, asset_href) + LOGGER.debug(f'Converted relative URL {asset.href} to absolute: {absolute_s3_url}') + asset.href = absolute_s3_url + + # Verify the S3 URL exists + if asset.href.startswith('s3://'): + bucket, path = self.__s3.split_s3_url(asset.href) + if not self.__s3.exists(bucket, path): + raise FileNotFoundError(f'Asset does not exist at S3 URL: {asset.href}') + LOGGER.debug(f'Verified asset exists: {asset.href}') + + # Links are not needed. removing them. + stac_item.clear_links() + # Store the updated item dictionary + processed_items[item_s3_url] = stac_item.to_dict() + LOGGER.info(f'Successfully processed STAC item: {granule_id}') + + except Exception as e: + LOGGER.exception(f'Error processing STAC item {item_s3_url}: {str(e)}') + raise + + LOGGER.info(f'Processed {len(processed_items)} STAC items') + return processed_items + + def start_with_event(self, event: dict): + result = AwsMessageTransformers().sqs_sns(event) + result1 = AwsMessageTransformers().get_s3_from_sns(result) + return self.start(f's3://{result1["bucket"]}/{result1["key"]}') + + def start(self, catalog_s3_url): + """ + Steps: + 1. You will be given an S3 URL. + Make sure it is an S3 URL. + 2. Download that catalog.json + 3. Call retrieve_all_stac_items with the downloaded dictionary. + 4. How they are retrieved will be abstracted. + The return will be a set or dictionary of item S3 URLs + 5. For each STAC item, download them, and convert them to a STAC Item object. + 6. for each asset, they will be relative URLs. + Convert them to S3 URL based on the item.json S3 URL where current folder is //item.json + Ensure those S3 URLs exist along the way. + Throw an exception to quit for now.. we'll revisit later. + 7. from ssm, retrieve __uds_api_creds_key. + 8. For each, use the UDS_API_CRED to call this method + @router.put("/{collection_id}/verbose_archive/{granule_id}") + Check out the file /cumulus_lambda_functions/catalya_uds_api/granules_archive_api.py for more details. + Do it in a serial fashion now. + :param catalog_s3_url: + :return: + """ + # Step 1: Validate S3 URL + LOGGER.info(f'Starting catalog archive trigger for: {catalog_s3_url}') + if not catalog_s3_url or not catalog_s3_url.startswith('s3://'): + raise ValueError(f'Invalid S3 URL: {catalog_s3_url}. Must start with s3://') + + parsed_url = urlparse(catalog_s3_url) + if not parsed_url.netloc or not parsed_url.path: + raise ValueError(f'Invalid S3 URL format: {catalog_s3_url}. Expected format: s3:///') + + # Step 2: Download catalog.json + LOGGER.info(f'Downloading catalog from: {catalog_s3_url}') + catalog_content = self.__s3.set_s3_url(catalog_s3_url).read_small_txt_file() + catalog_dict = json.loads(catalog_content) + LOGGER.debug(f'Catalog downloaded successfully') + + LOGGER.info('Retrieving username, algorithm name + version from HySDS Metadata') + hysds_metadata = self.retrieve_user_n_alg_name_version(catalog_s3_url) + + # Step 3: Retrieve all STAC items + LOGGER.info('Retrieving all STAC items from catalog') + item_s3_urls = self.retrieve_all_stac_items(catalog_dict, catalog_s3_url) + LOGGER.info(f'Found {len(item_s3_urls)} STAC items to process') + + # Step 4-6: Process all items (download, parse, update assets) + LOGGER.info('Processing and validating all STAC items') + processed_items = self.retrieve_items(item_s3_urls) + LOGGER.info(f'Successfully processed {len(processed_items)} STAC items') + + # Step 7: Retrieve UDS API credentials from SSM (do this once before loop) + LOGGER.info(f'Retrieving UDS API credentials from SSM: {self.__uds_api_creds_key}') + if not self.__uds_api_creds_key: + raise ValueError('UDS_API_CREDS environment variable not set') + + uds_api_creds_str = self.__ssm.get_param(self.__uds_api_creds_key) + uds_api_creds = json.loads(uds_api_creds_str) + + # Extract API base URL and bearer token + api_base_url = uds_api_creds.get('API_BASE_URL', '').rstrip('/') + user_creds = MaapApiClient().get_user_jwt_token(hysds_metadata['username']) + if not api_base_url or not user_creds: + raise ValueError('UDS API credentials must contain api_base_url and bearer_token') + + LOGGER.info(f'API base URL: {api_base_url}') + + # Step 8: Trigger archive API requests one by one + LOGGER.info(f'Triggering archive requests for all processed items: {processed_items}') + for item_s3_url, item_dict in processed_items.items(): + try: + collection_id = item_dict.get('collection') + granule_id = item_dict.get('id') + + if not collection_id or not granule_id: + LOGGER.error(f'Missing collection_id or granule_id in item: {item_s3_url}') + raise ValueError(f'Invalid STAC item missing collection or id: {item_s3_url}') + + LOGGER.info(f'Triggering archive for granule: {granule_id} from collection: {collection_id}') + + # Call the UDS API verbose_archive endpoint + api_url = f'{api_base_url}/collections/{collection_id}/verbose_archive/{granule_id}' + headers = { + # 'Authorization': user_creds, + 'proxy-ticket': user_creds, + 'Content-Type': 'application/json' + } + params = { + 'item_s3_url': item_s3_url + } + + LOGGER.info(f'Calling archive API: PUT {api_url}') + body_json = { + **hysds_metadata, + 'stac_item': item_dict + } + response = requests.put(api_url, headers=headers, params=params, json=body_json, timeout=30) + response.raise_for_status() + + LOGGER.info(f'Successfully triggered archive for granule {granule_id}: {response.json()}') + + except Exception as e: + LOGGER.exception(f'Error triggering archive for item {item_s3_url}: {str(e)}') + raise + + LOGGER.info(f'Completed triggering archive for all {len(processed_items)} STAC items') + return diff --git a/cumulus_lambda_functions/granules_cnm_response_writer/lambda_function.py b/cumulus_lambda_functions/catalya_archive_trigger/lambda_function.py similarity index 60% rename from cumulus_lambda_functions/granules_cnm_response_writer/lambda_function.py rename to cumulus_lambda_functions/catalya_archive_trigger/lambda_function.py index 9cb4c044..63a20dc6 100644 --- a/cumulus_lambda_functions/granules_cnm_response_writer/lambda_function.py +++ b/cumulus_lambda_functions/catalya_archive_trigger/lambda_function.py @@ -1,6 +1,6 @@ import json -from cumulus_lambda_functions.granules_cnm_response_writer.cnm_result_writer import CnmResultWriter +from cumulus_lambda_functions.catalya_archive_trigger.catalya_archive_trigger import CatalyaArchiveTrigger from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator @@ -11,5 +11,4 @@ def lambda_handler(event, context): :return: """ LambdaLoggerGenerator.remove_default_handlers() - CnmResultWriter().start(event) - return + return CatalyaArchiveTrigger().start_with_event(event) diff --git a/cumulus_lambda_functions/cumulus_es_setup/__init__.py b/cumulus_lambda_functions/catalya_uds_api/__init__.py similarity index 100% rename from cumulus_lambda_functions/cumulus_es_setup/__init__.py rename to cumulus_lambda_functions/catalya_uds_api/__init__.py diff --git a/cumulus_lambda_functions/catalya_uds_api/auth_admin_api.py b/cumulus_lambda_functions/catalya_uds_api/auth_admin_api.py new file mode 100644 index 00000000..b45a929c --- /dev/null +++ b/cumulus_lambda_functions/catalya_uds_api/auth_admin_api.py @@ -0,0 +1,250 @@ +from typing import Union + +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_auth_db import CataliaAuthDb +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_fast_api.fast_api_utils import FastApiUtils +from cumulus_lambda_functions.lib.uds_fast_api.web_service_constants import WebServiceConstants +from fastapi import APIRouter, HTTPException, Request + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + +import os + +from pydantic import BaseModel + +from mdps_ds_lib.lib.utils.json_validator import JsonValidator + +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + + +class AuthDeleteModel(BaseModel): + source: str + target: str + group_name: str + + +class AuthDeleteModelAlgorithm(BaseModel): + source: str + target: str + algorithm_name: str + algorithm_version: str + + +delete_schema = { + 'type': 'object', + 'required': ['source', 'target', 'group_name'], + 'properties': { + 'source': {'type': 'string'}, + 'target': {'type': 'string'}, + 'group_name': {'type': 'string'}, + } +} + + +class AuthListModel(BaseModel): + group_name: list[str] + + +list_schema = { + 'type': 'object', + 'properties': { + 'tenant': {'type': 'string'}, + 'venue': {'type': 'string'}, + 'group_names': { + 'type': 'array', + 'items': {'type': 'string'}, + 'minItems': 1, + }, + } +} + + +class AuthAddModel(BaseModel): + source: str + target: str + group_name: str + access: bool + +class AuthAddModelAlgorithm(BaseModel): + source: str + target: str + algorithm_name: str + algorithm_version: str + access: bool + +add_schema = { + 'type': 'object', + 'required': ['source', 'target', 'group_name', 'access'], + 'properties': { + 'source': {'type': 'string'}, + 'target': {'type': 'string'}, + 'group_name': {'type': 'string'}, + 'access': {'type': 'boolean'}, + } +} + + +class AuthCrud: + def __init__(self, authorization_info, request_body): + required_env = ['ADMIN_COMMA_SEP_GROUPS', 'CATALYA_DB_NAME'] + if not all([k in os.environ for k in required_env]): + raise EnvironmentError(f'one or more missing env: {required_env}') + self.__request_body = request_body + self.__authorization_info = authorization_info + self.__admin_groups = [k.strip() for k in os.getenv('ADMIN_COMMA_SEP_GROUPS').split(',')] + self.__cad = CataliaAuthDb(os.getenv('CATALYA_DB_NAME')) + + def is_admin(self): + belonged_admin_groups = list(set(self.__admin_groups) & set(self.__authorization_info['ldap_groups'])) + if len(belonged_admin_groups) < 1: + LOGGER.warn(f'unauthorized attempt to admin function: {self.__authorization_info}') + return { + 'statusCode': 403, + 'body': {'message': f'user is not in admin groups: {self.__admin_groups}'} + } + return { + 'statusCode': 200, + 'body': {} + } + + def list_all_record(self): + return { + 'statusCode': 501, + 'body': {'message': 'Not Implemented Yet'} + } + # return { + # 'statusCode': 200, + # 'body': all_records + # } + + def convert_algorithm_to_group_name(self): + self.__request_body['group_name'] = f"{self.__request_body['algorithm_name']}___{self.__request_body['algorithm_version']}" + return self + + def add_new_record(self): + body_validator_result = JsonValidator(add_schema).validate(self.__request_body) + if body_validator_result is not None: + LOGGER.error(f'invalid add body: {body_validator_result}. request_body: {self.__request_body}') + return { + 'statusCode': 500, + 'body': f'invalid add body: {body_validator_result}. request_body: {self.__request_body}' + } + self.__cad.add(self.__request_body['group_name'], self.__request_body['source'], self.__request_body['target'], self.__request_body['access']) + return { + 'statusCode': 200, + 'body': {'message': 'inserted'} + } + + def delete_record(self): + body_validator_result = JsonValidator(delete_schema).validate(self.__request_body) + if body_validator_result is not None: + LOGGER.error(f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}') + return { + 'statusCode': 500, + 'body': f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}' + } + self.__cad.delete(self.__request_body['group_name'], self.__request_body['source'], self.__request_body['target']) + return { + 'statusCode': 200, + 'body': {'message': 'deleted'} + } + + +router = APIRouter( + prefix=f'/{WebServiceConstants.ADMIN}/auth', + tags=["Admin Records CRUD (Admins-Only)"], + responses={404: {"description": "Not found"}}, +) + +@router.delete("") +@router.delete("/") +async def delete_auth_mapping(request: Request, delete_body: AuthDeleteModel): + """ + Deleting one authorization mapping + """ + LOGGER.debug(f'started delete_auth_mapping') + auth_info = FastApiUtils.get_authorization_info(request) + auth_crud = AuthCrud(auth_info, delete_body.model_dump()) + is_admin_result = auth_crud.is_admin() + if is_admin_result['statusCode'] != 200: + raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) + delete_result = auth_crud.delete_record() + if delete_result['statusCode'] == 200: + return delete_result['body'] + raise HTTPException(status_code=delete_result['statusCode'], detail=delete_result['body']) + +@router.post("") +@router.post("/") +async def add_auth_mapping(request: Request, new_body: AuthAddModel): + """ + Adding a new Authorization mapping + """ + LOGGER.debug(f'started add_auth_mapping. sss {new_body.model_dump()}') + auth_info = FastApiUtils.get_authorization_info(request) + auth_crud = AuthCrud(auth_info, new_body.model_dump()) + is_admin_result = auth_crud.is_admin() + if is_admin_result['statusCode'] != 200: + raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) + add_result = auth_crud.add_new_record() + if add_result['statusCode'] == 200: + return add_result['body'] + raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) + +@router.delete("/algorithm") +@router.delete("/algorithm/") +async def delete_auth_mapping(request: Request, delete_body: AuthDeleteModelAlgorithm): + """ + Deleting one authorization mapping + """ + LOGGER.debug(f'started delete_auth_mapping') + auth_info = FastApiUtils.get_authorization_info(request) + auth_crud = AuthCrud(auth_info, delete_body.model_dump()) + is_admin_result = auth_crud.is_admin() + if is_admin_result['statusCode'] != 200: + raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) + delete_result = auth_crud.convert_algorithm_to_group_name().delete_record() + if delete_result['statusCode'] == 200: + return delete_result['body'] + raise HTTPException(status_code=delete_result['statusCode'], detail=delete_result['body']) + +@router.post("/algorithm") +@router.post("/algorithm/") +async def add_auth_mapping_for_algorithms(request: Request, new_body: AuthAddModelAlgorithm): + """ + Adding a new Authorization mapping + """ + LOGGER.debug(f'started add_auth_mapping. sss {new_body.model_dump()}') + auth_info = FastApiUtils.get_authorization_info(request) + auth_crud = AuthCrud(auth_info, new_body.model_dump()) + is_admin_result = auth_crud.is_admin() + if is_admin_result['statusCode'] != 200: + raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) + add_result = auth_crud.convert_algorithm_to_group_name().add_new_record() + if add_result['statusCode'] == 200: + return add_result['body'] + raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) + +@router.get("") +@router.get("/") +async def list_auth_mappings(request: Request, tenant: Union[str, None]=None, venue: Union[str, None]=None, group_names: Union[str, None]=None): + """ + Listing all exsiting Authorization Mapping. + + """ + LOGGER.debug(f'started list_auth_mappings') + auth_info = FastApiUtils.get_authorization_info(request) + query_body = { + 'tenant': tenant, + 'venue': venue, + 'ldap_group_names': group_names if group_names is None else [k.strip() for k in group_names.split(',')], + } + auth_crud = AuthCrud(auth_info, query_body) + is_admin_result = auth_crud.is_admin() + if is_admin_result['statusCode'] != 200: + raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) + query_result = auth_crud.list_all_record() + if query_result['statusCode'] == 200: + return query_result['body'] + raise HTTPException(status_code=query_result['statusCode'], detail=query_result['body']) diff --git a/cumulus_lambda_functions/catalya_uds_api/granules_archive_api.py b/cumulus_lambda_functions/catalya_uds_api/granules_archive_api.py new file mode 100644 index 00000000..e223c1a2 --- /dev/null +++ b/cumulus_lambda_functions/catalya_uds_api/granules_archive_api.py @@ -0,0 +1,443 @@ +import json +import os +from typing import Optional +import uuid +import boto3 +from mdps_ds_lib.lib.aws.aws_param_store import AwsParamStore + +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_auth_db import CataliaAuthDb +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_daac_handshakes_db import CataliaDaacHandshakesDb +from cumulus_lambda_functions.daac_archiver.daac_archiver_catalia import DaacArchiverCatalia +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_status_db import CataliaStatusDb +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_fast_api.web_service_constants import WebServiceConstants +from cumulus_lambda_functions.lib.uds_fast_api.fast_api_utils import FastApiUtils +from fastapi import APIRouter, HTTPException, Request, Response +from pydantic import BaseModel +from mdps_ds_lib.lib.aws.aws_lambda import AwsLambda + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + +router = APIRouter( + prefix=f'/{WebServiceConstants.COLLECTIONS}', + tags=["Granules Archive CRUD API"], + responses={404: {"description": "Not found"}}, +) + +class ArchivingTypesModel(BaseModel): + data_type: str + file_extension: Optional[list[str]] = [] + + +class DaacUpdateModel(BaseModel): + api_key: str + daac_provider: str + daac_data_version: str + daac_sns_topic_arn: str + daac_role_arn: str + daac_role_session_name: str + archiving_types: Optional[list[ArchivingTypesModel]] = None + + +class VerboseArchiveRequestModel(BaseModel): + username: str + algorithm_name: str + algorithm_version: str + stac_item: dict + +class InternalDDBConnector: + def __init__(self): + required_env = ['CATALYA_DAAC_AGREEMENT_DB_NAME', 'CATALYA_DB_NAME'] + if not all([k in os.environ for k in required_env]): + raise EnvironmentError(f'one or more missing env: {required_env}') + self.cad = CataliaAuthDb(os.getenv('CATALYA_DB_NAME')) + self.cdhsd = CataliaDaacHandshakesDb(os.getenv('CATALYA_DAAC_AGREEMENT_DB_NAME')) + self.auth_info = {} + self.configured_daac_configs = [] + + def __archive_methods_initiator_internal(self, collection_id, daac_collection_id): + if daac_collection_id is None: + self.configured_daac_configs = self.cdhsd.search(collection_id) + configured_daac_ids = [] if len(self.configured_daac_configs) < 1 else [k[self.cdhsd.target_project] for k in self.configured_daac_configs] + else: + configured_daac_ids = [daac_collection_id] + + authorized_daacs = [] if len(configured_daac_ids) < 1 else self.cad.get_authorized_daac_full(self.auth_info.get('ldap_groups'), collection_id, configured_daac_ids) + if len(authorized_daacs) < 1: + LOGGER.debug(f'user: {self.auth_info["username"]} is not authorized for {collection_id}') + raise HTTPException(status_code=403, detail=json.dumps({ + 'message': 'not authorized to execute this action' + })) + return authorized_daacs + + def archive_methods_initiator(self, request, collection_id, daac_collection_id): + LOGGER.debug(f'started archive_methods_initiator.') + self.auth_info = FastApiUtils.get_authorization_info(request) + return self.__archive_methods_initiator_internal(collection_id, daac_collection_id) + + + def archive_methods_initiator_manual_algorithm(self, username, alg_name, alg_version, request, collection_id, daac_collection_id): + # Get user groups from the forwarded authorizer context + auth_info = FastApiUtils.get_authorization_info(request) + user_groups = auth_info.get('ldap_groups', []) + self.auth_info = { + 'username': username, + 'ldap_groups': user_groups + } + LOGGER.debug(f'self.auth_info: {self.auth_info}') + username_based_authorized_daacs = self.__archive_methods_initiator_internal(collection_id, daac_collection_id) + LOGGER.debug(f'username_based_authorized_daacs: {username_based_authorized_daacs}') + self.auth_info['ldap_groups'] = [f'{alg_name}___{alg_version}'] + algorithm_based_authorized_daacs = self.__archive_methods_initiator_internal(collection_id, daac_collection_id) + LOGGER.debug(f'algorithm_based_authorized_daacs: {algorithm_based_authorized_daacs}') + # Intersection: Only allow DAAC collections authorized by BOTH user groups AND algorithm + authorized_daacs = list(set(username_based_authorized_daacs) & set(algorithm_based_authorized_daacs)) + LOGGER.debug(f'authorized_daacs: {authorized_daacs}') + LOGGER.debug(f'Username authorized: {username_based_authorized_daacs}, Algorithm authorized: {algorithm_based_authorized_daacs}, Final: {authorized_daacs}') + if len(authorized_daacs) < 1: + LOGGER.debug(f'user: {username} is not authorized for {collection_id} based on {user_groups} and {alg_name} + {alg_version}') + raise HTTPException(status_code=403, detail=json.dumps({ + 'message': f'user: {username} is not authorized for {collection_id} based on {user_groups} and {alg_name} + {alg_version}' + })) + + return authorized_daacs +@router.post("/{collection_id}/{daac_collection_id}/archive") +@router.post("/{collection_id}/{daac_collection_id}/archive/") +async def add_daac_archive_config(request: Request, collection_id: str, daac_collection_id: str, new_body: DaacUpdateModel): + LOGGER.debug(f'started add_daac_archive_config. {new_body.model_dump()}') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, daac_collection_id) + # authorized_ldaps = [k['userGroup'] for k in authorized_daacs] + b1 = new_body.model_dump() + try: + # def add(self, catalia_collection, daac_collection, api_key, provider, data_version, sns_topic_arn, role_arn, role_session_name, archiving_types, user, user_group): + i1.cdhsd.add(collection_id, daac_collection_id, b1['api_key'], b1['daac_provider'], b1['daac_data_version'], + b1['daac_sns_topic_arn'], b1['daac_role_arn'], b1['daac_role_session_name'], b1['archiving_types'], i1.auth_info['username'], i1.auth_info.get('ldap_groups')) + except Exception as e: + LOGGER.exception(f'error while add_daac_archive_config: {b1}') + raise HTTPException(status_code=500, detail=e) + return {'message': 'archive config added'} + +@router.delete("/{collection_id}/{daac_collection_id}/archive") +@router.delete("/{collection_id}/{daac_collection_id}/archive/") +async def delete_daac_archive_config(request: Request, collection_id: str, daac_collection_id: str): + LOGGER.debug(f'started delete_daac_archive_config.') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, daac_collection_id) + try: + i1.cdhsd.delete(collection_id, daac_collection_id) + except Exception as e: + LOGGER.exception(f'error while delete_daac_archive_config: {collection_id}, {daac_collection_id}') + raise HTTPException(status_code=500, detail=e) + return {'message': 'archive config deleted'} + +@router.get("/{collection_id}/{daac_collection_id}/archive") +@router.get("/{collection_id}/{daac_collection_id}/archive/") +async def get_daac_archive_config(request: Request, collection_id: str, daac_collection_id: str): + LOGGER.debug(f'started get_daac_archive_config.') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, daac_collection_id) + try: + result = i1.cdhsd.get_single(collection_id, daac_collection_id) + except Exception as e: + LOGGER.exception(f'error while get_daac_archive_config: {collection_id}, {daac_collection_id}') + raise HTTPException(status_code=500, detail=e) + return {'result': result} +@router.put("/{collection_id}/archive/{granule_id}") +@router.put("/{collection_id}/archive/{granule_id}/") +async def archive_single_granule(request: Request, collection_id: str, granule_id: str, response: Response): + LOGGER.debug(f'started FAUX archive_single_granule.') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + + # Generate a UUID for this archive operation + operation_id = str(uuid.uuid4()) + + if os.getenv('IS_API_IN_DOCKER', 'FALSE') == 'TRUE': + LOGGER.debug(f'In docker. No time limit for archiving') + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.archive_granule(collection_id, granule_id, operation_id) + return {'message': 'archive initiated', 'operation_id': operation_id} + + # Async invocation for API Gateway to avoid timeout + archive_lambda_name = os.environ.get('ARCHIVE_LAMBDA_NAME', '').strip() + if not archive_lambda_name: + raise HTTPException(status_code=500, detail='ARCHIVE_LAMBDA_NAME environment variable not set') + + actual_path = f'{request.url.path}/actual/{operation_id}' if not request.url.path.endswith('/') else f'{request.url.path}actual/{operation_id}' + + # Extract the original authorizer context to forward it + lambda_event = request.scope.get('aws.event', {}) + authorizer_context = lambda_event.get('requestContext', {}).get('authorizer', {}) + + actual_event = { + 'resource': actual_path, + 'path': actual_path, + 'httpMethod': 'PUT', + 'headers': { + **FastApiUtils.get_authorization_token(request), # Forward all auth headers + 'Accept': '*/*', + 'Host': request.url.hostname, + }, + 'pathParameters': { + 'collection_id': collection_id, + 'granule_id': granule_id, + 'operation_id': operation_id + }, + 'requestContext': { + 'resourcePath': actual_path, + 'httpMethod': 'PUT', + 'domainName': request.url.hostname, + 'authorizer': authorizer_context, # Forward the authorizer context + }, + 'body': json.dumps({}), + 'isBase64Encoded': False + } + + LOGGER.info(f'Invoking async lambda for archive: {archive_lambda_name} with operation_id: {operation_id}') + response_lambda = AwsLambda().invoke_function( + function_name=archive_lambda_name, + payload=actual_event, + ) + LOGGER.debug(f'Async archive function started: {response_lambda}') + response.status_code = 202 + return {'message': 'archive processing', 'operation_id': operation_id} +@router.put("/{collection_id}/verbose_archive/{granule_id}") +@router.put("/{collection_id}/verbose_archive/{granule_id}/") +async def verbose_archive_single_granule(request: Request, collection_id: str, granule_id: str, item_s3_url: str, request_body: VerboseArchiveRequestModel, response: Response): + LOGGER.debug(f'started verbose_archive_single_granule with item_s3_url: {item_s3_url}') + LOGGER.debug(f'username: {request_body.username}, algorithm: {request_body.algorithm_name} v{request_body.algorithm_version}') + + # Validate item_s3_url + if not item_s3_url: + raise HTTPException(status_code=400, detail='item_s3_url parameter is required') + + if not item_s3_url.startswith('s3://') or len(item_s3_url.split('/')) < 4: + raise HTTPException(status_code=400, detail='item_s3_url must be in the format s3:///') + + # Extract STAC item from request body + stac_item = request_body.stac_item + LOGGER.debug(f'Received STAC item JSON: {json.dumps(stac_item)}') + + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator_manual_algorithm(request_body.username, request_body.algorithm_name, request_body.algorithm_version, request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + + # Generate a UUID for this archive operation + operation_id = str(uuid.uuid4()) + + if os.getenv('IS_API_IN_DOCKER', 'FALSE') == 'TRUE': + LOGGER.debug(f'In docker. No time limit for archiving') + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.verbose_archive_granule(collection_id, granule_id, item_s3_url, request_body.stac_item, operation_id) + + return {'message': 'archive initiated', 'operation_id': operation_id} + + # Async invocation for API Gateway to avoid timeout + archive_lambda_name = os.environ.get('ARCHIVE_LAMBDA_NAME', '').strip() + if not archive_lambda_name: + raise HTTPException(status_code=500, detail='ARCHIVE_LAMBDA_NAME environment variable not set') + + actual_path = f'{request.url.path}/actual/{operation_id}' if not request.url.path.endswith('/') else f'{request.url.path}actual/{operation_id}' + + # Extract the original authorizer context to forward it + lambda_event = request.scope.get('aws.event', {}) + authorizer_context = lambda_event.get('requestContext', {}).get('authorizer', {}) + + actual_event = { + 'resource': actual_path, + 'path': actual_path, + 'httpMethod': 'PUT', + 'headers': { + **FastApiUtils.get_authorization_token(request), # Forward all auth headers + 'Accept': '*/*', + 'Host': request.url.hostname, + }, + 'pathParameters': { + 'collection_id': collection_id, + 'granule_id': granule_id, + 'operation_id': operation_id + }, + 'queryStringParameters': { + 'item_s3_url': item_s3_url + }, + 'requestContext': { + 'resourcePath': actual_path, + 'httpMethod': 'PUT', + 'domainName': request.url.hostname, + 'authorizer': authorizer_context, # Forward the authorizer context + }, + 'body': json.dumps(request_body.model_dump()), + 'isBase64Encoded': False + } + + LOGGER.info(f'Invoking async lambda for verbose archive: {archive_lambda_name} with operation_id: {operation_id}') + response_lambda = AwsLambda().invoke_function( + function_name=archive_lambda_name, + payload=actual_event, + ) + LOGGER.debug(f'Async verbose archive function started: {response_lambda}') + response.status_code = 202 + return {'message': 'verbose archive processing', 'operation_id': operation_id} + +@router.put("/{collection_id}/verbose_archive/{granule_id}/actual/{operation_id}") +@router.put("/{collection_id}/verbose_archive/{granule_id}/actual/{operation_id}/") +async def verbose_archive_single_granule_actual(request: Request, collection_id: str, granule_id: str, operation_id: str, item_s3_url: str, request_body: VerboseArchiveRequestModel, response: Response): + LOGGER.debug(f'started verbose_archive_single_granule_actual with item_s3_url: {item_s3_url} and operation_id: {operation_id}') + LOGGER.debug(f'username: {request_body.username}, algorithm: {request_body.algorithm_name} v{request_body.algorithm_version}') + + + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator_manual_algorithm(request_body.username, request_body.algorithm_name, request_body.algorithm_version, request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + + + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.verbose_archive_granule(collection_id, granule_id, item_s3_url, request_body.stac_item, operation_id) + return {'message': 'archive initiated'} + +@router.put("/{collection_id}/archive/{granule_id}/actual/{operation_id}") +@router.put("/{collection_id}/archive/{granule_id}/actual/{operation_id}/") +async def archive_single_granule_actual(request: Request, collection_id: str, granule_id: str, operation_id: str): + LOGGER.debug(f'started archive_single_granule_actual with operation_id: {operation_id}') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.archive_granule(collection_id, granule_id, operation_id) + return {'message': 'archive initiated'} + +@router.put("/{collection_id}/archive") +@router.put("/{collection_id}/archive/") +async def archive_entire_collection(request: Request, collection_id: str, response: Response): + LOGGER.debug(f'started archive_entire_collection.') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + + if os.getenv('IS_API_IN_DOCKER', 'FALSE') == 'TRUE': + LOGGER.debug(f'In docker. No time limit for archiving collection') + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.archive_collection(collection_id) + return {'message': 'archive completed'} + + # Read Fargate configuration from SSM Parameter Store + prefix = os.getenv('PREFIX', '') + if not prefix: + raise HTTPException(status_code=500, detail='PREFIX environment variable not set') + + ssm_parameter_name = os.getenv('FARGATE_CONFIG', 'MISSING-FARGATE_CONFIG-Pls-Provide') + try: + fargate_config = AwsParamStore().get_param(ssm_parameter_name) + fargate_config = json.loads(fargate_config) + LOGGER.debug(f'Retrieved Fargate config from SSM: {ssm_parameter_name}') + except Exception as e: + LOGGER.exception(f'Failed to retrieve Fargate config from SSM: {ssm_parameter_name}') + raise HTTPException( + status_code=500, + detail=f'Failed to retrieve Fargate configuration from SSM: {str(e)}' + ) + + # Extract configuration from SSM + try: + ecs_cluster = fargate_config['CLUSTER_NAME'] + task_definition = fargate_config['TASK_DEFINITION'] + subnet_ids = fargate_config['SUBNET_IDs'] + security_group_ids = fargate_config['SECURITY_GROUPS'] + container_name = fargate_config['CONTAINER_NAME'] + except KeyError as e: + LOGGER.error(f'Missing required key in Fargate config: {e}') + raise HTTPException( + status_code=500, + detail=f'Invalid Fargate configuration in SSM, missing key: {str(e)}' + ) + + # Prepare environment variables for the Fargate task + # These match what docker_entrypoint/__main__.py expects for CATALYA_COLLECTION_ARCHIVE + container_overrides = { + 'environment': [ + {'name': 'CATALYA_UDS_STAGING_BUCKET', 'value': os.getenv('CATALYA_UDS_STAGING_BUCKET', '')}, + {'name': 'CATALYA_DAAC_CONFIGS', 'value': json.dumps(authorized_configured_daac_configs)}, + {'name': 'CATALYA_COLLECTION_ID', 'value': collection_id}, + {'name': 'LOG_LEVEL', 'value': os.getenv('LOG_LEVEL', '20')}, + ], + 'command': ['CATALYA_COLLECTION_ARCHIVE'] # This is passed as argv[1] to docker_entrypoint/__main__.py + } + + try: + ecs_client = boto3.client('ecs', region_name=os.getenv('AWS_REGION', 'us-west-2')) + + response_ecs = ecs_client.run_task( + cluster=ecs_cluster, + taskDefinition=task_definition, + launchType='FARGATE', + networkConfiguration={ + 'awsvpcConfiguration': { + 'subnets': subnet_ids, + 'securityGroups': security_group_ids, + 'assignPublicIp': 'ENABLED' # May need to adjust based on your VPC setup + } + }, + overrides={ + 'containerOverrides': [ + { + 'name': container_name, + **container_overrides + } + ] + } + ) + + task_arn = response_ecs['tasks'][0]['taskArn'] if response_ecs.get('tasks') else 'unknown' + LOGGER.info(f'Started Fargate task for collection archiving: {task_arn}') + + response.status_code = 202 + return { + 'message': 'collection archive processing started', + 'task_arn': task_arn, + 'collection_id': collection_id + } + except Exception as e: + LOGGER.exception(f'Failed to start Fargate task for collection archiving') + raise HTTPException(status_code=500, detail=f'Failed to start Fargate task: {str(e)}') + +@router.put("/{collection_id}/archive/actual") +@router.put("/{collection_id}/archive/actual/") +async def archive_entire_collection_actual(request: Request, collection_id: str): + LOGGER.debug(f'started archive_entire_collection.') + i1 = InternalDDBConnector() + authorized_daacs = i1.archive_methods_initiator(request, collection_id, None) + # authorized_ldaps = set([k['userGroup'] for k in authorized_daacs]) + authorized_configured_daac_configs = [k for k in i1.configured_daac_configs if k[i1.cdhsd.target_project] in authorized_daacs] + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = authorized_configured_daac_configs + dac.archive_collection(collection_id) # TODO accept filtering mechanisms? + return {'message': 'archive initiated'} + + +@router.get("/{operation_id}") +@router.get("/{operation_id}/") +async def get_archive_status(request: Request, operation_id: str): + LOGGER.debug(f'started get_archive_status with operation_id: {operation_id}') + status_ddb = CataliaStatusDb(os.getenv('CATALYA_STATUS_DB', None)) + existing_statuses = status_ddb.get(operation_id) + if len(existing_statuses) < 1: + raise HTTPException(status_code=404, detail=f'STATUS DB does not have any entry for {operation_id}') + return {'status_list': existing_statuses} diff --git a/cumulus_lambda_functions/uds_api/web_service.py b/cumulus_lambda_functions/catalya_uds_api/web_service.py similarity index 78% rename from cumulus_lambda_functions/uds_api/web_service.py rename to cumulus_lambda_functions/catalya_uds_api/web_service.py index edea8cd2..28123aa2 100644 --- a/cumulus_lambda_functions/uds_api/web_service.py +++ b/cumulus_lambda_functions/catalya_uds_api/web_service.py @@ -1,19 +1,18 @@ -from fastapi.staticfiles import StaticFiles - -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils +from cumulus_lambda_functions.catalya_uds_api import auth_admin_api +from cumulus_lambda_functions.catalya_uds_api import granules_archive_api +from cumulus_lambda_functions.lib.uds_fast_api.fast_api_utils import FastApiUtils from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator from dotenv import load_dotenv load_dotenv() import uvicorn -from fastapi import FastAPI +from fastapi import FastAPI, APIRouter from fastapi.middleware.cors import CORSMiddleware from mangum import Mangum from starlette.requests import Request -from cumulus_lambda_functions.uds_api.routes_api import main_router LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) api_base_prefix = FastApiUtils.get_api_base_prefix() @@ -30,11 +29,12 @@ allow_methods=["*"], allow_headers=["*"], ) + +main_router = APIRouter(redirect_slashes=False) +main_router.include_router(auth_admin_api.router) +main_router.include_router(granules_archive_api.router) app.include_router(main_router, prefix=f'/{api_base_prefix}') -stac_browser_prefix, temp_static_parent_dir = FastApiUtils.prep_stac_browser() -app.mount(stac_browser_prefix, StaticFiles(directory=temp_static_parent_dir, html=True), name="static") -app.mount(f'/{stac_browser_prefix}/', StaticFiles(directory=temp_static_parent_dir, html=True), name="static") """ Accept-Ranges: diff --git a/cumulus_lambda_functions/cumulus_auth_crud/auth_crud.py b/cumulus_lambda_functions/cumulus_auth_crud/auth_crud.py deleted file mode 100644 index fd769fae..00000000 --- a/cumulus_lambda_functions/cumulus_auth_crud/auth_crud.py +++ /dev/null @@ -1,181 +0,0 @@ -import json -import os - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory -from mdps_ds_lib.lib.utils.json_validator import JsonValidator - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants -from mdps_ds_lib.lib.utils.lambda_api_gateway_utils import LambdaApiGatewayUtils - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -delete_schema = { - 'type': 'object', - 'required': ['tenant', 'venue', 'group_name'], - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_name': {'type': 'string'}, - } -} - -list_schema = { - 'type': 'object', - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_names': { - 'type': 'array', - 'items': {'type': 'string'}, - 'minItems': 1, - }, - } -} - -add_schema = { - 'type': 'object', - 'required': ['tenant', 'venue', 'group_name', 'actions', 'resources'], - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_name': {'type': 'string'}, - 'actions': { - 'type': 'array', - 'items': { - 'type': 'string', - 'enum': [DBConstants.create, DBConstants.delete, DBConstants.update, DBConstants.read] - }, - 'minItems': 1, - }, - 'resources': { - 'type': 'array', - 'items': {'type': 'string'}, - 'minItems': 1, - }, - } - -} - - -class AuthCrud: - def __init__(self, event): - required_env = ['ES_URL', 'ADMIN_COMMA_SEP_GROUPS'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__admin_groups = [k.strip() for k in os.getenv('ADMIN_COMMA_SEP_GROUPS').split(',')] - self.__event = event - self.__request_body = {} - self.__es_url = os.getenv('ES_URL') - self.__es_port = int(os.getenv('ES_PORT', '443')) - self.__authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=self.__es_url, - es_port=self.__es_port - ) - self.__lambda_utils = LambdaApiGatewayUtils(self.__event, 10) - - def __is_admin(self): - auth_info = self.__lambda_utils.get_authorization_info() - belonged_admin_groups = list(set(self.__admin_groups) & set(auth_info['ldap_groups'])) - return len(belonged_admin_groups) > 0 - - def __load_request_body(self): - if 'body' in self.__event and self.__event['body'] is not None: - self.__request_body = json.loads(self.__event['body']) - return - - def list_all_record(self): - if not self.__is_admin(): - return { - 'statusCode': 403, - 'body': f'user is not in admin groups: {self.__admin_groups}' - } - self.__load_request_body() - all_records = self.__authorizer.list_groups( - tenant=self.__request_body['tenant'] if 'tenant' in self.__request_body else None, - venue=self.__request_body['venue'] if 'venue' in self.__request_body else None, - ldap_group_names=self.__request_body['group_names'] if 'group_names' in self.__request_body else None, - ) - return { - 'statusCode': 200, - 'body': json.dumps(all_records) - } - - def add_new_record(self): - if not self.__is_admin(): - return { - 'statusCode': 403, - 'body': f'user is not in admin groups: {self.__admin_groups}' - } - self.__load_request_body() - body_validator_result = JsonValidator(add_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid add body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid add body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.add_authorized_group( - action=self.__request_body['actions'], - resource=self.__request_body['resources'], - tenant=self.__request_body['tenant'], - venue=self.__request_body['venue'], - ldap_group_name=self.__request_body['group_name'], - ) - return { - 'statusCode': 200, - 'body': 'inserted' - } - - def update_record(self): - if not self.__is_admin(): - return { - 'statusCode': 403, - 'body': f'user is not in admin groups: {self.__admin_groups}' - } - self.__load_request_body() - body_validator_result = JsonValidator(add_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid update body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid update body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.update_authorized_group( - action=self.__request_body['actions'], - resource=self.__request_body['resources'], - tenant=self.__request_body['tenant'], - venue=self.__request_body['venue'], - ldap_group_name=self.__request_body['group_name'], - ) - return { - 'statusCode': 200, - 'body': 'updated' - } - - def delete_record(self): - if not self.__is_admin(): - return { - 'statusCode': 403, - 'body': f'user is not in admin groups: {self.__admin_groups}' - } - self.__load_request_body() - body_validator_result = JsonValidator(delete_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.delete_authorized_group( - tenant=self.__request_body.get('tenant'), - venue=self.__request_body.get('venue'), - ldap_group_name=self.__request_body.get('group_name'), - ) - return { - 'statusCode': 200, - 'body': 'deleted' - } diff --git a/cumulus_lambda_functions/cumulus_auth_crud/lambda_function.py b/cumulus_lambda_functions/cumulus_auth_crud/lambda_function.py deleted file mode 100644 index 4cb47ef9..00000000 --- a/cumulus_lambda_functions/cumulus_auth_crud/lambda_function.py +++ /dev/null @@ -1,22 +0,0 @@ -from cumulus_lambda_functions.cumulus_auth_crud.auth_crud import AuthCrud -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - - -def auth_list(event, context): - LambdaLoggerGenerator.remove_default_handlers() - return AuthCrud(event).list_all_record() - - -def auth_add(event, context): - LambdaLoggerGenerator.remove_default_handlers() - return AuthCrud(event).add_new_record() - - -def auth_update(event, context): - LambdaLoggerGenerator.remove_default_handlers() - return AuthCrud(event).update_record() - - -def auth_delete(event, context): - LambdaLoggerGenerator.remove_default_handlers() - return AuthCrud(event).delete_record() diff --git a/cumulus_lambda_functions/cumulus_es_setup/es_mappings.py b/cumulus_lambda_functions/cumulus_es_setup/es_mappings.py deleted file mode 100644 index 24e5b9c1..00000000 --- a/cumulus_lambda_functions/cumulus_es_setup/es_mappings.py +++ /dev/null @@ -1,50 +0,0 @@ -alias_pointer = { - "actions" : [ - {"add" : {"index" : "authorization_mappings_v1", "alias" : "authorization_mappings"}}, - {"add" : {"index" : "unity_collections_v1", "alias" : "unity_collections"}}, - {"add": {"index": "unity_granules_v1", "alias": "unity_granules_read"}}, - {"add": {"index": "unity_granules_v1", "alias": "unity_granules_write"}} - ] -} -unity_granules_v1 = { - "settings": { - "number_of_shards": 3, - "number_of_replicas": 2 - }, - "mappings": { - "properties": { - "granule_id": {"type": "keyword"} - } - } -} -authorization_mappings_v1 = { - "settings" : { - "number_of_shards" : 3, - "number_of_replicas" : 2 - }, - "mappings": { - "properties": { - "action": {"type": "keyword"}, - "collection_map": {"type": "keyword"}, - "user_group": {"type": "keyword"}, - "tenant": {"type": "keyword"}, - "tenant_venue": {"type": "keyword"} - } - } -} - -unity_collections_v1 = { - "settings" : { - "number_of_shards" : 3, - "number_of_replicas" : 2 - }, - "mappings": { - "properties": { - "collection_id": {"type": "keyword"}, - "bbox": {"type": "geo_shape"}, - "granule_count": {"type": "integer"}, - "start_time": {"type": "long"}, - "end_time": {"type": "long"} - } - } -} \ No newline at end of file diff --git a/cumulus_lambda_functions/cumulus_es_setup/es_setup.py b/cumulus_lambda_functions/cumulus_es_setup/es_setup.py deleted file mode 100644 index 8f595442..00000000 --- a/cumulus_lambda_functions/cumulus_es_setup/es_setup.py +++ /dev/null @@ -1,47 +0,0 @@ -import os - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from mdps_ds_lib.lib.aws.es_abstract import ESAbstract - -from mdps_ds_lib.lib.aws.es_factory import ESFactory - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants -from cumulus_lambda_functions.cumulus_es_setup import es_mappings -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class SetupESIndexAlias: - def __init__(self): - required_env = ['ES_URL'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__es: ESAbstract = ESFactory().get_instance(os.getenv('ES_TYPE', 'AWS'), - index=DBConstants.collections_index, - base_url=os.getenv('ES_URL'), - use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True, - port=int(os.getenv('ES_PORT', '443')) - ) - - def get_index_mapping(self, index_name: str): - if not hasattr(es_mappings, index_name): - raise ValueError(f'missing index_name: {index_name}') - index_json = getattr(es_mappings, index_name) - return index_json - - def start(self): - if not hasattr(es_mappings, 'alias_pointer'): - raise ValueError(f'missing alias_pointer') - alias_json = getattr(es_mappings, 'alias_pointer') - alias_json = [k['add'] for k in alias_json['actions']] - for each_action in alias_json: - try: - current_index = each_action['index'] - current_alias = each_action['alias'] - LOGGER.debug(f'working on {current_index}') - index_json = self.get_index_mapping(current_index) - self.__es.create_index(current_index, index_json) - self.__es.create_alias(current_index, current_alias) - except: - LOGGER.exception(f'failed to create index / alias for: {each_action}') - return self diff --git a/cumulus_lambda_functions/cumulus_es_setup/lambda_function.py b/cumulus_lambda_functions/cumulus_es_setup/lambda_function.py deleted file mode 100644 index 7992fa13..00000000 --- a/cumulus_lambda_functions/cumulus_es_setup/lambda_function.py +++ /dev/null @@ -1,13 +0,0 @@ -from cumulus_lambda_functions.cumulus_es_setup.es_setup import SetupESIndexAlias -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - - -def lambda_handler(event, context): - """ -{'cma': {'task_config': {'bucket': '{$.meta.buckets.internal.name}', 'collection': '{$.meta.collection}', 'cumulus_message': {'outputs': [{'source': '{$.files}', 'destination': '{$.payload}'}]}}, 'event': {'cumulus_meta': {'cumulus_version': '10.0.1', 'execution_name': 'c6d885dc-b4b2-4eb0-b22e-b6f58a7a0870', 'message_source': 'sfn', 'queueExecutionLimits': {'https://sqs.us-west-2.amazonaws.com/884500545225/am-uds-dev-cumulus-backgroundProcessing': 5}, 'state_machine': 'arn:aws:states:us-west-2:884500545225:stateMachine:am-uds-dev-cumulus-IngestGranule', 'system_bucket': 'am-uds-dev-cumulus-internal', 'workflow_start_time': 1646785175509, 'parentExecutionArn': 'arn:aws:states:us-west-2:884500545225:execution:am-uds-dev-cumulus-DiscoverGranules:885483b4-ba55-4db1-b197-661e1e595a45', 'queueUrl': 'arn:aws:sqs:us-west-2:884500545225:am-uds-dev-cumulus-startSF'}, 'exception': 'None', 'meta': {'buckets': {'internal': {'name': 'am-uds-dev-cumulus-internal', 'type': 'internal'}, 'protected': {'name': 'am-uds-dev-cumulus-protected', 'type': 'protected'}}, 'cmr': {'clientId': 'CHANGEME', 'cmrEnvironment': 'UAT', 'cmrLimit': 100, 'cmrPageSize': 50, 'oauthProvider': 'earthdata', 'passwordSecretName': 'am-uds-dev-cumulus-message-template-cmr-password20220216072916956000000002', 'provider': 'CHANGEME', 'username': 'username'}, 'collection': {'name': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'process': 'modis', 'granuleId': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$', 'granuleIdExtraction': '(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'duplicateHandling': 'replace', 'url_path': '{cmrMetadata.Granule.Collection.ShortName}___{cmrMetadata.Granule.Collection.VersionId}', 'provider_path': '/data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/', 'files': [{'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}00\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000000.PDS', 'type': 'data'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0\\.cmr\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}], 'updatedAt': 1646326197526, 'createdAt': 1646258167624}, 'distribution_endpoint': 's3://am-uds-dev-cumulus-internal/', 'launchpad': {'api': 'launchpadApi', 'certificate': 'launchpad.pfx', 'passphraseSecretName': ''}, 'provider': {'password': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQHFbE4iMnF/W0Y/NrsYvrfHAAAAajBoBgkqhkiG9w0BBwagWzBZAgEAMFQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMLaH13SdxPXREjXLtAgEQgCfA+lEu2c/xLTGwJsbtKlXJbKDy4pwV+rS3BnJqgBoLLMQZqOdoFhk=', 'host': 'snppl0.gesdisc.eosdis.nasa.gov', 'updatedAt': 1646244053419, 'protocol': 'https', 'createdAt': 1646244053419, 'encrypted': True, 'username': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQGRoY5EBMpvvyMASUowBM61AAAAYzBhBgkqhkiG9w0BBwagVDBSAgEAME0GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9OhRHwTuxiz74q4UAgEQgCDEHOhsVG6+LqXfnlw+Z3Wg9MDOCd9/K5/X5j3tPJYkaA==', 'allowedRedirects': ['https://urs.earthdata.nasa.gov', 'urs.earthdata.nasa.gov'], 'id': 'snpp_provider_02', 'globalConnectionLimit': 10}, 'stack': 'am-uds-dev-cumulus', 'template': 's3://am-uds-dev-cumulus-internal/am-uds-dev-cumulus/workflow_template.json', 'workflow_name': 'IngestGranule', 'workflow_tasks': {'SyncGranule': {'name': 'am-uds-dev-cumulus-SyncGranule', 'version': '$LATEST', 'arn': 'arn:aws:lambda:us-west-2:884500545225:function:am-uds-dev-cumulus-SyncGranule'}}, 'staticValue': 'aStaticValue', 'interpolatedValueStackName': 'am-uds-dev-cumulus', 'input_granules': [{'granuleId': 'P1570515ATMSSCIENCEAXT1134912000000', 'dataType': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'files': [{'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000000.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000000.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000000.PDS', 'type': 'data', 'size': 744}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS', 'type': 'metadata', 'size': 18084600}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'type': 'metadata', 'size': 9526}], 'sync_granule_duration': 9822, 'createdAt': 1647386972717}], 'process': 'modis'}, 'payload': {}, 'replace': {'Bucket': 'am-uds-dev-cumulus-internal', 'Key': 'events/5d8edf37-0a18-4af5-a76f-7c2091cdd1e2', 'TargetPath': '$.payload'}}}} - :param event: - :param context: - :return: - """ - LambdaLoggerGenerator.remove_default_handlers() - return SetupESIndexAlias().start() diff --git a/cumulus_lambda_functions/cumulus_wrapper/cumulus_base.py b/cumulus_lambda_functions/cumulus_wrapper/cumulus_base.py deleted file mode 100644 index 213eafb8..00000000 --- a/cumulus_lambda_functions/cumulus_wrapper/cumulus_base.py +++ /dev/null @@ -1,63 +0,0 @@ -import boto3 -import json - -from abc import ABC -from copy import deepcopy - - -class CumulusBase(ABC): - def __init__(self, cumulus_base: str, cumulus_token: str): - self.__cumulus_base = cumulus_base[:-1] if cumulus_base.endswith('/') else cumulus_base - self.__cumulus_token = cumulus_token - self.__base_headers = { - 'Authorization': f'Bearer {cumulus_token}' - } - self._conditions = [] - - def with_page_number(self, page_number): - self._conditions.append(f'page={page_number}') - return self - - def with_limit(self, limit: int): - self._conditions.append(f'limit={limit}') - return self - - def get_base_headers(self): - return deepcopy(self.__base_headers) - - def _invoke_api(self, payload, private_api_prefix: str): - """Function to invoke cumulus api via aws lambda""" - client = boto3.client('lambda') - response = client.invoke( - FunctionName=f'{private_api_prefix}-PrivateApiLambda', - Payload=json.dumps(payload), - ) - json_response_payload = response.get('Payload').read().decode('utf-8') - response_data = json.loads(json_response_payload) - return response_data - - @property - def cumulus_base(self): - return self.__cumulus_base - - @cumulus_base.setter - def cumulus_base(self, val): - """ - :param val: - :return: None - """ - self.__cumulus_base = val - return - - @property - def cumulus_token(self): - return self.__cumulus_token - - @cumulus_token.setter - def cumulus_token(self, val): - """ - :param val: - :return: None - """ - self.__cumulus_token = val - return diff --git a/cumulus_lambda_functions/cumulus_wrapper/query_collections.py b/cumulus_lambda_functions/cumulus_wrapper/query_collections.py deleted file mode 100644 index 0c43b3c6..00000000 --- a/cumulus_lambda_functions/cumulus_wrapper/query_collections.py +++ /dev/null @@ -1,488 +0,0 @@ -import json -import re - -import requests -from mdps_ds_lib.lib.cumulus_stac.collection_transformer import CollectionTransformer - -from cumulus_lambda_functions.cumulus_wrapper.cumulus_base import CumulusBase -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class CollectionsQuery(CumulusBase): - __collections_key = 'collections' - __providers_key = 'providers' - __rules_key = 'rules' - __stats_key = 'stats' - __collection_id_key = 'collectionId' - __collection_name = 'name' - __collection_version = 'version' - - def __init__(self, cumulus_base: str, cumulus_token: str): - super().__init__(cumulus_base, cumulus_token) - - def with_collection_id(self, collection_id: str): - # self._conditions.append(f'{self.__collection_id_key}={collection_id}') - split_collection = collection_id.split('___') - self._conditions.append(f'{self.__collection_name}={split_collection[0]}') - self._conditions.append(f'{self.__collection_version}={split_collection[1]}') - - return self - - def with_collections(self, collection_ids: list): - collection_names = [k.split('___')[0] for k in collection_ids] - self._conditions.append(f'{self.__collection_name}__in={",".join(collection_names)}') - return self - - def get_size(self, private_api_prefix: str): - query_params = {'field': 'status', 'type': 'collections'} - main_conditions = {k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]} - if self.__collection_name in main_conditions: - query_params[self.__collection_name] = main_conditions[self.__collection_name] - if self.__collection_version in main_conditions: - query_params[self.__collection_version] = main_conditions[self.__collection_version] - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/stats/aggregate', - 'queryStringParameters': query_params, - 'headers': { - 'Content-Type': 'application/json', - }, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - if query_result['statusCode'] >= 500: - raise ValueError(f'server_error: {query_result.statusCode}. details: {query_result}') - if query_result['statusCode'] >= 400: - raise ValueError(f'client_error: {query_result.statusCode}. details: {query_result}') - query_result = json.loads(query_result['body']) - LOGGER.info(f'json query_result: {query_result}') - if 'meta' not in query_result or 'count' not in query_result['meta']: - raise ValueError(f'server_error: missing key: results. invalid response json: {query_result}') - total_size = query_result['meta']['count'] - return {'total_size': total_size} - - def create_collection(self, new_collection: dict, private_api_prefix: str): - payload = { - 'httpMethod': 'POST', - 'resource': '/{proxy+}', - 'path': f'/{self.__collections_key}', - 'headers': { - 'Content-Type': 'application/json', - }, - 'body': json.dumps(new_collection) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'message' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result['message']} - - def delete_collection(self, private_api_prefix, collection_id, collection_version): - payload = { - 'httpMethod': 'DELETE', - 'resource': '/{proxy+}', - 'path': f'/{self.__collections_key}/{collection_id}/{collection_version}', - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'message' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result['message']} - - def query_rules(self, private_api_prefix: str): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/{self.__rules_key}', - # 'queryStringParameters': {k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result.statusCode}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result.statusCode}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'results' not in query_result: - LOGGER.error(f'missing key: results. invalid response json: {query_result}') - return {'server_error': f'missing key: results. invalid response json: {query_result}'} - query_result = query_result['results'] - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'results': query_result} - - def delete_sqs_rules(self, new_collection: dict, private_api_prefix: str): - # $ curl --request DELETE https://example.com/rules/repeat_test --header 'Authorization: Bearer ReplaceWithTheToken' - underscore_collection_name = re.sub(r'[^a-zA-Z0-9_]', '___', new_collection["name"]) # replace any character that's not alphanumeric or underscore with 3 underscores - rule_name = f'{underscore_collection_name}___{new_collection["version"]}___rules_sqs' - payload = { - 'httpMethod': 'DELETE', - 'resource': '/{proxy+}', - 'path': f'/{self.__rules_key}/{rule_name}', - 'headers': { - 'Content-Type': 'application/json', - }, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'message' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result['message']} - - def delete_executions(self, new_collection: dict, private_api_prefix: str): - # $ curl --request DELETE https://example.com/rules/repeat_test --header 'Authorization: Bearer ReplaceWithTheToken' - request_body = { - "collectionId": f'{new_collection["name"]}___{new_collection["version"]}', - "esBatchSize": 10000, - "dbBatchSize": 50000 - } - payload = { - 'httpMethod': 'POST', - 'resource': '/{proxy+}', - 'path': f'/executions/bulk-delete-by-collection', - 'headers': { - 'Content-Type': 'application/json', - }, - 'body': json.dumps(request_body) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'id' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result} - - def list_executions(self, new_collection: dict, private_api_prefix: str): - # $ curl --request DELETE https://example.com/rules/repeat_test --header 'Authorization: Bearer ReplaceWithTheToken' - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/executions', - 'queryStringParameters': {'limit': '100', 'collectionId': f'{new_collection["name"]}___{new_collection["version"]}'}, - 'headers': { - 'Content-Type': 'application/json', - } - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'results' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'results': query_result['results']} - - def create_sqs_rules(self, new_collection: dict, private_api_prefix: str, sqs_url: str, provider_name: str = '', workflow_name: str = 'CatalogGranule', visibility_timeout: int = 1800): - """ -curl --request POST "$CUMULUS_BASEURL/rules" --header "Authorization: Bearer $cumulus_token" --header 'Content-Type: application/json' --data '{ - "workflow": "DiscoverGranules", - "collection": { - "name": "ATMS_SCIENCE_Group", - "version": "001" - }, - "provider": "snpp_provider_03", - "name": "ATMS_SCIENCE_Group_2016_002_v1", - "rule": { - "type": "onetime" - }, - "meta": { "provider_path": "data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2016/002/", "publish": false, "distribution_endpoint": "s3://am-uds-dev-cumulus-internal/" }, - "state": "ENABLED" -}' - :return: - """ - underscore_collection_name = re.sub(r'[^a-zA-Z0-9_]', '___', new_collection["name"]) # replace any character that's not alphanumeric or underscore with 3 underscores - LOGGER.debug(f'underscore_collection_name: {underscore_collection_name}') - rule_body = { - 'workflow': workflow_name, - 'collection': { - 'name': new_collection['name'], - 'version': new_collection['version'], - }, - # 'provider': provider_name, - 'name': f'{underscore_collection_name}___{new_collection["version"]}___rules_sqs', - 'rule': { - # 'type': 'onetime', - 'type': 'sqs', - 'value': sqs_url, - }, - 'state': 'ENABLED', - "meta": { - 'retries': 1, - 'visibilityTimeout': visibility_timeout, - # "provider_path": "data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2016/002/", - # "publish": False, - # "distribution_endpoint": "s3://am-uds-dev-cumulus-internal/" - }, - - } - if provider_name is not None and provider_name != '': - rule_body['provider'] = provider_name - LOGGER.info(f'rule_body: {rule_body}') - payload = { - 'httpMethod': 'POST', - 'resource': '/{proxy+}', - 'path': f'/{self.__rules_key}', - 'headers': { - 'Content-Type': 'application/json', - }, - 'body': json.dumps(rule_body) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'message' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result['message']} - - def __get_stats(self, collection_id, private_api_prefix: str): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/{self.__collections_key}', - 'queryStringParameters': {k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}, - } - try: - query_stats_result = self._invoke_api(payload, private_api_prefix) - except: - LOGGER.exception(f'error while trying to retrieve stats for collection: {collection_id}') - return {} - if query_stats_result['statusCode'] >= 400: - return {} - return query_stats_result['results'] - - def get_stats(self, collection_id:str, private_api_prefix: str): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/stats', - 'queryStringParameters': {'type': 'granules', 'collectionId': collection_id}, - 'headers': { - 'Content-Type': 'application/json', - }, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - if query_result['statusCode'] >= 500: - raise ValueError(f'server_error: {query_result.statusCode}. details: {query_result}') - if query_result['statusCode'] >= 400: - raise ValueError(f'client_error: {query_result.statusCode}. details: {query_result}') - query_result = json.loads(query_result['body']) - LOGGER.info(f'json query_result: {query_result}') - if 'granules' not in query_result: - raise ValueError(f'server_error: missing key: results. invalid response json: {query_result}') - stats = query_result['granules'] - return stats - - def query_direct_to_private_api(self, private_api_prefix: str, output_base_url: str): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/{self.__collections_key}', - 'queryStringParameters': {k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result.statusCode}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result.statusCode}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'results' not in query_result: - LOGGER.error(f'missing key: results. invalid response json: {query_result}') - return {'server_error': f'missing key: results. invalid response json: {query_result}'} - query_result = query_result['results'] - for each_collection in query_result: - stac_collection_id = f"{each_collection['name']}___{each_collection['version']}" - stats = self.get_stats(stac_collection_id, private_api_prefix) - each_collection['dateFrom'] = stats['dateFrom'] - each_collection['dateTo'] = stats['dateTo'] - each_collection['total_size'] = stats['value'] - stac_list = [CollectionTransformer(items_base_url=output_base_url).to_stac(k) for k in query_result] - LOGGER.debug(f'stac_list: {stac_list}') - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'results': stac_list} - - def query(self): - conditions_str = '&'.join(self._conditions) - LOGGER.info(f'cumulus_base: {self.cumulus_base}') - LOGGER.info(f'get_base_headers: {self.get_base_headers()}') - try: - query_result = requests.get(url=f'{self.cumulus_base}/{self.__collections_key}?{conditions_str}', headers=self.get_base_headers()) - LOGGER.info(f'query_result: {query_result}') - if query_result.status_code >= 500: - return {'server_error': query_result.text} - if query_result.status_code >= 400: - return {'client_error': query_result.text} - query_result = json.loads(query_result.content.decode()) - LOGGER.info(f'query_result: {query_result}') - if 'results' not in query_result: - return {'server_error': f'missing key: results. invalid response json: {query_result}'} - query_result = query_result['results'] - except Exception as e: - LOGGER.exception('error during cumulus query') - return {'server_error': str(e)} - return {'results': [CollectionTransformer().to_stac(k) for k in query_result]} - - def create_provider(self, provider_name: str, s3_bucket: str, private_api_prefix: str): - # INSERT INTO providers (name, protocol, host) VALUES ('unity', 's3', 'https://dev.mdps.mcp.nasa.gov'); - # TODO : this fails - payload = { - 'httpMethod': 'POST', - 'resource': '/{proxy+}', - 'path': f'/{self.__providers_key}', - 'headers': { - 'Content-Type': 'application/json', - }, - 'body': json.dumps({ - "id": provider_name, - "host": s3_bucket, - "protocol": "s3", - # "port": 443, - "globalConnectionLimit": 1000, - # "maxDownloadTime": 300, - # "username": "na", - # "password": "na", - # "privateKey": "na", - # "cmKeyId": "na", - # "allowedRedirects": "na", - }) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 500, 'body': '', 'headers': {}} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.debug(f'json query_result: {query_result}') - if 'message' not in query_result: - return {'server_error': f'invalid response: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'status': query_result['message']} diff --git a/cumulus_lambda_functions/cumulus_wrapper/query_granules.py b/cumulus_lambda_functions/cumulus_wrapper/query_granules.py deleted file mode 100644 index e10da64d..00000000 --- a/cumulus_lambda_functions/cumulus_wrapper/query_granules.py +++ /dev/null @@ -1,232 +0,0 @@ -import json - -import boto3 -import requests -from mdps_ds_lib.lib.cumulus_stac.item_transformer import ItemTransformer - -from cumulus_lambda_functions.cumulus_wrapper.cumulus_base import CumulusBase -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class GranulesQuery(CumulusBase): - __granules_key = 'granules' - __ending_time_key = 'endingDateTime' - __beginning_time_key = 'beginningDateTime' - __collection_id_key = 'collectionId' - __granules_id = 'granuleId' - - def __init__(self, cumulus_base: str, cumulus_token: str): - super().__init__(cumulus_base, cumulus_token) - self._conditions.append('status=completed') - self._item_transformer = ItemTransformer() - self.__collection_id = None - - def with_filter(self, filter_key, filter_values: list): - if len(filter_values) < 1: - return self - if filter_key not in self._item_transformer.STAC_2_CUMULUS_KEYS_MAP: - LOGGER.error(f'unknown key in STAC_2_CUMULUS_KEYS_MAP: {filter_key} ') - return self - filter_key = self._item_transformer.STAC_2_CUMULUS_KEYS_MAP[filter_key] - self._conditions.append(f'{filter_key}__in={",".join(filter_values)}') - return self - - def with_collection_id(self, collection_id: str): - self._conditions.append(f'{self.__collection_id_key}={collection_id}') - self.__collection_id = collection_id - return self - - def with_bbox(self): - return self - - def with_time_from(self, from_time): - self._conditions.append(f'{self.__ending_time_key}__from={from_time}') - return self - - def with_time_to(self, to_time): - self._conditions.append(f'{self.__beginning_time_key}__to={to_time}') - return self - - def with_time(self, input_time): - self._conditions.append(f'{self.__beginning_time_key}__from={input_time}') - self._conditions.append(f'{self.__ending_time_key}__to={input_time}') - return self - - def with_time_range(self, from_time, to_time): - """ - - curl -k "$CUMULUS_BASEURL/granules?limit=1&beginningDateTime__from=2016-01-18T22:00:00&endingDateTime__to=2016-01-20T22:00:00" --header "Authorization: Bearer $cumulus_token"|jq - :param beginning_dt: - :param ending_dt: - :return: - """ - self._conditions.append(f'{self.__ending_time_key}__from={from_time}') - self._conditions.append(f'{self.__beginning_time_key}__to={to_time}') - return self - - def get_size(self, private_api_prefix: str): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/stats/aggregate', - 'queryStringParameters': {**{k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}, **{'field': 'status', 'type': 'granules'}}, - 'headers': { - 'Content-Type': 'application/json', - }, - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - if query_result['statusCode'] >= 500: - raise ValueError(f'server_error: {query_result.statusCode}. details: {query_result}') - if query_result['statusCode'] >= 400: - raise ValueError(f'client_error: {query_result.statusCode}. details: {query_result}') - query_result = json.loads(query_result['body']) - LOGGER.info(f'json query_result: {query_result}') - if 'meta' not in query_result or 'count' not in query_result['meta']: - raise ValueError(f'server_error: missing key: results. invalid response json: {query_result}') - total_size = query_result['meta']['count'] - return {'total_size': total_size} - - def query_direct_to_private_api(self, private_api_prefix: str, transform=True): - payload = { - 'httpMethod': 'GET', - 'resource': '/{proxy+}', - 'path': f'/{self.__granules_key}', - 'queryStringParameters': {**{k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}}, - # 'queryStringParameters': {'limit': '30'}, - 'headers': { - 'Content-Type': 'application/json', - }, - # 'body': json.dumps({"action": "removeFromCmr"}) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result.statusCode}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result.statusCode}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - LOGGER.info(f'json query_result: {query_result}') - if 'results' not in query_result: - LOGGER.error(f'missing key: results. invalid response json: {query_result}') - return {'server_error': f'missing key: results. invalid response json: {query_result}'} - query_result = query_result['results'] - stac_list = [ItemTransformer().to_stac(k) for k in query_result] if transform is True else query_result - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {'results': stac_list} - - def add_entry(self, private_api_prefix: str, new_granule: dict): - raise NotImplementedError(f'Please implement adding granules to Cumulus') - # https://nasa.github.io/cumulus-api/v18.4.0/#create-granule - # payload = { - # 'httpMethod': 'POST', - # 'resource': '/{proxy+}', - # 'path': f'/{self.__collections_key}', - # 'headers': { - # 'Content-Type': 'application/json', - # }, - # 'body': json.dumps(new_granule) - # } - # LOGGER.debug(f'payload: {payload}') - # try: - # query_result = self._invoke_api(payload, private_api_prefix) - # """ - # {'statusCode': 500, 'body': '', 'headers': {}} - # """ - # if query_result['statusCode'] >= 500: - # LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - # return {'server_error': query_result} - # if query_result['statusCode'] >= 400: - # LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - # return {'client_error': query_result} - # query_result = json.loads(query_result['body']) - # LOGGER.debug(f'json query_result: {query_result}') - # if 'message' not in query_result: - # return {'server_error': f'invalid response: {query_result}'} - # except Exception as e: - # LOGGER.exception('error while invoking') - # return {'server_error': f'error while invoking:{str(e)}'} - # return {'status': query_result['message']} - return - - def delete_entry(self, private_api_prefix: str, granule_id: str): - payload = { - 'httpMethod': 'DELETE', - 'resource': '/{proxy+}', - 'path': f'/{self.__granules_key}/{self.__collection_id}/{granule_id}', - 'queryStringParameters': {**{k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}}, - # 'queryStringParameters': {'limit': '30'}, - 'headers': { - 'Content-Type': 'application/json', - }, - # 'body': json.dumps({"action": "removeFromCmr"}) - } - LOGGER.debug(f'payload: {payload}') - try: - query_result = self._invoke_api(payload, private_api_prefix) - """ - {'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False} - """ - LOGGER.debug(f'json query_result: {query_result}') - if query_result['statusCode'] >= 500: - LOGGER.error(f'server error status code: {query_result["statusCode"]}. details: {query_result}') - return {'server_error': query_result} - if query_result['statusCode'] >= 400: - LOGGER.error(f'client error status code: {query_result["statusCode"]}. details: {query_result}') - return {'client_error': query_result} - query_result = json.loads(query_result['body']) - """ - { - "detail": "Record deleted" - } - """ - if 'detail' not in query_result: - LOGGER.error(f'missing key: detail. invalid response json: {query_result}') - return {'server_error': f'missing key: detail. invalid response json: {query_result}'} - if query_result['detail'] != 'Record deleted': - LOGGER.error(f'Wrong Message: {query_result}') - return {'server_error': f'Wrong Message: {query_result}'} - except Exception as e: - LOGGER.exception('error while invoking') - return {'server_error': f'error while invoking:{str(e)}'} - return {} - - def query(self, transform=True): - conditions_str = '&'.join(self._conditions) - LOGGER.info(f'cumulus_base: {self.cumulus_base}') - LOGGER.info(f'get_base_headers: {self.get_base_headers()}') - try: - query_result = requests.get(url=f'{self.cumulus_base}/{self.__granules_key}?{conditions_str}', headers=self.get_base_headers()) - LOGGER.info(f'query_result: {query_result}') - if query_result.status_code >= 500: - return {'server_error': query_result.text} - if query_result.status_code >= 400: - return {'client_error': query_result.text} - query_result = json.loads(query_result.content.decode()) - LOGGER.info(f'query_result: {query_result}') - if 'results' not in query_result: - return {'server_error': f'missing key: results. invalid response json: {query_result}'} - query_result = query_result['results'] - except Exception as e: - LOGGER.exception('error during cumulus query') - return {'server_error': str(e)} - if transform is True: - return {'results': [ItemTransformer().to_stac(k) for k in query_result]} - return query_result diff --git a/cumulus_lambda_functions/cumulus_wrapper/__init__.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/__init__.py similarity index 100% rename from cumulus_lambda_functions/cumulus_wrapper/__init__.py rename to cumulus_lambda_functions/daac_archiver/cnm_plugins/__init__.py diff --git a/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_abstract.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_abstract.py new file mode 100644 index 00000000..2a73efd7 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_abstract.py @@ -0,0 +1,18 @@ +from abc import ABC, abstractmethod + + +class CnmPluginAbstract(ABC): + sending_id = 'sending_id' + collection_id = 'collection_id' + granule_id = 'granule_id' + target_collection_id = 'target_collection_id' + cnm_msg = 'cnm_msg' + status_msg = 'status_msg' + CNM_PLUG_IN_NAMES = 'CNM_PLUG_IN_NAMES' + + def __init__(self, params: dict): + self._params = params + + @abstractmethod + def run(self): + return self diff --git a/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_factory.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_factory.py new file mode 100644 index 00000000..05894ac1 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_factory.py @@ -0,0 +1,28 @@ +from mdps_ds_lib.lib.utils.factory_abstract import FactoryAbstract + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_status_update_plugin import CnmStatusUpdatePlugin +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_storage_plugin import CnmStoragePlugin +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract + + +class CnmPluginFactory(FactoryAbstract): + STORAGE = 'STORAGE' + STATUS_UPDATE = 'STATUS_UPDATE' + + def get_instance_from_dict(self, env_dict: dict, **kwargs): + raise NotImplementedError('not a need yet') + + def get_instance_from_env(self, **kwargs): + raise NotImplementedError('Not Yet') + + def get_instance(self, file_repo, **kwargs) -> CnmPluginAbstract: + fr = file_repo.upper() + if 'params' not in kwargs or not isinstance(kwargs['params'], dict): + raise ValueError(f'missing or incorrect argument "params". Need to be a dictionary') + dd1 = { + self.STORAGE: CnmStoragePlugin, + self.STATUS_UPDATE: CnmStatusUpdatePlugin, + } + if fr not in dd1: + raise ModuleNotFoundError(f'cannot find Plugin class for {fr}') + return dd1[fr](kwargs['params']) diff --git a/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_processor.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_processor.py new file mode 100644 index 00000000..d457b318 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_plugin_processor.py @@ -0,0 +1,16 @@ +import os + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_factory import CnmPluginFactory +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract + + +class CnmPluginProcessor(CnmPluginAbstract): + def __init__(self, params: dict): + super().__init__(params) + plug_in_array = [k.strip() for k in os.environ.get(CnmPluginAbstract.CNM_PLUG_IN_NAMES, '').split(',')] + self.__plugins = [CnmPluginFactory().get_instance(k, params=params) for k in plug_in_array] + + def run(self): + for each in self.__plugins: + each.run() + return self diff --git a/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_status_update_plugin.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_status_update_plugin.py new file mode 100644 index 00000000..e7a685a5 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_status_update_plugin.py @@ -0,0 +1,45 @@ +import os + +from mdps_ds_lib.lib.utils.json_validator import JsonValidator + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract +from cumulus_lambda_functions.daac_archiver.services.status_update_svc import StatusUpdateSvc + + +class CnmStatusUpdatePlugin(CnmPluginAbstract): + CNM_RESPONSE_MSG_SCHEMA = { + 'type': 'object', + 'required': ['identifier', 'response'], + 'properties': { + 'identifier': {'type': 'string'}, + 'response': { + 'type': 'object', + 'required': ['status'], + 'properties': { + 'status': {'type': 'string'}, + }, + }, + } + } + + def __init__(self, params: dict): + super().__init__(params) + required_params = [self.sending_id, self.collection_id, self.granule_id, self.target_collection_id, self.cnm_msg] + if any([k not in self._params for k in required_params]): + raise ValueError(f'missing required params: {required_params} v. {self._params}') + + def run(self): + update_status_svc = StatusUpdateSvc().load_manually(self._params[self.sending_id], + self._params[self.collection_id], + self._params[self.target_collection_id], + self._params[self.granule_id]) + + required_params = [self.status_msg] + if all([k in self._params for k in required_params]): + update_status_svc.update_status(self._params[self.status_msg]) + return self + result = JsonValidator(self.CNM_RESPONSE_MSG_SCHEMA).validate(self._params[self.cnm_msg]) + if result is not None: + raise ValueError(f'input json has CNM_RESPONSE_MSG_SCHEMA validation errors: {result}') + update_status_svc.update_status_wrapper(self._params[self.cnm_msg]) + return self diff --git a/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_storage_plugin.py b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_storage_plugin.py new file mode 100644 index 00000000..ddefb230 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/cnm_plugins/cnm_storage_plugin.py @@ -0,0 +1,20 @@ +import os + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract +from cumulus_lambda_functions.daac_archiver.raw_cnm_storage.raw_cnm_storage_factory import RawCnmStorageFactory + + +class CnmStoragePlugin(CnmPluginAbstract): + def __init__(self, params: dict): + super().__init__(params) + required_params = [self.sending_id, self.collection_id, self.granule_id, self.target_collection_id, self.cnm_msg] + if any([k not in self._params for k in required_params]): + raise ValueError(f'missing required params: {required_params} v. {self._params}') + + def run(self): + (RawCnmStorageFactory().get_instance(os.environ.get('CNM_STORAGE_CLASS')) + .load_metadata(self._params[self.sending_id], + self._params[self.collection_id], + self._params[self.granule_id], + self._params[self.target_collection_id]).store_data(self._params[self.cnm_msg])) + return self diff --git a/cumulus_lambda_functions/daac_archiver/daac_archiver_catalia.py b/cumulus_lambda_functions/daac_archiver/daac_archiver_catalia.py new file mode 100644 index 00000000..d3ff5903 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/daac_archiver_catalia.py @@ -0,0 +1,532 @@ +import json +import os +from uuid import uuid4 + +from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 +from mdps_ds_lib.lib.aws.aws_sns import AwsSns +from mdps_ds_lib.lib.utils.time_utils import TimeUtils + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_processor import CnmPluginProcessor +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_archiving_traces import CataliaArchivingTraces +from cumulus_lambda_functions.daac_archiver.services.sfa_client_mw import SfaClientMw +from cumulus_lambda_functions.daac_archiver.services.staging_svc import StagingSvc +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_utils import backoff_wrapper + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + +class DaacArchiverCatalia: + SKIP_STAGING_STAC_ASSETS = 'SKIP_STAGING_STAC_ASSETS' + def __init__(self): + self.__staging_service = StagingSvc() + self.__sns = AwsSns() + self.__s3 = AwsS3() + self.__staged_s3_bucket = 'SET_ME_UP' # DONE. There is validation to see if it's original value, it will throw an error. + self.__uds_ctla_archiving_traces = CataliaArchivingTraces(os.getenv('CATALYA_TRACING_DB', None)) + self.__daac_agreements = [] + self.__update_status_to_sfa = os.getenv('UPDATE_STATUS_TO_SFA', 'FALSE').strip().upper() == 'TRUE' + self.__archiving_granules_stac = None + self.__archiving_status_extension_url = "https://stac-extensions.github.io/archival_statuses/v1.0.0/schema.json" + self.__cnm_msg_version = "1.6.0" + self.__tracing_s3_url = None + self.__sending_uuid = None + + @property + def archiving_granules_stac(self): + return self.__archiving_granules_stac + + @archiving_granules_stac.setter + def archiving_granules_stac(self, val): + """ + :param val: + :return: None + """ + self.__archiving_granules_stac = val + return + + @property + def staged_s3_bucket(self): + return self.__staged_s3_bucket + + @staged_s3_bucket.setter + def staged_s3_bucket(self, val): + """ + :param val: + :return: None + """ + self.__staged_s3_bucket = val + return + + @property + def daac_agreements(self): + return self.__daac_agreements + + @daac_agreements.setter + def daac_agreements(self, val): + """ + :param val: + :return: None + """ + self.__daac_agreements = val + return + + def archive_collection(self, collection_id): + """ + Archive all granules in a collection by querying the STAC Fast API + and processing them in parallel. + + NOTE: TODO This will not work if there are too many granules.. + :param collection_id: The collection ID to archive all granules from + :return: self + """ + LOGGER.info(f'Starting collection archival for collection: {collection_id}') + + try: + # Query all granules in the collection with pagination + all_granule_jsons = [] + page = 1 + limit = 100 # Reasonable batch size + sfa_client_mw = SfaClientMw() + while True: + LOGGER.debug(f'Fetching granules page {page} for collection {collection_id}') + + # Use backoff wrapper for STAC API call + granules_response = backoff_wrapper( + sfa_client_mw.sfa_client.get_items, + collection_id=collection_id, + limit=limit, + offset=(page - 1) * limit + ) + + if not granules_response or 'features' not in granules_response: + LOGGER.warning(f'No granules found in response for collection {collection_id}, page {page}') + break + + granules = granules_response['features'] + if not granules: + LOGGER.info(f'No more granules found for collection {collection_id}, stopping pagination') + break + + all_granule_jsons.extend(granules) + LOGGER.info(f'Fetched {len(granules)} granules from page {page}, total so far: {len(all_granule_jsons)}') + + # If we got fewer than the limit, we're done + if len(granules) < limit: + break + + page += 1 + + LOGGER.info(f'Found {len(all_granule_jsons)} total granules in collection {collection_id}') + + if not all_granule_jsons: + LOGGER.warning(f'No granules found in collection {collection_id}') + return self + + # Process all granules in parallel + return self.archive_granules(all_granule_jsons) + + except Exception as e: + LOGGER.error(f'Failed to archive collection {collection_id}: {e}') + raise RuntimeError(f'Collection archival failed: {e}') from e + + def archive_granules(self, granule_jsons: list, max_workers=10): + """ + Process multiple granules in parallel for archival. + + :param granule_jsons: List of granule JSON objects from STAC Fast API + :param max_workers: Maximum number of parallel workers (default: 10) + :return: self + """ + from concurrent.futures import ThreadPoolExecutor, as_completed + + if not granule_jsons: + LOGGER.warning('No granules provided for archival') + return self + + LOGGER.info(f'Starting parallel archival of {len(granule_jsons)} granules with {max_workers} workers') + + # Track results + successful_granules = [] + failed_granules = [] + + def archive_single_granule(granule_json): + """ + Archive a single granule - wrapper function for parallel execution. + + :param granule_json: Individual granule JSON object + :return: tuple (granule_id, success, error_message) + """ + granule_id = granule_json.get('id', 'unknown') + collection_id = granule_json.get('collection', 'unknown') + + try: + LOGGER.debug(f'Processing granule {granule_id} from collection {collection_id}') + + # Create a new instance for thread safety + # Each worker gets its own archiver instance with same configuration + worker_archiver = DaacArchiverCatalia() + worker_archiver.staged_s3_bucket = self.__staged_s3_bucket + worker_archiver.daac_agreements = self.__daac_agreements + + # Set the granule data directly instead of fetching again + worker_archiver.archiving_granules_stac = granule_json + + # Process the granule + worker_archiver.archive_granule_json() + + LOGGER.info(f'Successfully archived granule {granule_id}') + return granule_id, True, None + + except Exception as e: + error_msg = f'Failed to archive granule {granule_id}: {str(e)}' + LOGGER.error(error_msg) + return granule_id, False, error_msg + + # Execute parallel processing + with ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit all tasks + future_to_granule = { + executor.submit(archive_single_granule, granule_json): granule_json.get('id', 'unknown') + for granule_json in granule_jsons + } + + # Process completed tasks + for future in as_completed(future_to_granule): + granule_id = future_to_granule[future] + try: + result_granule_id, success, error_msg = future.result() + + if success: + successful_granules.append(result_granule_id) + else: + failed_granules.append({'granule_id': result_granule_id, 'error': error_msg}) + + except Exception as e: + error_msg = f'Unexpected error processing granule {granule_id}: {str(e)}' + LOGGER.error(error_msg) + failed_granules.append({'granule_id': granule_id, 'error': error_msg}) + + # Log final results + total_granules = len(granule_jsons) + success_count = len(successful_granules) + failed_count = len(failed_granules) + + LOGGER.info(f'Parallel archival completed: {success_count}/{total_granules} successful, {failed_count} failed') + + if failed_granules: + LOGGER.warning(f'Failed granules: {[f["granule_id"] for f in failed_granules]}') + for failure in failed_granules[:5]: # Log first 5 failures in detail + LOGGER.error(f'Failure details - {failure["granule_id"]}: {failure["error"]}') + + if successful_granules: + LOGGER.debug(f'Successfully archived granules: {successful_granules[:10]}...') # Log first 10 + + return self + + def archive_granule(self, collection_id, granule_id, sending_uuid: str): + # TODO look up granule details + sfa_client_mw = SfaClientMw() + self.__archiving_granules_stac = backoff_wrapper(sfa_client_mw.sfa_client.get_item, collection_id, item_id=granule_id) + LOGGER.debug(f'retrieved stac_item from STAC Fast API: {self.__archiving_granules_stac}') + self.__sending_uuid = sending_uuid + self.archive_granule_json() + return self + + def verbose_archive_granule(self, collection_id, granule_id, tracing_s3_url, item_json: dict, sending_uuid: str): + # TODO validate item.json is valid or just ask for STAC Item object + # TODO update collection and granule id in item.json if different. Needed ?? + LOGGER.debug(f'verbose_archive_granule input item_json: {item_json}') + self.__tracing_s3_url = tracing_s3_url + self.__archiving_granules_stac = item_json + self.__sending_uuid = sending_uuid + self.archive_granule_json() + return self + + def archive_granule_json(self): + """ + 1. Check UDS API if this granule is being pushed to archive(s) + 2. Copy Data and Metadata to staging bucket + 3. Update STAC Metadata to staging bucket. + Re-upload. + 4. Send message to DAAC SNS + + :param stac_granule_json: + :return: + """ + if self.__archiving_granules_stac is None: + raise ValueError(f'NULL archiving granule. Pls retrieve it first.') + self.archiving_granules_stac = SfaClientMw.add_archival_extension(self.archiving_granules_stac) + if len(self.__daac_agreements) < 1: + LOGGER.debug(f'this collection does not have any daac. {self.__archiving_granules_stac}') + return + if self.staged_s3_bucket != self.SKIP_STAGING_STAC_ASSETS: + self.__staging_service.staged_s3_bucket = self.staged_s3_bucket + self.__staging_service.stage_files(self.archiving_granules_stac) + else: + LOGGER.debug(f'Not staging assets to staging buckets. Sending them as hysds bucket S3 URLs') + for each_agreement in self.__daac_agreements: + LOGGER.debug(f'working on {each_agreement}') + self.send_daac_sns(each_agreement) + return + + def extract_files(self, daac_config: dict): + """ + Extract files from STAC assets and convert to CNM file format. + + This method has been updated to work with STAC assets instead of CNM JSON. + It extracts files from self.__archiving_granules_stac.assets and converts them + to the CNM file format expected by DAAC. + + Old CNM JSON structure: + { + "product: { + "files": [ + { + "type": "data", + "name": "cc_file.pdf", + "uri": "s3://bucket/path/cc_file.pdf", + "checksumType": "md5", + "checksum": "deb1087d3e614f31b7c9eb461edea93a", + "size": 1579135 + }, + ] + } + } + + STAC assets structure: + { + "assets": { + "cc_file.pdf": { + "href": "s3://bucket/path/cc_file.pdf", + "title": "cc_file.pdf", + "description": "size=1579135;checksumType=md5;checksum=deb1087d3e614f31b7c9eb461edea93a", + "file:size": 1579135, + "file:checksum": "deb1087d3e614f31b7c9eb461edea93a", + "roles": [ + "data" + ] + }, + } + } + + :param daac_config: DAAC configuration containing archiving_types + :return: List of files in CNM format + """ + if self.__archiving_granules_stac is None: + raise ValueError('NULL archiving granule. Cannot extract files.') + + # Get assets from STAC item + stac_assets = self.__archiving_granules_stac.assets + + # If no archiving types specified, include all assets + if 'archiving_types' not in daac_config or len(daac_config['archiving_types']) < 1: + LOGGER.debug('No archiving types specified in DAAC config, including all assets') + return self._convert_all_assets_to_cnm_format(stac_assets) + + # Build archiving types mapping: {data_type: [file_extensions]} + archiving_types = {} + for archiving_type in daac_config['archiving_types']: + data_type = archiving_type['data_type'] + file_extensions = archiving_type.get('file_extension', []) + if not isinstance(file_extensions, list): + file_extensions = [file_extensions] if file_extensions else [] + archiving_types[data_type] = file_extensions + + LOGGER.debug(f'Archiving types configuration: {archiving_types}') + + result_files = [] + for asset_key, asset in stac_assets.items(): + LOGGER.debug(f'Processing asset: {asset_key}') + + # Get asset type from roles (use first role as type, default to 'data') + asset_type = 'data' # Default type + if hasattr(asset, 'roles') and asset.roles and len(asset.roles) > 0: + asset_type = asset.roles[0] + + # Check if this asset type should be archived + if asset_type not in archiving_types: + LOGGER.debug(f'Asset {asset_key} type "{asset_type}" not in archiving types, skipping') + continue + + # Get file extensions for this asset type + file_extensions = archiving_types[asset_type] + + # Convert STAC asset to CNM file format + cnm_file = self._convert_stac_asset_to_cnm_file(asset_key, asset) + + # If no file extensions specified for this type, include the file + if len(file_extensions) == 0: + LOGGER.debug(f'No file extensions specified for type "{asset_type}", including asset {asset_key}') + result_files.append(cnm_file) + continue + + # Check if file matches any of the specified extensions + filename = cnm_file['name'].upper().strip() + if any(filename.endswith(ext.upper()) for ext in file_extensions): + LOGGER.debug(f'Asset {asset_key} matches extension filter, including') + result_files.append(cnm_file) + else: + LOGGER.debug(f'Asset {asset_key} does not match extension filter {file_extensions}, skipping') + + LOGGER.info(f'Extracted {len(result_files)} files from {len(stac_assets)} STAC assets') + return result_files + + def _convert_all_assets_to_cnm_format(self, stac_assets: dict): + """Convert all STAC assets to CNM file format without filtering.""" + result_files = [] + for asset_key, asset in stac_assets.items(): + cnm_file = self._convert_stac_asset_to_cnm_file(asset_key, asset) + result_files.append(cnm_file) + return result_files + + def _convert_stac_asset_to_cnm_file(self, asset_key: str, asset): + """ + Convert a single STAC asset to CNM file format. + + :param asset_key: The key/name of the asset in STAC + :param asset: The STAC Asset object + :return: Dictionary in CNM file format + """ + # Extract filename from href or use asset_key + filename = asset_key + if hasattr(asset, 'href') and asset.href: + filename = asset.href.split('/')[-1] + + # Get asset type from roles (use first role, default to 'data') + asset_type = 'data' + if hasattr(asset, 'roles') and asset.roles and len(asset.roles) > 0: + asset_type = asset.roles[0] + + # Get file size - REQUIRED for DAAC archival + file_size = None + if hasattr(asset, 'extra_fields') and 'file:size' in asset.extra_fields: + file_size = asset.extra_fields['file:size'] + elif hasattr(asset, 'extra_fields') and 'file_size' in asset.extra_fields: + file_size = asset.extra_fields['file_size'] + + # Validate file size is present + if file_size is None or file_size < 0: + raise ValueError(f'Missing or invalid file size for asset {asset_key}. Size is required for DAAC archival and will be rejected by receiver.') + + # Get checksum information - format should be : + checksum_type = None + checksum_value = None + + if hasattr(asset, 'extra_fields'): + checksum_field = None + if 'file:checksum' in asset.extra_fields: + checksum_field = asset.extra_fields['file:checksum'] + elif 'file_checksum' in asset.extra_fields: + checksum_field = asset.extra_fields['file_checksum'] + + if checksum_field: + # Parse checksum in format : + if ':' in checksum_field: + parts = checksum_field.split(':', 1) # Split only on first ':' + checksum_type = parts[0].strip() + checksum_value = parts[1].strip() + # NOTE: There will be no assumption. Type and value has to come from the field or it will throw an error. + # else: + # # If no colon, assume it's just the value with default md5 type + # LOGGER.warning(f'Checksum for asset {asset_key} is not in : format, assuming md5') + # checksum_type = 'md5' + # checksum_value = checksum_field.strip() + + # Validate checksum is present + if not checksum_type or not checksum_value: + raise ValueError(f'Missing or invalid checksum for asset {asset_key}. Checksum (in format :) is required for DAAC archival and will be rejected by receiver.') + + # Build CNM file structure + cnm_file = { + "type": asset_type, + "name": filename, + "uri": asset.href if hasattr(asset, 'href') else '', + "checksumType": checksum_type, + "checksum": checksum_value, + "size": file_size + } + + LOGGER.debug(f'Converted STAC asset {asset_key} to CNM file: {cnm_file}') + return cnm_file + + def send_daac_sns(self, daac_config): + """ + + { + "product": { + "files": [ + { + "name":"TROPESS_CrIS-JPSS1_L2_Standard_CH4_20250108_MUSES_R1p23_megacity_los_angeles_MGLOS_F2p5_J0.nc", + "type":"data", + "uri":"s3://unity-test-unity-storage/URN:NASA:UNITY:unity:test:TRPSDL2ALLCRS1MGLOS___2/URN:NASA:UNITY:unity:test:TRPSDL2ALLCRS1MGLOS___2:datum/TROPESS_Standard/TRPSDL2ALLCRS1MGLOS.2/2025/01/08/TROPESS_CrIS-JPSS1_L2_Standard_CH4_20250108_MUSES_R1p23_megacity_los_angeles_MGLOS_F2p5_J0/TROPESS_CrIS-JPSS1_L2_Standard_CH4_20250108_MUSES_R1p23_megacity_los_angeles_MGLOS_F2p5_J0.nc", + "size":280595 + } + ], + "name": "TROPESS_CrIS-JPSS1_L2_Standard_CH4_20250108_MUSES_R1p23_megacity_los_angeles_MGLOS_F2p5_J0" + }, + "identifier ":"testIdentifier123456", + "collection": { + "name": "TRPSDL2ALLCRS1MGLOS", + "version": "2" + }, + "provider":"tropess_testing" + } + :param daac_config: + :return: + """ + if self.__sending_uuid is None: + LOGGER.warning(f'missing __sending_uuid. generating one.') + self.__sending_uuid = str(uuid4()) + plugin_processor_params = { + CnmPluginAbstract.sending_id: self.__sending_uuid, + CnmPluginAbstract.collection_id: self.__archiving_granules_stac.collection_id, + CnmPluginAbstract.granule_id: self.__archiving_granules_stac.id, + CnmPluginAbstract.target_collection_id: daac_config['targetProject'], + } + try: + LOGGER.debug(f'send_daac_sns daac_config: {daac_config}') + self.__sns.set_topic_arn(daac_config['sns_topic_arn']) + # TODO store details to new DB. + if self.__tracing_s3_url is not None: + self.__uds_ctla_archiving_traces.add(self.__sending_uuid, self.__tracing_s3_url, 'TODO', ['TODO'], + self.__archiving_granules_stac.collection_id, self.__archiving_granules_stac.id, TimeUtils().get_datetime_str()) + daac_cnm_message = { + "collection": { + 'name': daac_config['targetProject'], + 'version': daac_config['data_version'], + }, + 'identifier': self.__sending_uuid, # "identifier": self.__archiving_granules_stac.id, # Seems like it's the same granule IDuds_cnm_json['identifier'], + # From DAAC: Unique identifier for the message as a whole. It is the senders responsibility to ensure uniqueness. This identifier can be used in response messages to provide tracability. + "submissionTime": f'{TimeUtils.get_current_time()}Z', + "provider": daac_config['provider'], # NOTE: we can't use tenant as provider anymore coz we aren't sure tennt will be there in CATALIA. if 'daac_provider' in daac_config else granule_identifier.tenant + "version": self.__cnm_msg_version, + "product": { + "name": self.__archiving_granules_stac.id, # NOTE: Original value = granule_identifier.granule. Should be the name of granule. + # "dataVersion": daac_config['daac_data_version'], + 'files': self.extract_files(daac_config), + } + } + plugin_processor_params[CnmPluginAbstract.cnm_msg] = daac_cnm_message + LOGGER.debug(f'daac_cnm_message: {daac_cnm_message}') + if 'role_arn' in daac_config and \ + 'role_session_name' in daac_config and \ + daac_config['role_arn'] != '' and \ + daac_config['role_session_name'] != '': + self.__sns.set_external_role(daac_config['role_arn'], daac_config['role_session_name']).publish_message(json.dumps(daac_cnm_message), True) + else: + self.__sns.publish_message(json.dumps(daac_cnm_message), False) + plugin_processor_params[CnmPluginAbstract.status_msg] = { + "status": "cnm-submit-success", + } + except Exception as e: + LOGGER.exception(f'failed during archival process') + plugin_processor_params[CnmPluginAbstract.status_msg] = { + "status": "cnm-submit-failed", + "errorMessage": str(e), + } + CnmPluginProcessor(plugin_processor_params).run() + return + +# TODO store all status messages in S3. index them in DB? Or use DDB to trace +# TODO store all statuses from DDB to another DB Postgres or Parquet for accountability tool. + diff --git a/cumulus_lambda_functions/daac_archiver/daac_archiver_logic.py b/cumulus_lambda_functions/daac_archiver/daac_archiver_logic.py deleted file mode 100644 index ab1a5c2f..00000000 --- a/cumulus_lambda_functions/daac_archiver/daac_archiver_logic.py +++ /dev/null @@ -1,225 +0,0 @@ -import json -import os -from time import sleep - -import requests -from mdps_ds_lib.lib.utils.file_utils import FileUtils - -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 - -from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from mdps_ds_lib.stac_fast_api_client.sfa_client_factory import SFAClientFactory - -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex -from mdps_ds_lib.lib.aws.aws_sns import AwsSns -from mdps_ds_lib.lib.utils.time_utils import TimeUtils -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections -from cumulus_lambda_functions.lib.uds_db.archive_index import UdsArchiveConfigIndex - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class DaacArchiverLogic: - def __init__(self): - self.__es_url, self.__es_port = os.getenv('ES_URL'), int(os.getenv('ES_PORT', '443')) - self.__archive_index_logic = UdsArchiveConfigIndex(self.__es_url, self.__es_port, os.getenv('ES_TYPE', 'AWS'), os.getenv('ES_USE_SSL', 'TRUE').strip() is True) - self.__granules_index = GranulesDbIndex() - self.__sns = AwsSns() - self.__s3 = AwsS3() - - def get_cnm_response_json_file(self, potential_file, granule_id): - if 'href' not in potential_file: - raise ValueError(f'missing href in potential_file: {potential_file}') - self.__s3.set_s3_url(potential_file['href']) - LOGGER.debug(f'attempting to retrieve cnm response from : {granule_id} & {potential_file}') - cnm_response_keys = [k for k, _ in self.__s3.get_child_s3_files(self.__s3.target_bucket, os.path.dirname(self.__s3.target_key)) if k.lower().endswith('.cnm.json')] - if len(cnm_response_keys) < 1: - LOGGER.debug(f'missing cnm response file: {os.path.dirname(self.__s3.target_key)}.. trying again in 30 second.') - sleep(30) # waiting 30 second. should be enough. - cnm_response_keys = [k for k, _ in self.__s3.get_child_s3_files(self.__s3.target_bucket, os.path.dirname(self.__s3.target_key)) if k.lower().endswith('.cnm.json')] - if len(cnm_response_keys) < 1: - LOGGER.debug(f'missing cnm response file after 2nd try: {os.path.dirname(self.__s3.target_key)}.. quitting.') - return None - if len(cnm_response_keys) > 1: - LOGGER.warning(f'more than 1 cnm response file: {cnm_response_keys}') - # assuming the names are the same, and it has processing date in the filename, it is easier to reverse it - cnm_response_keys = sorted(cnm_response_keys)[-1] # sort and get the last one which is supposed to be the most recent one. - LOGGER.debug(f'cnm_response_keys: {cnm_response_keys}') - local_file = self.__s3.set_s3_url(f's3://{self.__s3.target_bucket}/{cnm_response_keys}').download('/tmp') - cnm_response_json = FileUtils.read_json(local_file) - FileUtils.remove_if_exists(local_file) - return cnm_response_json - - @staticmethod - def revert_to_s3_url(input_url): - if input_url.startswith("s3://"): - return input_url - if input_url.startswith("http://") or input_url.startswith("https://"): - parts = input_url.split('/', 3) - if len(parts) < 4: - ValueError(f'invalid url: {input_url}') - path_parts = parts[3].split('/', 1) - if len(path_parts) != 2: - ValueError(f'invalid url: {input_url}') - bucket, key = path_parts - return f"s3://{bucket}/{key}" - raise ValueError(f'unknown schema: {input_url}') - - def __extract_files(self, uds_cnm_json: dict, daac_config: dict): - granule_files = uds_cnm_json['product']['files'] - if 'archiving_types' not in daac_config or len(daac_config['archiving_types']) < 1: - return granule_files # TODO remove missing md5? - archiving_types = {k['data_type']: [] if 'file_extension' not in k else k['file_extension'] for k in daac_config['archiving_types']} - result_files = [] - for each_file in granule_files: - LOGGER.debug(f'each_file: {each_file}') - """ - { - "type": "data", - "name": "abcd.1234.efgh.test_file05.nc", - "uri": "https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:SNDR-SNPP_ATMS@L1B$OUTPUT___2403261440/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:SNDR-SNPP_ATMS@L1B$OUTPUT___2403261440:abcd.1234.efgh.test_file05/abcd.1234.efgh.test_file05.nc", - "checksumType": "md5", - "checksum": "unknown", - "size": -1 - } - """ - a = each_file['type'] - if each_file['type'] not in archiving_types: - continue - file_extensions = archiving_types[each_file['type']] - each_file['uri'] = self.revert_to_s3_url(each_file['uri']) - if len(file_extensions) < 1: - result_files.append(each_file) # TODO remove missing md5? - temp_filename = each_file['name'].upper().strip() - if any([temp_filename.endswith(k.upper()) for k in file_extensions]): - result_files.append(each_file) # TODO remove missing md5? - return result_files - - def send_to_daac_internal(self, uds_cnm_json: dict): - LOGGER.debug(f'uds_cnm_json: {uds_cnm_json}') - granule_identifier = UdsCollections.decode_granule_identifier(uds_cnm_json['identifier']) # This is normally meant to be for collection. Since our granule ID also has collection id prefix. we can use this. - self.__archive_index_logic.set_tenant_venue(granule_identifier.tenant, granule_identifier.venue) - daac_config = self.__archive_index_logic.percolate_document(uds_cnm_json['identifier']) - if daac_config is None or len(daac_config) < 1: - LOGGER.debug(f'uds_cnm_json is not configured for archival. uds_cnm_json: {uds_cnm_json}') - return - daac_config = daac_config[0] # TODO This is currently not supporting more than 1 daac. - result = JsonValidator(UdsArchiveConfigIndex.db_record_schema).validate(daac_config) - if result is not None: - raise ValueError(f'daac_config does not have valid schema. Pls re-add the daac config: {result} for {daac_config}') - try: - self.__sns.set_topic_arn(daac_config['daac_sns_topic_arn']) - daac_cnm_message = { - "collection": { - 'name': daac_config['daac_collection_name'], - 'version': daac_config['daac_data_version'], - }, - "identifier": uds_cnm_json['identifier'], - "submissionTime": f'{TimeUtils.get_current_time()}Z', - "provider": daac_config['daac_provider'] if 'daac_provider' in daac_config else granule_identifier.tenant, - "version": "1.6.0", # TODO this is hardcoded? - "product": { - "name": granule_identifier.granule, - # "dataVersion": daac_config['daac_data_version'], - 'files': self.__extract_files(uds_cnm_json, daac_config), - } - } - LOGGER.debug(f'daac_cnm_message: {daac_cnm_message}') - self.__sns.set_external_role(daac_config['daac_role_arn'], daac_config['daac_role_session_name']).publish_message(json.dumps(daac_cnm_message), True) - self.__granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { - 'archive_status': 'cnm_s_success', - 'archive_error_message': '', - 'archive_error_code': '', - }, uds_cnm_json['identifier']) - except Exception as e: - LOGGER.exception(f'failed during archival process') - self.__granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { - 'archive_status': 'cnm_s_failed', - 'archive_error_message': str(e), - }, uds_cnm_json['identifier']) - return - - def send_to_daac(self, event: dict): - LOGGER.debug(f'send_to_daac#event: {event}') - uds_cnm_json = AwsMessageTransformers().sqs_sns(event) - LOGGER.debug(f'sns_msg: {uds_cnm_json}') - self.send_to_daac_internal(uds_cnm_json) - return - - def update_stac(self, cnm_notification_msg): - update_type = os.getenv('ARCHIVAL_STATUS_MECHANISM', '') - if not any([k for k in ['UDS', 'FAST_STAC'] if k == update_type]): - raise ValueError(f"missing ARCHIVAL_STATUS_MECHANISM environment variable or value is not {['UDS', 'FAST_STAC']}") - if update_type == 'UDS': - return self.update_stac_uds(cnm_notification_msg) - return self.update_stac_fast_api(cnm_notification_msg) - - def update_stac_fast_api(self, cnm_notification_msg): - sfa_client = SFAClientFactory().get_instance_from_env() - collection_id, granule_id = ':'.join(cnm_notification_msg['identifier'].split(':')[:-1]), cnm_notification_msg['identifier'] - # TODO assuming granule ID is URN:NASA:VENUE:TENANT:VENUE:COLLECTION_ID:COLLECTION_ID - existing_item = sfa_client.get_item(collection_id, granule_id) - # TODO handle error when no existing_item. Currently, it is requests.HTTPError with 404 - if cnm_notification_msg['response']['status'] == 'SUCCESS': - latest_daac_status = { - 'archive_status': 'cnm_r_success', - 'archive_error_message': '', - 'archive_error_code': '', - } - else: - latest_daac_status = { - 'archive_status': 'cnm_r_failed', - 'archive_error_message': cnm_notification_msg['response']['errorMessage'] if 'errorMessage' in cnm_notification_msg['response'] else 'unknown', - 'archive_error_code': cnm_notification_msg['response']['errorCode'] if 'errorCode' in cnm_notification_msg['response'] else 'unknown', - } - latest_daac_status['event_time'] = TimeUtils.get_current_time() - existing_item['properties']['archival_statuses'] = existing_item['properties']['archival_statuses'] + [latest_daac_status] if 'archival_statuses' in existing_item['properties'] else [latest_daac_status] - updated_item = sfa_client.update_item(collection_id, granule_id, existing_item, update_whole=True) # TODO partial update via patch is not working at this moment. - return - - def update_stac_uds(self, cnm_notification_msg): - granule_identifier = UdsCollections.decode_identifier(cnm_notification_msg['identifier']) # This is normally meant to be for collection. Since our granule ID also has collection id prefix. we can use this. - try: - existing_granule_object = self.__granules_index.get_entry(granule_identifier.tenant, - granule_identifier.venue, - cnm_notification_msg['identifier']) - except Exception as e: - LOGGER.exception( - f"error while attempting to retrieve existing record: {cnm_notification_msg['identifier']}, not continuing") - return - LOGGER.debug(f'existing_granule_object: {existing_granule_object}') - if cnm_notification_msg['response']['status'] == 'SUCCESS': - self.__granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { - 'archive_status': 'cnm_r_success', - 'archive_error_message': '', - 'archive_error_code': '', - }, cnm_notification_msg['identifier']) - return - self.__granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { - 'archive_status': 'cnm_r_failed', - 'archive_error_message': cnm_notification_msg['response']['errorMessage'] if 'errorMessage' in - cnm_notification_msg[ - 'response'] else 'unknown', - 'archive_error_code': cnm_notification_msg['response']['errorCode'] if 'errorCode' in cnm_notification_msg[ - 'response'] else 'unknown', - }, cnm_notification_msg['identifier']) - return - - def receive_from_daac(self, event: dict): - LOGGER.debug(f'receive_from_daac#event: {event}') - sns_msg = AwsMessageTransformers().sqs_sns(event) - LOGGER.debug(f'sns_msg: {sns_msg}') - cnm_notification_msg = sns_msg - - cnm_msg_schema = requests.get('https://raw.githubusercontent.com/podaac/cloud-notification-message-schema/v1.6.1/cumulus_sns_schema.json') - cnm_msg_schema.raise_for_status() - cnm_msg_schema = json.loads(cnm_msg_schema.text) - result = JsonValidator(cnm_msg_schema).validate(cnm_notification_msg) - if result is not None: - raise ValueError(f'input cnm event has cnm_msg_schema validation errors: {result}') - if 'response' not in cnm_notification_msg: - raise ValueError(f'missing response in {cnm_notification_msg}') - self.update_stac(cnm_notification_msg) - return diff --git a/cumulus_lambda_functions/daac_archiver/daac_receiver.py b/cumulus_lambda_functions/daac_archiver/daac_receiver.py new file mode 100644 index 00000000..b34fa48a --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/daac_receiver.py @@ -0,0 +1,81 @@ +import json +import os +import requests +from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers +from mdps_ds_lib.lib.utils.json_validator import JsonValidator + +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_processor import CnmPluginProcessor +from cumulus_lambda_functions.daac_archiver.cnm_plugins.cnm_plugin_abstract import CnmPluginAbstract +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_status_db import CataliaStatusDb +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + +class DaacReceiver: + def receive_from_daac(self, event: dict): + LOGGER.debug(f'receive_from_daac#event: {event}') + sns_msg = AwsMessageTransformers().sqs_sns(event) + LOGGER.debug(f'sns_msg: {sns_msg}') + cnm_notification_msg = sns_msg + + cnm_msg_schema = requests.get('https://raw.githubusercontent.com/podaac/cloud-notification-message-schema/v1.6.1/cumulus_sns_schema.json') + cnm_msg_schema.raise_for_status() + cnm_msg_schema = json.loads(cnm_msg_schema.text) + result = JsonValidator(cnm_msg_schema).validate(cnm_notification_msg) + if result is not None: + raise ValueError(f'input cnm event has cnm_msg_schema validation errors: {result}') + if 'response' not in cnm_notification_msg: + raise ValueError(f'missing response in {cnm_notification_msg}') + self.update_stac(cnm_notification_msg) + return + + def update_stac(self, cnm_notification_msg): + update_type = os.getenv('ARCHIVAL_STATUS_MECHANISM', '') + if not any([k for k in ['UDS', 'CATALYA'] if k == update_type]): + raise ValueError(f"missing ARCHIVAL_STATUS_MECHANISM environment variable or value is not {['UDS', 'FAST_STAC']}") + if update_type == 'UDS': + return self.update_stac_uds(cnm_notification_msg) + status_ddb = CataliaStatusDb(os.getenv('CATALYA_STATUS_DB', None)) + existing_statuses = status_ddb.get(cnm_notification_msg['identifier']) + if len(existing_statuses) < 1: + raise ValueError(f'unknown collection & granule: {cnm_notification_msg}') + plugin_processor_params = { + CnmPluginAbstract.sending_id: cnm_notification_msg['identifier'], + CnmPluginAbstract.collection_id: existing_statuses[0][CataliaStatusDb.collection], + CnmPluginAbstract.granule_id: existing_statuses[0][CataliaStatusDb.name_str], + CnmPluginAbstract.target_collection_id: existing_statuses[0][CataliaStatusDb.target_collection], + CnmPluginAbstract.cnm_msg: cnm_notification_msg, + } + CnmPluginProcessor(plugin_processor_params).run() + return self + + def update_stac_uds(self, cnm_notification_msg): + from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex + granules_index = GranulesDbIndex() + granule_identifier = UdsCollections.decode_identifier(cnm_notification_msg['identifier']) # This is normally meant to be for collection. Since our granule ID also has collection id prefix. we can use this. + try: + existing_granule_object = granules_index.get_entry(granule_identifier.tenant, + granule_identifier.venue, + cnm_notification_msg['identifier']) + except Exception as e: + LOGGER.exception( + f"error while attempting to retrieve existing record: {cnm_notification_msg['identifier']}, not continuing") + return + LOGGER.debug(f'existing_granule_object: {existing_granule_object}') + if cnm_notification_msg['response']['status'] == 'SUCCESS': + granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { + 'archive_status': 'cnm_r_success', + 'archive_error_message': '', + 'archive_error_code': '', + }, cnm_notification_msg['identifier']) + return + granules_index.update_entry(granule_identifier.tenant, granule_identifier.venue, { + 'archive_status': 'cnm_r_failed', + 'archive_error_message': cnm_notification_msg['response']['errorMessage'] if 'errorMessage' in + cnm_notification_msg[ + 'response'] else 'unknown', + 'archive_error_code': cnm_notification_msg['response']['errorCode'] if 'errorCode' in cnm_notification_msg[ + 'response'] else 'unknown', + }, cnm_notification_msg['identifier']) + return \ No newline at end of file diff --git a/cumulus_lambda_functions/granules_cnm_ingester/__init__.py b/cumulus_lambda_functions/daac_archiver/ddb_mws/__init__.py similarity index 100% rename from cumulus_lambda_functions/granules_cnm_ingester/__init__.py rename to cumulus_lambda_functions/daac_archiver/ddb_mws/__init__.py diff --git a/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_archiving_traces.py b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_archiving_traces.py new file mode 100644 index 00000000..2c2ac9c1 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_archiving_traces.py @@ -0,0 +1,45 @@ +import logging + +from mdps_ds_lib.lib.aws.no_sql_abstract import NoSqlProps +from mdps_ds_lib.lib.aws.no_sql_ddb import NoSqlDdb +from mdps_ds_lib.lib.aws.no_sql_factory import NoSqlFactory + +logger = logging.getLogger(__name__) + + +class CataliaArchivingTraces: + identifier = 'identifier' + s3_url = 's3Url' + collection = 'collection' + granule_id = 'granule' + username = 'username' + user_group = 'userGroup' + datetime_str = 'datetime' + + def __init__(self, table_name: str): + ddb_props = NoSqlProps() + ddb_props.table = table_name + ddb_props.primary_key = self.identifier + ddb_props.secondary_key = self.datetime_str + + param = ddb_props.to_json() + param['file_repo'] = 'AWS_DDB' + + self.__ddb: NoSqlDdb = NoSqlFactory().get_instance(**param) + + def get(self, identifier: str): + results = self.__ddb.get(identifier, secondary_key=None) + return results + + def add(self, identifier: str, s3_url: str, username: str, user_group: list, collection: str, granule_id: str, datetime_str: str): + item1 = { + self.s3_url: s3_url, + self.collection: collection, + self.granule_id: granule_id, + self.username: username, + self.user_group: user_group, + self.datetime_str: datetime_str, + } + item1 = {k: v for k, v in item1.items() if v is not None} + self.__ddb.add(identifier, None, item1, replace=False) + return \ No newline at end of file diff --git a/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_auth_db.py b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_auth_db.py new file mode 100644 index 00000000..abd02455 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_auth_db.py @@ -0,0 +1,189 @@ +import logging +import re +from collections import defaultdict + +from mdps_ds_lib.lib.aws.no_sql_abstract import NoSqlProps +from mdps_ds_lib.lib.aws.no_sql_ddb import NoSqlDdb +from mdps_ds_lib.lib.aws.no_sql_factory import NoSqlFactory +logger = logging.getLogger(__name__) + + +class CataliaAuthDb: + user_group = 'userGroup' + access = 'access' + project_map = 'projectMap' + source_project = 'sourceProject' + target_project = 'targetProject' + + def __init__(self, table_name: str): + ddb_props = NoSqlProps() + ddb_props.table = table_name + ddb_props.primary_key = self.user_group + ddb_props.secondary_key = self.project_map + + param = ddb_props.to_json() + param['file_repo'] = 'AWS_DDB' + + self.__ddb: NoSqlDdb = NoSqlFactory().get_instance(**param) + + def add(self, user_group, collection, daac_collection, access: bool): + item1 = { + # 'userGroup': user_group, + # 'projectMap': '', + self.source_project: collection, + self.target_project: daac_collection, + self.access: access, + } + sk1 = f'{collection}->{daac_collection}' + self.__ddb.add(user_group, sk1, item1, replace=True) + return + + def delete(self, user_group, collection, daac_collection): + sk1 = f'{collection}->{daac_collection}' + self.__ddb.delete(user_group, sk1) + return + + def get_authorized_catalia(self, user_group: list[str], catalia_collection): + results = [] + for group in user_group: + group_results = self.__ddb.get(group, secondary_key=None) + if group_results: + results.extend(group_results) + + if not results or len(results) == 0: + return [] + + source_matches = [] + for result in results: + source_pattern = result.get('sourceProject', '') + try: + if re.match(source_pattern, catalia_collection, re.IGNORECASE): + source_matches.append(result) + except re.error: + if source_pattern == catalia_collection: + source_matches.append(result) + return source_matches + + def get_authorized_daac(self, source_matches: list, catalia_collection, daac_collections: list[str]): + target_matches = defaultdict(list) + for result in source_matches: + target_pattern = result.get('targetProject', '') + for daac_collection in daac_collections: + try: + if re.match(target_pattern, daac_collection, re.IGNORECASE): + target_matches[daac_collection].append(result) + except re.error: + if target_pattern == daac_collection: + target_matches[daac_collection].append(result) + authorized_daac_collections = [] + for k, v in target_matches.items(): + if len(v) < 1: + continue + if len(v) == 1 and v[0].get(self.access, False): + authorized_daac_collections.append(k) + continue + closest_target = min(v, key=lambda x: self._string_distance(x.get(self.target_project, ''), k)) + final_matches = [r for r in v if r.get(self.target_project) == closest_target.get(self.target_project)] + if len(final_matches) < 1: + continue + if len(final_matches) == 0 and final_matches[0].get(self.access, False): + authorized_daac_collections.append(k) + continue + closest_target_1 = min(final_matches, key=lambda x: self._string_distance(x.get(self.source_project, ''), catalia_collection)) + final_matches_1 = [r for r in final_matches if r.get(self.source_project) == closest_target_1.get(self.source_project)] + if len(final_matches_1) < 1: + continue + if len(final_matches_1) > 1: + logger.warning(f'duplicated Auth rows ? :{k} = {v}') + if final_matches_1[0].get(self.access, False): + authorized_daac_collections.append(k) + continue + return authorized_daac_collections + + def get_authorized_daac_full(self, user_group: list[str], catalia_collection, daac_collections: list[str]): + source_matches = self.get_authorized_catalia(user_group, catalia_collection) + return self.get_authorized_daac(source_matches, catalia_collection, daac_collections) + + def authorize(self, user_group: list[str], catalia_collection, daac_collection): + """ + This will retrieve entries from DDB with user_group as PK. (This is done). + If results is None or empty array, return False. Not Authorized. + For each result, check sourceProject REGEX against catalia_collection input name. + If results is None or empty array, return False. Not Authorized. + For each result, check targetProject REGEX against daac_collection input name. + If results is None or empty array, return False. Not Authorized. + If only 1 row exists, return its "access" key. + If two or more rows exist, pick the one closest to the daac_collection in the targetProject name. + If only 1 row exists, return its "access" key. + If two or more rows exist, pick the one closest to the catalia_collection in the sourceProject. + If only 1 row exists, return its "access" key. + :param user_group: + :param catalia_collection: + :param daac_collection: + :return: + """ + results = [] + for group in user_group: + group_results = self.__ddb.get(group, secondary_key=None) + if group_results: + results.extend(group_results) + + if not results or len(results) == 0: + return False + + source_matches = [] + for result in results: + source_pattern = result.get('sourceProject', '') + try: + if re.match(source_pattern, catalia_collection, re.IGNORECASE): + source_matches.append(result) + except re.error: + if source_pattern == catalia_collection: + source_matches.append(result) + + if not source_matches or len(source_matches) == 0: + return False + + target_matches = [] + for result in source_matches: + target_pattern = result.get('targetProject', '') + try: + if re.match(target_pattern, daac_collection, re.IGNORECASE): + target_matches.append(result) + except re.error: + if target_pattern == daac_collection: + target_matches.append(result) + + if not target_matches or len(target_matches) == 0: + return False + + if len(target_matches) == 1: + return target_matches[0].get('access', False) + + closest_target = min(target_matches, key=lambda x: self._string_distance(x.get(self.target_project, ''), daac_collection)) + + final_matches = [r for r in target_matches if r.get(self.target_project) == closest_target.get(self.target_project)] + + if len(final_matches) == 1: + return final_matches[0].get('access', False) + + closest_source = min(final_matches, key=lambda x: self._string_distance(x.get(self.source_project, ''), catalia_collection)) + final_matches = [r for r in target_matches if r.get(self.source_project) == closest_source.get(self.source_project)] + + return final_matches[0].get('access', False) + + def _string_distance(self, s1, s2): + """ + Calculate the negative of the maximum common prefix length (case insensitive). + Returns negative value so that longer prefixes result in smaller distances for min() selection. + """ + s1_lower = s1.lower() + s2_lower = s2.lower() + + common_prefix_length = 0 + for i in range(min(len(s1_lower), len(s2_lower))): + if s1_lower[i] == s2_lower[i]: + common_prefix_length += 1 + else: + break + return -common_prefix_length diff --git a/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_daac_handshakes_db.py b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_daac_handshakes_db.py new file mode 100644 index 00000000..88c6785f --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_daac_handshakes_db.py @@ -0,0 +1,47 @@ +from mdps_ds_lib.lib.aws.no_sql_abstract import NoSqlProps +from mdps_ds_lib.lib.aws.no_sql_ddb import NoSqlDdb +from mdps_ds_lib.lib.aws.no_sql_factory import NoSqlFactory + + +class CataliaDaacHandshakesDb: + user_group = 'userGroup' + user = 'user' + source_project = 'sourceProject' + target_project = 'targetProject' + + def __init__(self, table_name: str): + ddb_props = NoSqlProps() + ddb_props.table = table_name + ddb_props.primary_key = self.source_project + ddb_props.secondary_key = self.target_project + + param = ddb_props.to_json() + param['file_repo'] = 'AWS_DDB' + + self.__ddb: NoSqlDdb = NoSqlFactory().get_instance(**param) + + def add(self, catalia_collection, daac_collection, api_key, provider, data_version, sns_topic_arn, role_arn, role_session_name, archiving_types, user, user_group): + item1 = { + self.user_group: user_group, + self.user: user, + 'provider': provider, + 'data_version': data_version, + 'sns_topic_arn': sns_topic_arn, + 'role_arn': role_arn, + 'role_session_name': role_session_name, + 'archiving_types': archiving_types, + 'api_key': api_key, + } + self.__ddb.add(catalia_collection, daac_collection, item1, replace=True) + return + + def delete(self, catalia_collection, daac_collection): + self.__ddb.delete(catalia_collection, daac_collection) + return + + def get_single (self, catalia_collection, daac_collection): + return self.__ddb.get(catalia_collection, daac_collection) + + def search(self, catalia_collection): + results = self.__ddb.get(catalia_collection, secondary_key=None) + return results diff --git a/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_status_db.py b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_status_db.py new file mode 100644 index 00000000..e9b98489 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/ddb_mws/catalia_status_db.py @@ -0,0 +1,44 @@ +from mdps_ds_lib.lib.aws.no_sql_abstract import NoSqlProps +from mdps_ds_lib.lib.aws.no_sql_ddb import NoSqlDdb +from mdps_ds_lib.lib.aws.no_sql_factory import NoSqlFactory + + +class CataliaStatusDb: + identifier = 'identifier' + collection = 'collection' + target_collection = 'target_collection' + name_str = 'name' + status = 'status' + error_code = 'errorCode' + error_message = 'errorMessage' + href_str = 'href' + datetime_str = 'datetime' + + def __init__(self, table_name: str): + ddb_props = NoSqlProps() + ddb_props.table = table_name + ddb_props.primary_key = self.identifier + ddb_props.secondary_key = self.datetime_str + + param = ddb_props.to_json() + param['file_repo'] = 'AWS_DDB' + + self.__ddb: NoSqlDdb = NoSqlFactory().get_instance(**param) + + def get(self, identifier: str): + results = self.__ddb.get(identifier, secondary_key=None) + return results + + def add(self, identifier: str, collection: dict, name_str: str, status: str, datetime_str: str, error_code: str=None, error_message: str=None, href_str: str=None, target_collection: str=None): + item1 = { + self.name_str: name_str, + self.collection: collection, + self.status: status, + self.error_code: error_code, + self.error_message: error_message, + self.href_str: href_str, + self.target_collection: target_collection + } + item1 = {k: v for k, v in item1.items() if v is not None} + self.__ddb.add(identifier, datetime_str, item1, replace=False) + return diff --git a/cumulus_lambda_functions/daac_archiver/lambda_function.py b/cumulus_lambda_functions/daac_archiver/lambda_function.py index b84e3fff..cc961438 100644 --- a/cumulus_lambda_functions/daac_archiver/lambda_function.py +++ b/cumulus_lambda_functions/daac_archiver/lambda_function.py @@ -1,19 +1,7 @@ -from cumulus_lambda_functions.daac_archiver.daac_archiver_logic import DaacArchiverLogic +from cumulus_lambda_functions.daac_archiver.daac_receiver import DaacReceiver from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -def lambda_handler_request(event, context): - """ - :param event: - :param context: - :return: - {'Records': [{'messageId': '6ff7c6fd-4053-4ab4-bc12-c042be1ed275', 'receiptHandle': 'AQEBYASiFPjQT5JBI2KKCTF/uQhHfJt/tHhgucslQQdvkNVxcXCNi2E5Ux4U9N0eu7RfvlnvtycjUh0gdL7jIeoyH+VRKSF61uAJuT4p31BsNe0GYu49N9A6+kxjP/RrykR7ZofmQRdHToX1ugRc76SMRic4H/ZZ89YAHA2QeomJFMrYywIxlk8OAzYaBf2dQI7WexjY5u1CW00XNMbTGyTo4foVPxcSn6bdFpfgxW/L7yJMX/0YQvrA9ruiuQ+lrui+6fWYh5zEk3f5v1bYtUQ6DtyyfbtMHZQJTJpUlWAFRzzN+3melilH7FySyOGDXhPb0BOSzmdKq9wBbfLW/YPb7l99ejq4GfRfj8LyI4EtB96vTeUw4LCgUqbZcBrxbGBLUXMacweh+gCjHav9ylqr2SeOiqG3vWPq9pwFYQIDqNE=', 'body': '{\n "Type" : "Notification",\n "MessageId" : "33e1075a-435c-5217-a33d-59fae85e19b2",\n "TopicArn" : "arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester",\n "Subject" : "Amazon S3 Notification",\n "Message" : "{\\"Service\\":\\"Amazon S3\\",\\"Event\\":\\"s3:TestEvent\\",\\"Time\\":\\"2024-04-22T18:13:22.416Z\\",\\"Bucket\\":\\"uds-sbx-cumulus-staging\\",\\"RequestId\\":\\"DQ4T0GRVFPSX45C9\\",\\"HostId\\":\\"gHBFnYNmfnGDZBmqoQwA3RScjtjBk5lr426moGxu8IDpe5UhWAqNTxHqilWBoPN1njzIrzNrf8c=\\"}",\n "Timestamp" : "2024-04-22T18:13:22.434Z",\n "SignatureVersion" : "1",\n "Signature" : "RvSxqpU7J7CCJXbin9cXqTxzjMjgAUFtk/n454mTMcOe5x3Ay1w4AHfzyeYQCFBdLHNBa8n3OdMDoDlJqyVQMb8k+nERaiZWN2oqFVDRqT9pqSr89b+4FwlhPv6TYy2pBa/bgjZ4cOSYsey1uSQ3hjl0idfssvuV5cCRxQScbA+yu8Gcv9K7Oqgy01mC0sDHiuPIifhFXxupG5ygbjqoHIB+1gdMEbBwyixoY5GOpHM/O2uHNF+dJDjax1WMxQ2FzVjiFeCa+tNcjovF059+tx2v1YmDq/kEAFrN6DAtP6R4zKag62P9jkvjU/wHYJ2jjXmZAqoG+nuzAo24HiZPSw==",\n "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-60eadc530605d63b8e62a523676ef735.pem",\n "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester:76cbefa1-addf-45c2-97e1-ae16986b195b"\n}', 'attributes': {'ApproximateReceiveCount': '1', 'SentTimestamp': '1713809602474', 'SenderId': 'AIDAIYLAVTDLUXBIEIX46', 'ApproximateFirstReceiveTimestamp': '1713809602483'}, 'messageAttributes': {}, 'md5OfBody': 'c6d06d1b742ad5bd2cfe5f542640aad2', 'eventSource': 'aws:sqs', 'eventSourceARN': 'arn:aws:sqs:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester', 'awsRegion': 'us-west-2'}]} - """ - LambdaLoggerGenerator.remove_default_handlers() - DaacArchiverLogic().send_to_daac(event) - return - - def lambda_handler_response(event, context): """ :param event: @@ -22,5 +10,5 @@ def lambda_handler_response(event, context): {'Records': [{'messageId': '6ff7c6fd-4053-4ab4-bc12-c042be1ed275', 'receiptHandle': 'AQEBYASiFPjQT5JBI2KKCTF/uQhHfJt/tHhgucslQQdvkNVxcXCNi2E5Ux4U9N0eu7RfvlnvtycjUh0gdL7jIeoyH+VRKSF61uAJuT4p31BsNe0GYu49N9A6+kxjP/RrykR7ZofmQRdHToX1ugRc76SMRic4H/ZZ89YAHA2QeomJFMrYywIxlk8OAzYaBf2dQI7WexjY5u1CW00XNMbTGyTo4foVPxcSn6bdFpfgxW/L7yJMX/0YQvrA9ruiuQ+lrui+6fWYh5zEk3f5v1bYtUQ6DtyyfbtMHZQJTJpUlWAFRzzN+3melilH7FySyOGDXhPb0BOSzmdKq9wBbfLW/YPb7l99ejq4GfRfj8LyI4EtB96vTeUw4LCgUqbZcBrxbGBLUXMacweh+gCjHav9ylqr2SeOiqG3vWPq9pwFYQIDqNE=', 'body': '{\n "Type" : "Notification",\n "MessageId" : "33e1075a-435c-5217-a33d-59fae85e19b2",\n "TopicArn" : "arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester",\n "Subject" : "Amazon S3 Notification",\n "Message" : "{\\"Service\\":\\"Amazon S3\\",\\"Event\\":\\"s3:TestEvent\\",\\"Time\\":\\"2024-04-22T18:13:22.416Z\\",\\"Bucket\\":\\"uds-sbx-cumulus-staging\\",\\"RequestId\\":\\"DQ4T0GRVFPSX45C9\\",\\"HostId\\":\\"gHBFnYNmfnGDZBmqoQwA3RScjtjBk5lr426moGxu8IDpe5UhWAqNTxHqilWBoPN1njzIrzNrf8c=\\"}",\n "Timestamp" : "2024-04-22T18:13:22.434Z",\n "SignatureVersion" : "1",\n "Signature" : "RvSxqpU7J7CCJXbin9cXqTxzjMjgAUFtk/n454mTMcOe5x3Ay1w4AHfzyeYQCFBdLHNBa8n3OdMDoDlJqyVQMb8k+nERaiZWN2oqFVDRqT9pqSr89b+4FwlhPv6TYy2pBa/bgjZ4cOSYsey1uSQ3hjl0idfssvuV5cCRxQScbA+yu8Gcv9K7Oqgy01mC0sDHiuPIifhFXxupG5ygbjqoHIB+1gdMEbBwyixoY5GOpHM/O2uHNF+dJDjax1WMxQ2FzVjiFeCa+tNcjovF059+tx2v1YmDq/kEAFrN6DAtP6R4zKag62P9jkvjU/wHYJ2jjXmZAqoG+nuzAo24HiZPSw==",\n "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-60eadc530605d63b8e62a523676ef735.pem",\n "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester:76cbefa1-addf-45c2-97e1-ae16986b195b"\n}', 'attributes': {'ApproximateReceiveCount': '1', 'SentTimestamp': '1713809602474', 'SenderId': 'AIDAIYLAVTDLUXBIEIX46', 'ApproximateFirstReceiveTimestamp': '1713809602483'}, 'messageAttributes': {}, 'md5OfBody': 'c6d06d1b742ad5bd2cfe5f542640aad2', 'eventSource': 'aws:sqs', 'eventSourceARN': 'arn:aws:sqs:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester', 'awsRegion': 'us-west-2'}]} """ LambdaLoggerGenerator.remove_default_handlers() - DaacArchiverLogic().receive_from_daac(event) + DaacReceiver().receive_from_daac(event) return diff --git a/cumulus_lambda_functions/granules_cnm_response_writer/__init__.py b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/__init__.py similarity index 100% rename from cumulus_lambda_functions/granules_cnm_response_writer/__init__.py rename to cumulus_lambda_functions/daac_archiver/raw_cnm_storage/__init__.py diff --git a/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_abstract.py b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_abstract.py new file mode 100644 index 00000000..b2350194 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_abstract.py @@ -0,0 +1,11 @@ +from abc import ABC, abstractmethod + + +class RawCnmStorageAbstract(ABC): + @abstractmethod + def load_metadata(self, sending_id, collection_id, granule_id, target_collection_id): + return self + + @abstractmethod + def store_data(self, cnm_msg: dict): + return self diff --git a/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_factory.py b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_factory.py new file mode 100644 index 00000000..ab0153db --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_factory.py @@ -0,0 +1,22 @@ +from mdps_ds_lib.lib.utils.factory_abstract import FactoryAbstract + +from cumulus_lambda_functions.daac_archiver.raw_cnm_storage.raw_cnm_storage_abstract import RawCnmStorageAbstract + + +class RawCnmStorageFactory(FactoryAbstract): + AWS = 'S3' + POSTGRES = 'POSTGRES' + OPENSEARCH = 'OPENSEARCH' + + def get_instance_from_dict(self, env_dict: dict, **kwargs): + raise NotImplementedError('not a need yet') + + def get_instance_from_env(self, **kwargs): + raise NotImplementedError('Not Yet') + + def get_instance(self, file_repo, **kwargs) -> RawCnmStorageAbstract: + fr = file_repo.upper() + if fr == self.AWS: + from cumulus_lambda_functions.daac_archiver.raw_cnm_storage.raw_cnm_storage_s3 import RawCnmStorageS3 + return RawCnmStorageS3() + raise ModuleNotFoundError(f'cannot find RawCnmStorage class for {fr}') \ No newline at end of file diff --git a/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_s3.py b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_s3.py new file mode 100644 index 00000000..07cb9680 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/raw_cnm_storage/raw_cnm_storage_s3.py @@ -0,0 +1,30 @@ +import json +import os +from datetime import datetime + +from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 + +from cumulus_lambda_functions.daac_archiver.raw_cnm_storage.raw_cnm_storage_abstract import RawCnmStorageAbstract + + +class RawCnmStorageS3(RawCnmStorageAbstract): + def __init__(self): + super().__init__() + self.__s3_bucket = os.environ.get('CNM_STORAGE_BUCKET') + self.__s3_base_path = os.environ.get('CNM_STORAGE_PREFIX', 'CNM_MESSAGES') + self.__s3 = AwsS3() + self.__s3.target_bucket = self.__s3_bucket + self.__sending_id, self.__collection_id, self.__granule_id, self.__target_collection_id = None, None, None, None + + def load_metadata(self, sending_id, collection_id, granule_id, target_collection_id): + self.__sending_id, self.__collection_id, self.__granule_id, self.__target_collection_id = sending_id, collection_id, granule_id, target_collection_id + return self + + def store_data(self, cnm_msg: dict): + if any([k is None for k in [self.__sending_id, self.__collection_id, self.__granule_id, self.__target_collection_id]]): + raise ValueError(f'one or more of sending_id, collection_id, granule_id, target_collection_id is null: {[self.__sending_id, self.__collection_id, self.__granule_id, self.__target_collection_id]}') + target_key_1 = [] if self.__s3_base_path == '' else [self.__s3_base_path] + target_key_1.extend([self.__collection_id, self.__granule_id, self.__target_collection_id, self.__sending_id, f"{datetime.strftime('%Y-%m-%dT%H:%M:%S%Z')}.json"]) + self.__s3.target_key = '/'.join(target_key_1) + self.__s3.upload_bytes(json.dumps(cnm_msg).encode()) + return self diff --git a/cumulus_lambda_functions/granules_to_es/__init__.py b/cumulus_lambda_functions/daac_archiver/services/__init__.py similarity index 100% rename from cumulus_lambda_functions/granules_to_es/__init__.py rename to cumulus_lambda_functions/daac_archiver/services/__init__.py diff --git a/cumulus_lambda_functions/daac_archiver/services/maap_api_client.py b/cumulus_lambda_functions/daac_archiver/services/maap_api_client.py new file mode 100644 index 00000000..f21418ab --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/services/maap_api_client.py @@ -0,0 +1,76 @@ +import json +import os + +import requests +from mdps_ds_lib.lib.aws.aws_param_store import AwsParamStore + + +class MaapApiClient: + def __init__(self): + """ + API_BASE_URL = var.UDS_API_BASE_URL + MAAP_API_HOST = var.MAAP_API_HOST + DPS_MACHINE_TOKEN = var.DPS_MACHINE_TOKEN + """ + self.__uds_api_param_key_path = os.getenv('UDS_API_CREDS', 'NA') + if self.__uds_api_param_key_path == 'NA': + raise ValueError(f'missing UDS_API_CREDS env') + self.__uds_api_creds = json.loads(AwsParamStore().get_param(self.__uds_api_param_key_path)) + + def get_user_jwt_token(self, username: str): + url = f"{self.__uds_api_creds['MAAP_API_HOST']}/api/members/{username}" + headers = {"dps-token": self.__uds_api_creds['DPS_MACHINE_TOKEN']} + response = requests.get(url, headers=headers) + + if response.status_code == 404: + raise ValueError(f"User '{username}' not found or invalid API endpoint: {url}. Details: {response.text}") + elif response.status_code == 401: + raise ValueError(f"Unauthorized: Invalid DPS_MACHINE_TOKEN") + elif response.status_code == 403: + raise ValueError(f"Forbidden: Access denied for user '{username}'") + elif response.status_code >= 500: + raise ValueError(f"Server error ({response.status_code}): {response.text}") + elif response.status_code != 200: + raise ValueError(f"Unexpected response ({response.status_code}): {response.text}") + + try: + response_data = response.json() + except ValueError as e: + raise ValueError(f"Invalid JSON response from MAAP API: {e}") + + if "session_key" not in response_data: + raise ValueError(f"Missing 'session_key' in MAAP API response: {response_data}") + + maap_pgt_token = response_data["session_key"] + return maap_pgt_token + + def get_user_details(self, user_token: str): + url = f"{self.__uds_api_creds['MAAP_API_HOST']}/api/members/self" + headers = { + "accept": 'application/json', + 'proxy-ticket': user_token + } + response = requests.get(url, headers=headers) + + if response.status_code != 200: + response_body = response.text if response.text else "null body" + raise ValueError(f"Failed to get user groups ({response.status_code}): {response_body}") + + try: + response_data = response.json() + except ValueError as e: + raise ValueError(f"Invalid JSON response from MAAP API: {e}") + + return { + # Add fake user context that would normally come from Keycloak JWT + "userId": response_data["id"], + "username": response_data["username"], + "email": response_data["email"], + "name": f'{response_data["last_name"]},{response_data["first_name"]}', + "roles": 'NA', + "groups": [org["name"] for org in response_data["organizations"] if "name" in org], + # Fake JWT token (base64 encoded) - simulating what Keycloak would provide + "jwtToken": response_data['session_key'], # TODO It's PGT though, not JWT + # Add a flag to indicate this is a placeholder + "authType": "PLACEHOLDER_KEYCLOAK" + } diff --git a/cumulus_lambda_functions/daac_archiver/services/sfa_client_mw.py b/cumulus_lambda_functions/daac_archiver/services/sfa_client_mw.py new file mode 100644 index 00000000..d60a0658 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/services/sfa_client_mw.py @@ -0,0 +1,121 @@ +import json +import os + +from mdps_ds_lib.lib.aws.aws_param_store import AwsParamStore +from mdps_ds_lib.stac_fast_api_client.sfa_client_factory import SFAClientFactory +from pystac import Item +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_utils import backoff_wrapper + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + + +class SfaClientMw: + archiving_status_extension_url = "https://stac-extensions.github.io/archival_statuses/v1.0.0/schema.json" + + @staticmethod + def add_archival_extension(archiving_granules_stac): + """ + 1. Convert dictionary to pystac object. store the modified object back to the self.__archiving_granules_stac + 2. Check if it has a stac_extensions, and it has self.__archiving_status_extension_url + 3. If so, done + 4. If not, add that extension, done + + :return: + """ + if archiving_granules_stac is None: + raise ValueError(f'NULL archiving granule. Cannot add archival extension.') + + # Convert to pystac Item if it's a dictionary + if isinstance(archiving_granules_stac, dict): + archiving_granules_stac = Item.from_dict(archiving_granules_stac) + + # Check if the archival extension is already present + if hasattr(archiving_granules_stac, 'stac_extensions'): + if SfaClientMw.archiving_status_extension_url not in archiving_granules_stac.stac_extensions: + archiving_granules_stac.stac_extensions.append(SfaClientMw.archiving_status_extension_url) + LOGGER.debug(f'Added archival extension to STAC item: {SfaClientMw.archiving_status_extension_url}') + else: + # Initialize stac_extensions if it doesn't exist + archiving_granules_stac.stac_extensions = [SfaClientMw.archiving_status_extension_url] + LOGGER.debug(f'Initialized stac_extensions with archival extension: {SfaClientMw.archiving_status_extension_url}') + + # Initialize archival:status property if it doesn't exist + if 'archival:status' not in archiving_granules_stac.properties: + archiving_granules_stac.properties['archival:status'] = [] + LOGGER.debug(f'Initialized archival:status property for STAC item') + return archiving_granules_stac + + def __init__(self): + self.__sfa_client = None + self.__update_status_to_sfa = os.getenv('UPDATE_STATUS_TO_SFA', 'FALSE').strip().upper() == 'TRUE' + self.__collection, self.__granule = None, None + self.__archiving_granules_stac = None + + def load_sfa_client(self): + sfa_auth_ssm_key = os.getenv('SFA_AUTH', None) + LOGGER.debug(f'retrieving SSM details from {sfa_auth_ssm_key}') + sfa_auth_ssm_dict = AwsParamStore().get_param(sfa_auth_ssm_key) + if sfa_auth_ssm_dict is None: + raise ValueError(f'missing SSM detaails for SFA Auth: {sfa_auth_ssm_dict}') + self.__sfa_client = SFAClientFactory().get_instance_from_dict(json.loads(sfa_auth_ssm_dict)) + return + + @property + def sfa_client(self): + if self.__sfa_client is None: + self.load_sfa_client() + return self.__sfa_client + + @sfa_client.setter + def sfa_client(self, val): + """ + :param val: + :return: None + """ + self.__sfa_client = val + return + + def load_manually(self, collection, granule_id): + self.__collection, self.__granule = collection, granule_id + return self + + def update_sfa_item_status(self, archival_status_with_timestamp): + if not self.__update_status_to_sfa: + LOGGER.debug(f'NOT updating SFA catalog due to setting UPDATE_STATUS_TO_SFA') + return self + + if self.__sfa_client is None: + self.load_sfa_client() + return self + + self.__archiving_granules_stac = backoff_wrapper(self.__sfa_client.get_item, self.__collection, tem_id=self.__granule) + if self.__archiving_granules_stac is None: + raise ValueError(f'NULL archiving granule. Cannot update status.') + self.add_archival_extension() + if 'archival:status' not in self.__archiving_granules_stac.properties: + self.__archiving_granules_stac.properties['archival:status'] = [] + elif not isinstance(self.__archiving_granules_stac.properties['archival:status'], list): + self.__archiving_granules_stac.properties['archival:status'] = [] + + # Add the new status to the list + self.__archiving_granules_stac.properties['archival:status'].append(archival_status_with_timestamp) + LOGGER.info(f'Added archival status: {archival_status_with_timestamp}') + + try: + # Convert STAC item to JSON dictionary + stac_item_dict = self.__archiving_granules_stac.to_dict() + + # Update the item using the STAC Fast API client + updated_item = backoff_wrapper(self.__sfa_client.update_item, + collection_id=self.__collection, + item_id=self.__granule, + item=stac_item_dict + ) + LOGGER.info( + f'Successfully updated STAC item {self.__granule} in collection {self.__collection} with new archival status') + LOGGER.debug(f'Updated item response: {updated_item}') + except Exception as e: + LOGGER.exception(f'Failed to update STAC item {self.__granule} in collection {self.__collection}') + raise e + return self diff --git a/cumulus_lambda_functions/daac_archiver/services/staging_svc.py b/cumulus_lambda_functions/daac_archiver/services/staging_svc.py new file mode 100644 index 00000000..3c5f3085 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/services/staging_svc.py @@ -0,0 +1,96 @@ +from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 +from mdps_ds_lib.lib.utils.time_utils import TimeUtils +from pystac import Item + +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.lib.uds_utils import backoff_wrapper + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + + +class StagingSvc: + SET_ME_UP = 'SET_ME_UP' + + def __init__(self): + self.__s3 = AwsS3() + self.__archiving_granules_stac = None + self.__staged_s3_bucket = self.SET_ME_UP # DONE. There is validation to see if it's original value, it will throw an error. + @property + def staged_s3_bucket(self): + return self.__staged_s3_bucket + + @staged_s3_bucket.setter + def staged_s3_bucket(self, val): + """ + :param val: + :return: None + """ + self.__staged_s3_bucket = val + return + + def stage_files(self, archiving_granules_stac: Item): + """ + 1. Check directory s3://// + 2. If not empty. log a warning message. + 3. Empty S3 directory + 4. Get file locations for each asset in self.__archiving_granules_stac which should be a pystac object. + 5. Copy them from source S3 to destination S3 from Step 1. + 6. After each copy, update the href of each asset to new location. + 7. If pystac is part of the assets, change its href to new location as well and upload it. + 8. How do I know if pystac is part of assets? + :return: + """ + self.__archiving_granules_stac = archiving_granules_stac + + if self.__archiving_granules_stac is None: + raise ValueError(f'NULL archiving granule. Cannot stage files.') + + if self.__staged_s3_bucket == self.SET_ME_UP: + raise ValueError(f'Staged S3 bucket is not configured. Please set self.__staged_s3_bucket.') + + # Get collection and item IDs + collection_id = self.__archiving_granules_stac.collection_id + item_id = self.__archiving_granules_stac.id + + # Define staging directory path + staging_prefix = f"{collection_id}/{item_id}/{TimeUtils.get_current_time()}/" + staging_s3_path = f"s3://{self.__staged_s3_bucket}/{staging_prefix}" + LOGGER.info(f'Staging files to: {staging_s3_path}') + + # Process each asset in the STAC item + staged_assets = {} + for asset_key, asset in self.__archiving_granules_stac.assets.items(): + if hasattr(asset, 'href') and asset.href: + source_href = asset.href + LOGGER.debug(f'Processing asset {asset_key} from {source_href}') + + # Parse S3 URL to get bucket and key + if source_href.startswith('s3://'): + # Remove s3:// prefix and split + s3_path = source_href[5:] + bucket_key_parts = s3_path.split('/', 1) + if len(bucket_key_parts) == 2: + source_bucket, source_key = bucket_key_parts + + # Define destination key (preserve original filename) + filename = source_key.split('/')[-1] + dest_key = f"{staging_prefix}{filename}" + dest_href = f"s3://{self.__staged_s3_bucket}/{dest_key}" + + try: + # Copy file to staging bucket + backoff_wrapper(self.__s3.copy_artifact, source_bucket, source_key, self.__staged_s3_bucket, dest_key, copy_tags=False, delete_original=False) + LOGGER.info(f'Copied {source_href} to {dest_href}') + + # Update asset href to new location + asset.href = dest_href + staged_assets[asset_key] = dest_href + + except Exception as e: + LOGGER.error(f'Failed to copy asset {asset_key} from {source_href} to {dest_href}: {e}') + raise + else: + LOGGER.warning(f'Invalid S3 URL format for asset {asset_key}: {source_href}') + else: + LOGGER.warning(f'Non-S3 asset {asset_key} not staged: {source_href}') + return self diff --git a/cumulus_lambda_functions/daac_archiver/services/status_update_svc.py b/cumulus_lambda_functions/daac_archiver/services/status_update_svc.py new file mode 100644 index 00000000..78ffc536 --- /dev/null +++ b/cumulus_lambda_functions/daac_archiver/services/status_update_svc.py @@ -0,0 +1,172 @@ +import json +import os + +from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 +from mdps_ds_lib.lib.utils.time_utils import TimeUtils +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_archiving_traces import CataliaArchivingTraces +from cumulus_lambda_functions.daac_archiver.services.sfa_client_mw import SfaClientMw +from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator +from cumulus_lambda_functions.daac_archiver.ddb_mws.catalia_status_db import CataliaStatusDb + +LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) + + +class StatusUpdateSvc: + archival_status_schema = { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "cnm-authorized-success", + "cnm-authorized-failed", + "cnm-staged-success", + "cnm-staged-failed", + "cnm-submit-success", + "cnm-submit-failed", + "cnm-receive-success", + "cnm-receive-failed" + ] + }, + "errorCode": { + "type": "string" + }, + "errorMessage": { + "type": "string" + }, + "href": { + "type": "string", + "format": "iri-reference" + }, + "datetime": { + "title": "Date and Time", + "description": "timestamp of this update, in UTC (Formatted in RFC 3339) ", + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" + } + }, + "additionalProperties": False + } + + def __init__(self): + self.__uds_ctla_archiving_traces = CataliaArchivingTraces(os.getenv('CATALYA_TRACING_DB', None)) + self.__status_ddb = CataliaStatusDb(os.getenv('CATALYA_STATUS_DB', None)) + self.__archiving_granules_stac = None + self.__identifier, self.__collection, self.__target_collection, self.__granule = None, None, None, None + + def load_manually(self, identifier, collection, target_collection, granule): + self.__identifier, self.__collection, self.__target_collection, self.__granule = identifier, collection, target_collection, granule + return self + + def load_from_db(self, identifier:str): + archived_granule_metadata = self.__uds_ctla_archiving_traces.get(identifier) + if archived_granule_metadata is None or (isinstance(archived_granule_metadata, list) and len(archived_granule_metadata) < 1): + raise ValueError(f'missing archived metadata for identifier : {identifier}') + self.__identifier, self.__collection, self.__target_collection, self.__granule = identifier, archived_granule_metadata[0][CataliaStatusDb.collection], archived_granule_metadata[0][CataliaStatusDb.target_collection], archived_granule_metadata[0][CataliaStatusDb.name_str] + return self + + def validate_status(self, archival_status): + import jsonschema + if not isinstance(archival_status, dict): + raise ValueError(f'archival_status must be a dictionary, got {type(archival_status)}') + + # Validate archival_status against schema + try: + jsonschema.validate(archival_status, self.archival_status_schema) + LOGGER.debug(f'archival_status validation successful: {archival_status}') + except jsonschema.ValidationError as e: + LOGGER.error(f'archival_status validation failed: {e}') + raise ValueError(f'Invalid archival_status format: {e.message}') + return + + def update_status_wrapper(self, cnm_notification_msg: dict): + if any([k is None for k in [self.__identifier, self.__collection, self.__target_collection, self.__granule]]): + existing_statuses = self.__status_ddb.get(cnm_notification_msg['identifier']) + if len(existing_statuses) < 1: + raise ValueError(f'unknown collection & granule: {cnm_notification_msg}') + self.__identifier, self.__collection, self.__target_collection, self.__granule = cnm_notification_msg['identifier'], existing_statuses[0][CataliaStatusDb.collection], existing_statuses[0][CataliaStatusDb.target_collection], existing_statuses[0][CataliaStatusDb.name_str] + if cnm_notification_msg['response']['status'] == 'SUCCESS': + latest_daac_status = { + 'status': 'cnm-receive-success', + } + # TODO ask DAAC if they pass HREF? + else: + latest_daac_status = { + 'status': 'cnm-receive-failed', + 'errorMessage': cnm_notification_msg['response']['errorMessage'] if 'errorMessage' in cnm_notification_msg['response'] else 'unknown', + 'errorCode': cnm_notification_msg['response']['errorCode'] if 'errorCode' in cnm_notification_msg['response'] else 'unknown', + } + self.update_status(latest_daac_status) + return self + + def update_status_ddb(self, archival_status): + if any([k is None for k in [self.__identifier, self.__collection, self.__granule]]): + raise ValueError(f'missing identifier, collection, or granule ID') + try: + self.__status_ddb.add(self.__identifier, self.__collection, self.__granule, archival_status['status'], + archival_status['datetime'], + archival_status['errorCode'] if 'errorCode' in archival_status else None, + archival_status['errorMessage'] if 'errorMessage' in archival_status else None, + archival_status['href'] if 'href' in archival_status else None, + self.__target_collection + ) + except Exception as e: + LOGGER.exception(f'Failed to store status in DDB {self.__collection}') + raise e + + def update_s3_url_from_traces_tbl(self, archival_status_with_timestamp): + if archival_status_with_timestamp['status'] != 'cnm-receive-success': + LOGGER.debug(f'NOT cnm-receive-success. skipping S3 Write for {self.__identifier}-{self.__collection}-{self.__granule}') + return self + traces_result = self.__uds_ctla_archiving_traces.get(self.__identifier) + if traces_result is None or len(traces_result) < 1: + LOGGER.debug(f'missing entry in TRACES table for {self.__identifier}-{self.__collection}-{self.__granule}') + return self + if len(traces_result) > 1: + LOGGER.warning(f'found duplicated identifiers: {traces_result}') + s3_url = traces_result[0][CataliaArchivingTraces.s3_url] + s3_url_result = f'{s3_url}.cnm_r.{archival_status_with_timestamp["datetime"]}.json' + archive_status = archival_status_with_timestamp.copy() + archive_status['identifier'] = self.__identifier + archive_status['collection'] = self.__collection + archive_status['id'] = self.__granule + s3 = AwsS3() + LOGGER.debug(f'writing to {s3_url_result}') + s3.set_s3_url(s3_url_result).upload_bytes(json.dumps(archive_status).encode('utf-8')) + return self + + def update_status(self, archival_status: dict): + """ + 1. validate archival_status from parameter against self.archival_status_schema + 2. Add archival_status to self.__archiving_granules_stac>properties>archival:status + 3. get collection and item id from self.__archiving_granules_stac + 4. convert self.__archiving_granules_stac to a json + 5. call self.__sfa_client.update_item() # Note partial may not be available. Just update whole for now. + :param archival_status: + :return: + """ + # TODO optional updating DEVSEED. configurable + # TODO store status to DDB? + # TODO if final status, write back to S3 + self.validate_status(archival_status) + # Add timestamp to the status + archival_status_with_timestamp = archival_status.copy() + archival_status_with_timestamp['datetime'] = f'{TimeUtils.get_current_time()}Z' + errors = [] + try: + self.update_status_ddb(archival_status_with_timestamp) + except Exception as e: + errors.append(e) + + try: + SfaClientMw().load_manually(self.__collection, self.__granule).update_sfa_item_status(archival_status_with_timestamp) + except Exception as e: + errors.append(e) + self.update_s3_url_from_traces_tbl(archival_status_with_timestamp) + if len(errors) > 0: + raise RuntimeError(f'Failed to update STAC item status: {errors}') + return \ No newline at end of file diff --git a/cumulus_lambda_functions/docker_entrypoint/__main__.py b/cumulus_lambda_functions/docker_entrypoint/__main__.py index 064f16d4..fffd973f 100644 --- a/cumulus_lambda_functions/docker_entrypoint/__main__.py +++ b/cumulus_lambda_functions/docker_entrypoint/__main__.py @@ -1,3 +1,4 @@ +import json import logging import os from sys import argv @@ -9,6 +10,7 @@ from mdps_ds_lib.stage_in_out.upoad_granules_factory import UploadGranulesFactory + def choose_process(): if argv[1].strip().upper() == 'SEARCH': logging.info('starting SEARCH script') @@ -30,6 +32,13 @@ def choose_process(): result_str = CatalogGranulesFactory().get_class(os.getenv('GRANULES_CATALOG_TYPE', 'MISSING_GRANULES_CATALOG_TYPE')).catalog() StageInOutUtils.write_output_to_file(result_str) return result_str + if argv[1].strip().upper() == 'CATALYA_COLLECTION_ARCHIVE': + logging.info('starting CATALYA_COLLECTION_ARCHIVE script') + from cumulus_lambda_functions.daac_archiver.daac_archiver_catalia import DaacArchiverCatalia + dac = DaacArchiverCatalia() + dac.staged_s3_bucket = os.getenv('CATALYA_UDS_STAGING_BUCKET') + dac.daac_agreements = json.loads(os.getenv('CATALYA_DAAC_CONFIGS')) + dac.archive_collection(os.getenv('CATALYA_COLLECTION_ID')) raise ValueError(f'invalid argument: {argv}') diff --git a/cumulus_lambda_functions/granules_cnm_ingester/granules_cnm_ingester_logic.py b/cumulus_lambda_functions/granules_cnm_ingester/granules_cnm_ingester_logic.py deleted file mode 100644 index b8ba59ae..00000000 --- a/cumulus_lambda_functions/granules_cnm_ingester/granules_cnm_ingester_logic.py +++ /dev/null @@ -1,210 +0,0 @@ -import os -import time -from concurrent.futures import ThreadPoolExecutor, as_completed - -from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from mdps_ds_lib.stage_in_out.stage_in_out_utils import StageInOutUtils - -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_cnm import CollectionsDapaCnm - -from mdps_ds_lib.lib.cumulus_stac.unity_collection_stac import UnityCollectionStac -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_creation import CollectionDapaCreation -from pystac import ItemCollection, Item -from mdps_ds_lib.lib.utils.file_utils import FileUtils -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -""" -TODO - -UNITY_DEFAULT_PROVIDER -CUMULUS_WORKFLOW_NAME -REPORT_TO_EMS -CUMULUS_WORKFLOW_SQS_URL -CUMULUS_LAMBDA_PREFIX -ES_URL -ES_PORT -SNS_TOPIC_ARN -""" -class GranulesCnmIngesterLogic: - def __init__(self): - self.__s3 = AwsS3() - self.__successful_features_json = None - self.__successful_features: ItemCollection = None - self.__collection_id = None - self.__chunk_size = StageInOutUtils.CATALOG_DEFAULT_CHUNK_SIZE - if 'UNITY_DEFAULT_PROVIDER' not in os.environ: - raise ValueError(f'missing UNITY_DEFAULT_PROVIDER') - self.__default_provider = os.environ.get('UNITY_DEFAULT_PROVIDER') - self.__uds_collection = UdsCollections(es_url=os.getenv('ES_URL'), es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS')) - - @property - def successful_features_json(self): - return self.__successful_features_json - - @successful_features_json.setter - def successful_features_json(self, val): - """ - :param val: - :return: None - """ - self.__successful_features_json = val - return - - @property - def collection_id(self): - return self.__collection_id - - @collection_id.setter - def collection_id(self, val): - """ - :param val: - :return: None - """ - self.__collection_id = val - return - - @property - def successful_features(self): - return self.__successful_features - - @successful_features.setter - def successful_features(self, val): - """ - :param val: - :return: None - """ - self.__successful_features = val - return - - def load_successful_features_s3(self, successful_features_s3_url): - self.__s3.set_s3_url(successful_features_s3_url) - if not self.__s3.exists(self.__s3.target_bucket, self.__s3.target_key): - LOGGER.error(f'missing successful_features: {successful_features_s3_url}') - raise ValueError(f'missing successful_features: {successful_features_s3_url}') - local_successful_features = self.__s3.download('/tmp') - self.__successful_features_json = FileUtils.read_json(local_successful_features) - FileUtils.remove_if_exists(local_successful_features) - self.__successful_features = ItemCollection.from_dict(self.__successful_features_json) - return - - def validate_granules(self): - if self.successful_features is None: - raise RuntimeError(f'NULL successful_features') - missing_granules = [] - for each_granule in self.successful_features.items: - missing_assets = [] - for each_asset_name, each_asset in each_granule.assets.items(): - temp_bucket, temp_key = self.__s3.split_s3_url(each_asset.href) - if not self.__s3.exists(temp_bucket, temp_key): - missing_assets.append({each_asset_name: each_asset.href}) - if len(missing_assets) > 0: - missing_granules.append({ - 'granule_id': each_granule.id, - 'missing_assets': missing_assets - }) - if len(missing_granules) > 0: - LOGGER.error(f'missing_granules: {missing_granules}') - raise ValueError(f'missing_granules: {missing_granules}') - return - - def extract_collection_id(self): - if self.successful_features is None: - raise RuntimeError(f'NULL successful_features') - if len(self.successful_features.items) < 1: - LOGGER.error(f'not required to process. No Granules: {self.successful_features.to_dict(False)}') - return - self.collection_id = list(set([k.collection_id for k in self.successful_features.items])) - return - - def has_collection(self, collection_id_custom=None): - collection_id_custom = collection_id_custom if collection_id_custom is not None else self.collection_id - uds_collection_result = self.__uds_collection.get_collection(collection_id_custom) - return len(uds_collection_result) > 0 - - def create_one_collection(self, collection_id): - try: - if collection_id is None: - raise RuntimeError(f'NULL collection_id') - if self.has_collection(collection_id): - LOGGER.debug(f'{collection_id} already exists. continuing..') - return {'status': 'success'} - # ref: https://github.com/unity-sds/unity-py/blob/0.4.0/unity_sds_client/services/data_service.py - dapa_collection = UnityCollectionStac() \ - .with_id(collection_id) \ - .with_graule_id_regex("^test_file.*$") \ - .with_granule_id_extraction_regex("(^test_file.*)(\\.nc|\\.nc\\.cas|\\.cmr\\.xml)") \ - .with_title(f'Collection: {collection_id}') \ - .with_process('stac') \ - .with_provider(self.__default_provider) \ - .add_file_type("test_file01.nc", "^test_file.*\\.nc$", 'unknown_bucket', 'application/json', 'root') \ - .add_file_type("test_file01.nc", "^test_file.*\\.nc$", 'protected', 'data', 'item') \ - .add_file_type("test_file01.nc.cas", "^test_file.*\\.nc.cas$", 'protected', 'metadata', 'item') \ - .add_file_type("test_file01.nc.cmr.xml", "^test_file.*\\.nc.cmr.xml$", 'protected', 'metadata', 'item') \ - .add_file_type("test_file01.nc.stac.json", "^test_file.*\\.nc.stac.json$", 'protected', 'metadata', - 'item') - - stac_collection = dapa_collection.start() - creation_result = CollectionDapaCreation(stac_collection).create() - if creation_result['statusCode'] >= 400: - raise RuntimeError( - f'failed to create collection: {collection_id}. details: {creation_result["body"]}') - time.sleep(3) # cool off period before checking DB - if not self.has_collection(collection_id): - LOGGER.error(f'missing collection. (failed to create): {collection_id}') - raise ValueError(f'missing collection. (failed to create): {collection_id}') - except Exception as e: - return {'status': 'error', 'details': str(e)} - return {'status': 'success'} - - def create_collection_async(self): - if self.collection_id is None: - raise RuntimeError(f'NULL collection_id') - with ThreadPoolExecutor() as executor: - futures = [executor.submit(self.create_one_collection, collection_id) for collection_id in self.collection_id] - results = [future.result() for future in as_completed(futures)] - errors = [k['details'] for k in results if k['status'] == 'error'] - if len(errors) > 0: - raise ValueError(f'error while creating collections: {errors}') - return - - def send_cnm_msg(self): - LOGGER.debug(f'starting ingest_cnm_dapa_actual') - try: - errors = [] - for i, features_chunk in enumerate(StageInOutUtils.chunk_list(self.successful_features_json['features'], self.__chunk_size)): - try: - LOGGER.debug(f'working on chunk_index {i}') - dapa_body = { - "provider_id": self.__default_provider, - "features": features_chunk - } - collections_dapa_cnm = CollectionsDapaCnm(dapa_body) - cnm_result = collections_dapa_cnm.start() - if cnm_result['statusCode'] != 200: - errors.extend(features_chunk) - except Exception as e1: - LOGGER.exception(f'failed to queue CNM process.') - errors.extend(features_chunk) - except Exception as e: - LOGGER.exception('failed to ingest to CNM') - raise ValueError(f'failed to ingest to CNM: {e}') - if len(errors) > 0: - raise RuntimeError(f'failures during CNM ingestion: {errors}') - return - - def start(self, event): - LOGGER.debug(f'event: {event}') - sns_msg = AwsMessageTransformers().sqs_sns(event) - s3_details = AwsMessageTransformers().get_s3_from_sns(sns_msg) - s3_url = f's3://{s3_details["bucket"]}/{s3_details["key"]}' - self.load_successful_features_s3(s3_url) - self.validate_granules() - self.extract_collection_id() - self.create_collection_async() - self.send_cnm_msg() - return diff --git a/cumulus_lambda_functions/granules_cnm_ingester/lambda_function.py b/cumulus_lambda_functions/granules_cnm_ingester/lambda_function.py deleted file mode 100644 index b5f7cbaf..00000000 --- a/cumulus_lambda_functions/granules_cnm_ingester/lambda_function.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - -from cumulus_lambda_functions.granules_cnm_ingester.granules_cnm_ingester_logic import GranulesCnmIngesterLogic -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - - -def lambda_handler(event, context): - """ - :param event: - :param context: - :return: - {'Records': [{'messageId': '6ff7c6fd-4053-4ab4-bc12-c042be1ed275', 'receiptHandle': 'AQEBYASiFPjQT5JBI2KKCTF/uQhHfJt/tHhgucslQQdvkNVxcXCNi2E5Ux4U9N0eu7RfvlnvtycjUh0gdL7jIeoyH+VRKSF61uAJuT4p31BsNe0GYu49N9A6+kxjP/RrykR7ZofmQRdHToX1ugRc76SMRic4H/ZZ89YAHA2QeomJFMrYywIxlk8OAzYaBf2dQI7WexjY5u1CW00XNMbTGyTo4foVPxcSn6bdFpfgxW/L7yJMX/0YQvrA9ruiuQ+lrui+6fWYh5zEk3f5v1bYtUQ6DtyyfbtMHZQJTJpUlWAFRzzN+3melilH7FySyOGDXhPb0BOSzmdKq9wBbfLW/YPb7l99ejq4GfRfj8LyI4EtB96vTeUw4LCgUqbZcBrxbGBLUXMacweh+gCjHav9ylqr2SeOiqG3vWPq9pwFYQIDqNE=', 'body': '{\n "Type" : "Notification",\n "MessageId" : "33e1075a-435c-5217-a33d-59fae85e19b2",\n "TopicArn" : "arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester",\n "Subject" : "Amazon S3 Notification",\n "Message" : "{\\"Service\\":\\"Amazon S3\\",\\"Event\\":\\"s3:TestEvent\\",\\"Time\\":\\"2024-04-22T18:13:22.416Z\\",\\"Bucket\\":\\"uds-sbx-cumulus-staging\\",\\"RequestId\\":\\"DQ4T0GRVFPSX45C9\\",\\"HostId\\":\\"gHBFnYNmfnGDZBmqoQwA3RScjtjBk5lr426moGxu8IDpe5UhWAqNTxHqilWBoPN1njzIrzNrf8c=\\"}",\n "Timestamp" : "2024-04-22T18:13:22.434Z",\n "SignatureVersion" : "1",\n "Signature" : "RvSxqpU7J7CCJXbin9cXqTxzjMjgAUFtk/n454mTMcOe5x3Ay1w4AHfzyeYQCFBdLHNBa8n3OdMDoDlJqyVQMb8k+nERaiZWN2oqFVDRqT9pqSr89b+4FwlhPv6TYy2pBa/bgjZ4cOSYsey1uSQ3hjl0idfssvuV5cCRxQScbA+yu8Gcv9K7Oqgy01mC0sDHiuPIifhFXxupG5ygbjqoHIB+1gdMEbBwyixoY5GOpHM/O2uHNF+dJDjax1WMxQ2FzVjiFeCa+tNcjovF059+tx2v1YmDq/kEAFrN6DAtP6R4zKag62P9jkvjU/wHYJ2jjXmZAqoG+nuzAo24HiZPSw==",\n "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-60eadc530605d63b8e62a523676ef735.pem",\n "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester:76cbefa1-addf-45c2-97e1-ae16986b195b"\n}', 'attributes': {'ApproximateReceiveCount': '1', 'SentTimestamp': '1713809602474', 'SenderId': 'AIDAIYLAVTDLUXBIEIX46', 'ApproximateFirstReceiveTimestamp': '1713809602483'}, 'messageAttributes': {}, 'md5OfBody': 'c6d06d1b742ad5bd2cfe5f542640aad2', 'eventSource': 'aws:sqs', 'eventSourceARN': 'arn:aws:sqs:us-west-2:237868187491:uds-sbx-cumulus-granules_cnm_ingester', 'awsRegion': 'us-west-2'}]} - """ - LambdaLoggerGenerator.remove_default_handlers() - GranulesCnmIngesterLogic().start(event) - return diff --git a/cumulus_lambda_functions/granules_cnm_response_writer/cnm_result_writer.py b/cumulus_lambda_functions/granules_cnm_response_writer/cnm_result_writer.py deleted file mode 100644 index cfca853c..00000000 --- a/cumulus_lambda_functions/granules_cnm_response_writer/cnm_result_writer.py +++ /dev/null @@ -1,90 +0,0 @@ -import json - -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class CnmResultWriter: - def __init__(self): - self.__s3 = AwsS3() - self.__cnm_response_schema = { - 'type': 'object', - 'required': ['collection', 'product', 'submissionTime'], - 'properties': { - 'submissionTime': {'type': 'string'}, - 'collection': {'type': 'string'}, - 'product': { - 'type': 'object', - 'required': ['name', 'files'], - 'properties': { - 'name': {'type': 'string'}, - 'files': { - 'type': 'array', - 'minItems': 1, - 'items': { - 'type': 'object', - 'required': ['name', 'uri'], - 'properties': { - 'name': {'type': 'string'}, - 'uri': {'type': 'string'}, - } - }, - } - } - } - } - } - self.__cnm_response = {} - self.__s3_url = None - - @property - def s3_url(self): - return self.__s3_url - - @s3_url.setter - def s3_url(self, val): - """ - :param val: - :return: None - """ - self.__s3_url = val - return - - @property - def cnm_response(self): - return self.__cnm_response - - @cnm_response.setter - def cnm_response(self, val): - """ - :param val: - :return: None - """ - self.__cnm_response = val - return - - def extract_s3_location(self): - result = JsonValidator(self.__cnm_response_schema).validate(self.cnm_response) - if result is not None: - LOGGER.error(f'invalid JSON: {result}. request_body: {self.cnm_response}') - raise ValueError(f'invalid JSON: {result}. request_body: {self.cnm_response}') - response_filename = f'{self.cnm_response["product"]["name"]}.{self.cnm_response["submissionTime"]}.cnm.json' - parsed_url = self.cnm_response['product']['files'][0]['uri'].split('//')[1] - s3_url = parsed_url.split('/') - s3_url[-1] = response_filename - self.__s3_url = 's3://' + '/'.join(s3_url[1:]) - LOGGER.debug(f'extracted s3_url: {self.__s3_url}') - return self - - def start(self, event): - LOGGER.debug(f'event: {event}') - sns_msg = AwsMessageTransformers().sqs_sns(event) - LOGGER.debug(f'sns_msg: {sns_msg}') - self.cnm_response = sns_msg - self.extract_s3_location() - self.__s3.set_s3_url(self.s3_url).upload_bytes(json.dumps(self.cnm_response, indent=4).encode()) - return diff --git a/cumulus_lambda_functions/granules_to_es/granules_index_mapping.py b/cumulus_lambda_functions/granules_to_es/granules_index_mapping.py deleted file mode 100644 index 2fdc5021..00000000 --- a/cumulus_lambda_functions/granules_to_es/granules_index_mapping.py +++ /dev/null @@ -1,118 +0,0 @@ -class GranulesIndexMapping: - archiving_keys = [ - 'archive_status', 'archive_error_message', 'archive_error_code' - ] - percolator_mappings = { - "daac_collection_name": { - "type": "keyword" - }, - "daac_data_version": { - "type": "keyword" - }, - "daac_provider": { - "type": "keyword" - }, - "daac_role_arn": { - "type": "keyword" - }, - "daac_role_session_name": { - "type": "keyword" - }, - "archiving_types": { - "type": "object", - "properties": { - "data_type": {"type": "keyword"}, - "file_extension": {"type": "keyword"}, - } - }, - "daac_sns_topic_arn": { - "type": "keyword" - }, - "ss_query": { - "type": "percolator" - }, - "ss_username": { - "type": "keyword" - }, - } - stac_mappings = { - "archive_status": {"type": "keyword"}, - "archive_error_message": {"type": "text"}, - "archive_error_code": {"type": "keyword"}, - - "event_time": {"type": "long"}, - "type": {"type": "keyword"}, - "stac_version": {"type": "keyword"}, - "id": {"type": "keyword"}, - "collection": {"type": "keyword"}, - "geometry": {"type": "geo_shape"}, - "bbox": {"type": "geo_shape"}, - "links": { - "type": "object", - "properties": { - "href": {"type": "keyword"}, - "rel": {"type": "keyword"}, - "type": {"type": "keyword"}, - "title": {"type": "text"} - } - }, - "stac_extensions": {"type": "keyword"}, - "properties": { - "dynamic": "false", - "properties": { - "provider": {"type": "keyword"}, - "status": {"type": "keyword"}, - "datetime": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss'Z'||yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'||yyyy-MM-dd||epoch_millis"}, - "updated": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss'Z'||yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'||yyyy-MM-dd||epoch_millis"}, - "start_datetime": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss'Z'||yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'||yyyy-MM-dd||epoch_millis"}, - "end_datetime": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss'Z'||yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'||yyyy-MM-dd||epoch_millis"}, - "created": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss'Z'||yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'||yyyy-MM-dd||epoch_millis"}, - }, - }, - "assets": { - "type": "object", - "dynamic": False, - } - } - mappings = { - "eventTime": {"type": "long"}, - "collectionId": {"type": "keyword"}, - "createdAt": {"type": "long"}, - "duration": {"type": "float"}, - "error": { - "type": "object", - "properties": { - "Cause": {"type": "text"}, - "Error": {"type": "text"} - } - }, - "execution": {"type": "text"}, - "granuleId": {"type": "keyword"}, - "processingEndDateTime": {"type": "date"}, - "processingStartDateTime": {"type": "date"}, - "productVolume": {"type": "keyword"}, - "provider": {"type": "keyword"}, - "published": {"type": "boolean"}, - "status": {"type": "keyword"}, - "timestamp": {"type": "long"}, - "timeToArchive": {"type": "float"}, - "timeToPreprocess": {"type": "float"}, - "updatedAt": {"type": "long"}, - "files": { - "type": "object", - "properties": { - "bucket": {"type": "keyword"}, - "checksum": {"type": "keyword"}, - "checksumType": {"type": "keyword"}, - "fileName": {"type": "keyword"}, - "key": {"type": "keyword"}, - "size": {"type": "integer"}, - "source": {"type": "text"}, - "type":{"type": "keyword"} - } - }, - "beginningDateTime": {"type": "date"}, - "endingDateTime": {"type": "date"}, - "lastUpdateDateTime": {"type": "date"}, - "productionDateTime": {"type": "date"}, - } diff --git a/cumulus_lambda_functions/granules_to_es/granules_indexer.py b/cumulus_lambda_functions/granules_to_es/granules_indexer.py deleted file mode 100644 index 5e98f4c2..00000000 --- a/cumulus_lambda_functions/granules_to_es/granules_indexer.py +++ /dev/null @@ -1,121 +0,0 @@ -import json -import os -from time import sleep - -from cumulus_lambda_functions.daac_archiver.daac_archiver_logic import DaacArchiverLogic -from mdps_ds_lib.lib.utils.file_utils import FileUtils - -from mdps_ds_lib.lib.cumulus_stac.item_transformer import ItemTransformer - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.metadata_stac_generate_cmr.stac_input_metadata import StacInputMetadata - -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from mdps_ds_lib.lib.utils.json_validator import JsonValidator - -from mdps_ds_lib.lib.aws.aws_message_transformers import AwsMessageTransformers -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class GranulesIndexer: - CUMULUS_SCHEMA = { - 'type': 'object', - 'required': ['event', 'record'], - 'properties': { - 'event': {'type': 'string'}, - 'record': {'type': 'object'}, - } - } - - def __init__(self, event) -> None: - self.__event = event - LOGGER.debug(f'event: {event}') - self.__cumulus_record = {} - self.__file_postfixes = os.getenv('FILE_POSTFIX', 'STAC.JSON') - self.__valid_filetype_name = os.getenv('VALID_FILETYPE', 'metadata').lower() - self.__file_postfixes = [k.upper().strip() for k in self.__file_postfixes.split(',')] - self.__input_file_list = [] - self.__s3 = AwsS3() - - def __get_potential_files(self): - potential_files = [] - self.__input_file_list = self.__cumulus_record['files'] - for each_file in self.__input_file_list: - if 'type' in each_file and self.__valid_filetype_name not in each_file['type'].strip().lower(): - LOGGER.debug(f'Not metadata. skipping {each_file}') - continue - if 'fileName' not in each_file and 'name' in each_file: # add fileName if there is only name - each_file['fileName'] = each_file['name'] - if 'url_path' in each_file: - s3_bucket, s3_key = self.__s3.split_s3_url(each_file['url_path']) - each_file['bucket'] = s3_bucket - each_file['key'] = s3_key - LOGGER.debug(f'checking file: {each_file}') - file_key_upper = each_file['key'].upper().strip() - LOGGER.debug(f'checking file_key_upper: {file_key_upper} against {self.__file_postfixes}') - if any([file_key_upper.endswith(k) for k in self.__file_postfixes]): - potential_files.append(each_file) - return potential_files - - def __read_pds_metadata_file(self, potential_file): - self.__s3.target_bucket = potential_file['bucket'] - self.__s3.target_key = potential_file['key'] - return self.__s3.read_small_txt_file() - - def start(self): - incoming_msg = AwsMessageTransformers().sqs_sns(self.__event) - result = JsonValidator(self.CUMULUS_SCHEMA).validate(incoming_msg) - if result is not None: - raise ValueError(f'input json has CUMULUS validation errors: {result}') - if 'event' not in incoming_msg or incoming_msg['event'].upper() == 'DELETE': - LOGGER.debug(f'missing event or it is DELETE event. Not inserting to ES') - return - self.__cumulus_record = incoming_msg['record'] - if len(self.__cumulus_record['files']) < 1: - LOGGER.debug(f'No files in cumulus record. Not inserting to ES') - # TODO ingest updating stage? - return - if 'status' not in self.__cumulus_record or self.__cumulus_record['status'].upper() != 'COMPLETED': - LOGGER.debug(f'missing status or it is NOT COMPLETED status. Not inserting to ES') - return - stac_input_meta = None - potential_files = self.__get_potential_files() - LOGGER.debug(f'potential_files: {potential_files}') - for each_potential_file in potential_files: - try: - LOGGER.debug(f'trying each_potential_file: {each_potential_file}') - stac_input_meta = StacInputMetadata(json.loads(self.__read_pds_metadata_file(each_potential_file))) - granules_metadata_props = stac_input_meta.start() - break - except: - LOGGER.exception(f'most likely not a STAC file: {each_potential_file}') - if stac_input_meta is not None: - self.__cumulus_record['custom_metadata'] = stac_input_meta.custom_properties - else: - LOGGER.warning(f'unable to find STAC JSON file in {potential_files}') - stac_item = ItemTransformer().to_stac(self.__cumulus_record) - if stac_input_meta is not None and stac_input_meta.bbox is not None: - stac_item['bbox'] = stac_input_meta.bbox - if 'bbox' in stac_item: - stac_item['bbox'] = GranulesDbIndex.to_es_bbox(stac_item['bbox']) - collection_identifier = UdsCollections.decode_identifier(self.__cumulus_record['collectionId']) - LOGGER.debug(f'stac_item: {stac_item}') - GranulesDbIndex().add_entry(collection_identifier.tenant, - collection_identifier.venue, - stac_item, - self.__cumulus_record['granuleId'] - ) - LOGGER.debug(f'added to GranulesDbIndex') - daac_archiver = DaacArchiverLogic() - cnm_response = daac_archiver.get_cnm_response_json_file(list(stac_item['assets'].values())[0], stac_item['id']) - if cnm_response is None: - LOGGER.error(f'no CNM Response file. Not continuing to DAAC Archiving') - return self - daac_archiver.send_to_daac_internal(cnm_response) - return self diff --git a/cumulus_lambda_functions/granules_to_es/lambda_function.py b/cumulus_lambda_functions/granules_to_es/lambda_function.py deleted file mode 100644 index ceb76351..00000000 --- a/cumulus_lambda_functions/granules_to_es/lambda_function.py +++ /dev/null @@ -1,15 +0,0 @@ -from cumulus_lambda_functions.granules_to_es.granules_indexer import GranulesIndexer -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - - -def lambda_handler(event, context): - """ -{'cma': {'task_config': {'bucket': '{$.meta.buckets.internal.name}', 'collection': '{$.meta.collection}', 'cumulus_message': {'outputs': [{'source': '{$.files}', 'destination': '{$.payload}'}]}}, 'event': {'cumulus_meta': {'cumulus_version': '10.0.1', 'execution_name': 'c6d885dc-b4b2-4eb0-b22e-b6f58a7a0870', 'message_source': 'sfn', 'queueExecutionLimits': {'https://sqs.us-west-2.amazonaws.com/884500545225/am-uds-dev-cumulus-backgroundProcessing': 5}, 'state_machine': 'arn:aws:states:us-west-2:884500545225:stateMachine:am-uds-dev-cumulus-IngestGranule', 'system_bucket': 'am-uds-dev-cumulus-internal', 'workflow_start_time': 1646785175509, 'parentExecutionArn': 'arn:aws:states:us-west-2:884500545225:execution:am-uds-dev-cumulus-DiscoverGranules:885483b4-ba55-4db1-b197-661e1e595a45', 'queueUrl': 'arn:aws:sqs:us-west-2:884500545225:am-uds-dev-cumulus-startSF'}, 'exception': 'None', 'meta': {'buckets': {'internal': {'name': 'am-uds-dev-cumulus-internal', 'type': 'internal'}, 'protected': {'name': 'am-uds-dev-cumulus-protected', 'type': 'protected'}}, 'cmr': {'clientId': 'CHANGEME', 'cmrEnvironment': 'UAT', 'cmrLimit': 100, 'cmrPageSize': 50, 'oauthProvider': 'earthdata', 'passwordSecretName': 'am-uds-dev-cumulus-message-template-cmr-password20220216072916956000000002', 'provider': 'CHANGEME', 'username': 'username'}, 'collection': {'name': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'process': 'modis', 'granuleId': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$', 'granuleIdExtraction': '(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'duplicateHandling': 'replace', 'url_path': '{cmrMetadata.Granule.Collection.ShortName}___{cmrMetadata.Granule.Collection.VersionId}', 'provider_path': '/data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/', 'files': [{'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}00\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000000.PDS', 'type': 'data'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0\\.cmr\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}], 'updatedAt': 1646326197526, 'createdAt': 1646258167624}, 'distribution_endpoint': 's3://am-uds-dev-cumulus-internal/', 'launchpad': {'api': 'launchpadApi', 'certificate': 'launchpad.pfx', 'passphraseSecretName': ''}, 'provider': {'password': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQHFbE4iMnF/W0Y/NrsYvrfHAAAAajBoBgkqhkiG9w0BBwagWzBZAgEAMFQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMLaH13SdxPXREjXLtAgEQgCfA+lEu2c/xLTGwJsbtKlXJbKDy4pwV+rS3BnJqgBoLLMQZqOdoFhk=', 'host': 'snppl0.gesdisc.eosdis.nasa.gov', 'updatedAt': 1646244053419, 'protocol': 'https', 'createdAt': 1646244053419, 'encrypted': True, 'username': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQGRoY5EBMpvvyMASUowBM61AAAAYzBhBgkqhkiG9w0BBwagVDBSAgEAME0GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9OhRHwTuxiz74q4UAgEQgCDEHOhsVG6+LqXfnlw+Z3Wg9MDOCd9/K5/X5j3tPJYkaA==', 'allowedRedirects': ['https://urs.earthdata.nasa.gov', 'urs.earthdata.nasa.gov'], 'id': 'snpp_provider_02', 'globalConnectionLimit': 10}, 'stack': 'am-uds-dev-cumulus', 'template': 's3://am-uds-dev-cumulus-internal/am-uds-dev-cumulus/workflow_template.json', 'workflow_name': 'IngestGranule', 'workflow_tasks': {'SyncGranule': {'name': 'am-uds-dev-cumulus-SyncGranule', 'version': '$LATEST', 'arn': 'arn:aws:lambda:us-west-2:884500545225:function:am-uds-dev-cumulus-SyncGranule'}}, 'staticValue': 'aStaticValue', 'interpolatedValueStackName': 'am-uds-dev-cumulus', 'input_granules': [{'granuleId': 'P1570515ATMSSCIENCEAXT1134912000000', 'dataType': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'files': [{'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000000.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000000.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000000.PDS', 'type': 'data', 'size': 744}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS', 'type': 'metadata', 'size': 18084600}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'type': 'metadata', 'size': 9526}], 'sync_granule_duration': 9822, 'createdAt': 1647386972717}], 'process': 'modis'}, 'payload': {}, 'replace': {'Bucket': 'am-uds-dev-cumulus-internal', 'Key': 'events/5d8edf37-0a18-4af5-a76f-7c2091cdd1e2', 'TargetPath': '$.payload'}}}} - :param event: - :param context: - :return: - """ - LambdaLoggerGenerator.remove_default_handlers() - # TODO implement - GranulesIndexer(event).start() - return {} diff --git a/cumulus_lambda_functions/granules_to_es/sample_messages_1.json b/cumulus_lambda_functions/granules_to_es/sample_messages_1.json deleted file mode 100644 index 9767d31b..00000000 --- a/cumulus_lambda_functions/granules_to_es/sample_messages_1.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Type" : "Notification", - "MessageId" : "f6441383-4a99-5f27-8a66-4e44177ea7f4", - "TopicArn" : "arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-report-granules-topic", - "Message" : "{\"event\":\"Update\",\"record\":{\"collectionId\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417\",\"createdAt\":1699568530472,\"duration\":20.553,\"error\":{\"Cause\":\"None\",\"Error\":\"Unknown Error\"},\"execution\":\"https://console.aws.amazon.com/states/home?region=us-west-2#/executions/details/arn:aws:states:us-west-2:237868187491:execution:uds-sbx-cumulus-CatalogGranule:3ae4c03e-dcd1-4d25-8b8a-b8c2a3c126ae\",\"files\":[],\"granuleId\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01\",\"processingEndDateTime\":\"2023-11-09T22:22:31.023Z\",\"processingStartDateTime\":\"2023-11-09T22:22:10.949Z\",\"productVolume\":\"-3\",\"provider\":\"unity\",\"published\":false,\"status\":\"running\",\"timestamp\":1699568551025,\"timeToArchive\":0,\"timeToPreprocess\":0,\"updatedAt\":1699568551025}}", - "Timestamp" : "2023-11-09T22:22:31.148Z", - "SignatureVersion" : "1", - "Signature" : "Skm4aumaUGxZA76/Jdya+6a42805KvAn6PrZIwXdKHE+ng37e+aN75SuCTDrv5hzeRFxA8YSoEYMG+00CvnoVN3gtsVt/o78Nkj5lr2oMCwNj2k5kwyEve4BetRelyXF1BTc7ptD7MYsSVGrIWZQwqNqUviDfBdI1nxujDiZvWnjAPWjJA8+cjx2acFAbaTzIhN90V3Fn0yOtveVXblAUZQ3EwF8Cv0CsTJFVPYliguw72s2r+9xPbc5Yj8dBL4B38HI7JC+u6qL8vgzIh+/wVlpqOef5P23qFeYDE533318EUEDfrkRs//LCbe+lcoTzka5qwOWaveMbIM9tstmeg==", - "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-01d088a6f77103d0fe307c0069e40ed6.pem", - "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-report-granules-topic:c003b5ee-09a7-4129-9873-a1629868e8bd" -} \ No newline at end of file diff --git a/cumulus_lambda_functions/granules_to_es/sample_messages_1_sns.json b/cumulus_lambda_functions/granules_to_es/sample_messages_1_sns.json deleted file mode 100644 index 930f328a..00000000 --- a/cumulus_lambda_functions/granules_to_es/sample_messages_1_sns.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "event": "Update", - "record": { - "collectionId": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417", - "createdAt": 1699568530472, - "duration": 20.553, - "error": { - "Cause": "None", - "Error": "Unknown Error" - }, - "execution": "https://console.aws.amazon.com/states/home?region=us-west-2#/executions/details/arn:aws:states:us-west-2:237868187491:execution:uds-sbx-cumulus-CatalogGranule:3ae4c03e-dcd1-4d25-8b8a-b8c2a3c126ae", - "granuleId": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01", - "processingEndDateTime": "2023-11-09T22:22:31.023Z", - "processingStartDateTime": "2023-11-09T22:22:10.949Z", - "productVolume": "-3", - "provider": "unity", - "published": false, - "status": "running", - "timestamp": 1699568551025, - "timeToArchive": 0, - "timeToPreprocess": 0, - "updatedAt": 1699568551025, - "files": [] - } -} \ No newline at end of file diff --git a/cumulus_lambda_functions/granules_to_es/sample_messages_2.json b/cumulus_lambda_functions/granules_to_es/sample_messages_2.json deleted file mode 100644 index 3608fc52..00000000 --- a/cumulus_lambda_functions/granules_to_es/sample_messages_2.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Type" : "Notification", - "MessageId" : "4a942646-3a1f-54dd-beda-7b8167e89190", - "TopicArn" : "arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-report-granules-topic", - "Message" : "{\"event\":\"Update\",\"record\":{\"beginningDateTime\":\"2016-01-31T18:00:00.009Z\",\"collectionId\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417\",\"createdAt\":1699568555580,\"duration\":36.81,\"endingDateTime\":\"2016-01-31T19:59:59.991Z\",\"error\":{\"Cause\":\"None\",\"Error\":\"Unknown Error\"},\"execution\":\"https://console.aws.amazon.com/states/home?region=us-west-2#/executions/details/arn:aws:states:us-west-2:237868187491:execution:uds-sbx-cumulus-CatalogGranule:3ae4c03e-dcd1-4d25-8b8a-b8c2a3c126ae\",\"files\":[{\"bucket\":\"uds-sbx-cumulus-staging\",\"checksum\":\"9817be382b87c48ebe482b9c47d1525a\",\"checksumType\":\"md5\",\"fileName\":\"test_file01.cmr.xml\",\"key\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml\",\"size\":1768,\"source\":\"s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml\",\"type\":\"metadata\"},{\"bucket\":\"uds-sbx-cumulus-staging\",\"checksum\":\"unknown\",\"checksumType\":\"md5\",\"fileName\":\"test_file01.nc.stac.json\",\"key\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json\",\"size\":-1,\"source\":\"s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json\",\"type\":\"metadata__stac\"},{\"bucket\":\"uds-sbx-cumulus-staging\",\"checksum\":\"unknown\",\"checksumType\":\"md5\",\"fileName\":\"test_file01.nc.cas\",\"key\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas\",\"size\":-1,\"source\":\"s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas\",\"type\":\"metadata__cas\"},{\"bucket\":\"uds-sbx-cumulus-staging\",\"checksum\":\"unknown\",\"checksumType\":\"md5\",\"fileName\":\"test_file01.nc\",\"key\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc\",\"size\":-1,\"source\":\"s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc\",\"type\":\"data\"}],\"granuleId\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01\",\"lastUpdateDateTime\":\"2018-04-25T21:45:45.524Z\",\"processingEndDateTime\":\"2023-11-09T22:22:45.944Z\",\"processingStartDateTime\":\"2023-11-09T22:22:10.949Z\",\"productionDateTime\":\"1970-01-01T00:00:00.000Z\",\"productVolume\":\"1765\",\"provider\":\"unity\",\"published\":false,\"queryFields\":{\"cnm\":{\"product\":{\"name\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01\",\"files\":[{\"uri\":\"https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc\",\"name\":\"test_file01.nc\",\"size\":-1,\"type\":\"data\",\"checksum\":\"unknown\",\"checksumType\":\"md5\"},{\"uri\":\"https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas\",\"name\":\"test_file01.nc.cas\",\"size\":-1,\"type\":\"metadata__cas\",\"checksum\":\"unknown\",\"checksumType\":\"md5\"},{\"uri\":\"https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json\",\"name\":\"test_file01.nc.stac.json\",\"size\":-1,\"type\":\"metadata__stac\",\"checksum\":\"unknown\",\"checksumType\":\"md5\"},{\"uri\":\"https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml\",\"name\":\"test_file01.cmr.xml\",\"size\":1768,\"type\":\"metadata\",\"checksum\":\"9817be382b87c48ebe482b9c47d1525a\",\"checksumType\":\"md5\"}],\"dataVersion\":\"2311091417\"},\"version\":\"1.6.0\",\"provider\":\"unity\",\"response\":{\"status\":\"SUCCESS\"},\"collection\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION\",\"identifier\":\"URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01\",\"receivedTime\":\"2023-11-09T22:22:23.399Z\",\"submissionTime\":\"2023-11-09T22:21:21.989722\",\"processCompleteTime\":\"2023-11-09T22:22:41.220Z\"}},\"status\":\"completed\",\"timestamp\":1699568567282,\"timeToArchive\":0,\"timeToPreprocess\":20.302,\"updatedAt\":1699568567282}}", - "Timestamp" : "2023-11-09T22:22:47.475Z", - "SignatureVersion" : "1", - "Signature" : "iFiqm/5cbGylkLpacwZyF6eg7m4ICZ5m8bEfWCsCps/6EHyduGMjpzoQWeULLOcY5pMk4n0eQyYwR4p3I7jdFjZfCY/LiB8zYcel4oUeenV+sMi0KhXvfYJeKByvJeMHbzMiyXt/WDcsucwz3g0hbgBb5rVS3o9/PbXBTB683nwAVd4Dt8KnHHxxBxm6o4a+ehaCicuUSpdjjosJyIA1pZ29OaVcAK/+m4wSYqv9VY7reWMrLFkdOEZkrrc3XT9bKV9wONQwBdcHv+2V24/RYZEjUBILDVIKAz011zI1PdFFvm1hKyLuqZm2ioGedWkRd97VGi9MhDbRE14vEuEY8g==", - "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-01d088a6f77103d0fe307c0069e40ed6.pem", - "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:237868187491:uds-sbx-cumulus-report-granules-topic:c003b5ee-09a7-4129-9873-a1629868e8bd" -} diff --git a/cumulus_lambda_functions/granules_to_es/sample_messages_2_sns.json b/cumulus_lambda_functions/granules_to_es/sample_messages_2_sns.json deleted file mode 100644 index 3e3612a4..00000000 --- a/cumulus_lambda_functions/granules_to_es/sample_messages_2_sns.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "event": "Update", - "record": { - "collectionId": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417", - "createdAt": 1699568555580, - "duration": 36.81, - "error": { - "Cause": "None", - "Error": "Unknown Error" - }, - "execution": "https://console.aws.amazon.com/states/home?region=us-west-2#/executions/details/arn:aws:states:us-west-2:237868187491:execution:uds-sbx-cumulus-CatalogGranule:3ae4c03e-dcd1-4d25-8b8a-b8c2a3c126ae", - "granuleId": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01", - "processingEndDateTime": "2023-11-09T22:22:45.944Z", - "processingStartDateTime": "2023-11-09T22:22:10.949Z", - "productVolume": "1765", - "provider": "unity", - "published": false, - "status": "completed", - "timestamp": 1699568567282, - "timeToArchive": 0, - "timeToPreprocess": 20.302, - "updatedAt": 1699568567282, - "files": [ - { - "bucket": "uds-sbx-cumulus-staging", - "checksum": "9817be382b87c48ebe482b9c47d1525a", - "checksumType": "md5", - "fileName": "test_file01.cmr.xml", - "key": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml", - "size": 1768, - "source": "s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml", - "type": "metadata" - }, - { - "bucket": "uds-sbx-cumulus-staging", - "checksum": "unknown", - "checksumType": "md5", - "fileName": "test_file01.nc.stac.json", - "key": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json", - "size": -1, - "source": "s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json", - "type": "metadata__stac" - }, - { - "bucket": "uds-sbx-cumulus-staging", - "checksum": "unknown", - "checksumType": "md5", - "fileName": "test_file01.nc.cas", - "key": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas", - "size": -1, - "source": "s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas", - "type": "metadata__cas" - }, - { - "bucket": "uds-sbx-cumulus-staging", - "checksum": "unknown", - "checksumType": "md5", - "fileName": "test_file01.nc", - "key": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc", - "size": -1, - "source": "s3://uds-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc", - "type": "data" - } - ], - "beginningDateTime": "2016-01-31T18:00:00.009Z", - "endingDateTime": "2016-01-31T19:59:59.991Z", - "lastUpdateDateTime": "2018-04-25T21:45:45.524Z", - "productionDateTime": "1970-01-01T00:00:00.000Z", - "queryFields": { - "cnm": { - "product": { - "name": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01", - "files": [ - { - "uri": "https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc", - "name": "test_file01.nc", - "size": -1, - "type": "data", - "checksum": "unknown", - "checksumType": "md5" - }, - { - "uri": "https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.cas", - "name": "test_file01.nc.cas", - "size": -1, - "type": "metadata__cas", - "checksum": "unknown", - "checksumType": "md5" - }, - { - "uri": "https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.nc.stac.json", - "name": "test_file01.nc.stac.json", - "size": -1, - "type": "metadata__stac", - "checksum": "unknown", - "checksumType": "md5" - }, - { - "uri": "https://uds-distribution-placeholder/uds-sbx-cumulus-staging/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417/URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01/test_file01.cmr.xml", - "name": "test_file01.cmr.xml", - "size": 1768, - "type": "metadata", - "checksum": "9817be382b87c48ebe482b9c47d1525a", - "checksumType": "md5" - } - ], - "dataVersion": "2311091417" - }, - "version": "1.6.0", - "provider": "unity", - "response": { - "status": "SUCCESS" - }, - "collection": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION", - "identifier": "URN:NASA:UNITY:UDS_LOCAL_TEST:DEV:UDS_COLLECTION___2311091417:test_file01", - "receivedTime": "2023-11-09T22:22:23.399Z", - "submissionTime": "2023-11-09T22:21:21.989722", - "processCompleteTime": "2023-11-09T22:22:41.220Z" - } - } - } -} \ No newline at end of file diff --git a/cumulus_lambda_functions/keycloak_authorizer/README.md b/cumulus_lambda_functions/keycloak_authorizer/README.md new file mode 100644 index 00000000..4cca265b --- /dev/null +++ b/cumulus_lambda_functions/keycloak_authorizer/README.md @@ -0,0 +1,33 @@ +# Keycloak Authorizer (Placeholder) + +This is a **temporary placeholder** Lambda authorizer that allows all requests for testing purposes. + +## Current Behavior + +- **Allows all requests** without validation +- Adds fake JWT token context similar to what Keycloak would provide +- Returns fake user information for testing + +## Fake Context Provided + +The authorizer adds the following fake context to requests: + +- `userId`: test-user-123 +- `username`: test-user +- `email`: test-user@example.com +- `name`: Test User +- `roles`: ["user", "admin", "developer"] +- `groups`: ["/unity/developers", "/unity/users"] +- `jwtToken`: Base64-encoded fake JWT payload +- `authType`: PLACEHOLDER_KEYCLOAK (flag to indicate this is a placeholder) + +## TODO + +⚠️ **Replace with actual Keycloak integration** once Keycloak is connected and configured. + +The actual implementation should: +1. Validate JWT tokens from Keycloak +2. Verify token signatures +3. Check token expiration +4. Extract real user claims from the token +5. Enforce proper authorization policies diff --git a/cumulus_lambda_functions/metadata_cas_generate_cmr/__init__.py b/cumulus_lambda_functions/keycloak_authorizer/__init__.py similarity index 100% rename from cumulus_lambda_functions/metadata_cas_generate_cmr/__init__.py rename to cumulus_lambda_functions/keycloak_authorizer/__init__.py diff --git a/cumulus_lambda_functions/keycloak_authorizer/lambda_function.py b/cumulus_lambda_functions/keycloak_authorizer/lambda_function.py new file mode 100644 index 00000000..814bf103 --- /dev/null +++ b/cumulus_lambda_functions/keycloak_authorizer/lambda_function.py @@ -0,0 +1,88 @@ +""" +Placeholder Keycloak Lambda Authorizer +This is a temporary authorizer that allows all requests and adds fake Keycloak JWT token data. +Replace with actual Keycloak integration once available. +""" + +import json +import base64 + +from cumulus_lambda_functions.daac_archiver.services.maap_api_client import MaapApiClient + + +def lambda_handler(event, context): + """ + Placeholder Lambda authorizer that allows all requests. + Adds fake context similar to what Keycloak would provide from a JWT token. + + :param event: API Gateway authorizer event + :param context: Lambda context + :return: IAM policy document allowing the request with fake user context + """ + + # Extract the authorization token from the event + # API Gateway TOKEN authorizer passes the token value in 'authorizationToken' field + token = event.get('authorizationToken', 'Fake') + method_arn = event.get('methodArn', '') + + user_details = MaapApiClient().get_user_details(token) + # Create a fake JWT token payload similar to what Keycloak would provide + # fake_jwt_payload = { + # "sub": "test-user-123", + # "preferred_username": "test-user", + # "email": "test-user@example.com", + # "name": "Test User", + # "given_name": "Test", + # "family_name": "User", + # "realm_access": { + # "roles": ["user", "admin", "developer"] + # }, + # "resource_access": { + # "unity-api": { + # "roles": ["read", "write"] + # } + # }, + # "groups": ["/unity/developers", "/unity/users"], + # "iat": 1642000000, + # "exp": 1642003600, + # "iss": "https://keycloak.example.com/auth/realms/unity", + # "aud": "unity-api" + # } + + # Encode as base64 to simulate a JWT token in context + # fake_jwt_string = base64.b64encode(json.dumps(fake_jwt_payload).encode()).decode() + + # Generate the IAM policy document that allows all actions + arn_parts = method_arn.split('/') + resource_arn = f"{arn_parts[0]}/{arn_parts[1]}/*/*" + + # API Gateway requires all context values to be strings + # Convert user_details to ensure all values are strings (avoid double conversion) + context = {} + for key, value in user_details.items(): + if key == 'groups': + # Handle groups specially - convert list to comma-separated string + context[key] = ','.join(value) if isinstance(value, list) else str(value) + elif isinstance(value, str): + # Already a string, use as-is + context[key] = value + else: + # Convert non-string values (int, bool, etc.) to string + context[key] = str(value) + + policy = { + "principalId": user_details["username"], + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "execute-api:Invoke", + "Effect": "Allow", + "Resource": resource_arn + } + ] + }, + "context": context + } + print(policy) + return policy diff --git a/cumulus_lambda_functions/lib/uds_db/granules_db_index.py b/cumulus_lambda_functions/lib/uds_db/granules_db_index.py deleted file mode 100644 index 103c8d3b..00000000 --- a/cumulus_lambda_functions/lib/uds_db/granules_db_index.py +++ /dev/null @@ -1,357 +0,0 @@ -import os -from copy import deepcopy - -from cumulus_lambda_functions.granules_to_es.granules_index_mapping import GranulesIndexMapping -from mdps_ds_lib.lib.utils.time_utils import TimeUtils - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from mdps_ds_lib.lib.aws.es_abstract import ESAbstract - -from mdps_ds_lib.lib.aws.es_factory import ESFactory - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class GranulesDbIndex: - def __init__(self): - required_env = ['ES_URL'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__es: ESAbstract = ESFactory().get_instance(os.getenv('ES_TYPE', 'AWS'), - index=DBConstants.collections_index, - base_url=os.getenv('ES_URL'), - port=int(os.getenv('ES_PORT', '443')), - use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True, - ) - # self.__default_fields = { - # "granule_id": {"type": "keyword"}, - # "collection_id": {"type": "keyword"}, - # "event_time": {"type": "long"} - # } - self.__default_fields = GranulesIndexMapping.stac_mappings - self.__ss_fields = GranulesIndexMapping.percolator_mappings - - @staticmethod - def to_es_bbox(bbox_array): - # lon = x, lat = y - # lon, lat, lon, lat - # -180, -90, 180, 90 - # x can be 170 to -170 - # 170, 0, -170, 10 - # latitude must be between -90.0 and 90.0 - # longitude must be between -180.0 and 180.0 - minX, minY, maxX, maxY = bbox_array - - # Ensure the values are properly sorted - # if minX > maxX: - # minX, maxX = maxX, minX - if minY > maxY: - minY, maxY = maxY, minY - - return { - "type": "envelope", - "coordinates": [[minX, maxY], [maxX, minY]], - # "coordinates": [ - # [bbox_array[0], bbox_array[3]], # Top-left corner (minLon, maxLat) - # [bbox_array[2], bbox_array[1]] # Bottom-right corner (maxLon, minLat) - # ] - } - - @staticmethod - def from_es_bbox(bbox_envelope_obj: dict): - missing_keys = [k for k in ['type', 'coordinates'] if k not in bbox_envelope_obj] - if len(missing_keys) > 0: - raise ValueError(f'invalid bbox_envelope_obj, missing {missing_keys}: {bbox_envelope_obj}') - if bbox_envelope_obj['type'] != 'envelope': - raise ValueError(f'bbox_envelope_obj is not envelope: {bbox_envelope_obj}') - return [ - bbox_envelope_obj["coordinates"][0][0], # minLon - bbox_envelope_obj["coordinates"][1][1], # minLat - bbox_envelope_obj["coordinates"][1][0], # maxLon - bbox_envelope_obj["coordinates"][0][1] # maxLat - ] - - @property - def default_fields(self): - return self.__default_fields - - @default_fields.setter - def default_fields(self, val): - """ - :param val: - :return: None - """ - self.__default_fields = val - return - - def __add_custom_mappings(self, es_mapping: dict, include_perc=False): - potential_ss_fields = {} if not include_perc else self.__ss_fields - customized_es_mapping = deepcopy(self.default_fields) - customized_es_mapping = { - **potential_ss_fields, - **self.default_fields, - } - customized_es_mapping['properties']['properties'] = { - **es_mapping, - **self.default_fields['properties']['properties'], - } - return customized_es_mapping - - def get_custom_metadata_fields(self, es_mapping: dict): - LOGGER.debug(f'get_custom_metadata_fields#es_mapping: {es_mapping}') - if [k for k in es_mapping.keys() if k == 'properties']: - custom_metadata_fields = {k: v for k, v in es_mapping['properties']['properties']['properties'].items() if - k not in self.default_fields['properties']['properties']} - return custom_metadata_fields - if [k for k in es_mapping.keys() if k == 'mappings']: - return self.get_custom_metadata_fields(es_mapping['mappings']) - for k, v in es_mapping.items(): - return self.get_custom_metadata_fields(v['mappings']) - raise ValueError(f'unknown format: {es_mapping}') - - def create_new_index(self, tenant, tenant_venue, es_mapping: dict): - # TODO validate es_mapping - # get current version from alias - # if not found, create a new alias - # get base definition. - # add custom definition - # create new version - # throw error and return if fails - # add new index to read alias - # add new index to write alias - # add delete current index from write alias - tenant = tenant.replace(':', '--') - write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - - current_alias = self.__es.get_alias(write_alias_name) - # {'meta_labels_v2': {'aliases': {'metadata_labels': {}}}} - current_index_name = f'{write_alias_name}__v0' if current_alias == {} else [k for k in current_alias.keys()][0] - new_version = int(current_index_name.split('__')[-1][1:]) + 1 - new_index_name = f'{DBConstants.granules_index_prefix}_{tenant}_{tenant_venue}__v{new_version:02d}'.lower().strip() - LOGGER.debug(f'new_index_name: {new_index_name}') - customized_es_mapping = self.__add_custom_mappings(es_mapping) - index_mapping = { - "settings": { - "number_of_shards": 3, - "number_of_replicas": 2 - }, - "mappings": { - "dynamic": "strict", - "properties": customized_es_mapping, - } - } - self.__es.create_index(new_index_name, index_mapping) - self.__es.create_alias(new_index_name, read_alias_name) - self.__es.swap_index_for_alias(write_alias_name, current_index_name, new_index_name) - - write_perc_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}_perc'.lower().strip() - read_perc_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}_perc'.lower().strip() - current_perc_alias = self.__es.get_alias(write_perc_alias_name) - current_perc_index_name = f'{write_alias_name}_perc__v0' if current_perc_alias == {} else [k for k in current_perc_alias.keys()][0] - new_perc_index_name = f'{DBConstants.granules_index_prefix}_{tenant}_{tenant_venue}_perc__v{new_version:02d}'.lower().strip() - customized_perc_es_mapping = self.__add_custom_mappings(es_mapping, True) - LOGGER.debug(f'customized_perc_es_mapping: {customized_perc_es_mapping}') - perc_index_mapping = { - "settings": { - "number_of_shards": 3, - "number_of_replicas": 2 - }, - "mappings": { - "dynamic": "strict", - "properties": customized_perc_es_mapping, - } - } - self.__es.create_index(new_perc_index_name, perc_index_mapping) - self.__es.create_alias(new_perc_index_name, read_perc_alias_name) - self.__es.swap_index_for_alias(write_perc_alias_name, current_perc_index_name, new_perc_index_name) - try: - self.__es.migrate_index_data(current_perc_index_name, new_perc_index_name) - except: - LOGGER.exception(f'failed to migrate index data: {(current_perc_index_name, new_perc_index_name)}') - return - - def get_latest_index_name(self, tenant, tenant_venue): - write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - write_alias_name = self.__es.get_alias(write_alias_name) - if len(write_alias_name) != 1: - raise ValueError(f'missing index for {tenant}_{tenant_venue}. {write_alias_name}') - latest_index_name = [k for k in write_alias_name.keys()][0] - return latest_index_name - - def get_latest_index(self, tenant, tenant_venue): - latest_index_name = self.get_latest_index_name(tenant, tenant_venue) - index_mapping = self.__es.get_index_mapping(latest_index_name) - if index_mapping is None: - raise ValueError(f'missing index: {latest_index_name}') - return index_mapping - - def delete_index(self, tenant, tenant_venue): - tenant = tenant.replace(':', '--') - write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - write_alias_name = self.__es.get_alias(write_alias_name) - if len(write_alias_name) != 1: - raise ValueError(f'missing index for {tenant}_{tenant_venue}. {write_alias_name}') - latest_index_name = [k for k in write_alias_name.keys()][0] - prev_version = int(latest_index_name.split('__v')[-1]) - 1 - if prev_version < 1: - LOGGER.warn(f'no previous index to point write index. {latest_index_name}') - else: - LOGGER.debug(f'updating write alias to previous index') - prev_index_name = f'{latest_index_name.split("__v")[0]}__v{prev_version:02d}'.lower().strip() - self.__es.swap_index_for_alias(write_alias_name, latest_index_name, prev_index_name) - self.__es.delete_index(latest_index_name) - return - - def destroy_indices(self, tenant, tenant_venue): - # TODO assuming that both read and write aliases are destroyed once indices are destroyed. - tenant = tenant.replace(':', '--') - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - actual_read_alias = self.__es.get_alias(read_alias_name) - for each_index in actual_read_alias.keys(): - LOGGER.debug(f'deleting index: {each_index}') - self.__es.delete_index(each_index) - return - - def get_entry(self, tenant: str, tenant_venue: str, doc_id: str, ): - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - result = self.__es.query_by_id(doc_id, read_alias_name) - if result is None: - raise ValueError(f"no such granule: {doc_id}") - return result - - def __query_by_id_local(self, tenant: str, tenant_venue: str, doc_id: str, ): - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - dsl = { - 'size': 9999, - 'sort': [ - {'properties.datetime': {'order': 'desc'}}, - {'id': {'order': 'asc'}} - ], - 'query': { - 'term': {'_id': doc_id} - } - } - result = self.__es.query(dsl, read_alias_name) - if result is None or len(result['hits']['hits']) < 1: - return [] - return result['hits']['hits'] - - def __delete_old_entries(self, dsl_result): - for each_granule in dsl_result: - LOGGER.debug(f"deleting {each_granule['_id']} from {each_granule['_index']}") - delete_result = self.__es.delete_by_query({ - 'query': {'term': {'id': each_granule['_id']}} - }, each_granule['_index']) - LOGGER.debug(f'delete_result: {delete_result}') - if delete_result is None: - raise ValueError(f"error deleting {each_granule}") - return - - def delete_entry(self, tenant: str, tenant_venue: str, doc_id: str, ): - result = self.__query_by_id_local(tenant, tenant_venue, doc_id) - if len(result) < 1: - raise ValueError(f"no such granule: {doc_id}") - self.__delete_old_entries(result) - return result - - def update_entry(self, tenant: str, tenant_venue: str, json_body: dict, doc_id: str, ): - # find existing doc_id - # if not found, throw error. Cannot update - # if found, check index. - # if latest index, proceed with update - # if older index, proceed with get + delete - # tweak meta locally, and add it. - write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - json_body['event_time'] = TimeUtils.get_current_unix_milli() - existing_entries = self.__query_by_id_local(tenant, tenant_venue, doc_id) - if len(existing_entries) < 1: - raise ValueError(f'unable to update {doc_id} as it is not found. ') - latest_index_name = self.get_latest_index_name(tenant, tenant_venue) - existing_entry = existing_entries[0] - if existing_entry['_index'] == latest_index_name: - LOGGER.debug(f'{doc_id} in latest index: {latest_index_name}. continuing with update') - self.__es.update_one(json_body, doc_id, index=write_alias_name) # TODO assuming granule_id is prefixed with collection id - self.__delete_old_entries(existing_entries[1:]) - return - LOGGER.debug(f'{doc_id} in older index: {latest_index_name} v. {existing_entry["_index"]}') - new_doc = {**existing_entry['_source'], **json_body} - self.__es.index_one(new_doc, doc_id, index=write_alias_name) # TODO assuming granule_id is prefixed with collection id - self.__delete_old_entries(existing_entries) - return - - def add_entry(self, tenant: str, tenant_venue: str, json_body: dict, doc_id: str, ): - # find existing doc_id - # if not found, add it - # if found, and it is in latest index, add it. - # if found, and it is in older index, add current one, and delete the older one. - - write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - json_body['event_time'] = TimeUtils.get_current_unix_milli() - existing_entries = self.__query_by_id_local(tenant, tenant_venue, doc_id) - if len(existing_entries) < 1: - self.__es.index_one(json_body, doc_id, index=write_alias_name) # TODO assuming granule_id is prefixed with collection id - return - latest_index_name = self.get_latest_index_name(tenant, tenant_venue) - existing_entry = existing_entries[0] - if existing_entry['_index'] == latest_index_name: - self.__es.index_one(json_body, doc_id, index=write_alias_name) # TODO assuming granule_id is prefixed with collection id - self.__delete_old_entries(existing_entries[1:]) - return - self.__es.index_one(json_body, doc_id, index=write_alias_name) # TODO assuming granule_id is prefixed with collection id - self.__delete_old_entries(existing_entries) - # TODO validate custom metadata vs the latest index to filter extra items - return - - def get_size(self, tenant: str, tenant_venue: str, collection_id: str): - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - search_dsl = { - 'query': {'bool': {'must': [{ - 'term': {'collection': collection_id} - }]}}, - 'size': 0 - } - search_result = self.__es.query(search_dsl, querying_index=read_alias_name) - return self.__es.get_result_size(search_result) - - def dsl_search(self, tenant: str, tenant_venue: str, search_dsl: dict): - read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip() - if 'sort' not in search_dsl: # We cannot paginate w/o sort. So, max is 10k items: - # This also assumes "size" should be part of search_dsl - search_result = self.__es.query(search_dsl, querying_index=read_alias_name) - LOGGER.debug(f'search_finished: {len(search_result["hits"]["hits"])}') - return search_result - # we can run paginate search - original_size = search_dsl['size'] if 'size' in search_dsl else 20 - total_size = -999 - result = [] - duplicates = set([]) - while len(result) < original_size: - search_dsl['size'] = (original_size - len(result)) * 2 - search_result = self.__es.query_pages(search_dsl, querying_index=read_alias_name) - if total_size == -999: - total_size = self.__es.get_result_size(search_result) - if len(search_result['hits']['hits']) < 1: - break - for each in search_result['hits']['hits']: - if each['_id'] not in duplicates: - duplicates.add(each['_id']) - result.append(each) - search_dsl['search_after'] = search_result['hits']['hits'][-1]['sort'] - - LOGGER.debug(f'search_finished: {len(result)}') - if len(result) > original_size: - result = result[:original_size] - return { - 'hits': { - "total": { - "value": total_size, - }, - 'hits': result - } - } - - - diff --git a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/__init__.py b/cumulus_lambda_functions/lib/uds_fast_api/__init__.py similarity index 100% rename from cumulus_lambda_functions/metadata_s4pa_generate_cmr/__init__.py rename to cumulus_lambda_functions/lib/uds_fast_api/__init__.py diff --git a/cumulus_lambda_functions/uds_api/fast_api_utils.py b/cumulus_lambda_functions/lib/uds_fast_api/fast_api_utils.py similarity index 56% rename from cumulus_lambda_functions/uds_api/fast_api_utils.py rename to cumulus_lambda_functions/lib/uds_fast_api/fast_api_utils.py index ffece58a..40140992 100644 --- a/cumulus_lambda_functions/uds_api/fast_api_utils.py +++ b/cumulus_lambda_functions/lib/uds_fast_api/fast_api_utils.py @@ -1,4 +1,3 @@ -import base64 import json import os @@ -7,14 +6,33 @@ from mdps_ds_lib.lib.constants import Constants from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from fastapi import APIRouter, HTTPException, Request, Response +from fastapi import Request -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants +from cumulus_lambda_functions.lib.uds_fast_api.web_service_constants import WebServiceConstants LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) class FastApiUtils: + @staticmethod + def get_authorization_token(request: Request): + """ + Try to look for and retrieve authorization token to put it as part of the next request + This supports Bearer tokens (Authorization), PGT tokens (proxy-ticket), and future auth methods + :param request: + :return: + """ + auth_headers = {} + auth_header_names = ['authorization', 'proxy-ticket', 'x-api-key'] # Add more as needed + + for header_name in auth_header_names: + header_value = request.headers.get(header_name) + if header_value: + # Preserve original header name casing for forwarding + auth_headers[header_name.title()] = header_value + + return auth_headers + @staticmethod def get_authorization_info(request: Request): """ @@ -22,21 +40,47 @@ def get_authorization_info(request: Request): """ action = request.method resource = request.url.path - bearer_token = request.headers.get('Authorization', '') - LOGGER.debug(f'raw bearer_token: {bearer_token}') - username_part = bearer_token.split('.')[1] - jwt_decoded = base64.standard_b64decode(f'{username_part}========'.encode()).decode() - LOGGER.debug(f'jwt_decoded: {jwt_decoded}') - jwt_decoded = json.loads(jwt_decoded) - ldap_groups = jwt_decoded['cognito:groups'] - username = jwt_decoded['username'] + # Access the Lambda event from Mangum + lambda_event = request.scope.get('aws.event', {}) + + # Debug logging to see what's in the event + LOGGER.debug(f'lambda_event keys: {lambda_event.keys() if lambda_event else "None"}') + LOGGER.debug(f'requestContext: {lambda_event.get("requestContext", {})}') + + # Get the authorizer context + authorizer_context = lambda_event.get('requestContext', {}).get('authorizer', {}) + LOGGER.debug(f'authorizer_context: {authorizer_context}') + + # Access the values from your Lambda authorizer + username = authorizer_context.get('username') + email = authorizer_context.get('email') + groups = authorizer_context.get('groups', '').split(',') if authorizer_context.get('groups') else [] + + LOGGER.debug(f'Extracted - username: {username}, email: {email}, groups: {groups}') + return { - 'username': username, - 'ldap_groups': list(set(ldap_groups)), 'action': action, + 'username': username, + 'ldap_groups': groups, 'resource': resource, + 'email': email, } + # bearer_token = request.headers.get('Authorization', '') + # LOGGER.debug(f'raw bearer_token: {bearer_token}') + # username_part = bearer_token.split('.')[1] + # jwt_decoded = base64.standard_b64decode(f'{username_part}========'.encode()).decode() + # LOGGER.debug(f'jwt_decoded: {jwt_decoded}') + # jwt_decoded = json.loads(jwt_decoded) + # ldap_groups = jwt_decoded['cognito:groups'] + # username = jwt_decoded['username'] + # return { + # 'username': username, + # 'ldap_groups': list(set(ldap_groups)), + # 'action': action, + # 'resource': resource, + # } + @staticmethod def get_cors_origins(): cors_origins = os.environ.get(Constants.CORS_ORIGINS, '').strip().split(',') diff --git a/cumulus_lambda_functions/uds_api/web_service_constants.py b/cumulus_lambda_functions/lib/uds_fast_api/web_service_constants.py similarity index 100% rename from cumulus_lambda_functions/uds_api/web_service_constants.py rename to cumulus_lambda_functions/lib/uds_fast_api/web_service_constants.py diff --git a/cumulus_lambda_functions/lib/uds_utils.py b/cumulus_lambda_functions/lib/uds_utils.py new file mode 100644 index 00000000..fe8dbe17 --- /dev/null +++ b/cumulus_lambda_functions/lib/uds_utils.py @@ -0,0 +1,22 @@ +import backoff + +class JitteredBackoffException(Exception): + pass + + +@backoff.on_exception( + backoff.expo, + Exception, + max_value=13, + max_time=34, + giveup=lambda e: isinstance(e, JitteredBackoffException), +) +def backoff_wrapper(func, *args, **kwargs): + """ + Run a function wrapped in exponential backoff. + :param func: function or method object + :param args: args to pass to function + :param kwargs: keyword args to pass to function + :return: + """ + return func(*args, **kwargs) \ No newline at end of file diff --git a/cumulus_lambda_functions/metadata_cas_generate_cmr/generate_cmr.py b/cumulus_lambda_functions/metadata_cas_generate_cmr/generate_cmr.py deleted file mode 100644 index 06afb790..00000000 --- a/cumulus_lambda_functions/metadata_cas_generate_cmr/generate_cmr.py +++ /dev/null @@ -1,416 +0,0 @@ -import hashlib -import os -from copy import deepcopy - -import xmltodict - -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.metadata_extraction.echo_metadata import EchoMetadata -from mdps_ds_lib.lib.utils.time_utils import TimeUtils -from cumulus_lambda_functions.metadata_cas_generate_cmr.l1a_input_metadata import L1AInputMetadata - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -INPUT_EVENT_SCHEMA = { - "type": "object", - "properties": { - "cma": { - "type": "object", - "properties": { - "event": { - "type": "object", - "properties": { - "meta": { - "type": "object", - "properties": { - "input_granules": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "granuleId": { - "type": "string" - }, - "files": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "bucket": { - "type": "string" - }, - "key": { - "type": "string" - }, - "url_path": { - "type": "string" - }, - "source": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "type": { - "type": "string" - }, - "size": { - "type": "number" - } - }, - "required": [ - "type" - ], - "oneOf": [ - {"required": ["bucket", "key"]}, - {"required": ["url_path"]} - ], - } - } - }, - "required": [ - "granuleId", - "files" - ] - } - }, - "collection": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "version": {"type": "string"}, - }, - "required": ["name", "version"] - } - }, - "required": [ - "input_granules", "collection" - ] - } - }, - "required": [ - "meta" - ] - }, - "extra_config": { - "required": [], - "properties": { - "add_extra_keys": {"type": "boolean"} - } - } - }, - "required": [] - } - }, - "required": [ - "cma" - ] -} - - -class GenerateCmr: - def __init__(self, event): - self.__event = event - self.__s3 = AwsS3() - self._pds_file_dict = None - self.__input_file_list = [] - - def __validate_input(self): - result = JsonValidator(INPUT_EVENT_SCHEMA).validate(self.__event) - if result is None: - return - raise ValueError(f'input json has validation errors: {result}') - - def __get_pds_metadata_file(self): - self.__input_file_list = self.__event['cma']['event']['meta']['input_granules'][0]['files'] - pds_metadata_file = None - for each_file in self.__input_file_list: - if 'fileName' not in each_file and 'name' in each_file: # add fileName if there is only name - each_file['fileName'] = each_file['name'] - if 'url_path' in each_file: - s3_bucket, s3_key = self.__s3.split_s3_url(each_file['url_path']) - each_file['bucket'] = s3_bucket - each_file['key'] = s3_key - LOGGER.debug(f'checking file: {each_file}') - if each_file['key'].upper().endswith('.NC.CAS'): - pds_metadata_file = each_file - return pds_metadata_file - - def __read_pds_metadata_file(self): - self._pds_file_dict = self.__get_pds_metadata_file() - if self._pds_file_dict is None: - raise ValueError('missing PDS metadata file') - self.__s3.target_bucket = self._pds_file_dict['bucket'] - self.__s3.target_key = self._pds_file_dict['key'] - return self.__s3.read_small_txt_file() - - def __is_adding_extra_keys(self): - if 'extra_config' not in self.__event['cma']: - return True - if 'add_extra_keys' not in self.__event['cma']['extra_config']: - return True - return self.__event['cma']['extra_config']['add_extra_keys'] - - def __generate_output_dict(self, echo_metadata_md5: str): - output_dict = { - "checksumType": "md5", - "checksum": echo_metadata_md5, - "type": "metadata", - - "key": self.__s3.target_key, - "fileName": os.path.basename(self.__s3.target_key), - "bucket": self.__s3.target_bucket, - "size": int(self.__s3.get_size()), - } - if not self.__is_adding_extra_keys(): - return output_dict - output_dict = {**output_dict, **{ - "path": os.path.dirname(self.__s3.target_key), - "name": os.path.basename(self.__s3.target_key), - "source_bucket": self.__s3.target_bucket, - "url_path": f's3://{self.__s3.target_bucket}/{self.__s3.target_key}', - }} - return output_dict - - def start(self): - """ - sample event -{ - "cma": { - "task_config": { - "bucket": "{$.meta.buckets.internal.name}", - "collection": "{$.meta.collection}", - "cumulus_message": { - "outputs": [ - { - "source": "{$.files}", - "destination": "{$.payload}" - } - ] - } - }, - "event": { - "cumulus_meta": { - "cumulus_version": "11.1.1", - "execution_name": "90c9c978-ca5e-47b1-9c4a-3d20c73a4743", - "message_source": "sfn", - "queueExecutionLimits": { - "https://sqs.us-west-2.amazonaws.com/237868187491/uds-dev-cumulus-backgroundProcessing": 5 - }, - "state_machine": "arn:aws:states:us-west-2:237868187491:stateMachine:uds-dev-cumulus-IngestGranule", - "system_bucket": "uds-dev-cumulus-internal", - "workflow_start_time": 1655943753534, - "parentExecutionArn": "arn:aws:states:us-west-2:237868187491:execution:uds-dev-cumulus-DiscoverGranules:707b8f70-ac78-4fa8-86f6-b74dcdfed287", - "queueUrl": "arn:aws:sqs:us-west-2:237868187491:uds-dev-cumulus-startSF" - }, - "exception": "None", - "meta": { - "buckets": { - "internal": { - "name": "uds-dev-cumulus-internal", - "type": "internal" - }, - "private": { - "name": "uds-dev-cumulus-private", - "type": "private" - }, - "protected": { - "name": "uds-dev-cumulus-protected", - "type": "protected" - }, - "public": { - "name": "uds-dev-cumulus-public", - "type": "public" - }, - "sps": { - "name": "uds-dev-cumulus-sps", - "type": "protected" - }, - "staging": { - "name": "uds-dev-cumulus-staging", - "type": "internal" - } - }, - "cmr": { - "clientId": "CHANGEME", - "cmrEnvironment": "UAT", - "cmrLimit": 100, - "cmrPageSize": 50, - "oauthProvider": "earthdata", - "passwordSecretName": "uds-dev-cumulus-message-template-cmr-password20220330223854670000000005", - "provider": "CHANGEME", - "username": "username" - }, - "collection": { - "duplicateHandling": "replace", - "process": "snpp.level1", - "files": [ - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "type": "data" - }, - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc\\.cas$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "type": "metadata" - }, - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc\\.cmr\\.xml$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cmr.xml", - "type": "metadata" - } - ], - "granuleId": "^SNDR.SNPP.ATMS.L1A.*$", - "granuleIdExtraction": "(^SNDR.SNPP.ATMS.L1A.*)(\\.nc|\\.nc\\.cas|\\.nc\\.cmr\\.xml)", - "name": "SNDR_SNPP_ATMS_L1A_1", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "url_path": "{cmrMetadata.Granule.Collection.ShortName}", - "version": "1", - "updatedAt": 1655943525719, - "createdAt": 1655943525719 - }, - "process": "snpp.level1", - "distribution_endpoint": null, - "launchpad": { - "api": "launchpadApi", - "certificate": "launchpad.pfx", - "passphraseSecretName": "" - }, - "provider": { - "id": "snpp_l1_s3", - "globalConnectionLimit": 1000, - "host": "uds-dev-cumulus-staging", - "protocol": "s3", - "createdAt": 1655943376376, - "updatedAt": 1655943376376 - }, - "stack": "uds-dev-cumulus", - "template": "s3://uds-dev-cumulus-internal/uds-dev-cumulus/workflow_template.json", - "workflow_name": "IngestGranule", - "workflow_tasks": { - "0": { - "name": "uds-dev-cumulus-SyncGranule", - "version": "$LATEST", - "arn": "arn:aws:lambda:us-west-2:237868187491:function:uds-dev-cumulus-SyncGranule" - } - }, - "staticValue": "aStaticValue", - "interpolatedValueStackName": "uds-dev-cumulus", - "input_granules": [ - { - "granuleId": "SNDR.SNPP.ATMS.L1A.nominal2.01", - "dataType": "SNDR_SNPP_ATMS_L1A_1", - "version": "1", - "files": [ - { - "size": 9194361, - "bucket": "uds-dev-cumulus-internal", - "key": "file-staging/uds-dev-cumulus/SNDR_SNPP_ATMS_L1A_1___1/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "source": "SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "fileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "type": "data" - }, - { - "size": 2673, - "bucket": "uds-dev-cumulus-internal", - "key": "file-staging/uds-dev-cumulus/SNDR_SNPP_ATMS_L1A_1___1/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "source": "SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "fileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "type": "metadata" - } - ], - "sync_granule_duration": 694, - "createdAt": 1656010982847 - } - ] - }, - "payload": {}, - "replace": { - "Bucket": "uds-dev-cumulus-internal", - "Key": "events/172fbbc4-f8ee-4974-8a77-37bc669accb0", - "TargetPath": "$.payload" - } - } - } -} - :return: - """ - self.__validate_input() - LOGGER.error(f'input: {self.__event}') - granule_metadata_props = L1AInputMetadata(xmltodict.parse(self.__read_pds_metadata_file())).load() - granule_metadata_props.granule_id = self.__event['cma']['event']['meta']['input_granules'][0]['granuleId'] - granule_metadata_props.collection_name = self.__event['cma']['event']['meta']['collection']['name'] - granule_metadata_props.collection_version = self.__event['cma']['event']['meta']['collection']['version'] - echo_metadata = EchoMetadata(granule_metadata_props).load().echo_metadata - echo_metadata_xml_str = xmltodict.unparse(echo_metadata, pretty=True) - self.__s3.target_key = os.path.join(os.path.dirname(self.__s3.target_key), f'{granule_metadata_props.granule_id}.cmr.xml') - self.__s3.upload_bytes(echo_metadata_xml_str.encode()) - echo_metadata_md5 = hashlib.md5(echo_metadata_xml_str.encode()).hexdigest() - returning_dict = deepcopy(self.__event['cma']['event']) - if 'replace' in returning_dict: - returning_dict.pop('replace') - # TODO This is a hack since distribution_endpoint = null is complained by MoveGranule. - """ - { - "errorType": "CumulusMessageAdapterExecutionError", - "errorMessage": "warning setting command to loadAndUpdateRemoteEvent\\nwarning setting command to loadNestedEvent\\nUnexpected Error . config schema: None is not of type 'string'\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None", - "trace": [ - "CumulusMessageAdapterExecutionError: warning setting command to loadAndUpdateRemoteEvent", - "warning setting command to loadNestedEvent", - "Unexpected Error . config schema: None is not of type 'string'", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - " at Interface. (/var/task/webpack:/node_modules/@cumulus/cumulus-message-adapter-js/dist/cma.js:155:1)", - " at Interface.emit (events.js:326:22)", - " at Interface.EventEmitter.emit (domain.js:483:12)", - " at Interface.close (readline.js:416:8)", - " at Socket.onend (readline.js:194:10)", - " at Socket.emit (events.js:326:22)", - " at Socket.EventEmitter.emit (domain.js:483:12)", - " at endReadableNT (_stream_readable.js:1241:12)", - " at processTicksAndRejections (internal/process/task_queues.js:84:21)" - ] - } - """ - if 'distribution_endpoint' in returning_dict['meta'] and returning_dict['meta']['distribution_endpoint'] is None: - returning_dict['meta']['distribution_endpoint'] = f's3://{self.__s3.target_bucket}/' - returning_dict['task_config'] = { - "inputGranules": "{$.meta.input_granules}", - "granuleIdExtraction": "{$.meta.collection.granuleIdExtraction}" - } - returning_dict['payload'] = { - "granules": [ - { - "granuleId": granule_metadata_props.granule_id, - "dataType": granule_metadata_props.collection_name, - "version": granule_metadata_props.collection_version, - "files": self.__input_file_list + [self.__generate_output_dict(echo_metadata_md5)], - # "files": self.__input_file_list, - "sync_granule_duration": 20302, - "createdAt": TimeUtils.get_current_unix_milli(), - } - ] - } - return returning_dict diff --git a/cumulus_lambda_functions/metadata_cas_generate_cmr/l1a_input_metadata.py b/cumulus_lambda_functions/metadata_cas_generate_cmr/l1a_input_metadata.py deleted file mode 100644 index 876a63ef..00000000 --- a/cumulus_lambda_functions/metadata_cas_generate_cmr/l1a_input_metadata.py +++ /dev/null @@ -1,69 +0,0 @@ -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.metadata_extraction.granule_metadata_props import GranuleMetadataProps - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) -L1A_INPUT_METADATA_SCHEMA = { - "type": "object", - "required": ["cas:metadata"], - "properties": { - "cas:metadata": { - "type": "object", - "required": ["keyval"], - "properties": { - "keyval": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": ["key", "val"], - "properties": { - "key": { - "type": "string", - }, - "val": { - "oneOf": [ - {"type": "string"}, - {"type": "array", "items": {"type": "string"}}, - ] - }, - } - }, - }, - }, - } - }, -} - - -class L1AInputMetadata: - def __init__(self, input_l1a_metadata: dict): - """ - :param input_l1a_metadata: str - XML string - """ - self.__granule_metadata_props = GranuleMetadataProps() - self.__input_l1a_metadata = input_l1a_metadata - self.__l1a_metadata_dict = {} - self.__mandatory_keys = ['EndDateTime', 'ProductName', 'ProductionDateTime', 'StartDateTime'] - - def __validate_pds_metadata(self): - result = JsonValidator(L1A_INPUT_METADATA_SCHEMA).validate(self.__input_l1a_metadata) - if result is not None: - raise ValueError(f'pds metadata has validation errors: {result}') - return - - def __load_to_dict(self): - self.__l1a_metadata_dict = {k['key']: k['val'] for k in self.__input_l1a_metadata['cas:metadata']['keyval']} - missing_keys = [k for k in self.__mandatory_keys if k not in self.__l1a_metadata_dict] - if len(missing_keys) > 0: - raise ValueError(f'missing mandatory keys: {missing_keys}') - return - - def load(self): - self.__validate_pds_metadata() - self.__load_to_dict() - self.__granule_metadata_props.beginning_dt = self.__l1a_metadata_dict['StartDateTime'] - self.__granule_metadata_props.ending_dt = self.__l1a_metadata_dict['EndDateTime'] - self.__granule_metadata_props.prod_dt = self.__l1a_metadata_dict['ProductionDateTime'] - # self.__granule_metadata_props.prod_name = self.__l1a_metadata_dict['ProductName'] # NOT in used at this moment. - return self.__granule_metadata_props diff --git a/cumulus_lambda_functions/metadata_cas_generate_cmr/lambda_function.py b/cumulus_lambda_functions/metadata_cas_generate_cmr/lambda_function.py deleted file mode 100644 index fb0460bf..00000000 --- a/cumulus_lambda_functions/metadata_cas_generate_cmr/lambda_function.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.metadata_cas_generate_cmr.generate_cmr import GenerateCmr - - -def lambda_handler(event, context): - """ -{'cma': {'task_config': {'bucket': '{$.meta.buckets.internal.name}', 'collection': '{$.meta.collection}', 'cumulus_message': {'outputs': [{'source': '{$.files}', 'destination': '{$.payload}'}]}}, 'event': {'cumulus_meta': {'cumulus_version': '10.0.1', 'execution_name': 'c6d885dc-b4b2-4eb0-b22e-b6f58a7a0870', 'message_source': 'sfn', 'queueExecutionLimits': {'https://sqs.us-west-2.amazonaws.com/884500545225/am-uds-dev-cumulus-backgroundProcessing': 5}, 'state_machine': 'arn:aws:states:us-west-2:884500545225:stateMachine:am-uds-dev-cumulus-IngestGranule', 'system_bucket': 'am-uds-dev-cumulus-internal', 'workflow_start_time': 1646785175509, 'parentExecutionArn': 'arn:aws:states:us-west-2:884500545225:execution:am-uds-dev-cumulus-DiscoverGranules:885483b4-ba55-4db1-b197-661e1e595a45', 'queueUrl': 'arn:aws:sqs:us-west-2:884500545225:am-uds-dev-cumulus-startSF'}, 'exception': 'None', 'meta': {'buckets': {'internal': {'name': 'am-uds-dev-cumulus-internal', 'type': 'internal'}, 'protected': {'name': 'am-uds-dev-cumulus-protected', 'type': 'protected'}}, 'cmr': {'clientId': 'CHANGEME', 'cmrEnvironment': 'UAT', 'cmrLimit': 100, 'cmrPageSize': 50, 'oauthProvider': 'earthdata', 'passwordSecretName': 'am-uds-dev-cumulus-message-template-cmr-password20220216072916956000000002', 'provider': 'CHANGEME', 'username': 'username'}, 'collection': {'name': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'process': 'modis', 'granuleId': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$', 'granuleIdExtraction': '(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'duplicateHandling': 'replace', 'url_path': '{cmrMetadata.Granule.Collection.ShortName}___{cmrMetadata.Granule.Collection.VersionId}', 'provider_path': '/data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/', 'files': [{'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}00\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000000.PDS', 'type': 'data'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0\\.cmr\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}], 'updatedAt': 1646326197526, 'createdAt': 1646258167624}, 'distribution_endpoint': 's3://am-uds-dev-cumulus-internal/', 'launchpad': {'api': 'launchpadApi', 'certificate': 'launchpad.pfx', 'passphraseSecretName': ''}, 'provider': {'password': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQHFbE4iMnF/W0Y/NrsYvrfHAAAAajBoBgkqhkiG9w0BBwagWzBZAgEAMFQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMLaH13SdxPXREjXLtAgEQgCfA+lEu2c/xLTGwJsbtKlXJbKDy4pwV+rS3BnJqgBoLLMQZqOdoFhk=', 'host': 'snppl0.gesdisc.eosdis.nasa.gov', 'updatedAt': 1646244053419, 'protocol': 'https', 'createdAt': 1646244053419, 'encrypted': True, 'username': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQGRoY5EBMpvvyMASUowBM61AAAAYzBhBgkqhkiG9w0BBwagVDBSAgEAME0GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9OhRHwTuxiz74q4UAgEQgCDEHOhsVG6+LqXfnlw+Z3Wg9MDOCd9/K5/X5j3tPJYkaA==', 'allowedRedirects': ['https://urs.earthdata.nasa.gov', 'urs.earthdata.nasa.gov'], 'id': 'snpp_provider_02', 'globalConnectionLimit': 10}, 'stack': 'am-uds-dev-cumulus', 'template': 's3://am-uds-dev-cumulus-internal/am-uds-dev-cumulus/workflow_template.json', 'workflow_name': 'IngestGranule', 'workflow_tasks': {'SyncGranule': {'name': 'am-uds-dev-cumulus-SyncGranule', 'version': '$LATEST', 'arn': 'arn:aws:lambda:us-west-2:884500545225:function:am-uds-dev-cumulus-SyncGranule'}}, 'staticValue': 'aStaticValue', 'interpolatedValueStackName': 'am-uds-dev-cumulus', 'input_granules': [{'granuleId': 'P1570515ATMSSCIENCEAXT1134912000000', 'dataType': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'files': [{'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000000.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000000.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000000.PDS', 'type': 'data', 'size': 744}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS', 'type': 'metadata', 'size': 18084600}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'type': 'metadata', 'size': 9526}], 'sync_granule_duration': 9822, 'createdAt': 1647386972717}], 'process': 'modis'}, 'payload': {}, 'replace': {'Bucket': 'am-uds-dev-cumulus-internal', 'Key': 'events/5d8edf37-0a18-4af5-a76f-7c2091cdd1e2', 'TargetPath': '$.payload'}}}} - :param event: - :param context: - :return: - """ - LambdaLoggerGenerator.remove_default_handlers() - # TODO implement - return GenerateCmr(event).start() diff --git a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/generate_cmr.py b/cumulus_lambda_functions/metadata_s4pa_generate_cmr/generate_cmr.py deleted file mode 100644 index 47fef13a..00000000 --- a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/generate_cmr.py +++ /dev/null @@ -1,207 +0,0 @@ -import os -from copy import deepcopy - -import xmltodict - -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.metadata_extraction.echo_metadata import EchoMetadata -from mdps_ds_lib.lib.utils.time_utils import TimeUtils -from cumulus_lambda_functions.metadata_s4pa_generate_cmr.pds_metadata import PdsMetadata - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -INPUT_EVENT_SCHEMA = { - "type": "object", - "properties": { - "cma": { - "type": "object", - "properties": { - "event": { - "type": "object", - "properties": { - "meta": { - "type": "object", - "properties": { - "input_granules": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "granuleId": { - "type": "string" - }, - "files": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "bucket": { - "type": "string" - }, - "key": { - "type": "string" - }, - "source": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "type": { - "type": "string" - }, - "size": { - "type": "number" - } - }, - "required": [ - "bucket", - "key", - "type" - ] - } - } - }, - "required": [ - "granuleId", - "files" - ] - } - } - }, - "required": [ - "input_granules" - ] - } - }, - "required": [ - "meta" - ] - } - }, - "required": [] - } - }, - "required": [ - "cma" - ] -} - - -class GenerateCmr: - def __init__(self, event): - self.__event = event - self.__s3 = AwsS3() - self._pds_file_dict = None - self.__file_postfixes = os.getenv('FILE_POSTFIX', '1.PDS.XML, NC.XML') - self.__file_postfixes = [k.upper().strip() for k in self.__file_postfixes.split(',')] - self.__input_file_list = [] - - def __validate_input(self): - result = JsonValidator(INPUT_EVENT_SCHEMA).validate(self.__event) - if result is None: - return - raise ValueError(f'input json has validation errors: {result}') - - def __get_pds_metadata_file(self): - self.__input_file_list = self.__event['cma']['event']['meta']['input_granules'][0]['files'] - for each_file in self.__input_file_list: - LOGGER.debug(f'checking file: {each_file}') - file_key_upper = each_file['key'].upper().strip() - LOGGER.debug(f'checking file_key_upper: {file_key_upper} against {self.__file_postfixes}') - if any([file_key_upper.endswith(k) for k in self.__file_postfixes]): - return each_file - return None - - def __read_pds_metadata_file(self): - self._pds_file_dict = self.__get_pds_metadata_file() - if self._pds_file_dict is None: - raise ValueError('missing PDS metadata file') - self.__s3.target_bucket = self._pds_file_dict['bucket'] - self.__s3.target_key = self._pds_file_dict['key'] - return self.__s3.read_small_txt_file() - - def start(self): - self.__validate_input() - LOGGER.error(f'input: {self.__event}') - pds_metadata = PdsMetadata(xmltodict.parse(self.__read_pds_metadata_file())).load() - echo_metadata = EchoMetadata(pds_metadata).load().echo_metadata - echo_metadata_xml_str = xmltodict.unparse(echo_metadata, pretty=True) - self.__s3.target_key = os.path.join(os.path.dirname(self.__s3.target_key), f'{pds_metadata.granule_id}.cmr.xml') - self.__s3.upload_bytes(echo_metadata_xml_str.encode()) - - # put payload - # remove replace - # add "task_config": { - # "inputGranules": "{$.meta.input_granules}", - # "granuleIdExtraction": "{$.meta.collection.granuleIdExtraction}" - # }, - # return { - # 'files': ['example', 'mock', 'return'], - # 'granules': self.__event - # } - returning_dict = deepcopy(self.__event['cma']['event']) - if 'replace' in returning_dict: - returning_dict.pop('replace') - # TODO This is a hack since distribution_endpoint = null is complained by MoveGranule. - """ - { - "errorType": "CumulusMessageAdapterExecutionError", - "errorMessage": "warning setting command to loadAndUpdateRemoteEvent\\nwarning setting command to loadNestedEvent\\nUnexpected Error . config schema: None is not of type 'string'\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None", - "trace": [ - "CumulusMessageAdapterExecutionError: warning setting command to loadAndUpdateRemoteEvent", - "warning setting command to loadNestedEvent", - "Unexpected Error . config schema: None is not of type 'string'", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - " at Interface. (/var/task/webpack:/node_modules/@cumulus/cumulus-message-adapter-js/dist/cma.js:155:1)", - " at Interface.emit (events.js:326:22)", - " at Interface.EventEmitter.emit (domain.js:483:12)", - " at Interface.close (readline.js:416:8)", - " at Socket.onend (readline.js:194:10)", - " at Socket.emit (events.js:326:22)", - " at Socket.EventEmitter.emit (domain.js:483:12)", - " at endReadableNT (_stream_readable.js:1241:12)", - " at processTicksAndRejections (internal/process/task_queues.js:84:21)" - ] - } - """ - if 'distribution_endpoint' in returning_dict['meta'] and returning_dict['meta']['distribution_endpoint'] is None: - returning_dict['meta']['distribution_endpoint'] = f's3://{self.__s3.target_bucket}/' - returning_dict['task_config'] = { - "inputGranules": "{$.meta.input_granules}", - "granuleIdExtraction": "{$.meta.collection.granuleIdExtraction}" - } - returning_dict['payload'] = { - "granules": [ - { - "granuleId": self.__event['cma']['event']['meta']['input_granules'][0]['granuleId'], - "dataType": pds_metadata.collection_name, - "version": f'{pds_metadata.collection_version}', - "files": self.__input_file_list + [{ - "key": self.__s3.target_key, - "fileName": os.path.basename(self.__s3.target_key), - "bucket": self.__s3.target_bucket, - "size": int(self.__s3.get_size()), - }], - # "files": self.__input_file_list, - "sync_granule_duration": 20302, - "createdAt": TimeUtils.get_current_unix_milli(), - } - ] - } - return returning_dict diff --git a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/lambda_function.py b/cumulus_lambda_functions/metadata_s4pa_generate_cmr/lambda_function.py deleted file mode 100644 index 794152af..00000000 --- a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/lambda_function.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.metadata_s4pa_generate_cmr.generate_cmr import GenerateCmr - - -def lambda_handler(event, context): - """ -{'cma': {'task_config': {'bucket': '{$.meta.buckets.internal.name}', 'collection': '{$.meta.collection}', 'cumulus_message': {'outputs': [{'source': '{$.files}', 'destination': '{$.payload}'}]}}, 'event': {'cumulus_meta': {'cumulus_version': '10.0.1', 'execution_name': 'c6d885dc-b4b2-4eb0-b22e-b6f58a7a0870', 'message_source': 'sfn', 'queueExecutionLimits': {'https://sqs.us-west-2.amazonaws.com/884500545225/am-uds-dev-cumulus-backgroundProcessing': 5}, 'state_machine': 'arn:aws:states:us-west-2:884500545225:stateMachine:am-uds-dev-cumulus-IngestGranule', 'system_bucket': 'am-uds-dev-cumulus-internal', 'workflow_start_time': 1646785175509, 'parentExecutionArn': 'arn:aws:states:us-west-2:884500545225:execution:am-uds-dev-cumulus-DiscoverGranules:885483b4-ba55-4db1-b197-661e1e595a45', 'queueUrl': 'arn:aws:sqs:us-west-2:884500545225:am-uds-dev-cumulus-startSF'}, 'exception': 'None', 'meta': {'buckets': {'internal': {'name': 'am-uds-dev-cumulus-internal', 'type': 'internal'}, 'protected': {'name': 'am-uds-dev-cumulus-protected', 'type': 'protected'}}, 'cmr': {'clientId': 'CHANGEME', 'cmrEnvironment': 'UAT', 'cmrLimit': 100, 'cmrPageSize': 50, 'oauthProvider': 'earthdata', 'passwordSecretName': 'am-uds-dev-cumulus-message-template-cmr-password20220216072916956000000002', 'provider': 'CHANGEME', 'username': 'username'}, 'collection': {'name': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'process': 'modis', 'granuleId': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$', 'granuleIdExtraction': '(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'duplicateHandling': 'replace', 'url_path': '{cmrMetadata.Granule.Collection.ShortName}___{cmrMetadata.Granule.Collection.VersionId}', 'provider_path': '/data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/', 'files': [{'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}00\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000000.PDS', 'type': 'data'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0\\.cmr\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}], 'updatedAt': 1646326197526, 'createdAt': 1646258167624}, 'distribution_endpoint': 's3://am-uds-dev-cumulus-internal/', 'launchpad': {'api': 'launchpadApi', 'certificate': 'launchpad.pfx', 'passphraseSecretName': ''}, 'provider': {'password': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQHFbE4iMnF/W0Y/NrsYvrfHAAAAajBoBgkqhkiG9w0BBwagWzBZAgEAMFQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMLaH13SdxPXREjXLtAgEQgCfA+lEu2c/xLTGwJsbtKlXJbKDy4pwV+rS3BnJqgBoLLMQZqOdoFhk=', 'host': 'snppl0.gesdisc.eosdis.nasa.gov', 'updatedAt': 1646244053419, 'protocol': 'https', 'createdAt': 1646244053419, 'encrypted': True, 'username': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQGRoY5EBMpvvyMASUowBM61AAAAYzBhBgkqhkiG9w0BBwagVDBSAgEAME0GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9OhRHwTuxiz74q4UAgEQgCDEHOhsVG6+LqXfnlw+Z3Wg9MDOCd9/K5/X5j3tPJYkaA==', 'allowedRedirects': ['https://urs.earthdata.nasa.gov', 'urs.earthdata.nasa.gov'], 'id': 'snpp_provider_02', 'globalConnectionLimit': 10}, 'stack': 'am-uds-dev-cumulus', 'template': 's3://am-uds-dev-cumulus-internal/am-uds-dev-cumulus/workflow_template.json', 'workflow_name': 'IngestGranule', 'workflow_tasks': {'SyncGranule': {'name': 'am-uds-dev-cumulus-SyncGranule', 'version': '$LATEST', 'arn': 'arn:aws:lambda:us-west-2:884500545225:function:am-uds-dev-cumulus-SyncGranule'}}, 'staticValue': 'aStaticValue', 'interpolatedValueStackName': 'am-uds-dev-cumulus', 'input_granules': [{'granuleId': 'P1570515ATMSSCIENCEAXT1134912000000', 'dataType': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'files': [{'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000000.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000000.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000000.PDS', 'type': 'data', 'size': 744}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS', 'type': 'metadata', 'size': 18084600}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'type': 'metadata', 'size': 9526}], 'sync_granule_duration': 9822, 'createdAt': 1647386972717}], 'process': 'modis'}, 'payload': {}, 'replace': {'Bucket': 'am-uds-dev-cumulus-internal', 'Key': 'events/5d8edf37-0a18-4af5-a76f-7c2091cdd1e2', 'TargetPath': '$.payload'}}}} - :param event: - :param context: - :return: - """ - LambdaLoggerGenerator.remove_default_handlers() - # TODO implement - return GenerateCmr(event).start() diff --git a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/pds_metadata.py b/cumulus_lambda_functions/metadata_s4pa_generate_cmr/pds_metadata.py deleted file mode 100644 index c5aed327..00000000 --- a/cumulus_lambda_functions/metadata_s4pa_generate_cmr/pds_metadata.py +++ /dev/null @@ -1,89 +0,0 @@ -import xmltodict - -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.metadata_extraction.granule_metadata_props import GranuleMetadataProps - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) -PDS_METADATA_SCHEMA = { - "type": "object", - "properties": { - "S4PAGranuleMetaDataFile": { - "type": "object", - "properties": { - "DataGranule": { - "type": "object", - "properties": { - "GranuleID": {"type": "string"}, - "ProductionDateTime": {"type": "string"}, - "InsertDateTime": {"type": "string"}, - }, - "required": ["GranuleID", "InsertDateTime", "ProductionDateTime"] - }, - "RangeDateTime": { - "type": "object", - "properties": { - "RangeEndingDate": {"type": "string"}, - "RangeEndingTime": {"type": "string"}, - "RangeBeginningDate": {"type": "string"}, - "RangeBeginningTime": {"type": "string"}, - }, - "required": ["RangeEndingDate", "RangeEndingTime", "RangeBeginningDate", "RangeBeginningTime"] - - }, - "CollectionMetaData": { - "type": "object", - "properties": { - "ShortName": {"type": "string"}, - "VersionID": {"type": "string"} - }, - "required": ["ShortName", "VersionID"] - - }, - }, - "required": ["RangeDateTime"] - } - }, - "required": ["S4PAGranuleMetaDataFile"] -} - - -class PdsMetadata: - def __init__(self, input_pds_metadata: dict): - """ - :param input_pds_metadata_str: str - XML string - """ - self.__input_pds_metadata = input_pds_metadata - self.__granule_metadata_props = GranuleMetadataProps() - - def __validate_pds_metadata(self): - result = JsonValidator(PDS_METADATA_SCHEMA).validate(self.__input_pds_metadata) - if result is None: - return - raise ValueError(f'pds metadata has validation errors: {result}') - - def __load_time_range(self): - range_dt = self.__input_pds_metadata['S4PAGranuleMetaDataFile']['RangeDateTime'] - self.__granule_metadata_props.beginning_dt = f"{range_dt['RangeBeginningDate']}T{range_dt['RangeBeginningTime']}" - self.__granule_metadata_props.ending_dt = f"{range_dt['RangeEndingDate']}T{range_dt['RangeEndingTime']}" - return - - def __load_collection_metadata(self): - collection_met = self.__input_pds_metadata['S4PAGranuleMetaDataFile']['CollectionMetaData'] - self.__granule_metadata_props.collection_name = collection_met['ShortName'] - self.__granule_metadata_props.collection_version = collection_met['VersionID'] - return - - def __load_granule_metadata(self): - granule_met = self.__input_pds_metadata['S4PAGranuleMetaDataFile']['DataGranule'] - self.__granule_metadata_props.granule_id = granule_met['GranuleID'] - self.__granule_metadata_props.prod_dt = granule_met['ProductionDateTime'] - self.__granule_metadata_props.insert_dt = granule_met['InsertDateTime'] - return - - def load(self) -> GranuleMetadataProps: - self.__validate_pds_metadata() - self.__load_time_range() - self.__load_collection_metadata() - self.__load_granule_metadata() - return self.__granule_metadata_props diff --git a/cumulus_lambda_functions/metadata_stac_generate_cmr/generate_cmr.py b/cumulus_lambda_functions/metadata_stac_generate_cmr/generate_cmr.py deleted file mode 100644 index 4c912e4c..00000000 --- a/cumulus_lambda_functions/metadata_stac_generate_cmr/generate_cmr.py +++ /dev/null @@ -1,452 +0,0 @@ -import hashlib -import json -import os -from copy import deepcopy - -import xmltodict - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from mdps_ds_lib.lib.aws.aws_s3 import AwsS3 -from mdps_ds_lib.lib.utils.json_validator import JsonValidator -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.metadata_extraction.echo_metadata import EchoMetadata -from mdps_ds_lib.lib.utils.time_utils import TimeUtils -from cumulus_lambda_functions.metadata_stac_generate_cmr.stac_input_metadata import StacInputMetadata -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -INPUT_EVENT_SCHEMA = { - "type": "object", - "properties": { - "cma": { - "type": "object", - "properties": { - "event": { - "type": "object", - "properties": { - "meta": { - "type": "object", - "properties": { - "input_granules": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "granuleId": { - "type": "string" - }, - "files": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "bucket": { - "type": "string" - }, - "key": { - "type": "string" - }, - "url_path": { - "type": "string" - }, - "source": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "type": { - "type": "string" - }, - "size": { - "type": "number" - } - }, - "required": [ - "type" - ], - "anyOf": [ - {"required": ["bucket", "key"]}, - {"required": ["url_path"]} - ], - } - } - }, - "required": [ - "granuleId", - "files" - ] - } - }, - "collection": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "version": {"type": "string"}, - }, - "required": ["name", "version"] - } - }, - "required": [ - "input_granules", "collection" - ] - } - }, - "required": [ - "meta" - ] - }, - "extra_config": { - "required": [], - "properties": { - "add_extra_keys": {"type": "boolean"} - } - } - }, - "required": [] - } - }, - "required": [ - "cma" - ] -} - - -class GenerateCmr: - def __init__(self, event): - self.__event = event - self.__s3 = AwsS3() - self._pds_file_dict = None - self.__valid_filetype_name = os.getenv('VALID_FILETYPE', 'metadata').lower() - self.__file_postfixes = os.getenv('FILE_POSTFIX', 'STAC.JSON') - self.__file_postfixes = [k.upper().strip() for k in self.__file_postfixes.split(',')] - self.__input_file_list = [] - - def __validate_input(self): - result = JsonValidator(INPUT_EVENT_SCHEMA).validate(self.__event) - if result is None: - return - raise ValueError(f'input json has validation errors: {result}') - - def __get_potential_files(self): - potential_files = [] - self.__input_file_list = self.__event['cma']['event']['meta']['input_granules'][0]['files'] - LOGGER.debug(f'before restructure: {self.__input_file_list}') - for each_file in self.__input_file_list: - if 'fileName' not in each_file and 'name' in each_file: # add fileName if there is only name - each_file['fileName'] = each_file['name'] - if 'url_path' in each_file: - s3_bucket, s3_key = self.__s3.split_s3_url(each_file['url_path']) - each_file['bucket'] = s3_bucket - each_file['key'] = s3_key - LOGGER.debug(f'checking file: {each_file}') - if 'type' in each_file and each_file['type'].strip().lower() != self.__valid_filetype_name: - LOGGER.debug(f'Not metadata. skipping {each_file}') - continue - file_key_upper = each_file['key'].upper().strip() - LOGGER.debug(f'checking file_key_upper: {file_key_upper} against {self.__file_postfixes}') - if any([file_key_upper.endswith(k) for k in self.__file_postfixes]): - potential_files.append(each_file) - LOGGER.debug(f'after restructure: {self.__input_file_list}') - return potential_files - - def __read_pds_metadata_file(self, potential_file): - self.__s3.target_bucket = potential_file['bucket'] - self.__s3.target_key = potential_file['key'] - return self.__s3.read_small_txt_file() - - def __is_adding_extra_keys(self): - if 'extra_config' not in self.__event['cma']: - return True - if 'add_extra_keys' not in self.__event['cma']['extra_config']: - return True - return self.__event['cma']['extra_config']['add_extra_keys'] - - def __generate_output_dict(self, echo_metadata_md5: str): - output_dict = { - "checksumType": "md5", - "checksum": echo_metadata_md5, - "type": "metadata", - - "key": self.__s3.target_key, - "fileName": os.path.basename(self.__s3.target_key), - "bucket": self.__s3.target_bucket, - "size": int(self.__s3.get_size()), - } - if not self.__is_adding_extra_keys(): - return output_dict - output_dict = {**output_dict, **{ - "path": os.path.dirname(self.__s3.target_key), - "name": os.path.basename(self.__s3.target_key), - "source_bucket": self.__s3.target_bucket, - "url_path": f's3://{self.__s3.target_bucket}/{self.__s3.target_key}', - }} - return output_dict - - def __ingest_custom_metadata(self, custom_metadata: dict): - if os.getenv('REGISTER_CUSTOM_METADATA', 'TRUE').strip().upper() != 'TRUE': - LOGGER.debug(f'not registering custom metadata due to ENV setting. {custom_metadata}') - return - LOGGER.debug(f'custom_metadata: {custom_metadata}') - if 'granule_id' not in custom_metadata or 'collection_id' not in custom_metadata: - LOGGER.error(f'unable to write custom metadata w/o granule or collection id: {custom_metadata}') - return - collection_identifier = UdsCollections.decode_identifier(custom_metadata['collection_id']) - GranulesDbIndex().add_entry(collection_identifier.tenant, - collection_identifier.venue, - custom_metadata, - custom_metadata['granule_id'] - ) - return - - def start(self): - """ - sample event -{ - "cma": { - "task_config": { - "bucket": "{$.meta.buckets.internal.name}", - "collection": "{$.meta.collection}", - "cumulus_message": { - "outputs": [ - { - "source": "{$.files}", - "destination": "{$.payload}" - } - ] - } - }, - "event": { - "cumulus_meta": { - "cumulus_version": "11.1.1", - "execution_name": "90c9c978-ca5e-47b1-9c4a-3d20c73a4743", - "message_source": "sfn", - "queueExecutionLimits": { - "https://sqs.us-west-2.amazonaws.com/237868187491/uds-dev-cumulus-backgroundProcessing": 5 - }, - "state_machine": "arn:aws:states:us-west-2:237868187491:stateMachine:uds-dev-cumulus-IngestGranule", - "system_bucket": "uds-dev-cumulus-internal", - "workflow_start_time": 1655943753534, - "parentExecutionArn": "arn:aws:states:us-west-2:237868187491:execution:uds-dev-cumulus-DiscoverGranules:707b8f70-ac78-4fa8-86f6-b74dcdfed287", - "queueUrl": "arn:aws:sqs:us-west-2:237868187491:uds-dev-cumulus-startSF" - }, - "exception": "None", - "meta": { - "buckets": { - "internal": { - "name": "uds-dev-cumulus-internal", - "type": "internal" - }, - "private": { - "name": "uds-dev-cumulus-private", - "type": "private" - }, - "protected": { - "name": "uds-dev-cumulus-protected", - "type": "protected" - }, - "public": { - "name": "uds-dev-cumulus-public", - "type": "public" - }, - "sps": { - "name": "uds-dev-cumulus-sps", - "type": "protected" - }, - "staging": { - "name": "uds-dev-cumulus-staging", - "type": "internal" - } - }, - "cmr": { - "clientId": "CHANGEME", - "cmrEnvironment": "UAT", - "cmrLimit": 100, - "cmrPageSize": 50, - "oauthProvider": "earthdata", - "passwordSecretName": "uds-dev-cumulus-message-template-cmr-password20220330223854670000000005", - "provider": "CHANGEME", - "username": "username" - }, - "collection": { - "duplicateHandling": "replace", - "process": "snpp.level1", - "files": [ - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "type": "data" - }, - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc\\.cas$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "type": "metadata" - }, - { - "bucket": "protected", - "regex": "^SNDR.SNPP.ATMS.L1A.*\\.nc\\.cmr\\.xml$", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cmr.xml", - "type": "metadata" - } - ], - "granuleId": "^SNDR.SNPP.ATMS.L1A.*$", - "granuleIdExtraction": "(^SNDR.SNPP.ATMS.L1A.*)(\\.nc|\\.nc\\.cas|\\.nc\\.cmr\\.xml)", - "name": "SNDR_SNPP_ATMS_L1A_1", - "reportToEms": false, - "sampleFileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "url_path": "{cmrMetadata.Granule.Collection.ShortName}", - "version": "1", - "updatedAt": 1655943525719, - "createdAt": 1655943525719 - }, - "process": "snpp.level1", - "distribution_endpoint": null, - "launchpad": { - "api": "launchpadApi", - "certificate": "launchpad.pfx", - "passphraseSecretName": "" - }, - "provider": { - "id": "snpp_l1_s3", - "globalConnectionLimit": 1000, - "host": "uds-dev-cumulus-staging", - "protocol": "s3", - "createdAt": 1655943376376, - "updatedAt": 1655943376376 - }, - "stack": "uds-dev-cumulus", - "template": "s3://uds-dev-cumulus-internal/uds-dev-cumulus/workflow_template.json", - "workflow_name": "IngestGranule", - "workflow_tasks": { - "0": { - "name": "uds-dev-cumulus-SyncGranule", - "version": "$LATEST", - "arn": "arn:aws:lambda:us-west-2:237868187491:function:uds-dev-cumulus-SyncGranule" - } - }, - "staticValue": "aStaticValue", - "interpolatedValueStackName": "uds-dev-cumulus", - "input_granules": [ - { - "granuleId": "SNDR.SNPP.ATMS.L1A.nominal2.01", - "dataType": "SNDR_SNPP_ATMS_L1A_1", - "version": "1", - "files": [ - { - "size": 9194361, - "bucket": "uds-dev-cumulus-internal", - "key": "file-staging/uds-dev-cumulus/SNDR_SNPP_ATMS_L1A_1___1/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "source": "SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "fileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "type": "data" - }, - { - "size": 2673, - "bucket": "uds-dev-cumulus-internal", - "key": "file-staging/uds-dev-cumulus/SNDR_SNPP_ATMS_L1A_1___1/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "source": "SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "fileName": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "type": "metadata" - } - ], - "sync_granule_duration": 694, - "createdAt": 1656010982847 - } - ] - }, - "payload": {}, - "replace": { - "Bucket": "uds-dev-cumulus-internal", - "Key": "events/172fbbc4-f8ee-4974-8a77-37bc669accb0", - "TargetPath": "$.payload" - } - } - } -} - :return: - """ - self.__validate_input() - LOGGER.debug(f'input: {self.__event}') - stac_input_meta = None - potential_files = self.__get_potential_files() - for each_potential_file in potential_files: - try: - stac_input_meta = StacInputMetadata(json.loads(self.__read_pds_metadata_file(each_potential_file))) - granules_metadata_props = stac_input_meta.start() - break - except Exception as e: - LOGGER.exception(f'most likely not a STAC file: {each_potential_file}') - if stac_input_meta is None: - raise RuntimeError(f'unable to find STAC JSON file in {potential_files}') - LOGGER.debug(f'starting __ingest_custom_metadata') - self.__ingest_custom_metadata(stac_input_meta.custom_properties) - echo_metadata = EchoMetadata(granules_metadata_props).load().echo_metadata - echo_metadata_xml_str = xmltodict.unparse(echo_metadata, pretty=True) - self.__s3.target_key = os.path.join(os.path.dirname(self.__s3.target_key), f'{granules_metadata_props.granule_id}.cmr.xml') - self.__s3.upload_bytes(echo_metadata_xml_str.encode()) - echo_metadata_md5 = hashlib.md5(echo_metadata_xml_str.encode()).hexdigest() - returning_dict = deepcopy(self.__event['cma']['event']) - if 'replace' in returning_dict: - returning_dict.pop('replace') - # TODO This is a hack since distribution_endpoint = null is complained by MoveGranule. - """ - { - "errorType": "CumulusMessageAdapterExecutionError", - "errorMessage": "warning setting command to loadAndUpdateRemoteEvent\\nwarning setting command to loadNestedEvent\\nUnexpected Error . config schema: None is not of type 'string'\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None\\n\\nFailed validating 'type' in schema['properties']['distribution_endpoint']:\\n {'description': 'The api distribution endpoint', 'type': 'string'}\\n\\nOn instance['distribution_endpoint']:\\n None", - "trace": [ - "CumulusMessageAdapterExecutionError: warning setting command to loadAndUpdateRemoteEvent", - "warning setting command to loadNestedEvent", - "Unexpected Error . config schema: None is not of type 'string'", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - "", - "Failed validating 'type' in schema['properties']['distribution_endpoint']:", - " {'description': 'The api distribution endpoint', 'type': 'string'}", - "", - "On instance['distribution_endpoint']:", - " None", - " at Interface. (/var/task/webpack:/node_modules/@cumulus/cumulus-message-adapter-js/dist/cma.js:155:1)", - " at Interface.emit (events.js:326:22)", - " at Interface.EventEmitter.emit (domain.js:483:12)", - " at Interface.close (readline.js:416:8)", - " at Socket.onend (readline.js:194:10)", - " at Socket.emit (events.js:326:22)", - " at Socket.EventEmitter.emit (domain.js:483:12)", - " at endReadableNT (_stream_readable.js:1241:12)", - " at processTicksAndRejections (internal/process/task_queues.js:84:21)" - ] - } - """ - if 'distribution_endpoint' in returning_dict['meta'] and returning_dict['meta']['distribution_endpoint'] is None: - returning_dict['meta']['distribution_endpoint'] = f's3://{self.__s3.target_bucket}/' - returning_dict['task_config'] = { - "inputGranules": "{$.meta.input_granules}", - "granuleIdExtraction": "{$.meta.collection.granuleIdExtraction}" - } - returning_dict['payload'] = { - "granules": [ - { - "granuleId": self.__event['cma']['event']['meta']['input_granules'][0]['granuleId'], - "dataType": granules_metadata_props.collection_name, - "version": f'{granules_metadata_props.collection_version}', - "files": self.__input_file_list + [self.__generate_output_dict(echo_metadata_md5)], - # "files": self.__input_file_list, - "sync_granule_duration": 20302, - "createdAt": TimeUtils.get_current_unix_milli(), - } - ] - } - return returning_dict diff --git a/cumulus_lambda_functions/metadata_stac_generate_cmr/lambda_function.py b/cumulus_lambda_functions/metadata_stac_generate_cmr/lambda_function.py deleted file mode 100644 index 4bd54a6b..00000000 --- a/cumulus_lambda_functions/metadata_stac_generate_cmr/lambda_function.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.metadata_stac_generate_cmr.generate_cmr import GenerateCmr - - -def lambda_handler(event, context): - """ -{'cma': {'task_config': {'bucket': '{$.meta.buckets.internal.name}', 'collection': '{$.meta.collection}', 'cumulus_message': {'outputs': [{'source': '{$.files}', 'destination': '{$.payload}'}]}}, 'event': {'cumulus_meta': {'cumulus_version': '10.0.1', 'execution_name': 'c6d885dc-b4b2-4eb0-b22e-b6f58a7a0870', 'message_source': 'sfn', 'queueExecutionLimits': {'https://sqs.us-west-2.amazonaws.com/884500545225/am-uds-dev-cumulus-backgroundProcessing': 5}, 'state_machine': 'arn:aws:states:us-west-2:884500545225:stateMachine:am-uds-dev-cumulus-IngestGranule', 'system_bucket': 'am-uds-dev-cumulus-internal', 'workflow_start_time': 1646785175509, 'parentExecutionArn': 'arn:aws:states:us-west-2:884500545225:execution:am-uds-dev-cumulus-DiscoverGranules:885483b4-ba55-4db1-b197-661e1e595a45', 'queueUrl': 'arn:aws:sqs:us-west-2:884500545225:am-uds-dev-cumulus-startSF'}, 'exception': 'None', 'meta': {'buckets': {'internal': {'name': 'am-uds-dev-cumulus-internal', 'type': 'internal'}, 'protected': {'name': 'am-uds-dev-cumulus-protected', 'type': 'protected'}}, 'cmr': {'clientId': 'CHANGEME', 'cmrEnvironment': 'UAT', 'cmrLimit': 100, 'cmrPageSize': 50, 'oauthProvider': 'earthdata', 'passwordSecretName': 'am-uds-dev-cumulus-message-template-cmr-password20220216072916956000000002', 'provider': 'CHANGEME', 'username': 'username'}, 'collection': {'name': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'process': 'modis', 'granuleId': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$', 'granuleIdExtraction': '(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'duplicateHandling': 'replace', 'url_path': '{cmrMetadata.Granule.Collection.ShortName}___{cmrMetadata.Granule.Collection.VersionId}', 'provider_path': '/data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/', 'files': [{'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}00\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000000.PDS', 'type': 'data'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}01\\.PDS\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}, {'bucket': 'internal', 'regex': '^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0\\.cmr\\.xml$', 'sampleFileName': 'P1570515ATMSSCIENCEAXT11344000000001.PDS.xml', 'type': 'metadata'}], 'updatedAt': 1646326197526, 'createdAt': 1646258167624}, 'distribution_endpoint': 's3://am-uds-dev-cumulus-internal/', 'launchpad': {'api': 'launchpadApi', 'certificate': 'launchpad.pfx', 'passphraseSecretName': ''}, 'provider': {'password': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQHFbE4iMnF/W0Y/NrsYvrfHAAAAajBoBgkqhkiG9w0BBwagWzBZAgEAMFQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMLaH13SdxPXREjXLtAgEQgCfA+lEu2c/xLTGwJsbtKlXJbKDy4pwV+rS3BnJqgBoLLMQZqOdoFhk=', 'host': 'snppl0.gesdisc.eosdis.nasa.gov', 'updatedAt': 1646244053419, 'protocol': 'https', 'createdAt': 1646244053419, 'encrypted': True, 'username': 'AQICAHhSagsGDAl5tQWM010IEvxKgj2LcsNub5v5FHoRpOjXcQGRoY5EBMpvvyMASUowBM61AAAAYzBhBgkqhkiG9w0BBwagVDBSAgEAME0GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9OhRHwTuxiz74q4UAgEQgCDEHOhsVG6+LqXfnlw+Z3Wg9MDOCd9/K5/X5j3tPJYkaA==', 'allowedRedirects': ['https://urs.earthdata.nasa.gov', 'urs.earthdata.nasa.gov'], 'id': 'snpp_provider_02', 'globalConnectionLimit': 10}, 'stack': 'am-uds-dev-cumulus', 'template': 's3://am-uds-dev-cumulus-internal/am-uds-dev-cumulus/workflow_template.json', 'workflow_name': 'IngestGranule', 'workflow_tasks': {'SyncGranule': {'name': 'am-uds-dev-cumulus-SyncGranule', 'version': '$LATEST', 'arn': 'arn:aws:lambda:us-west-2:884500545225:function:am-uds-dev-cumulus-SyncGranule'}}, 'staticValue': 'aStaticValue', 'interpolatedValueStackName': 'am-uds-dev-cumulus', 'input_granules': [{'granuleId': 'P1570515ATMSSCIENCEAXT1134912000000', 'dataType': 'ATMS_SCIENCE_Group_2011', 'version': '001', 'files': [{'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000000.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000000.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000000.PDS', 'type': 'data', 'size': 744}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS', 'type': 'metadata', 'size': 18084600}, {'bucket': 'am-uds-dev-cumulus-internal', 'key': 'file-staging/am-uds-dev-cumulus/ATMS_SCIENCE_Group_2011___001/P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'source': 'data/SNPP_ATMS_Level0_T/ATMS_SCIENCE_Group/2011/349//P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'fileName': 'P1570515ATMSSCIENCEAXT11349120000001.PDS.xml', 'type': 'metadata', 'size': 9526}], 'sync_granule_duration': 9822, 'createdAt': 1647386972717}], 'process': 'modis'}, 'payload': {}, 'replace': {'Bucket': 'am-uds-dev-cumulus-internal', 'Key': 'events/5d8edf37-0a18-4af5-a76f-7c2091cdd1e2', 'TargetPath': '$.payload'}}}} - :param event: - :param context: - :return: - """ - LambdaLoggerGenerator.remove_default_handlers() - # TODO implement - return GenerateCmr(event).start() diff --git a/cumulus_lambda_functions/metadata_stac_generate_cmr/stac_input_metadata.py b/cumulus_lambda_functions/metadata_stac_generate_cmr/stac_input_metadata.py deleted file mode 100644 index 06686210..00000000 --- a/cumulus_lambda_functions/metadata_stac_generate_cmr/stac_input_metadata.py +++ /dev/null @@ -1,161 +0,0 @@ -from copy import deepcopy -from pystac import Item - -from mdps_ds_lib.lib.cumulus_stac.item_transformer import ItemTransformer -from cumulus_lambda_functions.lib.metadata_extraction.granule_metadata_props import GranuleMetadataProps -from mdps_ds_lib.lib.utils.time_utils import TimeUtils - - -class StacInputMetadata: - def __init__(self, input_stac_dict: dict): - self.__input_stac_dict = input_stac_dict - self.__beginning_dt = None - self.__ending_dt = None - self.__collection_name = None - self.__collection_version = None - self.__granule_id = None - self.__prod_dt = None - self.__insert_dt = None - self.__custom_properties = {} - self.__bbox = None - - @property - def bbox(self): - return self.__bbox - - @bbox.setter - def bbox(self, val): - """ - :param val: - :return: None - """ - self.__bbox = val - return - - @property - def custom_properties(self): - return self.__custom_properties - - @custom_properties.setter - def custom_properties(self, val): - """ - :param val: - :return: None - """ - self.__custom_properties = val - return - - @property - def beginning_dt(self): - return self.__beginning_dt - - @beginning_dt.setter - def beginning_dt(self, val): - """ - :param val: - :return: None - """ - self.__beginning_dt = val - return - - @property - def ending_dt(self): - return self.__ending_dt - - @ending_dt.setter - def ending_dt(self, val): - """ - :param val: - :return: None - """ - self.__ending_dt = val - return - - @property - def collection_name(self): - return self.__collection_name - - @collection_name.setter - def collection_name(self, val): - """ - :param val: - :return: None - """ - self.__collection_name = val - return - - @property - def collection_version(self): - return self.__collection_version - - @collection_version.setter - def collection_version(self, val): - """ - :param val: - :return: None - """ - self.__collection_version = val - return - - @property - def granule_id(self): - return self.__granule_id - - @granule_id.setter - def granule_id(self, val): - """ - :param val: - :return: None - """ - self.__granule_id = val - return - - @property - def prod_dt(self): - return self.__prod_dt - - @prod_dt.setter - def prod_dt(self, val): - """ - :param val: - :return: None - """ - self.__prod_dt = val - return - - @property - def insert_dt(self): - return self.__insert_dt - - @insert_dt.setter - def insert_dt(self, val): - """ - :param val: - :return: None - """ - self.__insert_dt = val - return - - def __remove_default_keys_in_custom_props(self): - ignoring_keys = ['start_datetime', 'end_datetime', 'created', 'updated', 'datetime'] - for each_key in ignoring_keys: - if each_key in self.__custom_properties: - self.__custom_properties.pop(each_key) - return - def start(self) -> GranuleMetadataProps: - stac_item: Item = ItemTransformer().from_stac(self.__input_stac_dict) - self.__custom_properties = deepcopy(stac_item.properties) - self.__remove_default_keys_in_custom_props() - self.__bbox = stac_item.bbox - # self.__custom_properties['collection_id'] = stac_item.collection_id # TODO version is included - # collection_led_granule_id = stac_item.id if stac_item.id.startswith(stac_item.collection_id) else f'{stac_item.collection_id}:{stac_item.id}' - # self.__custom_properties['granule_id'] = collection_led_granule_id # This needs to be start with collection_id to be consistent with cumulus granule_id which starts with collection - granule_metadata_props = GranuleMetadataProps() - granule_metadata_props.granule_id = stac_item.id - collection_id_split = stac_item.collection_id.split('___') - granule_metadata_props.collection_name = collection_id_split[0] - granule_metadata_props.collection_version = stac_item.collection_id.split('___')[1] if len(collection_id_split) > 1 else '' - granule_metadata_props.prod_dt = TimeUtils().parse_from_unix(stac_item.datetime.timestamp()).get_datetime_str(fmt='%Y-%m-%dT%H:%M:%S.%fZ') - granule_metadata_props.beginning_dt = stac_item.properties['start_datetime'] if stac_item.properties['start_datetime'] else TimeUtils().parse_from_unix(0).get_datetime_str() - granule_metadata_props.ending_dt = stac_item.properties['end_datetime'] if stac_item.properties['end_datetime'] else TimeUtils().parse_from_unix(0).get_datetime_str() - return granule_metadata_props diff --git a/cumulus_lambda_functions/uds_api/auth_admin_api.py b/cumulus_lambda_functions/uds_api/auth_admin_api.py deleted file mode 100644 index 287f9cd6..00000000 --- a/cumulus_lambda_functions/uds_api/auth_admin_api.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import Union - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.uds_api.dapa.auth_crud import AuthCrud, AuthDeleteModel, AuthListModel, AuthAddModel -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants -from fastapi import APIRouter, HTTPException, Request, Response - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.ADMIN}/auth', - tags=["Admin Records CRUD (Admins-Only)"], - responses={404: {"description": "Not found"}}, -) - -@router.delete("") -@router.delete("/") -async def delete_auth_mapping(request: Request, delete_body: AuthDeleteModel): - """ - Deleting one authorization mapping - """ - LOGGER.debug(f'started delete_auth_mapping') - auth_info = FastApiUtils.get_authorization_info(request) - auth_crud = AuthCrud(auth_info, delete_body.model_dump()) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - delete_result = auth_crud.delete_record() - if delete_result['statusCode'] == 200: - return delete_result['body'] - raise HTTPException(status_code=delete_result['statusCode'], detail=delete_result['body']) - -@router.put("") -@router.put("/") -async def add_auth_mapping(request: Request, new_body: AuthAddModel): - """ - Adding a new Authorization mapping - """ - LOGGER.debug(f'started add_auth_mapping. sss {new_body.model_dump()}') - auth_info = FastApiUtils.get_authorization_info(request) - auth_crud = AuthCrud(auth_info, new_body.model_dump()) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - add_result = auth_crud.add_new_record() - if add_result['statusCode'] == 200: - return add_result['body'] - raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) - -@router.post("") -@router.post("/") -async def update_auth_mapping(request: Request, update_body: AuthAddModel): - """ - Updating existing authorization mapping - """ - LOGGER.debug(f'started update_auth_mapping') - auth_info = FastApiUtils.get_authorization_info(request) - auth_crud = AuthCrud(auth_info, update_body.model_dump()) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - update_result = auth_crud.update_record() - if update_result['statusCode'] == 200: - return update_result['body'] - raise HTTPException(status_code=update_result['statusCode'], detail=update_result['body']) - -@router.get("") -@router.get("/") -async def list_auth_mappings(request: Request, tenant: Union[str, None]=None, venue: Union[str, None]=None, group_names: Union[str, None]=None): - """ - Listing all exsiting Authorization Mapping. - - """ - LOGGER.debug(f'started list_auth_mappings') - auth_info = FastApiUtils.get_authorization_info(request) - query_body = { - 'tenant': tenant, - 'venue': venue, - 'ldap_group_names': group_names if group_names is None else [k.strip() for k in group_names.split(',')], - } - auth_crud = AuthCrud(auth_info, query_body) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - query_result = auth_crud.list_all_record() - if query_result['statusCode'] == 200: - return query_result['body'] - raise HTTPException(status_code=query_result['statusCode'], detail=query_result['body']) diff --git a/cumulus_lambda_functions/uds_api/catalog_api.py b/cumulus_lambda_functions/uds_api/catalog_api.py deleted file mode 100644 index 521c77cc..00000000 --- a/cumulus_lambda_functions/uds_api/catalog_api.py +++ /dev/null @@ -1,78 +0,0 @@ -import os -from typing import Union - -from pystac import Catalog, Link - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from fastapi import APIRouter, HTTPException, Request, Response -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_query import CollectionDapaQuery -from cumulus_lambda_functions.uds_api.dapa.pagination_links_generator import PaginationLinksGenerator -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.CATALOG}', - tags=["Collection CRUD API"], - responses={404: {"description": "Not found"}}, -) - -@router.get("") -@router.get("/") -async def get_catalog(request: Request, limit: Union[int, None] = 10, offset: Union[int, None] = 0, ): - LOGGER.debug(f'starting get_catalog request: {request}') - - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - uds_collections = UdsCollections(es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS')) - collection_regexes = authorizer.get_authorized_collections(DBConstants.read, auth_info['ldap_groups']) - LOGGER.info(f'collection_regexes: {collection_regexes}') - authorized_collections = uds_collections.get_collections(collection_regexes) - LOGGER.info(f'authorized_collections: {authorized_collections}') - # Example: [{'collection_id': 'URN:NASA:UNITY:MAIN_PROJECT:DEV:CUMULUS_DAPA_UNIT_TEST___1697606545', 'granule_count': 0, 'start_time': 1697581345446, 'end_time': 1697581345446}] - collection_id = [k[DBConstants.collection_id] for k in authorized_collections] - LOGGER.info(f'authorized_collection_ids: {collection_id}') - # NOTE: 2022-11-21: only pass collections. not versions - - try: - custom_params = {} - if limit > CollectionDapaQuery.max_limit: - LOGGER.debug(f'incoming limit > {CollectionDapaQuery.max_limit}. resetting to max. incoming limit: {limit}') - limit = CollectionDapaQuery.max_limit - custom_params['limit'] = limit - LOGGER.debug(f'new limit: {limit}') - pg_link_generator = PaginationLinksGenerator(request, custom_params) - - catalog = Catalog( - id='unity_ds', - description='Unity DS Catalog', - title='Unity DS Catalog', - href=pg_link_generator.base_url - ) - api_base_prefix = FastApiUtils.get_api_base_prefix() - authorized_collections_links = [Link( - rel='child', - target=f'{pg_link_generator.base_url}/{api_base_prefix}/collections/{k["collection_id"]}', - media_type='application/json', - title=k["collection_id"], - ) for k in authorized_collections] - catalog.add_links(authorized_collections_links) - return catalog.to_dict(True, False) - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) diff --git a/cumulus_lambda_functions/uds_api/collections_api.py b/cumulus_lambda_functions/uds_api/collections_api.py deleted file mode 100644 index 6a7c2d2e..00000000 --- a/cumulus_lambda_functions/uds_api/collections_api.py +++ /dev/null @@ -1,346 +0,0 @@ -import json -import os -from datetime import datetime -from typing import Union - -from pystac import Catalog, Link, Collection, Extent, SpatialExtent, TemporalExtent, Summaries, Provider - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from fastapi import APIRouter, HTTPException, Request, Response - -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_cnm import CnmRequestBody, CollectionsDapaCnm -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_creation import CollectionDapaCreation, \ - CumulusCollectionModel -from cumulus_lambda_functions.uds_api.dapa.collections_dapa_query import CollectionDapaQuery -from cumulus_lambda_functions.uds_api.dapa.pagination_links_generator import PaginationLinksGenerator -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants -from fastapi.responses import PlainTextResponse, JSONResponse - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.COLLECTIONS}', - tags=["Collection CRUD API"], - responses={404: {"description": "Not found"}}, -) - -@router.put("") -@router.put("/") -async def ingest_cnm_dapa(request: Request, new_cnm_body: CnmRequestBody, response: Response, response_class=JSONResponse): - """ - Ingestion of Granules for a given collection via CNM - - This is a facade endpoint which will trigger another endpoint which takes some time to execute ingestion - """ - LOGGER.debug(f'starting ingest_cnm_dapa') - collection_id = new_cnm_body.model_dump() - if 'features' not in collection_id or len(collection_id['features']) < 1 or 'collection' not in collection_id['features'][0]: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': 'missing collection_id in request_body["features"][0]["collection"]' - })) - collection_id = collection_id['features'][0]['collection'] - collection_id = collection_id.split('___')[0] # split id, version and only keeping id. TODO need this? - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - cnm_prep_result = CollectionsDapaCnm(new_cnm_body.model_dump()).start_facade(request.url) - except Exception as e: - LOGGER.exception('failed during ingest_cnm_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if cnm_prep_result['statusCode'] < 300: - response.status_code = cnm_prep_result['statusCode'] - return cnm_prep_result['body'] - raise HTTPException(status_code=cnm_prep_result['statusCode'], detail=cnm_prep_result['body']) - - -@router.put("/actual") -@router.put("/actual/") -async def ingest_cnm_dapa_actual(request: Request, new_cnm_body: CnmRequestBody, response_class=JSONResponse): - """ - Real ingestion of Granules for a given collection via CNM - - This will take some time to repeatedly create SNS messages for CNM - - """ - LOGGER.debug(f'starting ingest_cnm_dapa_actual') - try: - collections_dapa_cnm = CollectionsDapaCnm(new_cnm_body.model_dump()) - cnm_result = collections_dapa_cnm.start() - except Exception as e: - LOGGER.exception('failed during ingest_cnm_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if cnm_result['statusCode'] == 200: - return cnm_result['body'] - raise HTTPException(status_code=cnm_result['statusCode'], detail=cnm_result['body']) - - -@router.post("") -@router.post("/") -async def create_new_collection(request: Request, new_collection: CumulusCollectionModel, response: Response): - """ - Creating a new Cumulus Collection - - This is a facade endpoint which will trigger another endpoint which takes some time to hit Cumulus collection creation endpoint. - """ - LOGGER.debug(f'starting create_new_collection') - new_collection = new_collection.model_dump() - collection_id = new_collection - if 'id' not in collection_id: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': 'missing collection_id in request_body["id"]' - })) - collection_id = new_collection['id'] - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - # new_collection = request.body() - bearer_token = request.headers.get('Authorization', '') - LOGGER.debug(f'create_new_collection--bearer_token: {bearer_token}') - creation_result = CollectionDapaCreation(new_collection).start(request.url, bearer_token) - except Exception as e: - LOGGER.exception('failed during ingest_cnm_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if creation_result['statusCode'] < 300: - response.status_code = creation_result['statusCode'] - return creation_result['body'] - raise HTTPException(status_code=creation_result['statusCode'], detail=creation_result['body']) - - -@router.post("/actual") -@router.post("/actual/") -async def create_new_collection_real(request: Request, new_collection: CumulusCollectionModel): - """ - Actual endpoint to create a new Cumulus Collection - """ - LOGGER.debug(f'starting create_new_collection_real') - new_collection = new_collection.model_dump() - collection_id = new_collection - if 'id' not in collection_id: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': 'missing collection_id in request_body["id"]' - })) - collection_id = new_collection['id'] - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - - try: - creation_result = CollectionDapaCreation(new_collection).create() - except Exception as e: - LOGGER.exception('failed during ingest_cnm_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if creation_result['statusCode'] < 300: - return creation_result['body'], creation_result['statusCode'] - raise HTTPException(status_code=creation_result['statusCode'], detail=creation_result['body']) - -@router.get("/{collection_id}") -@router.get("/{collection_id}/") -async def get_single_collection(request: Request, collection_id: str, limit: Union[int, None] = 10, offset: Union[int, None] = 0, ): - LOGGER.debug(f'starting get_single_collection: {collection_id}') - LOGGER.debug(f'starting get_single_collection request: {request}') - - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - uds_collections = UdsCollections(es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS')) - if collection_id is None or collection_id == '': - raise HTTPException(status_code=500, detail=f'missing or invalid collection_id: {collection_id}') - collection_identifier = uds_collections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - - try: - custom_params = {} - if limit > CollectionDapaQuery.max_limit: - LOGGER.debug(f'incoming limit > {CollectionDapaQuery.max_limit}. resetting to max. incoming limit: {limit}') - limit = CollectionDapaQuery.max_limit - custom_params['limit'] = limit - LOGGER.debug(f'new limit: {limit}') - pg_link_generator = PaginationLinksGenerator(request, custom_params) - api_base_prefix = FastApiUtils.get_api_base_prefix() - collections_dapa_query = CollectionDapaQuery(collection_id, limit, offset, None, - f'{pg_link_generator.base_url}/{api_base_prefix}') - collections_result = collections_dapa_query.get_collection() - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if collections_result['statusCode'] == 200: - return collections_result['body'] - raise HTTPException(status_code=collections_result['statusCode'], detail=collections_result['body']) - -@router.get("") -@router.get("/") -async def query_collections(request: Request, collection_id: Union[str, None] = None, limit: Union[int, None] = 10, offset: Union[int, None] = 0, ): - LOGGER.debug(f'starting query_collections: {collection_id}') - LOGGER.debug(f'starting query_collections request: {request}') - - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - uds_collections = UdsCollections(es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS')) - if collection_id is not None: - collection_identifier = uds_collections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - else: - collection_regexes = authorizer.get_authorized_collections(DBConstants.read, auth_info['ldap_groups']) - LOGGER.info(f'collection_regexes: {collection_regexes}') - authorized_collections = uds_collections.get_collections(collection_regexes) - LOGGER.info(f'authorized_collections: {authorized_collections}') - collection_id = [k[DBConstants.collection_id] for k in authorized_collections] - LOGGER.info(f'authorized_collection_ids: {collection_id}') - # NOTE: 2022-11-21: only pass collections. not versions - - try: - custom_params = {} - if limit > CollectionDapaQuery.max_limit: - LOGGER.debug(f'incoming limit > {CollectionDapaQuery.max_limit}. resetting to max. incoming limit: {limit}') - limit = CollectionDapaQuery.max_limit - custom_params['limit'] = limit - LOGGER.debug(f'new limit: {limit}') - pg_link_generator = PaginationLinksGenerator(request, custom_params) - pagination_links = pg_link_generator.generate_pagination_links() - api_base_prefix = FastApiUtils.get_api_base_prefix() - collections_dapa_query = CollectionDapaQuery(collection_id, limit, offset, pagination_links, f'{pg_link_generator.base_url}/{api_base_prefix}') - collections_result = collections_dapa_query.start() - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if collections_result['statusCode'] == 200: - return collections_result['body'] - raise HTTPException(status_code=collections_result['statusCode'], detail=collections_result['body']) - -@router.delete("/{collection_id}") -@router.delete("/{collection_id}/") -async def delete_single_collection(request: Request, collection_id: str): - LOGGER.debug(f'starting delete_single_collection: {collection_id}') - LOGGER.debug(f'starting delete_single_collection request: {request}') - - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - uds_collections = UdsCollections(es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS')) - if collection_id is None or collection_id == '': - raise HTTPException(status_code=500, detail=f'missing or invalid collection_id: {collection_id}') - collection_identifier = uds_collections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.delete, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - - granules_count = GranulesDbIndex().get_size(collection_identifier.tenant, collection_identifier.venue, - collection_id) - LOGGER.debug(f'granules_count: {granules_count} for {collection_id}') - if granules_count > 0: - LOGGER.debug(f'NOT deleting {collection_id} as it is not empty') - raise HTTPException(status_code=409, detail=f'NOT deleting {collection_id} as it is not empty') - - try: - new_collection = Collection( - id=collection_id, - title=collection_id, - description='TODO', - extent = Extent( - SpatialExtent([[0.0, 0.0, 0.0, 0.0]]), - TemporalExtent([[datetime.utcnow(), datetime.utcnow()]]) - ), - license = "proprietary", - providers = [], - # title=input_collection['LongName'], - # keywords=[input_collection['SpatialKeywords']['Keyword']], - summaries = Summaries({ - "totalGranules": [-1], - }), - ) - new_collection.links = [ - Link(rel='root', - target=f'./collection.json', - media_type='application/json', title=f"{new_collection.id}"), - Link(rel='item', - target='./collection.json', - media_type='application/json', title=f"{new_collection.id} Granules") - ] - creation_result = CollectionDapaCreation(new_collection.to_dict(False, False)).delete() - except Exception as e: - LOGGER.exception('failed during ingest_cnm_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if creation_result['statusCode'] < 300: - return creation_result['body'], creation_result['statusCode'] - raise HTTPException(status_code=creation_result['statusCode'], detail=creation_result['body']) diff --git a/cumulus_lambda_functions/uds_api/custom_meta_admin_api.py b/cumulus_lambda_functions/uds_api/custom_meta_admin_api.py deleted file mode 100644 index dda8a303..00000000 --- a/cumulus_lambda_functions/uds_api/custom_meta_admin_api.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import Union - -from cumulus_lambda_functions.cumulus_es_setup.es_setup import SetupESIndexAlias -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.uds_api.dapa.auth_crud import AuthCrud, AuthDeleteModel, AuthListModel, AuthAddModel -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants -from fastapi import APIRouter, HTTPException, Request, Response - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.ADMIN}', - tags=["Custom Metadata Index CRUD"], - responses={404: {"description": "Not found"}}, -) - - -@router.put("/custom_metadata/{tenant}") -@router.put("/custom_metadata/{tenant}/") -async def custom_metadata_add(request: Request, tenant: str, venue: Union[str, None] = None, request_body: dict = {}): - LOGGER.debug(f'started es_granules_index_setup') - auth_info = FastApiUtils.get_authorization_info(request) - query_body = { - 'tenant': tenant, - 'venue': venue, - } - auth_crud = AuthCrud(auth_info, query_body) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - try: - GranulesDbIndex().create_new_index(tenant, venue, request_body) - except Exception as e: - LOGGER.exception(f'') - raise HTTPException(status_code=500, detail=str(e)) - return {'message': 'successful'} - - -@router.get("/custom_metadata/{tenant}") -@router.get("/custom_metadata/{tenant}/") -async def custom_metadata_get(request: Request, tenant: str, venue: Union[str, None] = None): - LOGGER.debug(f'started es_granules_index_setup') - auth_info = FastApiUtils.get_authorization_info(request) - query_body = { - 'tenant': tenant, - 'venue': venue, - } - auth_crud = AuthCrud(auth_info, query_body) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - try: - granules_index_mapping = GranulesDbIndex().get_latest_index(tenant, venue) - except Exception as e: - LOGGER.exception(f'failed to retrieve granules mapping') - raise HTTPException(status_code=500, detail=str(e)) - return granules_index_mapping - - -@router.delete("/custom_metadata/{tenant}/destroy") -@router.delete("/custom_metadata/{tenant}/destroy/") -async def custom_metadata_destroy(request: Request, tenant: str, venue: Union[str, None] = None): - LOGGER.debug(f'started es_granules_index_setup') - auth_info = FastApiUtils.get_authorization_info(request) - query_body = { - 'tenant': tenant, - 'venue': venue, - } - auth_crud = AuthCrud(auth_info, query_body) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - try: - GranulesDbIndex().destroy_indices(tenant, venue) - except Exception as e: - LOGGER.exception(f'') - raise HTTPException(status_code=500, detail=str(e)) - return {'message': 'successful'} - - -@router.delete("/custom_metadata/{tenant}") -@router.delete("/custom_metadata/{tenant}/") -async def custom_metadata_delete(request: Request, tenant: str, venue: Union[str, None] = None): - LOGGER.debug(f'started es_granules_index_delete_setup') - auth_info = FastApiUtils.get_authorization_info(request) - query_body = { - 'tenant': tenant, - 'venue': venue, - } - auth_crud = AuthCrud(auth_info, query_body) - is_admin_result = auth_crud.is_admin() - if is_admin_result['statusCode'] != 200: - raise HTTPException(status_code=is_admin_result['statusCode'], detail=is_admin_result['body']) - try: - GranulesDbIndex().delete_index(tenant, venue) - except Exception as e: - LOGGER.exception(f'') - raise HTTPException(status_code=500, detail=str(e)) - return {'message': 'successful'} diff --git a/cumulus_lambda_functions/uds_api/dapa/__init__.py b/cumulus_lambda_functions/uds_api/dapa/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cumulus_lambda_functions/uds_api/dapa/auth_crud.py b/cumulus_lambda_functions/uds_api/dapa/auth_crud.py deleted file mode 100644 index 9c1ae795..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/auth_crud.py +++ /dev/null @@ -1,183 +0,0 @@ -import json -import os - -from pydantic import BaseModel - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory -from mdps_ds_lib.lib.utils.json_validator import JsonValidator - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants -from mdps_ds_lib.lib.utils.lambda_api_gateway_utils import LambdaApiGatewayUtils - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class AuthDeleteModel(BaseModel): - tenant: str - venue: str - group_name: str - - -delete_schema = { - 'type': 'object', - 'required': ['tenant', 'venue', 'group_name'], - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_name': {'type': 'string'}, - } -} - - -class AuthListModel(BaseModel): - tenant: str - venue: str - group_name: list[str] - - -list_schema = { - 'type': 'object', - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_names': { - 'type': 'array', - 'items': {'type': 'string'}, - 'minItems': 1, - }, - } -} - - -class AuthAddModel(BaseModel): - tenant: str - venue: str - group_name: str - resources: list[str] - actions: list[str] - - -add_schema = { - 'type': 'object', - 'required': ['tenant', 'venue', 'group_name', 'actions', 'resources'], - 'properties': { - 'tenant': {'type': 'string'}, - 'venue': {'type': 'string'}, - 'group_name': {'type': 'string'}, - 'actions': { - 'type': 'array', - 'items': { - 'type': 'string', - 'enum': [DBConstants.create, DBConstants.delete, DBConstants.update, DBConstants.read] - }, - 'minItems': 1, - }, - 'resources': { - 'type': 'array', - 'items': {'type': 'string'}, - 'minItems': 1, - }, - } - -} - - -class AuthCrud: - def __init__(self, authorization_info, request_body): - self.__request_body = request_body - self.__authorization_info = authorization_info - required_env = ['ES_URL', 'ADMIN_COMMA_SEP_GROUPS'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__admin_groups = [k.strip() for k in os.getenv('ADMIN_COMMA_SEP_GROUPS').split(',')] - self.__es_url = os.getenv('ES_URL') - self.__es_port = int(os.getenv('ES_PORT', '443')) - self.__authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=self.__es_url, - es_port=self.__es_port, es_type=os.getenv('ES_TYPE', 'AWS') - ) - - def is_admin(self): - belonged_admin_groups = list(set(self.__admin_groups) & set(self.__authorization_info['ldap_groups'])) - if len(belonged_admin_groups) < 1: - LOGGER.warn(f'unauthorized attempt to admin function: {self.__authorization_info}') - return { - 'statusCode': 403, - 'body': {'message': f'user is not in admin groups: {self.__admin_groups}'} - } - return { - 'statusCode': 200, - 'body': {} - } - - def list_all_record(self): - all_records = self.__authorizer.list_groups( - tenant=self.__request_body['tenant'] if 'tenant' in self.__request_body else None, - venue=self.__request_body['venue'] if 'venue' in self.__request_body else None, - ldap_group_names=self.__request_body['group_names'] if 'group_names' in self.__request_body else None, - ) - return { - 'statusCode': 200, - 'body': all_records - } - - def add_new_record(self): - body_validator_result = JsonValidator(add_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid add body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid add body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.add_authorized_group( - action=self.__request_body['actions'], - resource=self.__request_body['resources'], - tenant=self.__request_body['tenant'], - venue=self.__request_body['venue'], - ldap_group_name=self.__request_body['group_name'], - ) - return { - 'statusCode': 200, - 'body': {'message': 'inserted'} - } - - def update_record(self): - body_validator_result = JsonValidator(add_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid update body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid update body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.update_authorized_group( - action=self.__request_body['actions'], - resource=self.__request_body['resources'], - tenant=self.__request_body['tenant'], - venue=self.__request_body['venue'], - ldap_group_name=self.__request_body['group_name'], - ) - return { - 'statusCode': 200, - 'body': {'message': 'updated'} - } - - def delete_record(self): - body_validator_result = JsonValidator(delete_schema).validate(self.__request_body) - if body_validator_result is not None: - LOGGER.error(f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}') - return { - 'statusCode': 500, - 'body': f'invalid delete body: {body_validator_result}. request_body: {self.__request_body}' - } - self.__authorizer.delete_authorized_group( - tenant=self.__request_body.get('tenant'), - venue=self.__request_body.get('venue'), - ldap_group_name=self.__request_body.get('group_name'), - ) - return { - 'statusCode': 200, - 'body': {'message': 'deleted'} - } diff --git a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_cnm.py b/cumulus_lambda_functions/uds_api/dapa/collections_dapa_cnm.py deleted file mode 100644 index 96381885..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_cnm.py +++ /dev/null @@ -1,234 +0,0 @@ -import json -import os -from typing import Union - -from mdps_ds_lib.lib.aws.aws_lambda import AwsLambda -from starlette.datastructures import URL - -from mdps_ds_lib.lib.aws.aws_sns import AwsSns - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from pydantic import BaseModel -from mdps_ds_lib.lib.utils.time_utils import TimeUtils - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class SingleFeatureRequestBody(BaseModel): - id: str - collection: str - assets: dict - - -class CnmRequestBody(BaseModel): - provider_id: str - features: list[SingleFeatureRequestBody] - - -class CollectionsDapaCnm: - def __init__(self, request_body): - required_env = ['SNS_TOPIC_ARN', 'COLLECTION_CREATION_LAMBDA_NAME'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__sns_topic_arn = os.getenv('SNS_TOPIC_ARN') - self.__request_body = request_body - self.__collection_cnm_lambda_name = os.environ.get('COLLECTION_CREATION_LAMBDA_NAME', '').strip() - - - def start_facade(self, current_url: URL): - LOGGER.debug(f'request body: {self.__request_body}') - - actual_path = current_url.path - actual_path = actual_path if actual_path.endswith('/') else f'{actual_path}/' - actual_path = f'{actual_path}actual' - LOGGER.info(f'sanity_check') - - actual_event = { - 'resource': actual_path, - 'path': actual_path, - 'httpMethod': 'PUT', - 'headers': { - 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Authorization': 'Bearer xxx', - 'Host': current_url.hostname, 'User-Agent': 'python-requests/2.28.2', - 'X-Amzn-Trace-Id': 'Root=1-64a66e90-6fa8b7a64449014639d4f5b4', 'X-Forwarded-For': '44.236.15.58', - 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https'}, - 'multiValueHeaders': { - 'Accept': ['*/*'], 'Accept-Encoding': ['gzip, deflate'], 'Authorization': ['Bearer xxx'], - 'Host': [current_url.hostname], 'User-Agent': ['python-requests/2.28.2'], - 'X-Amzn-Trace-Id': ['Root=1-64a66e90-6fa8b7a64449014639d4f5b4'], - 'X-Forwarded-For': ['127.0.0.1'], 'X-Forwarded-Port': ['443'], 'X-Forwarded-Proto': ['https'] - }, - 'queryStringParameters': {}, - 'multiValueQueryStringParameters': {}, - 'pathParameters': {}, - 'stageVariables': None, - 'requestContext': { - 'resourceId': '', - 'authorizer': {'principalId': '', 'integrationLatency': 0}, - 'resourcePath': actual_path, 'httpMethod': 'PUT', - 'extendedRequestId': '', 'requestTime': '', - 'path': actual_path, 'accountId': '', - 'protocol': 'HTTP/1.1', 'stage': '', 'domainPrefix': '', 'requestTimeEpoch': 0, - 'requestId': '', - 'identity': { - 'cognitoIdentityPoolId': None, 'accountId': None, 'cognitoIdentityId': None, 'caller': None, - 'sourceIp': '127.0.0.1', 'principalOrgId': None, 'accessKey': None, - 'cognitoAuthenticationType': None, - 'cognitoAuthenticationProvider': None, 'userArn': None, 'userAgent': 'python-requests/2.28.2', - 'user': None - }, - 'domainName': current_url.hostname, 'apiId': '' - }, - 'body': json.dumps(self.__request_body), - 'isBase64Encoded': False - } - LOGGER.info(f'actual_event: {actual_event}') - response = AwsLambda().invoke_function( - function_name=self.__collection_cnm_lambda_name, - payload=actual_event, - ) - LOGGER.debug(f'async function started: {response}') - return { - 'statusCode': 202, - 'body': { - 'message': 'processing' - } - } - - def __generate_cumulus_asset(self, v): - cumulus_asset = { - 'name': os.path.basename(v['href']), - 'type': v['roles'][0] if 'roles' in v and len(v['roles']) > 0 else 'unknown', - 'uri': v['href'], - 'checksumType': 'md5', # TODO Is this the only type? - 'checksum': v['file:checksum'] if 'file:checksum' in v else '00000000000000000000000000000000', - 'size': v['file:size'] if 'file:size' in v else 0, - } - return cumulus_asset - - def start(self): - """ - Publish granule messages to CNM SNS Topic. - This is the workflow: cnm (Rest endpoint) -> sns -> sqs -> event bridge rule (every 1 minute) -> uds-dev-cumulus-sqsMessageConsumer lambda -> sqs -> uds-dev-cumulus-sqs2sf lambda -> step function -Sample Output: -{ - "collection": "SNDR_SNPP_ATMS_L1A", - "identifier": "SNDR.SNPP.ATMS.L1A.nominal2.01", - "product": { - "dataVersion": "1", - "files": [ - { - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "size": 9194361, - "type": "data", - "uri": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "checksumType": "md5", - "checksum": "2eafd390e5e7ac4b4d7d86d361fed50b" - }, - { - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "size": 2673, - "type": "metadata", - "uri": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "checksumType": "md5", - "checksum": "60e834d887ac6fb81ef63c4056c5b673" - } - ], - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01" - }, - "provider": "SNPP", - "submissionTime": "2022-07-06T00:00:00Z", - "version": "1.6.0" - } - - -Input to trigger CNM -Message: -{ - "collection": "SNDR_SNPP_ATMS_L1A", - "identifier": "SNDR.SNPP.ATMS.L1A.nominal2.01", - "product": { - "dataVersion": "1", - "files": [ - { - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "size": 9194361, - "type": "data", - "uri": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc", - "checksumType": "md5", - "checksum": "2eafd390e5e7ac4b4d7d86d361fed50b" - }, - { - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "size": 2673, - "type": "metadata", - "uri": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas", - "checksumType": "md5", - "checksum": "60e834d887ac6fb81ef63c4056c5b673" - } - ], - "name": "SNDR.SNPP.ATMS.L1A.nominal2.01" - }, - "provider": "SNPP", - "submissionTime": "2022-07-06T00:00:00Z", - "version": "1.6.0" -} -aws sns publish --topic-arn arn:aws:sns:us-west-2:884500545225:am-uds-dev-cumulus-cnm-submission-sns --message file:///tmp/SNDR.SNPP.ATMS.L1A.nominal2.01.json - - - -Test Input message -{ - "requestContext": { - "provider_id": "SNPP", - "features": [ - { - "id": "SNDR.SNPP.ATMS.L1A.nominal2.01", - "collection": "SNDR_SNPP_ATMS_L1A___1", - "assets": { - "data": { - "href": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc" - }, - "metadata": { - "href": "s3://am-uds-dev-cumulus-staging/SNDR_SNPP_ATMS_L1A/SNDR.SNPP.ATMS.L1A.nominal2.01.nc.cas" - } - } - } - ] - } -} - :return: - """ - error_list = [] - for each_granule in self.__request_body['features']: - LOGGER.debug(f'executing: {each_granule}') - try: - collection_id_version = each_granule['collection'].split('___') - sns_msg = { - 'collection': collection_id_version[0], - 'identifier': each_granule['id'], - 'submissionTime': TimeUtils.get_current_time(), - "provider": self.__request_body['provider_id'], - "version": '1.6.0', # TODO - 'product': { - 'name': each_granule['id'], - 'dataVersion': collection_id_version[1] if len(collection_id_version) > 1 else '', - 'files': [self.__generate_cumulus_asset(v) for k, v in each_granule['assets'].items()], - } - } - LOGGER.debug(f'sending sns message: {sns_msg}') - sns_response = AwsSns().set_topic_arn(self.__sns_topic_arn).publish_message(json.dumps(sns_msg)) - LOGGER.debug(f'published message result: {sns_response}') - except Exception as e: - LOGGER.exception(f'error while sending SNS msg for granule: {each_granule}') - error_list.append({'message': str(e), 'feature': each_granule}) - if len(error_list) < 1: - return { - 'statusCode': 200, - 'body': 'registered' - } - return { - 'statusCode': 500, - 'body': {'message': f'failed {len(error_list)}/{len(self.__request_body["features"])}', - 'details': error_list} - } diff --git a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_creation.py b/cumulus_lambda_functions/uds_api/dapa/collections_dapa_creation.py deleted file mode 100644 index 6b111a9d..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_creation.py +++ /dev/null @@ -1,320 +0,0 @@ -import json -import os -from time import sleep -from typing import Optional - -import pystac -from pydantic import BaseModel - -from mdps_ds_lib.lib.utils.time_utils import TimeUtils - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections -from starlette.datastructures import URL - -from cumulus_lambda_functions.cumulus_wrapper.query_collections import CollectionsQuery - -from mdps_ds_lib.lib.cumulus_stac.collection_transformer import CollectionTransformer - -from mdps_ds_lib.lib.aws.aws_lambda import AwsLambda - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -class SummariesModel(BaseModel): - granuleId: list[str] - granuleIdExtraction: list[str] - process: list[str] - - -class ExtentModel(BaseModel): - temporal: dict - spatial: dict - -class CumulusLinkModel(BaseModel): - rel: str - href: str - type: Optional[str] = '' - title: Optional[str] = '' - -class CumulusCollectionModel(BaseModel): - """ - {"type": "Collection", "id": "URN:NASA:UNITY:MAIN_PROJECT:DEV:CUMULUS_DAPA_UNIT_TEST___1697248243", "stac_version": "1.0.0", - "description": "TODO", - "links": [ -{"rel": "root", "href": "./collection.json?bucket=internal®ex=%5EP%5B0-9%5D%7B3%7D%5B0-9%5D%7B4%7D%5BA-Z%5D%7B13%7DT%5B0-9%5D%7B12%7D01.PDS%24", "type": "application/json", "title": "P1570515ATMSSCIENCEAXT11344000000001.PDS"}, -{"rel": "item", "href": "./collection.json?bucket=internal®ex=%5EP%5B0-9%5D%7B3%7D%5B0-9%5D%7B4%7D%5BA-Z%5D%7B13%7DT%5B0-9%5D%7B12%7D00.PDS.cmr.xml%24", "type": "metadata", "title": "P1570515ATMSSCIENCEAXT11344000000000.PDS.cmr.xml"}, -{"rel": "item", "href": "./collection.json?bucket=internal®ex=%5EP%5B0-9%5D%7B3%7D%5B0-9%5D%7B4%7D%5BA-Z%5D%7B13%7DT%5B0-9%5D%7B12%7D01%5C.PDS%5C.xml%24", "type": "metadata", "title": "P1570515ATMSSCIENCEAXT11344000000001.PDS.xml"}, -{"rel": "item", "href": "./collection.json?bucket=internal®ex=%5EP%5B0-9%5D%7B3%7D%5B0-9%5D%7B4%7D%5BA-Z%5D%7B13%7DT%5B0-9%5D%7B12%7D00%5C.PDS%24", "type": "data", "title": "P1570515ATMSSCIENCEAXT11344000000000.PDS"}], -"title": "P1570515ATMSSCIENCEAXT11344000000001.PDS", -"extent": {"spatial": {"bbox": [[0, 0, 0, 0]]}, -"temporal": {"interval": [["2023-10-13T18:51:02.397693Z", "2023-10-13T18:51:02.397698Z"]]}}, -"license": "proprietary", -"providers": [{"name": "unity"}], -"summaries": {"granuleId": ["^P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0$"], "granuleIdExtraction": ["(P[0-9]{3}[0-9]{4}[A-Z]{13}T[0-9]{12}0).+"], -"process": ["modis"]}} - """ - type: Optional[str] = 'Collection' - stac_version: Optional[str] = '1.0.0' - id: str - title: str - description: Optional[str] = 'TODO' - license: Optional[str] = 'proprietary' - summaries: SummariesModel - links: list[CumulusLinkModel] - providers: list[dict] - extent: ExtentModel - -class CollectionDapaCreation: - def __init__(self, request_body): - required_env = ['CUMULUS_LAMBDA_PREFIX', 'CUMULUS_WORKFLOW_SQS_URL'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - - self.__request_body = request_body - self.__collection_creation_lambda_name = os.environ.get('COLLECTION_CREATION_LAMBDA_NAME', '').strip() - self.__cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX') - self.__include_cumulus = os.getenv('CUMULUS_INCLUSION', 'TRUE').upper().strip() == 'TRUE' - self.__ingest_sqs_url = os.getenv('CUMULUS_WORKFLOW_SQS_URL') - self.__report_to_ems = os.getenv('REPORT_TO_EMS', 'TRUE').strip().upper() == 'TRUE' - self.__workflow_name = os.getenv('CUMULUS_WORKFLOW_NAME', 'CatalogGranule') - self.__provider_id = os.getenv('UNITY_DEFAULT_PROVIDER', '') - self.__collection_transformer = CollectionTransformer(self.__report_to_ems) - self.__uds_collection = UdsCollections(es_url=os.getenv('ES_URL'), es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS'), use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True) - self.__cumulus_collection_query = CollectionsQuery('', '') - - def analyze_cumulus_result(self, cumulus_request_result): - if 'status' not in cumulus_request_result: - LOGGER.error(f'status not in cumulus_request_result: {cumulus_request_result}') - return { - 'statusCode': 500, - 'body': { - 'message': cumulus_request_result - } - }, None - return None, cumulus_request_result - - - def __delete_collection_uds(self): - try: - delete_collection_result = self.__uds_collection.delete_collection( - collection_id=self.__collection_transformer.get_collection_id() - ) - except Exception as e: - LOGGER.exception(f'failed to add collection to Elasticsearch') - return { - 'statusCode': 500, - 'body': { - 'message': f'unable to delete collection to Elasticsearch: {str(e)}', - } - } - return None - - def __create_collection_uds(self, cumulus_collection_doc): - - try: - time_range = self.__collection_transformer.get_collection_time_range() - self.__uds_collection.add_collection( - collection_id=self.__collection_transformer.get_collection_id(), - start_time=TimeUtils().set_datetime_obj(time_range[0][0]).get_datetime_unix(True), - end_time=TimeUtils().set_datetime_obj(time_range[0][1]).get_datetime_unix(True), - bbox=self.__collection_transformer.get_collection_bbox(), - granules_count=0, - ) - except Exception as e: - LOGGER.exception(f'failed to add collection to Elasticsearch') - delete_collection_result = 'NA' - if self.__include_cumulus: - delete_collection_result = self.__cumulus_collection_query.delete_collection(self.__cumulus_lambda_prefix, - cumulus_collection_doc['name'], - cumulus_collection_doc['version']) - return { - 'statusCode': 500, - 'body': { - 'message': f'unable to add collection to Elasticsearch: {str(e)}', - 'details': f'collection deletion result: {delete_collection_result}' - } - } - return None - - def delete(self): - deletion_result = {} - try: - - cumulus_collection_doc = self.__collection_transformer.from_stac(self.__request_body) - self.__provider_id = self.__provider_id if self.__collection_transformer.output_provider is None else self.__collection_transformer.output_provider - LOGGER.debug(f'__provider_id: {self.__provider_id}') - creation_result = 'NA' - - if self.__include_cumulus: - result = self.__cumulus_collection_query.list_executions(cumulus_collection_doc, self.__cumulus_lambda_prefix) - LOGGER.debug(f'execution list result: {result}') - if len(result['results']) > 0: - self.__delete_collection_execution(cumulus_collection_doc, deletion_result) - return { - 'statusCode': 409, - 'body': { - 'message': f'There are cumulus executions for this collection. Deleting them. Pls try again in a few minutes.', - } - } - # self.__delete_collection_execution(cumulus_collection_doc, deletion_result) - self.__delete_collection_rule(cumulus_collection_doc, deletion_result) - delete_result = self.__cumulus_collection_query.delete_collection(self.__cumulus_lambda_prefix, cumulus_collection_doc['name'], cumulus_collection_doc['version']) - delete_err, delete_result = self.analyze_cumulus_result(delete_result) - if delete_err is not None: - LOGGER.error(f'deleting collection ends in error. Trying again. {delete_err}') - # self.__delete_collection_execution(cumulus_collection_doc, deletion_result) - self.__delete_collection_rule(cumulus_collection_doc, deletion_result) - delete_result = self.__cumulus_collection_query.delete_collection(self.__cumulus_lambda_prefix, cumulus_collection_doc['name'], cumulus_collection_doc['version']) - delete_err, delete_result = self.analyze_cumulus_result(delete_result) - deletion_result['cumulus_collection_deletion'] = delete_err if delete_err is not None else delete_result - else: - deletion_result['cumulus_executions_deletion'] = 'NA' - deletion_result['cumulus_rule_deletion'] = 'NA' - deletion_result['cumulus_collection_deletion'] = 'NA' - - uds_deletion_result = self.__delete_collection_uds() - deletion_result['uds_collection_deletion'] = uds_deletion_result if uds_deletion_result is not None else 'succeeded' - except Exception as e: - LOGGER.exception('error while creating new collection in Cumulus') - return { - 'statusCode': 500, - 'body': { - 'message': f'error while creating new collection in Cumulus. check details', - 'details': str(e) - } - } - LOGGER.info(f'creation_result: {creation_result}') - return { - 'statusCode': 200, - 'body': { - 'message': deletion_result - } - } - - def __delete_collection_rule(self, cumulus_collection_doc, deletion_result): - if 'cumulus_rule_deletion' in deletion_result and 'statusCode' not in deletion_result['cumulus_rule_deletion']: - return - rule_deletion_result = self.__cumulus_collection_query.delete_sqs_rules(cumulus_collection_doc, self.__cumulus_lambda_prefix) - rule_delete_err, rule_delete_result = self.analyze_cumulus_result(rule_deletion_result) - deletion_result['cumulus_rule_deletion'] = rule_delete_err if rule_delete_err is not None else rule_delete_result - return - - def __delete_collection_execution(self, cumulus_collection_doc, deletion_result): - executions_delete_result = self.__cumulus_collection_query.delete_executions(cumulus_collection_doc, self.__cumulus_lambda_prefix) - exec_delete_err, exec_delete_result = self.analyze_cumulus_result(executions_delete_result) - deletion_result['cumulus_executions_deletion'] = exec_delete_err if exec_delete_err is not None else exec_delete_result - sleep(10) - return - def create(self): - try: - cumulus_collection_doc = self.__collection_transformer.from_stac(self.__request_body) - self.__provider_id = self.__provider_id if self.__collection_transformer.output_provider is None else self.__collection_transformer.output_provider - LOGGER.debug(f'__provider_id: {self.__provider_id}') - creation_result = 'NA' - if self.__include_cumulus: - creation_cumulus_result = self.__cumulus_collection_query.create_collection(cumulus_collection_doc, self.__cumulus_lambda_prefix) - creation_err, creation_result = self.analyze_cumulus_result(creation_cumulus_result) - if creation_err is not None: - return creation_err - uds_creation_result = self.__create_collection_uds(cumulus_collection_doc) - if uds_creation_result is not None: - return uds_creation_result - if self.__include_cumulus: - rule_creation_result = self.__cumulus_collection_query.create_sqs_rules( - cumulus_collection_doc, - self.__cumulus_lambda_prefix, - self.__ingest_sqs_url, - self.__provider_id, - self.__workflow_name, - ) - create_rule_err, create_rule_result = self.analyze_cumulus_result(rule_creation_result) - if create_rule_err is not None: - return create_rule_err - # validation_result = pystac.Collection.from_dict(self.__request_body).validate() - # cumulus_collection_query = CollectionsQuery('', '') - # - # collection_transformer = CollectionTransformer(self.__report_to_ems) - # cumulus_collection_doc = collection_transformer.from_stac(self.__request_body) - # self.__provider_id = self.__provider_id if collection_transformer.output_provider is None else collection_transformer.output_provider - except Exception as e: - LOGGER.exception('error while creating new collection in Cumulus') - return { - 'statusCode': 500, - 'body': { - 'message': f'error while creating new collection in Cumulus. check details', - 'details': str(e) - } - } - LOGGER.info(f'creation_result: {creation_result}') - return { - 'statusCode': 200, - 'body': { - 'message': creation_result - } - } - - def start(self, current_url: URL, bearer_token: str): - LOGGER.debug(f'request body: {self.__request_body}') - validation_result = pystac.Collection.from_dict(self.__request_body).validate() - if not isinstance(validation_result, list): - LOGGER.error(f'request body is not valid STAC collection: {validation_result}') - return { - 'statusCode': 500, - 'body': {'message': f'request body is not valid STAC Collection schema. check details', - 'details': validation_result} - } - actual_path = current_url.path - actual_path = actual_path if actual_path.endswith('/') else f'{actual_path}/' - actual_path = f'{actual_path}actual' - LOGGER.info(f'sanity_check') - - actual_event = { - 'resource': actual_path, - 'path': actual_path, - 'httpMethod': 'POST', - 'headers': { - 'Authorization': bearer_token, - 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', - 'Host': current_url.hostname, 'User-Agent': 'python-requests/2.28.2', - 'X-Amzn-Trace-Id': 'Root=1-64a66e90-6fa8b7a64449014639d4f5b4', 'X-Forwarded-For': '44.236.15.58', - 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https'}, - 'multiValueHeaders': { - 'Accept': ['*/*'], 'Accept-Encoding': ['gzip, deflate'], 'Authorization': [bearer_token], - 'Host': [current_url.hostname], 'User-Agent': ['python-requests/2.28.2'], - 'X-Amzn-Trace-Id': ['Root=1-64a66e90-6fa8b7a64449014639d4f5b4'], - 'X-Forwarded-For': ['127.0.0.1'], 'X-Forwarded-Port': ['443'], 'X-Forwarded-Proto': ['https'] - }, - 'queryStringParameters': {}, - 'multiValueQueryStringParameters': {}, - 'pathParameters': {}, - 'stageVariables': None, - 'requestContext': { - 'resourceId': '', - 'authorizer': {'principalId': '', 'integrationLatency': 0}, - 'resourcePath': actual_path, 'httpMethod': 'POST', - 'extendedRequestId': '', 'requestTime': '', - 'path': actual_path, 'accountId': '', - 'protocol': 'HTTP/1.1', 'stage': '', 'domainPrefix': '', 'requestTimeEpoch': 0, - 'requestId': '', - 'identity': { - 'cognitoIdentityPoolId': None, 'accountId': None, 'cognitoIdentityId': None, 'caller': None, - 'sourceIp': '127.0.0.1', 'principalOrgId': None, 'accessKey': None, 'cognitoAuthenticationType': None, - 'cognitoAuthenticationProvider': None, 'userArn': None, 'userAgent': 'python-requests/2.28.2', 'user': None - }, - 'domainName': current_url.hostname, 'apiId': '' - }, - 'body': json.dumps(self.__request_body), - 'isBase64Encoded': False - } - LOGGER.info(f'actual_event: {actual_event}') - response = AwsLambda().invoke_function( - function_name=self.__collection_creation_lambda_name, - payload=actual_event, - ) - LOGGER.debug(f'async function started: {response}') - return { - 'statusCode': 202, - 'body': json.dumps({ - 'message': 'processing' - }) - } \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_query.py b/cumulus_lambda_functions/uds_api/dapa/collections_dapa_query.py deleted file mode 100644 index 55696aa7..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/collections_dapa_query.py +++ /dev/null @@ -1,110 +0,0 @@ -import json -import os - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from cumulus_lambda_functions.cumulus_wrapper.query_collections import CollectionsQuery -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class CollectionDapaQuery: - max_limit = 50 - - def __init__(self, collection_id, limit, offset, pagination_links, base_url): - self.__base_url = base_url - self.__pagination_links = pagination_links - self.__collection_id = collection_id - page_number = (offset // limit) + 1 - if 'CUMULUS_LAMBDA_PREFIX' not in os.environ: - raise EnvironmentError('missing key: CUMULUS_LAMBDA_PREFIX') - - self.__cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX') - self.__cumulus = CollectionsQuery('https://na/dev', 'NA') - self.__cumulus.with_limit(limit) - LOGGER.debug(f'collection_id: {collection_id}') - if collection_id is not None: - if isinstance(collection_id, str): - self.__cumulus.with_collection_id(collection_id) - else: - self.__cumulus.with_collections(collection_id) - self.__cumulus.with_page_number(page_number) - - def __get_size(self): - try: - cumulus_size = self.__cumulus.get_size(self.__cumulus_lambda_prefix) - except: - LOGGER.exception(f'cannot get cumulus_size') - cumulus_size = {'total_size': -1} - return cumulus_size - - def get_collection(self): - """ - A method to retrieve a single collection. - :return: - """ - if self.__collection_id is None or self.__collection_id == '': - return { - 'statusCode': 500, - 'body': {'message': f'missing or invalid collection ID: {self.__collection_id}'} - } - try: - cumulus_result = self.__cumulus.query_direct_to_private_api(self.__cumulus_lambda_prefix, self.__base_url) - if 'server_error' in cumulus_result: - return { - 'statusCode': 500, - 'body': {'message': cumulus_result['server_error']} - } - if 'client_error' in cumulus_result: - return { - 'statusCode': 400, - 'body': {'message': cumulus_result['client_error']} - } - if len(cumulus_result['results']) != 1: - return { - 'statusCode': 500, - 'body': {'message': f'cannot determine exact collection. returning length: {len(cumulus_result["results"])}'} - } - - return { - 'statusCode': 200, - 'body': cumulus_result['results'][0] - } - except Exception as e: - LOGGER.exception(f'unexpected error') - return { - 'statusCode': 500, - 'body': {'message': f'unpredicted error: {str(e)}'} - } - return - - def start(self): - try: - cumulus_result = self.__cumulus.query_direct_to_private_api(self.__cumulus_lambda_prefix, self.__base_url) - if 'server_error' in cumulus_result: - return { - 'statusCode': 500, - 'body': {'message': cumulus_result['server_error']} - } - if 'client_error' in cumulus_result: - return { - 'statusCode': 400, - 'body': {'message': cumulus_result['client_error']} - } - cumulus_size = self.__get_size() - return { - 'statusCode': 200, - 'body': { - 'numberMatched': cumulus_size['total_size'], - 'numberReturned': len(cumulus_result['results']), - 'stac_version': '1.0.0', - 'type': 'FeatureCollection', - 'links': self.__pagination_links, - 'features': cumulus_result['results'], - } - } - except Exception as e: - LOGGER.exception(f'unexpected error') - return { - 'statusCode': 500, - 'body': {'message': f'unpredicted error: {str(e)}'} - } diff --git a/cumulus_lambda_functions/uds_api/dapa/daac_archive_crud.py b/cumulus_lambda_functions/uds_api/dapa/daac_archive_crud.py deleted file mode 100644 index b5f54412..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/daac_archive_crud.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -from typing import Optional - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from pydantic import BaseModel - -from cumulus_lambda_functions.lib.uds_db.archive_index import UdsArchiveConfigIndex -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class ArchivingTypesModel(BaseModel): - data_type: str - file_extension: Optional[list[str]] = [] - - -class DaacUpdateModel(BaseModel): - daac_collection_id: str - daac_provider: Optional[str] = None - daac_data_version: Optional[str] = None - daac_sns_topic_arn: Optional[str] = None - daac_role_arn: Optional[str] = None - daac_role_session_name: Optional[str] = None - archiving_types: Optional[list[ArchivingTypesModel]] = None - - -class DaacAddModel(BaseModel): - daac_collection_id: str - daac_provider: Optional[str] = None - daac_data_version: str - daac_sns_topic_arn: str - daac_role_arn: str - daac_role_session_name: str - archiving_types: Optional[list[ArchivingTypesModel]] = [] - - -class DaacDeleteModel(BaseModel): - daac_collection_id: str - - -class DaacArchiveCrud: - def __init__(self, authorization_info, collection_id, request_body): - self.__daac_sns_topic_arn = os.getenv('DAAC_SNS_TOPIC_ARN') - self.__request_body = request_body - self.__collection_id = collection_id - self.__authorization_info = authorization_info - required_env = ['ES_URL', 'ADMIN_COMMA_SEP_GROUPS'] - if not all([k in os.environ for k in required_env]): - raise EnvironmentError(f'one or more missing env: {required_env}') - self.__es_url = os.getenv('ES_URL') - self.__es_port = int(os.getenv('ES_PORT', '443')) - self.__daac_config = UdsArchiveConfigIndex(self.__es_url, self.__es_port, os.getenv('ES_TYPE', 'AWS'), os.getenv('ES_USE_SSL', 'TRUE').strip() is True) - collection_identifier = UdsCollections.decode_identifier(collection_id) - self.__daac_config.set_tenant_venue(collection_identifier.tenant, collection_identifier.venue) - - def delete_config(self): - try: - result = self.__daac_config.delete_config(self.__collection_id, self.__request_body['daac_collection_id']) - except Exception as e: - LOGGER.exception(f'error during add config: {self.__request_body}') - return { - 'statusCode': 500, - 'body': {'message': f'error during operation: {e}'} - } - return { - 'statusCode': 200, - 'body': {'message': 'deleted'} - } - - def update_config(self): - try: - current_result = self.__daac_config.get_config(self.__collection_id, None, self.__request_body['daac_collection_id']) - if len(current_result) != 1: - return { - 'statusCode': 500, - 'body': {'message': f'Invalid current result for the update: missing or duplicate results: {current_result}'} - } - updating_dict = { - **{k: v for k, v in self.__request_body.items() if v is not None}, - 'ss_username': self.__authorization_info['username'], - 'collection': self.__collection_id, - } - result = self.__daac_config.update_config(updating_dict) - except Exception as e: - LOGGER.exception(f'error during update config: {self.__request_body}') - return { - 'statusCode': 500, - 'body': {'message': f'error during operation: {e}'} - } - - return { - 'statusCode': 200, - 'body': {'message': 'updated'} - } - - def add_new_config(self): - try: - current_result = self.__daac_config.get_config(self.__collection_id, None, - self.__request_body['daac_collection_id']) - if len(current_result) > 0: - return { - 'statusCode': 500, - 'body': { - 'message': f'Already have config for specified daac: {current_result}'} - } - - ingesting_dict = { - **{k: v for k, v in self.__request_body.items() if v is not None}, - 'ss_username': self.__authorization_info['username'], - 'collection': self.__collection_id, - } - result = self.__daac_config.add_new_config(ingesting_dict) - except Exception as e: - LOGGER.exception(f'error during add config: {self.__request_body}') - return { - 'statusCode': 500, - 'body': {'message': f'error during operation: {e}'} - } - return { - 'statusCode': 200, - 'body': {'message': 'inserted'} - } - - def get_config(self): - try: - result = self.__daac_config.get_config(self.__collection_id) - except Exception as e: - LOGGER.exception(f'error during get config: {self.__request_body}') - return { - 'statusCode': 500, - 'body': {'uds_daac_sns_arn': self.__daac_sns_topic_arn, 'message': f'error during operation: {e}'} - } - return { - 'statusCode': 200, - 'body': { - 'uds_daac_sns_arn': self.__daac_sns_topic_arn, - 'configs': result - } - } diff --git a/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query.py b/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query.py deleted file mode 100644 index 20c4570c..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query.py +++ /dev/null @@ -1,231 +0,0 @@ -import json -import os - -from mdps_ds_lib.lib.cumulus_stac.item_transformer import ItemTransformer -from cumulus_lambda_functions.lib.cql_parser import CqlParser - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from mdps_ds_lib.lib.utils.json_validator import JsonValidator - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from cumulus_lambda_functions.cumulus_wrapper.query_granules import GranulesQuery -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class GranulesDapaQuery: - def __init__(self, collection_id, limit, offset, datetime, filter_input, pagination_links): - self.__pagination_links = pagination_links - self.__limit = limit - self.__offset = offset - self.__filter_input = filter_input - page_number = (offset // limit) + 1 - if 'CUMULUS_LAMBDA_PREFIX' not in os.environ: - raise EnvironmentError('missing key: CUMULUS_LAMBDA_PREFIX') - self.__granules_index = GranulesDbIndex() - self.__cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX') - self.__cumulus = GranulesQuery('https://na/dev', 'NA') - self.__cumulus.with_limit(limit) - self.__cumulus.with_page_number(page_number) - self.__cumulus.with_collection_id(collection_id) - self.__collection_id = collection_id - self.__get_time_range(datetime) - self.__get_filter(filter_input) - self.__es_granules_result = None # this is where Elasticsearch granules result is stored - - def __custom_metadata_query(self): - if self.__filter_input is None: - return self - LOGGER.debug(f'filter_input: {self.__filter_input}') - dsl_query = CqlParser().transform(self.__filter_input) - LOGGER.debug(f'CqlParser result: {dsl_query}') - custom_metadata_query_dsl = { - 'from': self.__offset, - 'size': self.__limit, - 'query': { - 'bool': { - 'must': [ - - dsl_query, - ] - } - } - } - - LOGGER.debug(f'custom_metadata_query_dsl: {custom_metadata_query_dsl}') - collection_identifier = UdsCollections.decode_identifier(self.__collection_id) - LOGGER.debug(f'custom_metadata_query_dsl: {custom_metadata_query_dsl}') - custom_metadata_result = GranulesDbIndex().dsl_search(collection_identifier.tenant, collection_identifier.venue, - custom_metadata_query_dsl) - LOGGER.debug(f'custom_metadata_result: {custom_metadata_result}') - custom_metadata_result = [k['_source'] for k in custom_metadata_result['hits']['hits']] - self.__es_granules_result = {k['granule_id']: k for k in custom_metadata_result} - return self - - - def __get_time_range(self, datetime: str): - if datetime is None: - return self - if '/' not in datetime: - self.__cumulus.with_time(datetime) - return self - split_time_range = [k.strip() for k in datetime.split('/')] - if split_time_range[0] == '..': - self.__cumulus.with_time_to(split_time_range[1]) - return - if split_time_range[1] == '..': - self.__cumulus.with_time_from(split_time_range[0]) - return - self.__cumulus.with_time_range(split_time_range[0], split_time_range[1]) - return self - - def __get_filter(self, filter_input: str): - """ - https://portal.ogc.org/files/96288#rc_filter - https://portal.ogc.org/files/96288#simple-cql_comparison-predicates - - { "eq": [ { "property": "city" }, "Toronto" ] } - - { - "like": [ - { "property": "name" }, - "Smith." - ], - "singleChar": ".", - "nocase": true - } - -{ - "in": { - "value": { "property": "cityName" }, - "list": [ "Toronto", "Franfurt", "Tokyo", "New York" ], - "nocase": false - } -} - :return: - """ - if filter_input is None: - return self - filter_event = json.loads(filter_input) - if 'in' not in filter_event: - return self - schema = { - "type": { - "required": ["in"], - "properties": { - "in": { - "type": "object", - "required": ["value", "list"], - "properties": { - "value": { - "type": "object", - "required": ["property"], - "properties": { - "property": {"type": "string"} - } - }, - "list": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - } - } - } - } - } - filter_event_validator_result = JsonValidator(schema).validate(filter_event) - if filter_event_validator_result is not None: - LOGGER.error(f'invalid event: {filter_event_validator_result}. event: {filter_event}') - return self - search_key = filter_event['in']['value']['property'] - search_values = filter_event['in']['value']['list'] - self.__cumulus.with_filter(search_key, search_values) - return self - - def __get_size(self): - try: - cumulus_size = self.__cumulus.get_size(self.__cumulus_lambda_prefix) - except: - LOGGER.exception(f'cannot get cumulus_size') - cumulus_size = {'total_size': -1} - return cumulus_size - - def __get_custom_metadata(self, cumulus_result) -> dict: - custom_meta_query_conditions = [{ - 'bool': { - 'must': [ # TODO split if array is more than 1024 - {'term': {'collection_id': self.__collection_id}}, - {'term': {'granule_id': k['granuleId']}}, - ] - } - } for k in cumulus_result['results']] - custom_metadata_query_dsl = { - '_source': { - 'exclude': ['collection_id'] - }, - 'sort': [{'granule_id': {'order': 'ASC'}}], - 'query': { - 'bool': { - 'should': custom_meta_query_conditions - } - } - } - collection_identifier = UdsCollections.decode_identifier(self.__collection_id) - LOGGER.debug(f'custom_metadata_query_dsl: {custom_metadata_query_dsl}') - custom_metadata_result = GranulesDbIndex().dsl_search(collection_identifier.tenant, collection_identifier.venue, custom_metadata_query_dsl) - LOGGER.debug(f'custom_metadata_result: {custom_metadata_result}') - custom_metadata_result = [k['_source'] for k in custom_metadata_result['hits']['hits']] - custom_metadata_result = {k['granule_id']: k for k in custom_metadata_result} - return custom_metadata_result - - def start(self): - try: - self.__custom_metadata_query() - if self.__es_granules_result is not None: - # already queried custom metadata. - # just need to find those granule ids from Cumulus. - self.__get_filter('granules_id', [k for k in self.__es_granules_result.keys()]) - cumulus_result = self.__cumulus.query_direct_to_private_api(self.__cumulus_lambda_prefix, False) - if 'server_error' in cumulus_result: - return { - 'statusCode': 500, - 'body': {'message': cumulus_result['server_error']} - } - if 'client_error' in cumulus_result: - return { - 'statusCode': 400, - 'body': {'message': cumulus_result['client_error']} - } - cumulus_size = self.__get_size() - LOGGER.debug(f'cumulus_result: {cumulus_result}') - custom_metadata_result = self.__get_custom_metadata(cumulus_result) - main_result_dict = {k['granuleId']: k for k in cumulus_result['results']} - for k, v in main_result_dict.items(): - if k in custom_metadata_result: - if 'granule_id' in custom_metadata_result[k]: - custom_metadata_result[k].pop('granule_id') - v['custom_metadata'] = custom_metadata_result[k] - combined_cumulus_result = [ItemTransformer().to_stac(k) for k in main_result_dict.values()] - return { - 'statusCode': 200, - 'body': { - 'numberMatched': cumulus_size['total_size'], - 'numberReturned': len(cumulus_result['results']), - 'stac_version': '1.0.0', - 'type': 'FeatureCollection', # TODO correct name? - 'links': self.__pagination_links, - 'features': combined_cumulus_result - } - } - except Exception as e: - LOGGER.exception(f'unexpected error') - return { - 'statusCode': 500, - 'body': {'message': f'unpredicted error: {str(e)}'} - } diff --git a/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query_es.py b/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query_es.py deleted file mode 100644 index 345674cd..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/granules_dapa_query_es.py +++ /dev/null @@ -1,285 +0,0 @@ -import json -import os -from copy import deepcopy - -from mdps_ds_lib.lib.aws.aws_lambda import AwsLambda -from pystac import Link -from starlette.datastructures import URL - -from cumulus_lambda_functions.daac_archiver.daac_archiver_logic import DaacArchiverLogic -from cumulus_lambda_functions.granules_to_es.granules_index_mapping import GranulesIndexMapping -from cumulus_lambda_functions.uds_api.dapa.pagination_links_generator import PaginationLinksGenerator -from mdps_ds_lib.lib.aws.es_middleware import ESMiddleware -from cumulus_lambda_functions.lib.cql_parser import CqlParser -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class GranulesDapaQueryEs: - def __init__(self, collection_id, limit, offset, input_datetime, filter_input, pagination_link_obj: PaginationLinksGenerator, base_url, bbox=None, sort_by=None): - self.__collection_cnm_lambda_name = os.environ.get('COLLECTION_CREATION_LAMBDA_NAME', '').strip() - self.__pagination_link_obj = pagination_link_obj - self.__input_datetime = input_datetime - self.__collection_id = collection_id - self.__limit = limit - self.__offset = offset - self.__base_url = base_url - self.__filter_input = filter_input - self.__granules_index = GranulesDbIndex() - self.__bbox = bbox - self.__sort_by = sort_by - - def get_sorting_arguments(self): - if self.__sort_by is None or self.__sort_by == '': - return [ - {'properties.datetime': {'order': 'desc'}}, - {'id': {'order': 'asc'}} - ] - sort_keys = [k.strip() for k in self.__sort_by.split(',')] - sort_key_set = set() - sort_keys_dsl = [] - for each_key in sort_keys: - if each_key.startswith('+'): - current_sort_key = each_key[1:] - current_sort_dict = {current_sort_key: {'order': 'asc'}} - elif each_key.startswith('-'): - current_sort_key = each_key[1:] - current_sort_dict = {current_sort_key: {'order': 'desc'}} - else: - current_sort_key = each_key - current_sort_dict = {current_sort_key: {'order': 'asc'}} - if current_sort_key not in sort_key_set: - sort_keys_dsl.append(current_sort_dict) - sort_key_set.add(current_sort_key) - if 'properties.datetime' not in sort_key_set: - sort_keys_dsl.append({'properties.datetime': {'order': 'desc'}}) - if 'id' not in sort_key_set: - sort_keys_dsl.append({'id': {'order': 'asc'}}) - return sort_keys_dsl - - def __generate_es_dsl(self): - query_terms = [] - if not self.__collection_id.endswith(':*'): - query_terms.append({'term': {'collection': {'value': self.__collection_id}}}) - query_terms.extend(self.__get_time_range_terms()) - if self.__bbox is not None: - query_terms.append({ - "geo_shape": { - "bbox": { - "shape": self.__granules_index.to_es_bbox(self.__bbox), - "relation": "intersects" - } - } - }) - if self.__filter_input is not None: - query_terms.append(CqlParser('properties').transform(self.__filter_input)) - query_dsl = { - 'track_total_hits': self.__offset is None, - 'size': self.__limit, - # "collapse": {"field": "id"}, - 'sort': self.get_sorting_arguments(), - 'query': { - 'bool': { - 'must': query_terms - } - }, - } - if self.__offset is not None: - query_dsl['search_after'] = [k.strip() for k in self.__offset.split(',')] - LOGGER.debug(f'query_dsl: {query_dsl}') - return query_dsl - - def __get_time_range_terms(self): - if self.__input_datetime is None: - return [] - if '/' not in self.__input_datetime: - return [ - {'range': {'properties.start_datetime': {'lte': self.__input_datetime}}}, - {'range': {'properties.end_datetime': {'gte': self.__input_datetime}}}, - ] - split_time_range = [k.strip() for k in self.__input_datetime.split('/')] - if split_time_range[0] == '..': - return [ - {'range': {'properties.end_datetime': {'gte': split_time_range[1]}}}, - ] - if split_time_range[1] == '..': - return [ - {'range': {'properties.start_datetime': {'lte': split_time_range[0]}}}, - ] - return [ - {'range': {'properties.start_datetime': {'lte': split_time_range[1]}}}, - {'range': {'properties.end_datetime': {'gte': split_time_range[0]}}}, - ] - - def __create_pagination_links(self, page_marker_str): - if self.__pagination_link_obj is None: - return [] - new_queries = deepcopy(self.__pagination_link_obj.org_query_params) - new_queries['limit'] = int(new_queries['limit'] if 'limit' in new_queries else self.__limit) - current_page = f"{self.__pagination_link_obj.requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}" - pagination_links = [ - {'rel': 'self', 'href': current_page}, - {'rel': 'root', 'href': self.__pagination_link_obj.base_url}, - # {'rel': 'prev', 'href': self.__get_prev_page()}, - ] - new_queries = deepcopy(self.__pagination_link_obj.org_query_params) - limit = int(new_queries['limit'] if 'limit' in new_queries else self.__limit) - if limit > 0 and page_marker_str != '': - new_queries['limit'] = limit - new_queries['offset'] = page_marker_str - pagination_links.append({'rel': 'next', 'href': f"{self.__pagination_link_obj.requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}"}) - return pagination_links - - def archive_single_granule(self, granule_id): - granules_query_dsl = { - 'query': {'bool': {'must': [{ - 'term': {'id': granule_id} - }]}} - } - LOGGER.debug(f'granules_query_dsl: {granules_query_dsl}') - collection_identifier = UdsCollections.decode_identifier(self.__collection_id) - granules_query_result = GranulesDbIndex().dsl_search(collection_identifier.tenant, - collection_identifier.venue, - granules_query_dsl) - LOGGER.debug(f'granules_query_result: {granules_query_result}') - if len(granules_query_result['hits']['hits']) < 1: - raise ValueError(f'cannot find granule for : {granule_id}') - each_granules_query_result_stripped = granules_query_result['hits']['hits'][0]['_source'] - daac_archiver = DaacArchiverLogic() - cnm_response = daac_archiver.get_cnm_response_json_file(list(each_granules_query_result_stripped['assets'].values())[0], granule_id) - if cnm_response is None: - LOGGER.error(f'no CNM Response file. Not continuing to DAAC Archiving') - return - daac_archiver.send_to_daac_internal(cnm_response) - return - - def __restructure_each_granule_result(self, each_granules_query_result_stripped): - if 'event_time' in each_granules_query_result_stripped: - each_granules_query_result_stripped.pop('event_time') - if 'bbox' in each_granules_query_result_stripped: - each_granules_query_result_stripped['bbox'] = GranulesDbIndex.from_es_bbox(each_granules_query_result_stripped['bbox']) - for each_archiving_key in GranulesIndexMapping.archiving_keys: - if each_archiving_key in each_granules_query_result_stripped: - each_granules_query_result_stripped['properties'][each_archiving_key] = each_granules_query_result_stripped.pop(each_archiving_key) - return - - def get_single_granule(self, granule_id): - granules_query_dsl = { - 'size': 1, - 'sort': [{'id': {'order': 'asc'}}], - 'query': {'bool': {'must': [{ - 'term': {'id': granule_id}}, { - 'term': {'collection': self.__collection_id}, - }]}} - } - LOGGER.debug(f'granules_query_dsl: {granules_query_dsl}') - collection_identifier = UdsCollections.decode_identifier(self.__collection_id) - granules_query_result = GranulesDbIndex().dsl_search(collection_identifier.tenant, - collection_identifier.venue, - granules_query_dsl) - LOGGER.debug(f'granules_query_result: {granules_query_result}') - if len(granules_query_result['hits']['hits']) < 1: - return None - - each_granules_query_result_stripped = granules_query_result['hits']['hits'][0]['_source'] - self_link = Link(rel='self', target=f'{self.__base_url}/{WebServiceConstants.COLLECTIONS}/{self.__collection_id}/items/{each_granules_query_result_stripped["id"]}', media_type='application/json', title=each_granules_query_result_stripped["id"]).to_dict(False) - each_granules_query_result_stripped['links'].append(self_link) - self.__restructure_each_granule_result(each_granules_query_result_stripped) - return each_granules_query_result_stripped - - def delete_facade(self, current_url: URL, bearer_token: str): - actual_path = current_url.path - actual_path = actual_path if actual_path.endswith('/') else f'{actual_path}/' - actual_path = f'{actual_path}actual' - LOGGER.info(f'sanity_check') - - actual_event = { - 'resource': actual_path, - 'path': actual_path, - 'httpMethod': 'DELETE', - 'headers': { - 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Authorization': bearer_token, - 'Host': current_url.hostname, 'User-Agent': 'python-requests/2.28.2', - 'X-Amzn-Trace-Id': 'Root=1-64a66e90-6fa8b7a64449014639d4f5b4', 'X-Forwarded-For': '44.236.15.58', - 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https'}, - 'multiValueHeaders': { - 'Accept': ['*/*'], 'Accept-Encoding': ['gzip, deflate'], 'Authorization': [bearer_token], - 'Host': [current_url.hostname], 'User-Agent': ['python-requests/2.28.2'], - 'X-Amzn-Trace-Id': ['Root=1-64a66e90-6fa8b7a64449014639d4f5b4'], - 'X-Forwarded-For': ['127.0.0.1'], 'X-Forwarded-Port': ['443'], 'X-Forwarded-Proto': ['https'] - }, - 'queryStringParameters': {}, - 'multiValueQueryStringParameters': {}, - 'pathParameters': {}, - 'stageVariables': None, - 'requestContext': { - 'resourceId': '', - 'authorizer': {'principalId': '', 'integrationLatency': 0}, - 'resourcePath': actual_path, 'httpMethod': 'PUT', - 'extendedRequestId': '', 'requestTime': '', - 'path': actual_path, 'accountId': '', - 'protocol': 'HTTP/1.1', 'stage': '', 'domainPrefix': '', 'requestTimeEpoch': 0, - 'requestId': '', - 'identity': { - 'cognitoIdentityPoolId': None, 'accountId': None, 'cognitoIdentityId': None, 'caller': None, - 'sourceIp': '127.0.0.1', 'principalOrgId': None, 'accessKey': None, - 'cognitoAuthenticationType': None, - 'cognitoAuthenticationProvider': None, 'userArn': None, 'userAgent': 'python-requests/2.28.2', - 'user': None - }, - 'domainName': current_url.hostname, 'apiId': '' - }, - 'body': json.dumps({}), - 'isBase64Encoded': False - } - LOGGER.info(f'actual_event: {actual_event}') - response = AwsLambda().invoke_function( - function_name=self.__collection_cnm_lambda_name, - payload=actual_event, - ) - LOGGER.debug(f'async function started: {response}') - return { - 'statusCode': 202, - 'body': { - 'message': 'processing' - } - } - - def start(self): - try: - granules_query_dsl = self.__generate_es_dsl() - LOGGER.debug(f'granules_query_dsl: {granules_query_dsl}') - collection_identifier = UdsCollections.decode_identifier(self.__collection_id) - granules_query_result = GranulesDbIndex().dsl_search(collection_identifier.tenant, - collection_identifier.venue, - granules_query_dsl) - LOGGER.debug(f'granules_query_result: {granules_query_result}') - result_size = ESMiddleware.get_result_size(granules_query_result) - granules_query_result_stripped = [k['_source'] for k in granules_query_result['hits']['hits']] - for each_granules_query_result_stripped in granules_query_result_stripped: - self_link = Link(rel='self', target=f'{self.__base_url}/{WebServiceConstants.COLLECTIONS}/{self.__collection_id}/items/{each_granules_query_result_stripped["id"]}', media_type='application/json', title=each_granules_query_result_stripped["id"]).to_dict(False) - each_granules_query_result_stripped['links'].append(self_link) - self.__restructure_each_granule_result(each_granules_query_result_stripped) - - pagination_link = '' if len(granules_query_result['hits']['hits']) < 1 else ','.join([k if isinstance(k, str) else str(k) for k in granules_query_result['hits']['hits'][-1]['sort']]) - return { - 'statusCode': 200, - 'body': { - 'numberMatched': {'total_size': -1 if self.__offset is not None else result_size}, - 'numberReturned': len(granules_query_result['hits']['hits']), - 'stac_version': '1.0.0', - 'type': 'FeatureCollection', # TODO correct name? - 'links': self.__create_pagination_links(pagination_link), - 'features': granules_query_result_stripped - } - } - except Exception as e: - LOGGER.exception(f'unexpected error') - return { - 'statusCode': 500, - 'body': {'message': f'unpredicted error: {str(e)}'} - } diff --git a/cumulus_lambda_functions/uds_api/dapa/pagination_links_generator.py b/cumulus_lambda_functions/uds_api/dapa/pagination_links_generator.py deleted file mode 100644 index dbd4eeeb..00000000 --- a/cumulus_lambda_functions/uds_api/dapa/pagination_links_generator.py +++ /dev/null @@ -1,119 +0,0 @@ -import os -from copy import deepcopy - -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator -from fastapi import Request -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - - -class PaginationLinksGenerator: - def __init__(self, request: Request, custom_params: dict = {}): - self.__default_limit = 10 - self.__request = request - self.__org_query_params = {k: v for k, v in request.query_params.items()} - self.__org_query_params = {**self.__org_query_params, **custom_params} - self.__base_url = os.environ.get(WebServiceConstants.BASE_URL, f'{self.__request.url.scheme}://{self.__request.url.netloc}') - self.__base_url = self.__base_url[:-1] if self.__base_url.endswith('/') else self.__base_url - self.__base_url = self.__base_url if self.__base_url.startswith('http') else f'https://{self.__base_url}' - self.__requesting_base_url = f"{self.__base_url}{self.__request.url.path}" - - @property - def requesting_base_url(self): - return self.__requesting_base_url - - @requesting_base_url.setter - def requesting_base_url(self, val): - """ - :param val: - :return: None - """ - self.__requesting_base_url = val - return - - @property - def base_url(self): - return self.__base_url - - @base_url.setter - def base_url(self, val): - """ - :param val: - :return: None - """ - self.__base_url = val - return - - @property - def org_query_params(self): - return self.__org_query_params - - @org_query_params.setter - def org_query_params(self, val): - """ - :param val: - :return: None - """ - self.__org_query_params = val - return - - def __get_current_page(self): - try: - new_queries = deepcopy(self.__org_query_params) - limit = int(new_queries['limit'] if 'limit' in new_queries else self.__default_limit) - offset = int(new_queries['offset'] if 'offset' in new_queries else 0) - new_queries['limit'] = limit - new_queries['offset'] = offset - requesting_url = f"{self.__requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}" - except Exception as e: - LOGGER.exception(f'error while getting current page URL') - return f'unable to get current page URL, {str(e)}' - return requesting_url - - def __get_next_page(self): - try: - new_queries = deepcopy(self.__org_query_params) - limit = int(new_queries['limit'] if 'limit' in new_queries else self.__default_limit) - if limit == 0: - return '' - offset = int(new_queries['offset'] if 'offset' in new_queries else 0) - offset += limit - new_queries['limit'] = limit - new_queries['offset'] = offset - requesting_url = f"{self.__requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}" - except Exception as e: - LOGGER.exception(f'error while getting next page URL') - return f'unable to get next page URL, {str(e)}' - return requesting_url - - def __get_prev_page(self): - try: - new_queries = deepcopy(self.__org_query_params) - limit = int(new_queries['limit'] if 'limit' in new_queries else self.__default_limit) - if limit == 0: - return '' - offset = int(new_queries['offset'] if 'offset' in new_queries else 0) - offset -= limit - if offset < 0: - offset = 0 - new_queries['limit'] = limit - new_queries['offset'] = offset - requesting_url = f"{self.__requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}" - except Exception as e: - LOGGER.exception(f'error while getting previous page URL') - return f'unable to get previous page URL, {str(e)}' - return requesting_url - - def generate_pagination_links(self): - try: - pagination_links = [ - {'rel': 'self', 'href': self.__get_current_page()}, - {'rel': 'root', 'href': self.__base_url}, - {'rel': 'next', 'href': self.__get_next_page()}, - {'rel': 'prev', 'href': self.__get_prev_page()}, - ] - except Exception as e: - LOGGER.exception(f'error while generating pagination links') - return [{'message': f'error while generating pagination links: {str(e)}'}] - return pagination_links diff --git a/cumulus_lambda_functions/uds_api/granules_api.py b/cumulus_lambda_functions/uds_api/granules_api.py deleted file mode 100644 index 0e74887f..00000000 --- a/cumulus_lambda_functions/uds_api/granules_api.py +++ /dev/null @@ -1,328 +0,0 @@ -import json -import os -from time import sleep -from typing import Union, Optional - -from pydantic import BaseModel -from starlette.responses import Response, JSONResponse - -from cumulus_lambda_functions.cumulus_wrapper.query_granules import GranulesQuery - -from cumulus_lambda_functions.uds_api.dapa.granules_dapa_query_es import GranulesDapaQueryEs -from cumulus_lambda_functions.lib.uds_db.granules_db_index import GranulesDbIndex -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from fastapi import APIRouter, HTTPException, Request, Query, Path - -from cumulus_lambda_functions.uds_api.dapa.pagination_links_generator import PaginationLinksGenerator -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.COLLECTIONS}', - tags=["Granules CRUD API"], - responses={404: {"description": "Not found"}}, -) - -# https://docs.ogc.org/per/20-025r1.html#_get_collectionscollectionidvariables -@router.get("/{collection_id}/variables") -@router.get("/{collection_id}/variables/") -async def get_granules_dapa(request: Request, collection_id: str): - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - - try: - granules_db_index = GranulesDbIndex() - granule_index_mapping = granules_db_index.get_latest_index(collection_identifier.tenant, collection_identifier.venue) - # This is the response from the method - # {"unity_granule_main_project1694791693139_dev__v02":{"mappings":{"dynamic":"strict","properties":{"collection_id":{"type":"keyword"},"event_time":{"type":"long"},"granule_id":{"type":"keyword"},"last_updated":{"type":"long"},"tag":{"type":"keyword"}}}}} - # needs to drill down to properties - custom_metadata = granules_db_index.get_custom_metadata_fields(granule_index_mapping) - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - return custom_metadata - - -@router.get("/{collection_id}/items") -@router.get("/{collection_id}/items/") -async def get_granules_dapa(request: Request, collection_id: str=Path(description="Collection ID. To query across different collections, use '*'. Example: 'URN:NASA:UNITY:MY_TENANT:DEV:\\*'"), - limit: Union[int, None] = Query(10, description='Number of items in each page.'), - offset: Union[str, None] = Query(None, description='Pagination Item from current page to get the next page'), - datetime: Union[str, None] = Query(None, description='Example: 2018-02-12T23:20:50Z'), - filter: Union[str, None] = Query(None, description="OGC CQL filters: https://portal.ogc.org/files/96288#rc_cql-text -- Example: id in (g1,g2,g3) and tags::core = 'level-3' and (time1 < 34 or time1 > 14)"), - bbox: Union[str, None]=Query(None, description='Bounding box in minx,miny,maxx,maxy -- Example: bbox=12.3,0.3,14.4,2.3'), - sortby: Union[str, None]=Query(None, description='Sort the results based on the comma separated parameters, each sorting key can be started with + / - for ascending / descending order. missing operator is assumed "+". Example: sortby=+id,-properties.created'), - ): - # https://docs.ogc.org/DRAFTS/24-030.html#sortby-parameter - # https://docs.ogc.org/DRAFTS/24-030.html#_declaring_default_sort_order - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - - try: - pagination_links = PaginationLinksGenerator(request) - api_base_prefix = FastApiUtils.get_api_base_prefix() - bbox_array = [float(k) for k in bbox.split(',')] if bbox is not None else None - granules_dapa_query = GranulesDapaQueryEs(collection_id, limit, offset, datetime, filter, pagination_links, f'{pagination_links.base_url}/{api_base_prefix}', bbox_array, sortby) - granules_result = granules_dapa_query.start() - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if granules_result['statusCode'] == 200: - return granules_result['body'] - raise HTTPException(status_code=granules_result['statusCode'], detail=granules_result['body']) - - -@router.get("/{collection_id}/items/{granule_id}") -@router.get("/{collection_id}/items/{granule_id}/") -async def get_single_granule_dapa(request: Request, collection_id: str, granule_id: str): - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - api_base_prefix = FastApiUtils.get_api_base_prefix() - pg_link_generator = PaginationLinksGenerator(request) - granules_dapa_query = GranulesDapaQueryEs(collection_id, 1, None, None, filter, None, f'{pg_link_generator.base_url}/{api_base_prefix}') - granules_result = granules_dapa_query.get_single_granule(granule_id) - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - if granules_result is None: - raise HTTPException(status_code=404, detail={'message': f'no granule with id: {granule_id} in collection: {collection_id}'}) - return granules_result - -@router.delete("/{collection_id}/items/{granule_id}") -@router.delete("/{collection_id}/items/{granule_id}/") -async def delete_single_granule_dapa_actual(request: Request, collection_id: str, granule_id: str): - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.delete, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - LOGGER.debug(f'deleting granule: {granule_id}') - include_cumulus = os.getenv('CUMULUS_INCLUSION', 'TRUE').upper().strip() == 'TRUE' - if include_cumulus: - cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX') - cumulus = GranulesQuery('https://na/dev', 'NA') - cumulus.with_collection_id(collection_id) - cumulus_delete_result = cumulus.delete_entry(cumulus_lambda_prefix, granule_id) # TODO not sure it is correct granule ID - LOGGER.debug(f'cumulus_delete_result: {cumulus_delete_result}') - es_delete_result = GranulesDbIndex().delete_entry(collection_identifier.tenant, - collection_identifier.venue, - granule_id - ) - LOGGER.debug(f'es_delete_result: {es_delete_result}') - # es_delete_result = [Item.from_dict(k['_source']) for k in es_delete_result['hits']['hits']] - # if delete_files is False: - # LOGGER.debug(f'Not deleting files as it is set to false in the request') - # return {} - # s3 = AwsS3() - # for each_granule in es_delete_result: - # s3_urls = [v.href for k, v in each_granule.assets.items()] - # LOGGER.debug(f'deleting S3 for {each_granule.id} - s3_urls: {s3_urls}') - # delete_result = s3.delete_multiple(s3_urls=s3_urls) - # LOGGER.debug(f'delete_result for {each_granule.id} - delete_result: {delete_result}') - except Exception as e: - LOGGER.exception('failed during delete_single_granule_dapa_actual') - raise HTTPException(status_code=500, detail=str(e)) - return {} - - -class StacGranuleModel(BaseModel): - """ - "type": "Feature", - "stac_version": "1.0.0", - "id": "URN:NASA:AVIRIS:f240424t01:p00_r10", - "properties": { - "start_datetime": "24-04-24T20:37:00.000000", - "end_datetime": "24-04-24T20:50:00.000000", - "site_name": "x001(orthocorrected)", - "nasa_log": 232016.0, - "investigator": "Raymond Kokaly", - "comments": "x001 s-)n; LN2 refill 2042", - "site_info": "GEMx - RFLY02", - "datetime": "1970-01-01T00:00:00Z" - }, - "geometry": { - "type": "Point", - "coordinates": [ - 0.0, - 0.0 - ] - }, - "links": [], - "assets": { - "l1b": { - "href": "https://popo.jpl.nasa.gov/gemx/data_products/l1b/f240424t01p00r10rdn_g.tar.gz", - "title": "f240424t01p00r10rdn_g.tar.gz", - "description": "2024-10-08 15:16", - "file:size": 2362232012.8 - }, - "l2": { - "href": "https://popo.jpl.nasa.gov/gemx/data_products/l2/f240424t01p00r10rfl.tar.gz", - "title": "f240424t01p00r10rfl.tar.gz", - "description": "2024-10-18 08:22", - "file:size": 4187593113.6 - }, - "quicklook": { - "href": "http://aviris.jpl.nasa.gov/ql/24qlook/f240424t01p00r10_geo.jpeg", - "title": "f240424t01p00r10_geo.jpeg", - "description": "Quicklook Link" - } - }, - "bbox": [ - 32.5, - -114.314407, - 33.5, - -114.314407 - ], - "stac_extensions": [ - "https://stac-extensions.github.io/file/v2.1.0/schema.json" - ], - "collection": "URN:NASA:AVIRIS:f240424t01" - """ - stac_extensions: list[str] - collection: str - bbox: list[float] - assets: dict - links: list - geometry: dict - properties: dict - id: str - stac_version: str - type: str - -@router.put("/{collection_id}/items/{granule_id}") -@router.put("/{collection_id}/items/{granule_id}/") -async def add_single_granule_dapa(request: Request, collection_id: str, granule_id: str, new_granule: StacGranuleModel, response: Response): - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')), - use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True, - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - LOGGER.debug(f'adding granule: {granule_id}') - new_granule = new_granule.model_dump() - include_cumulus = os.getenv('CUMULUS_INCLUSION', 'TRUE').upper().strip() == 'TRUE' - if include_cumulus: - cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX') - cumulus = GranulesQuery('https://na/dev', 'NA') - cumulus.with_collection_id(collection_id) - raise NotImplementedError(f'Please implement to convert stac into cumulus granule') - cumulus_add_result = cumulus.add_entry(cumulus_lambda_prefix, {}) # TODO not sure it is correct granule ID - LOGGER.debug(f'cumulus_add_result: {cumulus_add_result}') - if 'bbox' in new_granule: - new_granule['bbox'] = GranulesDbIndex.to_es_bbox(new_granule['bbox']) - collection_identifier = UdsCollections.decode_identifier(collection_id) - LOGGER.debug(f'new_granule: {new_granule}') - es_add_result = GranulesDbIndex().add_entry(collection_identifier.tenant, - collection_identifier.venue, - new_granule, - granule_id - ) - LOGGER.debug(f'es_add_result: {es_add_result}') - except Exception as e: - LOGGER.exception('failed during add_single_granule_dapa') - raise HTTPException(status_code=500, detail=str(e)) - return {} - -# @router.delete("/{collection_id}/items/{granule_id}") -# @router.delete("/{collection_id}/items/{granule_id}/") -# async def delete_single_granule_dapa_facade(request: Request, collection_id: str, granule_id: str, response: Response, response_class=JSONResponse): -# authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ -# .get_instance(UDSAuthorizerFactory.cognito, -# es_url=os.getenv('ES_URL'), -# es_port=int(os.getenv('ES_PORT', '443')) -# ) -# auth_info = FastApiUtils.get_authorization_info(request) -# collection_identifier = UdsCollections.decode_identifier(collection_id) -# if not authorizer.is_authorized_for_collection(DBConstants.delete, collection_id, -# auth_info['ldap_groups'], -# collection_identifier.tenant, -# collection_identifier.venue): -# LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') -# raise HTTPException(status_code=403, detail=json.dumps({ -# 'message': 'not authorized to execute this action' -# })) -# try: -# LOGGER.debug(f'deleting granule: {granule_id}') -# granules_dapa_query = GranulesDapaQueryEs(collection_id, -1, -1, None, None, None, '') -# delete_prep_result = granules_dapa_query.delete_facade(request.url, request.headers.get('Authorization', '')) -# except Exception as e: -# LOGGER.exception('failed during delete_single_granule_dapa') -# raise HTTPException(status_code=500, detail=str(e)) -# if delete_prep_result['statusCode'] < 300: -# response.status_code = delete_prep_result['statusCode'] -# return delete_prep_result['body'] -# raise HTTPException(status_code=delete_prep_result['statusCode'], detail=delete_prep_result['body']) diff --git a/cumulus_lambda_functions/uds_api/granules_archive_api.py b/cumulus_lambda_functions/uds_api/granules_archive_api.py deleted file mode 100644 index 39fc302e..00000000 --- a/cumulus_lambda_functions/uds_api/granules_archive_api.py +++ /dev/null @@ -1,172 +0,0 @@ -import json -import os - -from cumulus_lambda_functions.uds_api.dapa.granules_dapa_query_es import GranulesDapaQueryEs -from cumulus_lambda_functions.uds_api.dapa.pagination_links_generator import PaginationLinksGenerator - -from cumulus_lambda_functions.uds_api.dapa.daac_archive_crud import DaacArchiveCrud, DaacDeleteModel, DaacAddModel, \ - DaacUpdateModel - -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract - -from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory - -from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants - -from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from fastapi import APIRouter, HTTPException, Request - -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.COLLECTIONS}', - tags=["Granules Archive CRUD API"], - responses={404: {"description": "Not found"}}, -) - -@router.put("/{collection_id}/archive") -@router.put("/{collection_id}/archive/") -async def dapa_archive_add_config(request: Request, collection_id: str, new_body: DaacAddModel): - LOGGER.debug(f'started dapa_archive_add_config. {new_body.model_dump()}') - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - if '___' not in collection_identifier.id: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': f'missing version in collection ID. collection_id: {collection_id}' - })) - daac_crud = DaacArchiveCrud(auth_info, collection_id, new_body.model_dump()) - add_result = daac_crud.add_new_config() - if add_result['statusCode'] == 200: - return add_result['body'] - raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) - -@router.post("/{collection_id}/archive") -@router.post("/{collection_id}/archive/") -async def dapa_archive_update_config(request: Request, collection_id: str, new_body: DaacUpdateModel): - LOGGER.debug(f'started dapa_archive_add_config. {new_body.model_dump()}') - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - if '___' not in collection_identifier.id: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': f'missing version in collection ID. collection_id: {collection_id}' - })) - daac_crud = DaacArchiveCrud(auth_info, collection_id, new_body.model_dump()) - add_result = daac_crud.update_config() - if add_result['statusCode'] == 200: - return add_result['body'] - raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) - -@router.delete("/{collection_id}/archive") -@router.delete("/{collection_id}/archive/") -async def dapa_archive_delete_config(request: Request, collection_id: str, new_body: DaacDeleteModel): - LOGGER.debug(f'started dapa_archive_add_config. {new_body.model_dump()}') - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - if '___' not in collection_identifier.id: - raise HTTPException(status_code=500, detail=json.dumps({ - 'message': f'missing version in collection ID. collection_id: {collection_id}' - })) - daac_crud = DaacArchiveCrud(auth_info, collection_id, new_body.model_dump()) - add_result = daac_crud.delete_config() - if add_result['statusCode'] == 200: - return add_result['body'] - raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) - -@router.get("/{collection_id}/archive") -@router.get("/{collection_id}/archive/") -async def dapa_archive_get_config(request: Request, collection_id: str): - # TODO return UDS SNS to accept DAAC messages here - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - daac_crud = DaacArchiveCrud(auth_info, collection_id, {}) - add_result = daac_crud.get_config() - if add_result['statusCode'] == 200: - return add_result['body'] - raise HTTPException(status_code=add_result['statusCode'], detail=add_result['body']) - -@router.put("/{collection_id}/archive/{granule_id}") -@router.put("/{collection_id}/archive/{granule_id}/") -async def archive_single_granule_dapa(request: Request, collection_id: str, granule_id: str): - authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \ - .get_instance(UDSAuthorizerFactory.cognito, - es_url=os.getenv('ES_URL'), - es_port=int(os.getenv('ES_PORT', '443')) - ) - auth_info = FastApiUtils.get_authorization_info(request) - collection_identifier = UdsCollections.decode_identifier(collection_id) - if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id, - auth_info['ldap_groups'], - collection_identifier.tenant, - collection_identifier.venue): - LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}') - raise HTTPException(status_code=403, detail=json.dumps({ - 'message': 'not authorized to execute this action' - })) - try: - api_base_prefix = FastApiUtils.get_api_base_prefix() - pg_link_generator = PaginationLinksGenerator(request) - granules_dapa_query = GranulesDapaQueryEs(collection_id, 1, None, None, filter, None, f'{pg_link_generator.base_url}/{api_base_prefix}') - granules_result = granules_dapa_query.archive_single_granule(granule_id) - except Exception as e: - LOGGER.exception('failed during get_granules_dapa') - raise HTTPException(status_code=500, detail=str(e)) - return {'message': 'archive initiated'} diff --git a/cumulus_lambda_functions/uds_api/misc_api.py b/cumulus_lambda_functions/uds_api/misc_api.py deleted file mode 100644 index ae1f1e47..00000000 --- a/cumulus_lambda_functions/uds_api/misc_api.py +++ /dev/null @@ -1,110 +0,0 @@ -import json -import os -from glob import glob -from time import time -from typing import Union - -from mdps_ds_lib.lib.utils.file_utils import FileUtils -from starlette.responses import Response, RedirectResponse -from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils - -from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator - -from fastapi import APIRouter, HTTPException, Request -from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants - - -LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env()) - -router = APIRouter( - prefix=f'/{WebServiceConstants.MISC}', - tags=["Miscellaneous API"], - responses={404: {"description": "Not found"}}, -) - - -@router.get(f'/catalog_list') -@router.get(f'/catalog_list/') -async def catalog_list(request: Request, response: Response): - """ - This is to list all catalogs for STAC Browser. - This doesn't require any authorization token. - :param request: - :param response: - :return: - """ - base_url = os.environ.get(WebServiceConstants.BASE_URL, f'{request.url.scheme}://{request.url.netloc}') - base_url = base_url[:-1] if base_url.endswith('/') else base_url - base_url = base_url if base_url.startswith('http') else f'https://{base_url}' - api_base_prefix = FastApiUtils.get_api_base_prefix() - stac_browser_expecting_result = [{ - "id": 1, - "url": f'{base_url}/{api_base_prefix}/catalog', - "slug": "unity-ds", - "title": "Unity DS (Venue: TODO)", - "summary": "Unity DS collections & granules", - "access": "public", - "created": "2023-03-16T09:15:31.242Z", - "updated": "2023-03-16T09:15:31.242Z", - "isPrivate": False, - "isApi": False, - "accessInfo": None - }] - return stac_browser_expecting_result - - -@router.get(f'/stac_entry') -@router.get(f'/stac_entry/') -async def stac_entry(request: Request, response: Response): - """ - This is an API to start STAC Browser. - Optionally, it will add a required authorization cookie if available. - However, this endpoint should be called from a separate URL due to the infrastructure. - - How to re-load UCS - https://github.com/unity-sds/unity-data-services/issues/381#issuecomment-2201165672 - - :param request: - :param response: - :return: - """ - request_headers = dict(request.headers) - LOGGER.debug(f'stac_entry - request_headers: {request_headers}') - print(request_headers) - base_url = os.environ.get(WebServiceConstants.BASE_URL, f'{request.url.scheme}://{request.url.netloc}') - base_url = base_url[:-1] if base_url.endswith('/') else base_url - base_url = base_url if base_url.startswith('http') else f'https://{base_url}' - api_base_prefix = FastApiUtils.get_api_base_prefix() - ending_url = f'{WebServiceConstants.STAC_BROWSER}/' if str(request.url).endswith('/') else WebServiceConstants.STAC_BROWSER - redirect_response = RedirectResponse(f'/{api_base_prefix}/{ending_url}') - if 'oidc_access_token' in request_headers: - # TODO not sure cookie settings need to be stricter - redirect_response.set_cookie(key="unity_token", value=request_headers['oidc_access_token'], httponly=False, secure=False, samesite='strict') # missing , domain=base_url - redirect_response.set_cookie(key="test1", value=f"{time()}", httponly=False, secure=False, samesite='strict') # missing , domain=base_url - return redirect_response - - -@router.get(f'/version') -@router.get(f'/version/') -async def ds_version(request: Request, response: Response): - """ - This is to list all catalogs for STAC Browser. - This doesn't require any authorization token. - :param request: - :param response: - :return: - """ - version_details_unknown = { - 'version': 'unknown', - 'built': 'unknown' - } - if not FileUtils.file_exist('/var/task/ds_version.json'): - print(f'missing file : {[k for k in glob("/var/task/*.json")]}') - return version_details_unknown - version_details = FileUtils.read_json('/var/task/ds_version.json') - - version_details = { - **version_details_unknown, - **version_details, - } - return version_details diff --git a/cumulus_lambda_functions/uds_api/routes_api.py b/cumulus_lambda_functions/uds_api/routes_api.py deleted file mode 100644 index f04d5b3f..00000000 --- a/cumulus_lambda_functions/uds_api/routes_api.py +++ /dev/null @@ -1,21 +0,0 @@ -from fastapi import APIRouter - -from cumulus_lambda_functions.uds_api import collections_api, granules_api, auth_admin_api, system_admin_api, \ - custom_meta_admin_api, catalog_api, misc_api, granules_archive_api - -# from ideas_api.src.endpoints import job_endpoints -# from ideas_api.src.endpoints import process_endpoints -# from ideas_api.src.endpoints import setup_es - - -main_router = APIRouter(redirect_slashes=False) -# main_router.include_router(setup_es.router) -main_router.include_router(auth_admin_api.router) -main_router.include_router(system_admin_api.router) -main_router.include_router(collections_api.router) -main_router.include_router(catalog_api.router) -main_router.include_router(granules_api.router) -main_router.include_router(granules_archive_api.router) -main_router.include_router(custom_meta_admin_api.router) -main_router.include_router(misc_api.router) - diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/1228.1719e529.css b/cumulus_lambda_functions/uds_api/stac_browser/css/1228.1719e529.css deleted file mode 100644 index 9337167b..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/1228.1719e529.css +++ /dev/null @@ -1 +0,0 @@ -fieldset[disabled] .multiselect{pointer-events:none}.multiselect__spinner{position:absolute;right:1px;top:1px;width:40px;height:38px;background:#fff;display:block}.multiselect__spinner:after,.multiselect__spinner:before{position:absolute;content:"";top:50%;left:50%;margin:-8px 0 0 -8px;width:16px;height:16px;border-radius:100%;border:2px solid transparent;border-top-color:#41b883;box-shadow:0 0 0 1px transparent}.multiselect__spinner:before{animation:spinning 2.4s cubic-bezier(.41,.26,.2,.62);animation-iteration-count:infinite}.multiselect__spinner:after{animation:spinning 2.4s cubic-bezier(.51,.09,.21,.8);animation-iteration-count:infinite}.multiselect__loading-enter-active,.multiselect__loading-leave-active{transition:opacity .4s ease-in-out;opacity:1}.multiselect__loading-enter,.multiselect__loading-leave-active{opacity:0}.multiselect,.multiselect__input,.multiselect__single{font-family:inherit;font-size:16px;touch-action:manipulation}.multiselect{box-sizing:content-box;display:block;position:relative;width:100%;min-height:40px;text-align:left;color:#35495e}.multiselect *{box-sizing:border-box}.multiselect:focus{outline:none}.multiselect--disabled{background:#ededed;pointer-events:none;opacity:.6}.multiselect--active{z-index:50}.multiselect--active:not(.multiselect--above) .multiselect__current,.multiselect--active:not(.multiselect--above) .multiselect__input,.multiselect--active:not(.multiselect--above) .multiselect__tags{border-bottom-left-radius:0;border-bottom-right-radius:0}.multiselect--active .multiselect__select{transform:rotate(180deg)}.multiselect--above.multiselect--active .multiselect__current,.multiselect--above.multiselect--active .multiselect__input,.multiselect--above.multiselect--active .multiselect__tags{border-top-left-radius:0;border-top-right-radius:0}.multiselect__input,.multiselect__single{position:relative;display:inline-block;min-height:20px;line-height:20px;border:none;border-radius:5px;background:#fff;padding:0 0 0 5px;width:100%;transition:border .1s ease;box-sizing:border-box;margin-bottom:8px;vertical-align:top}.multiselect__input::-moz-placeholder{color:#35495e}.multiselect__input::placeholder{color:#35495e}.multiselect__tag~.multiselect__input,.multiselect__tag~.multiselect__single{width:auto}.multiselect__input:hover,.multiselect__single:hover{border-color:#cfcfcf}.multiselect__input:focus,.multiselect__single:focus{border-color:#a8a8a8;outline:none}.multiselect__single{padding-left:5px;margin-bottom:8px}.multiselect__tags-wrap{display:inline}.multiselect__tags{min-height:40px;display:block;padding:8px 40px 0 8px;border-radius:5px;border:1px solid #e8e8e8;background:#fff;font-size:14px}.multiselect__tag{position:relative;display:inline-block;padding:4px 26px 4px 10px;border-radius:5px;margin-right:10px;color:#fff;line-height:1;background:#41b883;margin-bottom:5px;white-space:nowrap;overflow:hidden;max-width:100%;text-overflow:ellipsis}.multiselect__tag-icon{cursor:pointer;margin-left:7px;position:absolute;right:0;top:0;bottom:0;font-weight:700;font-style:normal;width:22px;text-align:center;line-height:22px;transition:all .2s ease;border-radius:5px}.multiselect__tag-icon:after{content:"\D7";color:#266d4d;font-size:14px}.multiselect__tag-icon:focus,.multiselect__tag-icon:hover{background:#369a6e}.multiselect__tag-icon:focus:after,.multiselect__tag-icon:hover:after{color:#fff}.multiselect__current{min-height:40px;overflow:hidden;padding:8px 30px 0 12px;white-space:nowrap;border-radius:5px;border:1px solid #e8e8e8}.multiselect__current,.multiselect__select{line-height:16px;box-sizing:border-box;display:block;margin:0;text-decoration:none;cursor:pointer}.multiselect__select{position:absolute;width:40px;height:38px;right:1px;top:1px;padding:4px 8px;text-align:center;transition:transform .2s ease}.multiselect__select:before{position:relative;right:0;top:65%;color:#999;margin-top:4px;border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 0;content:""}.multiselect__placeholder{color:#adadad;display:inline-block;margin-bottom:10px;padding-top:2px}.multiselect--active .multiselect__placeholder{display:none}.multiselect__content-wrapper{position:absolute;display:block;background:#fff;width:100%;max-height:240px;overflow:auto;border:1px solid #e8e8e8;border-top:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;z-index:50;-webkit-overflow-scrolling:touch}.multiselect__content{list-style:none;display:inline-block;padding:0;margin:0;min-width:100%;vertical-align:top}.multiselect--above .multiselect__content-wrapper{bottom:100%;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom:none;border-top:1px solid #e8e8e8}.multiselect__content::webkit-scrollbar{display:none}.multiselect__element{display:block}.multiselect__option{display:block;padding:12px;min-height:40px;line-height:16px;text-decoration:none;text-transform:none;vertical-align:middle;position:relative;cursor:pointer;white-space:nowrap}.multiselect__option:after{top:0;right:0;position:absolute;line-height:40px;padding-right:12px;padding-left:20px;font-size:13px}.multiselect__option--highlight{background:#41b883;outline:none;color:#fff}.multiselect__option--highlight:after{content:attr(data-select);background:#41b883;color:#fff}.multiselect__option--selected{background:#f3f3f3;color:#35495e;font-weight:700}.multiselect__option--selected:after{content:attr(data-selected);color:silver;background:inherit}.multiselect__option--selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect--disabled .multiselect__current,.multiselect--disabled .multiselect__select{background:#ededed;color:#a6a6a6}.multiselect__option--disabled{background:#ededed!important;color:#a6a6a6!important;cursor:text;pointer-events:none}.multiselect__option--group{background:#ededed;color:#35495e}.multiselect__option--group.multiselect__option--highlight{background:#35495e;color:#fff}.multiselect__option--group.multiselect__option--highlight:after{background:#35495e}.multiselect__option--disabled.multiselect__option--highlight{background:#dedede}.multiselect__option--group-selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--group-selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect-enter-active,.multiselect-leave-active{transition:all .15s ease}.multiselect-enter,.multiselect-leave-active{opacity:0}.multiselect__strong{margin-bottom:8px;line-height:20px;display:inline-block;vertical-align:top}[dir=rtl] .multiselect{text-align:right}[dir=rtl] .multiselect__select{right:auto;left:1px}[dir=rtl] .multiselect__tags{padding:8px 8px 0 40px}[dir=rtl] .multiselect__content{text-align:right}[dir=rtl] .multiselect__option:after{right:auto;left:0}[dir=rtl] .multiselect__clear{right:auto;left:12px}[dir=rtl] .multiselect__spinner{right:auto;left:1px}@keyframes spinning{0%{transform:rotate(0)}to{transform:rotate(2turn)}}.mx-icon-double-left:after,.mx-icon-double-left:before,.mx-icon-double-right:after,.mx-icon-double-right:before,.mx-icon-left:before,.mx-icon-right:before{content:"";position:relative;top:-1px;display:inline-block;width:10px;height:10px;vertical-align:middle;border-style:solid;border-color:currentColor;border-width:2px 0 0 2px;border-radius:1px;box-sizing:border-box;transform-origin:center;transform:rotate(-45deg) scale(.7)}.mx-icon-double-left:after{left:-4px}.mx-icon-double-right:before{left:4px}.mx-icon-double-right:after,.mx-icon-double-right:before,.mx-icon-right:before{transform:rotate(135deg) scale(.7)}.mx-btn{box-sizing:border-box;line-height:1;font-size:14px;font-weight:500;padding:7px 15px;margin:0;cursor:pointer;background-color:transparent;outline:none;border:1px solid rgba(0,0,0,.1);border-radius:4px;color:#6c757d;white-space:nowrap}.mx-btn:hover{border-color:#188191;color:#188191}.mx-btn.disabled,.mx-btn:disabled{color:#ccc;cursor:not-allowed}.mx-btn-text{border:0;padding:0 4px;text-align:left;line-height:inherit}.mx-scrollbar{height:100%}.mx-scrollbar:hover .mx-scrollbar-track{opacity:1}.mx-scrollbar-wrap{height:100%;overflow-x:hidden;overflow-y:auto}.mx-scrollbar-track{position:absolute;top:2px;right:2px;bottom:2px;width:6px;z-index:1;border-radius:4px;opacity:0;transition:opacity .24s ease-out}.mx-scrollbar-track .mx-scrollbar-thumb{position:absolute;width:100%;height:0;cursor:pointer;border-radius:inherit;background-color:hsla(220,4%,58%,.3);transition:background-color .3s}.mx-zoom-in-down-enter-active,.mx-zoom-in-down-leave-active{opacity:1;transform:scaleY(1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transform-origin:center top}.mx-zoom-in-down-enter,.mx-zoom-in-down-enter-from,.mx-zoom-in-down-leave-to{opacity:0;transform:scaleY(0)}.mx-datepicker{position:relative;display:inline-block;width:210px}.mx-datepicker svg{width:1em;height:1em;vertical-align:-.15em;fill:currentColor;overflow:hidden}.mx-datepicker-range{width:320px}.mx-datepicker-inline{width:auto}.mx-input-wrapper{position:relative}.mx-input{display:inline-block;box-sizing:border-box;width:100%;height:34px;padding:6px 30px;padding-left:10px;font-size:14px;line-height:1.4;color:#555;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.mx-input:focus,.mx-input:hover{border-color:#409aff}.mx-input.disabled,.mx-input:disabled{color:#ccc;background-color:#f3f3f3;border-color:#ccc;cursor:not-allowed}.mx-input:focus{outline:none}.mx-input::-ms-clear{display:none}.mx-icon-calendar,.mx-icon-clear{position:absolute;top:50%;right:8px;transform:translateY(-50%);font-size:16px;line-height:1;color:rgba(0,0,0,.5);vertical-align:middle}.mx-icon-clear{cursor:pointer}.mx-icon-clear:hover{color:rgba(0,0,0,.8)}.mx-datepicker-main{font:14px/1.5 Helvetica Neue,Helvetica,Arial,Microsoft Yahei,sans-serif;color:#6c757d;background-color:#fff;border:1px solid #e8e8e8}.mx-datepicker-popup{position:absolute;margin-top:1px;margin-bottom:1px;box-shadow:0 6px 12px rgba(0,0,0,.175);z-index:2001}.mx-datepicker-sidebar{float:left;box-sizing:border-box;width:100px;padding:6px;overflow:auto}.mx-datepicker-sidebar+.mx-datepicker-content{margin-left:100px;border-left:1px solid #e8e8e8}.mx-datepicker-body{position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.mx-btn-shortcut{display:block;padding:0 6px;line-height:24px}.mx-range-wrapper{display:flex}@media(max-width:750px){.mx-range-wrapper{flex-direction:column}}.mx-datepicker-header{padding:6px 8px;border-bottom:1px solid #e8e8e8}.mx-datepicker-footer{padding:6px 8px;text-align:right;border-top:1px solid #e8e8e8}.mx-calendar{box-sizing:border-box;width:248px;padding:6px 12px}.mx-calendar+.mx-calendar{border-left:1px solid #e8e8e8}.mx-calendar-header,.mx-time-header{box-sizing:border-box;height:34px;line-height:34px;text-align:center;overflow:hidden}.mx-btn-icon-double-left,.mx-btn-icon-left{float:left}.mx-btn-icon-double-right,.mx-btn-icon-right{float:right}.mx-calendar-header-label{font-size:14px}.mx-calendar-decade-separator{margin:0 2px}.mx-calendar-decade-separator:after{content:"~"}.mx-calendar-content{position:relative;height:224px;box-sizing:border-box}.mx-calendar-content .cell{cursor:pointer}.mx-calendar-content .cell:hover{color:#6c757d;background-color:#f3f9fa}.mx-calendar-content .cell.active{color:#fff;background-color:#188191}.mx-calendar-content .cell.hover-in-range,.mx-calendar-content .cell.in-range{color:#6c757d;background-color:#dcecef}.mx-calendar-content .cell.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}.mx-calendar-week-mode .mx-date-row{cursor:pointer}.mx-calendar-week-mode .mx-date-row:hover{background-color:#f3f9fa}.mx-calendar-week-mode .mx-date-row.mx-active-week{background-color:#dcecef}.mx-calendar-week-mode .mx-date-row .cell.active,.mx-calendar-week-mode .mx-date-row .cell:hover{color:inherit;background-color:transparent}.mx-week-number{opacity:.5}.mx-table{table-layout:fixed;border-collapse:separate;border-spacing:0;width:100%;height:100%;box-sizing:border-box;text-align:center}.mx-table th{font-weight:500}.mx-table td,.mx-table th{padding:0;vertical-align:middle}.mx-table-date td,.mx-table-date th{height:32px;font-size:12px}.mx-table-date .today{color:#2f8e9c}.mx-table-date .cell.not-current-month{color:#ccc;background:none}.mx-time{flex:1;width:224px;background:#fff}.mx-time+.mx-time{border-left:1px solid #e8e8e8}.mx-calendar-time{position:absolute;top:0;left:0;width:100%;height:100%}.mx-time-header{border-bottom:1px solid #e8e8e8}.mx-time-content{height:224px;box-sizing:border-box;overflow:hidden}.mx-time-columns{display:flex;width:100%;height:100%;overflow:hidden}.mx-time-column{flex:1;position:relative;border-left:1px solid #e8e8e8;text-align:center}.mx-time-column:first-child{border-left:0}.mx-time-column .mx-time-list{margin:0;padding:0;list-style:none}.mx-time-column .mx-time-list:after{content:"";display:block;height:192px}.mx-time-column .mx-time-item{cursor:pointer;font-size:12px;height:32px;line-height:32px}.mx-time-column .mx-time-item:hover{color:#6c757d;background-color:#f3f9fa}.mx-time-column .mx-time-item.active{color:#188191;background-color:transparent;font-weight:700}.mx-time-column .mx-time-item.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}.mx-time-option{cursor:pointer;padding:8px 10px;font-size:14px;line-height:20px}.mx-time-option:hover{color:#6c757d;background-color:#f3f9fa}.mx-time-option.active{color:#188191;background-color:transparent;font-weight:700}.mx-time-option.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}#stac-browser .multiselect__tags:focus-within{border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}#stac-browser .multiselect__select:before{color:#495057;border-color:#495057 transparent transparent}#stac-browser .multiselect__single,#stac-browser .multiselect__tags{border-color:#ced4da;padding-left:.75rem;font-size:16px}#stac-browser .multiselect__input,#stac-browser .multiselect__single{padding:4px 0 3px 0}#stac-browser .multiselect__option--highlight,#stac-browser .multiselect__option--highlight:after,#stac-browser .multiselect__tag,#stac-browser .multiselect__tag-icon:hover{background-color:#188191}#stac-browser .multiselect__option--selected.multiselect__option--highlight,#stac-browser .multiselect__option--selected.multiselect__option--highlight:after{background-color:#6c757d}#stac-browser .multiselect__placeholder{color:#999;font-size:16px}.queryables .dropdown-menu{max-height:90vh;overflow:auto}.filter{position:relative}.filter .mx-datepicker{width:100%}.filter .form-group>div{margin-left:1em}.filter .form-group>label{font-weight:600}.search .left[data-v-7e03b515]{min-width:200px;flex-basis:40%}.search .right[data-v-7e03b515]{min-width:300px;flex-basis:60%;position:relative!important}@media(min-width:576px)and (max-width:767.98px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:1;column-count:1}}@media(min-width:768px)and (max-width:991.98px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:1;column-count:1}}@media(min-width:992px)and (max-width:1099.98px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:2;column-count:2}}@media(min-width:1100px)and (max-width:1599.98px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:2;column-count:2}}@media(min-width:1600px)and (max-width:2499.98px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:3;column-count:3}}@media(min-width:2500px){.search .items .card-columns[data-v-7e03b515]{-moz-column-count:4;column-count:4}} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/1528.e6c27227.css b/cumulus_lambda_functions/uds_api/stac_browser/css/1528.e6c27227.css deleted file mode 100644 index a5b1bd24..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/1528.e6c27227.css +++ /dev/null @@ -1 +0,0 @@ -.deprecation h3[data-v-0943ed92]{font-size:1em;font-weight:700;display:inline-block;margin:0;margin-right:1em}.deprecation ul[data-v-0943ed92]{margin-top:.5em;margin-bottom:0} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/208.c771d657.css b/cumulus_lambda_functions/uds_api/stac_browser/css/208.c771d657.css deleted file mode 100644 index e5e81c14..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/208.c771d657.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .provider .btn-provider .badge{text-transform:uppercase}#stac-browser .provider .metadata .card-columns{-moz-column-count:1;column-count:1}#stac-browser .provider .metadata .card-body{padding:0} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/218.0530e224.css b/cumulus_lambda_functions/uds_api/stac_browser/css/218.0530e224.css deleted file mode 100644 index 566098d7..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/218.0530e224.css +++ /dev/null @@ -1 +0,0 @@ -fieldset[disabled] .multiselect{pointer-events:none}.multiselect__spinner{position:absolute;right:1px;top:1px;width:40px;height:38px;background:#fff;display:block}.multiselect__spinner:after,.multiselect__spinner:before{position:absolute;content:"";top:50%;left:50%;margin:-8px 0 0 -8px;width:16px;height:16px;border-radius:100%;border:2px solid transparent;border-top-color:#41b883;box-shadow:0 0 0 1px transparent}.multiselect__spinner:before{animation:spinning 2.4s cubic-bezier(.41,.26,.2,.62);animation-iteration-count:infinite}.multiselect__spinner:after{animation:spinning 2.4s cubic-bezier(.51,.09,.21,.8);animation-iteration-count:infinite}.multiselect__loading-enter-active,.multiselect__loading-leave-active{transition:opacity .4s ease-in-out;opacity:1}.multiselect__loading-enter,.multiselect__loading-leave-active{opacity:0}.multiselect,.multiselect__input,.multiselect__single{font-family:inherit;font-size:16px;touch-action:manipulation}.multiselect{box-sizing:content-box;display:block;position:relative;width:100%;min-height:40px;text-align:left;color:#35495e}.multiselect *{box-sizing:border-box}.multiselect:focus{outline:none}.multiselect--disabled{background:#ededed;pointer-events:none;opacity:.6}.multiselect--active{z-index:50}.multiselect--active:not(.multiselect--above) .multiselect__current,.multiselect--active:not(.multiselect--above) .multiselect__input,.multiselect--active:not(.multiselect--above) .multiselect__tags{border-bottom-left-radius:0;border-bottom-right-radius:0}.multiselect--active .multiselect__select{transform:rotate(180deg)}.multiselect--above.multiselect--active .multiselect__current,.multiselect--above.multiselect--active .multiselect__input,.multiselect--above.multiselect--active .multiselect__tags{border-top-left-radius:0;border-top-right-radius:0}.multiselect__input,.multiselect__single{position:relative;display:inline-block;min-height:20px;line-height:20px;border:none;border-radius:5px;background:#fff;padding:0 0 0 5px;width:100%;transition:border .1s ease;box-sizing:border-box;margin-bottom:8px;vertical-align:top}.multiselect__input::-moz-placeholder{color:#35495e}.multiselect__input::placeholder{color:#35495e}.multiselect__tag~.multiselect__input,.multiselect__tag~.multiselect__single{width:auto}.multiselect__input:hover,.multiselect__single:hover{border-color:#cfcfcf}.multiselect__input:focus,.multiselect__single:focus{border-color:#a8a8a8;outline:none}.multiselect__single{padding-left:5px;margin-bottom:8px}.multiselect__tags-wrap{display:inline}.multiselect__tags{min-height:40px;display:block;padding:8px 40px 0 8px;border-radius:5px;border:1px solid #e8e8e8;background:#fff;font-size:14px}.multiselect__tag{position:relative;display:inline-block;padding:4px 26px 4px 10px;border-radius:5px;margin-right:10px;color:#fff;line-height:1;background:#41b883;margin-bottom:5px;white-space:nowrap;overflow:hidden;max-width:100%;text-overflow:ellipsis}.multiselect__tag-icon{cursor:pointer;margin-left:7px;position:absolute;right:0;top:0;bottom:0;font-weight:700;font-style:normal;width:22px;text-align:center;line-height:22px;transition:all .2s ease;border-radius:5px}.multiselect__tag-icon:after{content:"\D7";color:#266d4d;font-size:14px}.multiselect__tag-icon:focus,.multiselect__tag-icon:hover{background:#369a6e}.multiselect__tag-icon:focus:after,.multiselect__tag-icon:hover:after{color:#fff}.multiselect__current{min-height:40px;overflow:hidden;padding:8px 30px 0 12px;white-space:nowrap;border-radius:5px;border:1px solid #e8e8e8}.multiselect__current,.multiselect__select{line-height:16px;box-sizing:border-box;display:block;margin:0;text-decoration:none;cursor:pointer}.multiselect__select{position:absolute;width:40px;height:38px;right:1px;top:1px;padding:4px 8px;text-align:center;transition:transform .2s ease}.multiselect__select:before{position:relative;right:0;top:65%;color:#999;margin-top:4px;border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 0;content:""}.multiselect__placeholder{color:#adadad;display:inline-block;margin-bottom:10px;padding-top:2px}.multiselect--active .multiselect__placeholder{display:none}.multiselect__content-wrapper{position:absolute;display:block;background:#fff;width:100%;max-height:240px;overflow:auto;border:1px solid #e8e8e8;border-top:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;z-index:50;-webkit-overflow-scrolling:touch}.multiselect__content{list-style:none;display:inline-block;padding:0;margin:0;min-width:100%;vertical-align:top}.multiselect--above .multiselect__content-wrapper{bottom:100%;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom:none;border-top:1px solid #e8e8e8}.multiselect__content::webkit-scrollbar{display:none}.multiselect__element{display:block}.multiselect__option{display:block;padding:12px;min-height:40px;line-height:16px;text-decoration:none;text-transform:none;vertical-align:middle;position:relative;cursor:pointer;white-space:nowrap}.multiselect__option:after{top:0;right:0;position:absolute;line-height:40px;padding-right:12px;padding-left:20px;font-size:13px}.multiselect__option--highlight{background:#41b883;outline:none;color:#fff}.multiselect__option--highlight:after{content:attr(data-select);background:#41b883;color:#fff}.multiselect__option--selected{background:#f3f3f3;color:#35495e;font-weight:700}.multiselect__option--selected:after{content:attr(data-selected);color:silver;background:inherit}.multiselect__option--selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect--disabled .multiselect__current,.multiselect--disabled .multiselect__select{background:#ededed;color:#a6a6a6}.multiselect__option--disabled{background:#ededed!important;color:#a6a6a6!important;cursor:text;pointer-events:none}.multiselect__option--group{background:#ededed;color:#35495e}.multiselect__option--group.multiselect__option--highlight{background:#35495e;color:#fff}.multiselect__option--group.multiselect__option--highlight:after{background:#35495e}.multiselect__option--disabled.multiselect__option--highlight{background:#dedede}.multiselect__option--group-selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--group-selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect-enter-active,.multiselect-leave-active{transition:all .15s ease}.multiselect-enter,.multiselect-leave-active{opacity:0}.multiselect__strong{margin-bottom:8px;line-height:20px;display:inline-block;vertical-align:top}[dir=rtl] .multiselect{text-align:right}[dir=rtl] .multiselect__select{right:auto;left:1px}[dir=rtl] .multiselect__tags{padding:8px 8px 0 40px}[dir=rtl] .multiselect__content{text-align:right}[dir=rtl] .multiselect__option:after{right:auto;left:0}[dir=rtl] .multiselect__clear{right:auto;left:12px}[dir=rtl] .multiselect__spinner{right:auto;left:1px}@keyframes spinning{0%{transform:rotate(0)}to{transform:rotate(2turn)}}.mx-icon-double-left:after,.mx-icon-double-left:before,.mx-icon-double-right:after,.mx-icon-double-right:before,.mx-icon-left:before,.mx-icon-right:before{content:"";position:relative;top:-1px;display:inline-block;width:10px;height:10px;vertical-align:middle;border-style:solid;border-color:currentColor;border-width:2px 0 0 2px;border-radius:1px;box-sizing:border-box;transform-origin:center;transform:rotate(-45deg) scale(.7)}.mx-icon-double-left:after{left:-4px}.mx-icon-double-right:before{left:4px}.mx-icon-double-right:after,.mx-icon-double-right:before,.mx-icon-right:before{transform:rotate(135deg) scale(.7)}.mx-btn{box-sizing:border-box;line-height:1;font-size:14px;font-weight:500;padding:7px 15px;margin:0;cursor:pointer;background-color:transparent;outline:none;border:1px solid rgba(0,0,0,.1);border-radius:4px;color:#6c757d;white-space:nowrap}.mx-btn:hover{border-color:#188191;color:#188191}.mx-btn.disabled,.mx-btn:disabled{color:#ccc;cursor:not-allowed}.mx-btn-text{border:0;padding:0 4px;text-align:left;line-height:inherit}.mx-scrollbar{height:100%}.mx-scrollbar:hover .mx-scrollbar-track{opacity:1}.mx-scrollbar-wrap{height:100%;overflow-x:hidden;overflow-y:auto}.mx-scrollbar-track{position:absolute;top:2px;right:2px;bottom:2px;width:6px;z-index:1;border-radius:4px;opacity:0;transition:opacity .24s ease-out}.mx-scrollbar-track .mx-scrollbar-thumb{position:absolute;width:100%;height:0;cursor:pointer;border-radius:inherit;background-color:hsla(220,4%,58%,.3);transition:background-color .3s}.mx-zoom-in-down-enter-active,.mx-zoom-in-down-leave-active{opacity:1;transform:scaleY(1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transform-origin:center top}.mx-zoom-in-down-enter,.mx-zoom-in-down-enter-from,.mx-zoom-in-down-leave-to{opacity:0;transform:scaleY(0)}.mx-datepicker{position:relative;display:inline-block;width:210px}.mx-datepicker svg{width:1em;height:1em;vertical-align:-.15em;fill:currentColor;overflow:hidden}.mx-datepicker-range{width:320px}.mx-datepicker-inline{width:auto}.mx-input-wrapper{position:relative}.mx-input{display:inline-block;box-sizing:border-box;width:100%;height:34px;padding:6px 30px;padding-left:10px;font-size:14px;line-height:1.4;color:#555;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.mx-input:focus,.mx-input:hover{border-color:#409aff}.mx-input.disabled,.mx-input:disabled{color:#ccc;background-color:#f3f3f3;border-color:#ccc;cursor:not-allowed}.mx-input:focus{outline:none}.mx-input::-ms-clear{display:none}.mx-icon-calendar,.mx-icon-clear{position:absolute;top:50%;right:8px;transform:translateY(-50%);font-size:16px;line-height:1;color:rgba(0,0,0,.5);vertical-align:middle}.mx-icon-clear{cursor:pointer}.mx-icon-clear:hover{color:rgba(0,0,0,.8)}.mx-datepicker-main{font:14px/1.5 Helvetica Neue,Helvetica,Arial,Microsoft Yahei,sans-serif;color:#6c757d;background-color:#fff;border:1px solid #e8e8e8}.mx-datepicker-popup{position:absolute;margin-top:1px;margin-bottom:1px;box-shadow:0 6px 12px rgba(0,0,0,.175);z-index:2001}.mx-datepicker-sidebar{float:left;box-sizing:border-box;width:100px;padding:6px;overflow:auto}.mx-datepicker-sidebar+.mx-datepicker-content{margin-left:100px;border-left:1px solid #e8e8e8}.mx-datepicker-body{position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.mx-btn-shortcut{display:block;padding:0 6px;line-height:24px}.mx-range-wrapper{display:flex}@media(max-width:750px){.mx-range-wrapper{flex-direction:column}}.mx-datepicker-header{padding:6px 8px;border-bottom:1px solid #e8e8e8}.mx-datepicker-footer{padding:6px 8px;text-align:right;border-top:1px solid #e8e8e8}.mx-calendar{box-sizing:border-box;width:248px;padding:6px 12px}.mx-calendar+.mx-calendar{border-left:1px solid #e8e8e8}.mx-calendar-header,.mx-time-header{box-sizing:border-box;height:34px;line-height:34px;text-align:center;overflow:hidden}.mx-btn-icon-double-left,.mx-btn-icon-left{float:left}.mx-btn-icon-double-right,.mx-btn-icon-right{float:right}.mx-calendar-header-label{font-size:14px}.mx-calendar-decade-separator{margin:0 2px}.mx-calendar-decade-separator:after{content:"~"}.mx-calendar-content{position:relative;height:224px;box-sizing:border-box}.mx-calendar-content .cell{cursor:pointer}.mx-calendar-content .cell:hover{color:#6c757d;background-color:#f3f9fa}.mx-calendar-content .cell.active{color:#fff;background-color:#188191}.mx-calendar-content .cell.hover-in-range,.mx-calendar-content .cell.in-range{color:#6c757d;background-color:#dcecef}.mx-calendar-content .cell.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}.mx-calendar-week-mode .mx-date-row{cursor:pointer}.mx-calendar-week-mode .mx-date-row:hover{background-color:#f3f9fa}.mx-calendar-week-mode .mx-date-row.mx-active-week{background-color:#dcecef}.mx-calendar-week-mode .mx-date-row .cell.active,.mx-calendar-week-mode .mx-date-row .cell:hover{color:inherit;background-color:transparent}.mx-week-number{opacity:.5}.mx-table{table-layout:fixed;border-collapse:separate;border-spacing:0;width:100%;height:100%;box-sizing:border-box;text-align:center}.mx-table th{font-weight:500}.mx-table td,.mx-table th{padding:0;vertical-align:middle}.mx-table-date td,.mx-table-date th{height:32px;font-size:12px}.mx-table-date .today{color:#2f8e9c}.mx-table-date .cell.not-current-month{color:#ccc;background:none}.mx-time{flex:1;width:224px;background:#fff}.mx-time+.mx-time{border-left:1px solid #e8e8e8}.mx-calendar-time{position:absolute;top:0;left:0;width:100%;height:100%}.mx-time-header{border-bottom:1px solid #e8e8e8}.mx-time-content{height:224px;box-sizing:border-box;overflow:hidden}.mx-time-columns{display:flex;width:100%;height:100%;overflow:hidden}.mx-time-column{flex:1;position:relative;border-left:1px solid #e8e8e8;text-align:center}.mx-time-column:first-child{border-left:0}.mx-time-column .mx-time-list{margin:0;padding:0;list-style:none}.mx-time-column .mx-time-list:after{content:"";display:block;height:192px}.mx-time-column .mx-time-item{cursor:pointer;font-size:12px;height:32px;line-height:32px}.mx-time-column .mx-time-item:hover{color:#6c757d;background-color:#f3f9fa}.mx-time-column .mx-time-item.active{color:#188191;background-color:transparent;font-weight:700}.mx-time-column .mx-time-item.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}.mx-time-option{cursor:pointer;padding:8px 10px;font-size:14px;line-height:20px}.mx-time-option:hover{color:#6c757d;background-color:#f3f9fa}.mx-time-option.active{color:#188191;background-color:transparent;font-weight:700}.mx-time-option.disabled{cursor:not-allowed;color:#ccc;background-color:#f3f3f3}#stac-browser .multiselect__tags:focus-within{border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}#stac-browser .multiselect__select:before{color:#495057;border-color:#495057 transparent transparent}#stac-browser .multiselect__single,#stac-browser .multiselect__tags{border-color:#ced4da;padding-left:.75rem;font-size:16px}#stac-browser .multiselect__input,#stac-browser .multiselect__single{padding:4px 0 3px 0}#stac-browser .multiselect__option--highlight,#stac-browser .multiselect__option--highlight:after,#stac-browser .multiselect__tag,#stac-browser .multiselect__tag-icon:hover{background-color:#188191}#stac-browser .multiselect__option--selected.multiselect__option--highlight,#stac-browser .multiselect__option--selected.multiselect__option--highlight:after{background-color:#6c757d}#stac-browser .multiselect__placeholder{color:#999;font-size:16px}.queryables .dropdown-menu{max-height:90vh;overflow:auto}.filter{position:relative}.filter .mx-datepicker{width:100%}.filter .form-group>div{margin-left:1em}.filter .form-group>label{font-weight:600} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/2760.6fac7cd8.css b/cumulus_lambda_functions/uds_api/stac_browser/css/2760.6fac7cd8.css deleted file mode 100644 index 0499f03c..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/2760.6fac7cd8.css +++ /dev/null @@ -1 +0,0 @@ -.queryable-row{margin:.25em 0;gap:.5em;flex-direction:row;flex-wrap:nowrap;align-content:center}.queryable-row .delete{width:auto}.queryable-row .title,.queryable-row .value{flex-grow:4;width:8rem!important}.op{min-width:4rem}.queryable-help{display:block;margin:.25em 0 1em 0;line-height:1.1em}.queryable-group{margin:.75em 0} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/3036.081b4583.css b/cumulus_lambda_functions/uds_api/stac_browser/css/3036.081b4583.css deleted file mode 100644 index ff8265ac..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/3036.081b4583.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .fullscreen{position:absolute!important;top:0!important;left:0!important;width:100%!important;height:100%!important;background-color:#fff;overflow:auto} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/3816.572c83fc.css b/cumulus_lambda_functions/uds_api/stac_browser/css/3816.572c83fc.css deleted file mode 100644 index 1864889a..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/3816.572c83fc.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .links ul{list-style-type:"-";margin:0 0 1em 1em;padding:0}#stac-browser .links ul>li{margin-bottom:.2em;padding-left:.5em} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/572.4149415b.css b/cumulus_lambda_functions/uds_api/stac_browser/css/572.4149415b.css deleted file mode 100644 index c45603aa..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/572.4149415b.css +++ /dev/null @@ -1 +0,0 @@ -.leaflet-control-fullscreen a{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAA0CAYAAACU7CiIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACoSURBVFiF7ZZhDoAgCIWxdbF3suxkHM3+1FaOmNqyIr6fiHuJTyKklKgHQxcVF7rCKAUBiA5h5tCSR/T0iTakL9PWz05IZNEM3YSCt6BvCgFI2ps4Q9v3k9Ldgdrr8nrX9LYc7wwu5EIu9KCQT6rq+r8mVbV0ewBEIpqy8MzMsWR/8f+oxmES9u7olZPqLKQeYtqkWuy61V2xND/H3h35pNqMPTPYE1oAnZZStKN8jj8AAAAASUVORK5CYII=) no-repeat 0 0;background-size:26px 52px}.leaflet-touch .leaflet-control-fullscreen a{background-position:2px 2px}.leaflet-fullscreen-on .leaflet-control-fullscreen a{background-position:0 -26px}.leaflet-touch.leaflet-fullscreen-on .leaflet-control-fullscreen a{background-position:2px -24px}.leaflet-container:-webkit-full-screen{width:100%!important;height:100%!important}.leaflet-container.leaflet-fullscreen-on,.leaflet-pseudo-fullscreen{width:100%!important;height:100%!important}.leaflet-pseudo-fullscreen{position:fixed!important;top:0!important;left:0!important;z-index:99999}@media (min-resolution:192dpi){.leaflet-control-fullscreen a{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAABoCAYAAAC+NNNnAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAbrwAAG68BXhqRHAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEhSURBVHic7dpBDoIwFADRj/FiPRlwsh4NN5CoiVKg1Ukzb43ApKK1dliWJXpy+/cN1GYQnUF0BtEZRHcvPTCldGhKkXMefnm+TXcjZBBd8TP0rvQ9ffb1R5+xTXcjZBCdQXQG0Q2u+sAZRGcQnUF0p9cUrv4eanW97kbIIDqD6AyiO70ut7du1mrdbU93I2QQnWsKdAbRGURnEJ1BdAbRGURnEJ1BdAbRueeUziA695zSGURnEN3pT7lvUkpTRIw7h80556n2tauPUGFMRMS4HltV9f+HWs3RSnX3DBlEZxCdQXQt9pzOUfbFuh179Xovqo/QOp35eKNPmkx9mszl1hudWpx7T3fPkEF0BtG555TOIDr3nNIZRGcQnUF0BtE9AF5WX48h7QeZAAAAAElFTkSuQmCC)}}.leaflet-image-layer,.leaflet-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane,.leaflet-pane>canvas,.leaflet-pane>svg,.leaflet-tile,.leaflet-tile-container,.leaflet-zoom-box{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden}.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile{-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-tile::-moz-selection{background:transparent}.leaflet-tile::selection{background:transparent}.leaflet-safari .leaflet-tile{image-rendering:-webkit-optimize-contrast}.leaflet-safari .leaflet-tile-container{width:1600px;height:1600px;-webkit-transform-origin:0 0}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container .leaflet-overlay-pane svg{max-width:none!important;max-height:none!important}.leaflet-container .leaflet-marker-pane img,.leaflet-container .leaflet-shadow-pane img,.leaflet-container .leaflet-tile,.leaflet-container .leaflet-tile-pane img,.leaflet-container img.leaflet-image-layer{max-width:none!important;max-height:none!important;width:auto;padding:0}.leaflet-container img.leaflet-tile{mix-blend-mode:plus-lighter}.leaflet-container.leaflet-touch-zoom{touch-action:pan-x pan-y}.leaflet-container.leaflet-touch-drag{touch-action:none;touch-action:pinch-zoom}.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom{touch-action:none}.leaflet-container{-webkit-tap-highlight-color:transparent}.leaflet-container a{-webkit-tap-highlight-color:rgba(51,181,229,.4)}.leaflet-tile{filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0;box-sizing:border-box;z-index:800}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-pane{z-index:400}.leaflet-tile-pane{z-index:200}.leaflet-overlay-pane{z-index:400}.leaflet-shadow-pane{z-index:500}.leaflet-marker-pane{z-index:600}.leaflet-tooltip-pane{z-index:650}.leaflet-popup-pane{z-index:700}.leaflet-map-pane canvas{z-index:100}.leaflet-map-pane svg{z-index:200}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:800;pointer-events:visiblePainted;pointer-events:auto}.leaflet-bottom,.leaflet-top{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-control{float:left;clear:both}.leaflet-right .leaflet-control{float:right}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-right .leaflet-control{margin-right:10px}.leaflet-fade-anim .leaflet-popup{opacity:0;transition:opacity .2s linear}.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-animated{transform-origin:0 0}svg.leaflet-zoom-animated{will-change:transform}.leaflet-zoom-anim .leaflet-zoom-animated{transition:transform .25s cubic-bezier(0,0,.25,1)}.leaflet-pan-anim .leaflet-tile,.leaflet-zoom-anim .leaflet-tile{transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-interactive{cursor:pointer}.leaflet-grab{cursor:grab}.leaflet-crosshair,.leaflet-crosshair .leaflet-interactive{cursor:crosshair}.leaflet-control,.leaflet-popup-pane{cursor:auto}.leaflet-dragging .leaflet-grab,.leaflet-dragging .leaflet-grab .leaflet-interactive,.leaflet-dragging .leaflet-marker-draggable{cursor:move;cursor:grabbing}.leaflet-image-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane>svg path,.leaflet-tile-container{pointer-events:none}.leaflet-image-layer.leaflet-interactive,.leaflet-marker-icon.leaflet-interactive,.leaflet-pane>svg path.leaflet-interactive,svg.leaflet-image-layer.leaflet-interactive path{pointer-events:visiblePainted;pointer-events:auto}.leaflet-container{background:#ddd;outline-offset:1px}.leaflet-container a{color:#0078a8}.leaflet-zoom-box{border:2px dotted #38f;background:hsla(0,0%,100%,.5)}.leaflet-container{font-family:Helvetica Neue,Arial,Helvetica,sans-serif;font-size:12px;font-size:.75rem;line-height:1.5}.leaflet-bar{box-shadow:0 1px 5px rgba(0,0,0,.65);border-radius:4px}.leaflet-bar a{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;display:block;text-align:center;text-decoration:none;color:#000}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:focus,.leaflet-bar a:hover{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-touch .leaflet-bar a:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.leaflet-touch .leaflet-bar a:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:700 18px Lucida Console,Monaco,monospace;text-indent:1px}.leaflet-touch .leaflet-control-zoom-in,.leaflet-touch .leaflet-control-zoom-out{font-size:22px}.leaflet-control-layers{box-shadow:0 1px 5px rgba(0,0,0,.4);background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAQAAAADQ4RFAAACf0lEQVR4AY1UM3gkARTePdvdoTxXKc+qTl3aU5U6b2Kbkz3Gtq3Zw6ziLGNPzrYx7946Tr6/ee/XeCQ4D3ykPtL5tHno4n0d/h3+xfuWHGLX81cn7r0iTNzjr7LrlxCqPtkbTQEHeqOrTy4Yyt3VCi/IOB0v7rVC7q45Q3Gr5K6jt+3Gl5nCoDD4MtO+j96Wu8atmhGqcNGHObuf8OM/x3AMx38+4Z2sPqzCxRFK2aF2e5Jol56XTLyggAMTL56XOMoS1W4pOyjUcGGQdZxU6qRh7B9Zp+PfpOFlqt0zyDZckPi1ttmIp03jX8gyJ8a/PG2yutpS/Vol7peZIbZcKBAEEheEIAgFbDkz5H6Zrkm2hVWGiXKiF4Ycw0RWKdtC16Q7qe3X4iOMxruonzegJzWaXFrU9utOSsLUmrc0YjeWYjCW4PDMADElpJSSQ0vQvA1Tm6/JlKnqFs1EGyZiFCqnRZTEJJJiKRYzVYzJck2Rm6P4iH+cmSY0YzimYa8l0EtTODFWhcMIMVqdsI2uiTvKmTisIDHJ3od5GILVhBCarCfVRmo4uTjkhrhzkiBV7SsaqS+TzrzM1qpGGUFt28pIySQHR6h7F6KSwGWm97ay+Z+ZqMcEjEWebE7wxCSQwpkhJqoZA5ivCdZDjJepuJ9IQjGGUmuXJdBFUygxVqVsxFsLMbDe8ZbDYVCGKxs+W080max1hFCarCfV+C1KATwcnvE9gRRuMP2prdbWGowm1KB1y+zwMMENkM755cJ2yPDtqhTI6ED1M/82yIDtC/4j4BijjeObflpO9I9MwXTCsSX8jWAFeHr05WoLTJ5G8IQVS/7vwR6ohirYM7f6HzYpogfS3R2OAAAAAElFTkSuQmCC);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAQAAABvcdNgAAAEsklEQVR4AWL4TydIhpZK1kpWOlg0w3ZXP6D2soBtG42jeI6ZmQTHzAxiTbSJsYLjO9HhP+WOmcuhciVnmHVQcJnp7DFvScowZorad/+V/fVzMdMT2g9Cv9guXGv/7pYOrXh2U+RRR3dSd9JRx6bIFc/ekqHI29JC6pJ5ZEh1yWkhkbcFeSjxgx3L2m1cb1C7bceyxA+CNjT/Ifff+/kDk2u/w/33/IeCMOSaWZ4glosqT3DNnNZQ7Cs58/3Ce5HL78iZH/vKVIaYlqzfdLu8Vi7dnvUbEza5Idt36tquZFldl6N5Z/POLof0XLK61mZCmJSWjVF9tEjUluu74IUXvgttuVIHE7YxSkaYhJZam7yiM9Pv82JYfl9nptxZaxMJE4YSPty+vF0+Y2up9d3wwijfjZbabqm/3bZ9ecKHsiGmRflnn1MW4pjHf9oLufyn2z3y1D6n8g8TZhxyzipLNPnAUpsOiuWimg52psrTZYnOWYNDTMuWBWa0tJb4rgq1UvmutpaYEbZlwU3CLJm/ayYjHW5/h7xWLn9Hh1vepDkyf7dE7MtT5LR4e7yYpHrkhOUpEfssBLq2pPhAqoSWKUkk7EDqkmK6RrCEzqDjhNDWNE+XSMvkJRDWlZTmCW0l0PHQGRZY5t1L83kT0Y3l2SItk5JAWHl2dCOBm+fPu3fo5/3v61RMCO9Jx2EEYYhb0rmNQMX/vm7gqOEJLcXTGw3CAuRNeyaPWwjR8PRqKQ1PDA/dpv+on9Shox52WFnx0KY8onHayrJzm87i5h9xGw/tfkev0jGsQizqezUKjk12hBMKJ4kbCqGPVNXudyyrShovGw5CgxsRICxF6aRmSjlBnHRzg7Gx8fKqEubI2rahQYdR1YgDIRQO7JvQyD52hoIQx0mxa0ODtW2Iozn1le2iIRdzwWewedyZzewidueOGqlsn1MvcnQpuVwLGG3/IR1hIKxCjelIDZ8ldqWz25jWAsnldEnK0Zxro19TGVb2ffIZEsIO89EIEDvKMPrzmBOQcKQ+rroye6NgRRxqR4U8EAkz0CL6uSGOm6KQCdWjvjRiSP1BPalCRS5iQYiEIvxuBMJEWgzSoHADcVMuN7IuqqTeyUPq22qFimFtxDyBBJEwNyt6TM88blFHao/6tWWhuuOM4SAK4EI4QmFHA+SEyWlp4EQoJ13cYGzMu7yszEIBOm2rVmHUNqwAIQabISNMRstmdhNWcFLsSm+0tjJH1MdRxO5Nx0WDMhCtgD6OKgZeljJqJKc9po8juskR9XN0Y1lZ3mWjLR9JCO1jRDMd0fpYC2VnvjBSEFg7wBENc0R9HFlb0xvF1+TBEpF68d+DHR6IOWVv2BECtxo46hOFUBd/APU57WIoEwJhIi2CdpyZX0m93BZicktMj1AS9dClteUFAUNUIEygRZCtik5zSxI9MubTBH1GOiHsiLJ3OCoSZkILa9PxiN0EbvhsAo8tdAf9Seepd36lGWHmtNANTv5Jd0z4QYyeo/UEJqxKRpg5LZx6btLPsOaEmdMyxYdlc8LMaJnikDlhclqmPiQnTEpLUIZEwkRagjYkEibQErwhkTAKCLQEbUgkzJQWc/0PstHHcfEdQ+UAAAAASUVORK5CYII=);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-scrollbar{overflow-y:scroll;overflow-x:hidden;padding-right:5px}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block;font-size:13px;font-size:1.08333em}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-default-icon-path{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=)}.leaflet-container .leaflet-control-attribution{background:#fff;background:hsla(0,0%,100%,.8);margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333;line-height:1.4}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:focus,.leaflet-control-attribution a:hover{text-decoration:underline}.leaflet-attribution-flag{display:inline!important;vertical-align:baseline!important;width:1em;height:.6669em}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;white-space:nowrap;box-sizing:border-box;background:hsla(0,0%,100%,.8);text-shadow:1px 1px #fff}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers{box-shadow:none}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-layers{border:2px solid rgba(0,0,0,.2);background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center;margin-bottom:20px}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 24px 13px 20px;line-height:1.3;font-size:13px;font-size:1.08333em;min-height:1px}.leaflet-popup-content p{margin:17px 0;margin:1.3em 0}.leaflet-popup-tip-container{width:40px;height:20px;position:absolute;left:50%;margin-top:-1px;margin-left:-20px;overflow:hidden;pointer-events:none}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;pointer-events:auto;transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:#fff;color:#333;box-shadow:0 3px 14px rgba(0,0,0,.4)}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;border:none;text-align:center;width:24px;height:24px;font:16px/24px Tahoma,Verdana,sans-serif;color:#757575;text-decoration:none;background:transparent}.leaflet-container a.leaflet-popup-close-button:focus,.leaflet-container a.leaflet-popup-close-button:hover{color:#585858}.leaflet-popup-scrolled{overflow:auto}.leaflet-oldie .leaflet-popup-content-wrapper{-ms-zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto;-ms-filter:"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678,M12=0.70710678,M21=-0.70710678,M22=0.70710678)}.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-tooltip{position:absolute;padding:6px;background-color:#fff;border:1px solid #fff;border-radius:3px;color:#222;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,.4)}.leaflet-tooltip.leaflet-interactive{cursor:pointer;pointer-events:auto}.leaflet-tooltip-bottom:before,.leaflet-tooltip-left:before,.leaflet-tooltip-right:before,.leaflet-tooltip-top:before{position:absolute;pointer-events:none;border:6px solid transparent;background:transparent;content:""}.leaflet-tooltip-bottom{margin-top:6px}.leaflet-tooltip-top{margin-top:-6px}.leaflet-tooltip-bottom:before,.leaflet-tooltip-top:before{left:50%;margin-left:-6px}.leaflet-tooltip-top:before{bottom:0;margin-bottom:-12px;border-top-color:#fff}.leaflet-tooltip-bottom:before{top:0;margin-top:-12px;margin-left:-6px;border-bottom-color:#fff}.leaflet-tooltip-left{margin-left:-6px}.leaflet-tooltip-right{margin-left:6px}.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{top:50%;margin-top:-6px}.leaflet-tooltip-left:before{right:0;margin-right:-12px;border-left-color:#fff}.leaflet-tooltip-right:before{left:0;margin-left:-12px;border-right-color:#fff}@media print{.leaflet-control{-webkit-print-color-adjust:exact;print-color-adjust:exact}}.leaflet-areaselect-shade{position:absolute;background:rgba(0,0,0,.4);cursor:inherit}.leaflet-areaselect-handle{position:absolute;background:#fff;border:1px solid #666;box-shadow:1px 1px rgba(0,0,0,.2);width:14px;height:14px;cursor:move} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/5824.4f7e2a66.css b/cumulus_lambda_functions/uds_api/stac_browser/css/5824.4f7e2a66.css deleted file mode 100644 index 0f42d6e2..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/5824.4f7e2a66.css +++ /dev/null @@ -1 +0,0 @@ -.badge[data-v-3c9cfbc0]{text-transform:uppercase}#stac-browser .providers .btn-provider,#stac-browser .providers .list-group-item-provider{display:flex;justify-content:space-between;align-items:center;padding:.5rem .8rem;background-color:rgba(0,0,0,.03)} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/5900.d1fd0faa.css b/cumulus_lambda_functions/uds_api/stac_browser/css/5900.d1fd0faa.css deleted file mode 100644 index d855c70c..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/5900.d1fd0faa.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .metadata .card{border:0;margin-top:1rem;margin-bottom:1rem;text-align:left}#stac-browser .metadata .card .metadata-rows{border-radius:.25rem}#stac-browser .metadata .card .row{padding:.4rem;border-top:1px solid rgba(0,0,0,.125)}#stac-browser .metadata .card .row:first-child{border-top:0}#stac-browser .metadata .card .row:nth-child(odd){background:rgba(0,0,0,.03)}#stac-browser .metadata .card .label{padding-left:.4rem}#stac-browser .metadata .card .value{padding-right:.4rem}#stac-browser .metadata .row{margin:0;padding:0}#stac-browser .metadata .label{margin:0;padding-left:0;font-weight:600;vertical-align:top}#stac-browser .metadata .value{margin:0;padding-right:0}#stac-browser .metadata .value>.description,#stac-browser .metadata .value>dl,#stac-browser .metadata .value>ol,#stac-browser .metadata .value>pre,#stac-browser .metadata .value>ul{max-height:15em;overflow:auto}#stac-browser .metadata .value .styled-description h1{font-size:1.5em}#stac-browser .metadata .value .styled-description h2{font-size:1.4em}#stac-browser .metadata .value .styled-description h3{font-size:1.3em}#stac-browser .metadata .value .styled-description h4{font-size:1.2em}#stac-browser .metadata .value .styled-description h5{font-size:1.1em}#stac-browser .metadata .value .styled-description h6{font-size:1em}#stac-browser .metadata ul{padding-left:1.4em;margin-bottom:0}#stac-browser .metadata ol{padding-left:2em;margin-bottom:0}#stac-browser .metadata ul li{list-style-type:"- "}#stac-browser .metadata dl{margin:0;margin-left:1em;margin-bottom:.5em}#stac-browser .metadata dl:only-child{margin-left:0}#stac-browser .metadata dl dl:only-child{margin-left:1em}#stac-browser .metadata ol>li>dl,#stac-browser .metadata ul>li>dl{margin-left:0}#stac-browser .metadata dt{display:inline}#stac-browser .metadata dt:after{content:": "}#stac-browser .metadata dd{display:inline}#stac-browser .metadata dd:not(:last-of-type)>dl:only-child{margin-bottom:-1em}#stac-browser .metadata dd:after{content:"\a";white-space:pre;line-height:1px}#stac-browser .metadata dd:last-of-type:after{content:"";white-space:normal}#stac-browser .metadata dd .description{display:inline-block}#stac-browser .metadata dd .description>p:only-child{display:inline}#stac-browser .metadata dd>ol,#stac-browser .metadata dd>ul{max-height:15em;overflow:auto}#stac-browser .metadata .provider .description{font-size:.9em;line-height:1.5em;margin-bottom:.5em}#stac-browser .metadata .checksum-input{width:100%}#stac-browser .metadata .color{text-align:center}#stac-browser .metadata .color .color-code{color:#fff;text-shadow:1px 1px 1px #000;text-align:center} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/7412.4b50bc38.css b/cumulus_lambda_functions/uds_api/stac_browser/css/7412.4b50bc38.css deleted file mode 100644 index ec2cfac4..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/7412.4b50bc38.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .previews{height:350px;box-sizing:border-box;overflow:auto}#stac-browser .previews a{display:inline-block;padding:1px;border:1px solid #fff;margin:5px;border-radius:.25rem}#stac-browser .previews a:hover{border-color:#188191}#stac-browser .previews .content{text-align:center}#stac-browser .previews .content .thumbnail{max-width:100%;max-height:335px;border-radius:.25rem}#stac-browser .previews .overlay{text-align:right;position:sticky;top:0;right:0;left:0;height:0;width:100%;z-index:1}#stac-browser .previews .overlay .fullscreen-button{margin:10px}#stac-browser .previews.fullscreen .thumbnail{max-height:none;border-radius:0} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/8980.a94c9ca6.css b/cumulus_lambda_functions/uds_api/stac_browser/css/8980.a94c9ca6.css deleted file mode 100644 index f277b11a..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/8980.a94c9ca6.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .auth{position:absolute;top:0;left:0;width:100vw;height:100vh;background-color:rgba(0,0,0,.5);margin:auto;display:flex;justify-content:center;align-items:center;z-index:9999}#stac-browser .auth>form{min-width:200px;width:50vw;border-radius:.25rem}#stac-browser .auth>form>.card{background-color:#fff} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/9044.72c8c98f.css b/cumulus_lambda_functions/uds_api/stac_browser/css/9044.72c8c98f.css deleted file mode 100644 index 57933280..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/9044.72c8c98f.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .select-data-source{display:flex;flex-direction:column;flex:1;overflow:hidden}#stac-browser .select-data-source hr{width:100%}#stac-browser .select-data-source .stac-index{margin:0;display:flex;flex-direction:column;flex:1;overflow:hidden}#stac-browser .select-data-source .stac-index>div{display:flex;flex-direction:column;flex:1;overflow:auto;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}#stac-browser .select-data-source .stac-index>div .list-group{width:100%}#stac-browser .select-data-source .stac-index>div .list-group .list-group-item{border:0;border-bottom:1px solid rgba(0,0,0,.125)}#stac-browser .select-data-source .stac-index>div .list-group .active .styled-description a{color:#fff} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/9156.480cfd4a.css b/cumulus_lambda_functions/uds_api/stac_browser/css/9156.480cfd4a.css deleted file mode 100644 index aa12473e..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/9156.480cfd4a.css +++ /dev/null @@ -1 +0,0 @@ -.tree[data-v-27897853]{list-style-type:none;margin:0;padding:0}.tree>li[data-v-27897853]{white-space:nowrap}.tree .path[data-v-27897853]{font-weight:700}.tree .tree[data-v-27897853]{margin-left:1.5em}.tree .show-more[data-v-27897853]{width:calc(100% - 1.5em);box-sizing:border-box;margin-left:1.5em}#stac-browser #sidebar{width:33%;min-width:300px}#stac-browser #sidebar .b-sidebar-body{padding:.5rem 1rem}#stac-browser #sidebar .b-sidebar-body .tree.root{margin:0;padding:0}#stac-browser #sidebar .b-sidebar-footer{border-top:1px solid rgba(0,0,0,.125)}#stac-browser #sidebar .b-sidebar-footer .switch-catalog{width:100%} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/9392.6ad781a6.css b/cumulus_lambda_functions/uds_api/stac_browser/css/9392.6ad781a6.css deleted file mode 100644 index 84bf6f07..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/9392.6ad781a6.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .search-box{position:relative}#stac-browser .search-box .icon,#stac-browser .search-box input{height:1.5em;font-size:1em;margin:0}#stac-browser .search-box input{padding:.25em .3em;padding-left:1.9em;z-index:1;display:inline-block;border:1px solid #ccc;box-sizing:content-box;background-color:#fff;width:calc(100% - 2.15em - 2px)}#stac-browser .search-box .icon{-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-top:.3em;margin-left:.3em;width:1em;z-index:2;position:absolute;top:0;left:0} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/9856.0780c10b.css b/cumulus_lambda_functions/uds_api/stac_browser/css/9856.0780c10b.css deleted file mode 100644 index 50d2a56f..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/9856.0780c10b.css +++ /dev/null @@ -1 +0,0 @@ -#stac-browser .metadata-table .table thead th{vertical-align:middle}.b-table-sticky-header>.table.b-table>thead>tr>th{position:sticky!important} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/app.b5efa536.css b/cumulus_lambda_functions/uds_api/stac_browser/css/app.b5efa536.css deleted file mode 100644 index 4f9402ba..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/app.b5efa536.css +++ /dev/null @@ -1,7 +0,0 @@ -.alert[data-v-09bb7d1b]{margin-bottom:0}.message[data-v-09bb7d1b]{font-weight:700}.message[data-v-09bb7d1b]:last-child{margin-bottom:0}dl[data-v-09bb7d1b]{font-size:.9em}.loading[data-v-67931bb8]{text-align:center;box-sizing:border-box}.loading.loading-stretch[data-v-67931bb8]{flex-grow:1;display:flex;justify-content:center;align-items:center}.loading.loading-fill[data-v-67931bb8]{z-index:2000;position:absolute;background-color:hsla(0,0%,100%,.75);width:100%;height:100%;top:0;left:0;border-radius:.25rem;display:flex;justify-content:center;align-items:center}.loading.top[data-v-67931bb8]{align-items:start!important;padding-top:1em}.loading.top>.spinner-border[data-v-67931bb8]{position:sticky;top:1em}#stac-browser .styled-description{line-height:1.4em}#stac-browser .styled-description h1,#stac-browser .styled-description h2,#stac-browser .styled-description h3,#stac-browser .styled-description h4,#stac-browser .styled-description h5,#stac-browser .styled-description h6{color:#6c757d;font-weight:600}#stac-browser .styled-description h1{font-size:1.5rem}#stac-browser .styled-description h2{font-size:1.4rem}#stac-browser .styled-description h3{font-size:1.3rem}#stac-browser .styled-description h4{font-size:1.2rem}#stac-browser .styled-description h5{font-size:1.1rem}#stac-browser .styled-description h6{font-size:1rem}#stac-browser .styled-description pre{background-color:#eee;width:100%;border:1px solid #ccc;max-height:15em;overflow-y:auto}#stac-browser .styled-description pre code{background-color:transparent;display:block;margin:.5em}#stac-browser .styled-description code{color:maroon;display:inline-block;padding:0 .1em}#stac-browser .styled-description.compact h1,#stac-browser .styled-description.compact h2,#stac-browser .styled-description.compact h3,#stac-browser .styled-description.compact h4,#stac-browser .styled-description.compact h5,#stac-browser .styled-description.compact h6{font-weight:700;font-size:1.1em;margin:.5em 0}#stac-browser .styled-description.compact p{margin:.5em 0}#stac-browser .styled-description.compact p:first-child{margin-top:0}#stac-browser .styled-description.compact p:last-child{margin-bottom:0}#stac-browser .styled-description.compact pre{max-height:7em;width:auto;max-width:100%}#stac-browser .styled-description.inline,#stac-browser .styled-description.inline code,#stac-browser .styled-description.inline h1,#stac-browser .styled-description.inline h2,#stac-browser .styled-description.inline h3,#stac-browser .styled-description.inline h4,#stac-browser .styled-description.inline h5,#stac-browser .styled-description.inline h6,#stac-browser .styled-description.inline p,#stac-browser .styled-description.inline pre{display:inline}#stac-browser .asset .btn-asset{text-align:left;display:flex;justify-content:space-between;align-items:center;padding:.5rem .8rem}#stac-browser .asset .btn-asset .badges .badge{line-height:1.2em;height:1.7em;text-transform:uppercase}#stac-browser .asset .metadata .card-columns{-moz-column-count:1;column-count:1}#stac-browser .asset .metadata .card{border:0}#stac-browser .asset .metadata .card-body{padding:0}#stac-browser .item .left,#stac-browser .item .right{max-width:50%}@media(max-width:767.98px){#stac-browser .item .left,#stac-browser .item .right{max-width:100%;min-width:100%}}#stac-browser .item .card-columns .thumbnail{align-self:center}#stac-browser .item .metadata .card-columns{-moz-column-count:1;column-count:1}@media(min-width:2500px){#stac-browser .item .metadata .card-columns:not(.count-1){-moz-column-count:2;column-count:2}}#stac-browser .catalog-card.deprecated{opacity:.5}#stac-browser .catalog-card.deprecated:hover{opacity:1}#stac-browser .catalog-card .intro{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;text-align:left}#stac-browser .catalog-card .badge.deprecated{text-transform:uppercase}#stac-browser .card-list{flex-direction:row}#stac-browser .card-list .catalog-card{box-sizing:border-box;margin-top:.5em;margin-bottom:.5em}#stac-browser .card-list .catalog-card.has-extent:not(.has-thumbnail){padding-top:.75em}@media(max-width:1099.98px){#stac-browser .card-list .catalog-card{margin-bottom:.2em}#stac-browser .card-list .catalog-card .card-title{margin-top:.6em}}#stac-browser .card-list .catalog-card .card-img-right{min-height:100px;height:100%;max-height:8.5rem;max-width:33%;-o-object-fit:contain;object-fit:contain;-o-object-position:right;object-position:right}#stac-browser .card-list .catalog-card .intro{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;text-align:left;margin-bottom:0}#stac-browser .card-list .catalog-card .datetime{display:inline-block;padding:.25rem;border:0;background-color:rgba(0,0,0,.6);color:#f8f9fa;border-radius:0 0 0 .25rem;position:absolute;top:0;right:0;font-size:80%;white-space:nowrap;max-width:100%;text-overflow:ellipsis;overflow:hidden}#stac-browser .card-columns .catalog-card{box-sizing:border-box;margin-top:.5em;margin-bottom:.5em;text-align:center}#stac-browser .card-columns .catalog-card.queued{min-height:10rem}#stac-browser .card-columns .catalog-card .card-img{width:auto;height:auto;max-width:100%;max-height:300px}#stac-browser .card-columns .catalog-card .card-title{text-align:center}#stac-browser .card-columns .catalog-card .datetime{color:#6c757d;font-size:85%}.catalogs>h2 .badge[data-v-7aceb6cd],.catalogs>h2 .title[data-v-7aceb6cd]{vertical-align:middle}#stac-browser .item-card{text-align:center}#stac-browser .item-card.deprecated{opacity:.7}#stac-browser .item-card.deprecated:hover{opacity:1}#stac-browser .item-card.queued{min-height:200px}#stac-browser .item-card .intro{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin-bottom:.5rem}#stac-browser .item-card.description .intro{text-align:left;margin-bottom:.75rem}#stac-browser .item-card .badge.deprecated{text-transform:uppercase}#stac-browser .item-card .card-img{width:auto;height:auto;max-width:100%;max-height:200px}#stac-browser .item-card .card-body{text-align:center}.items .list[data-v-c0719580]{position:relative}.items>h2 .badge[data-v-c0719580],.items>h2 .title[data-v-c0719580]{vertical-align:middle}#stac-browser .cc .catalogs-container,#stac-browser .cc .items-container{max-width:50%}#stac-browser .cc .catalogs-container .card-list,#stac-browser .cc .items-container .card-list{flex-flow:column wrap}#stac-browser .cc .catalogs-container .catalogs .card-columns,#stac-browser .cc .catalogs-container .items .card-columns,#stac-browser .cc .items-container .catalogs .card-columns,#stac-browser .cc .items-container .items .card-columns{-moz-column-count:1;column-count:1}#stac-browser .cc .catalogs-container .catalogs .card-columns .thumbnail,#stac-browser .cc .catalogs-container .items .card-columns .thumbnail,#stac-browser .cc .items-container .catalogs .card-columns .thumbnail,#stac-browser .cc .items-container .items .card-columns .thumbnail{align-self:center}#stac-browser .cc.catalog .catalogs-container,#stac-browser .cc.catalog .items-container{max-width:100%}@media(min-width:576px){#stac-browser .cc.catalog .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog .catalogs-container .items .card-columns,#stac-browser .cc.catalog .items-container .catalogs .card-columns,#stac-browser .cc.catalog .items-container .items .card-columns{-moz-column-count:2;column-count:2}}@media(min-width:992px){#stac-browser .cc.catalog .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog .catalogs-container .items .card-columns,#stac-browser .cc.catalog .items-container .catalogs .card-columns,#stac-browser .cc.catalog .items-container .items .card-columns{-moz-column-count:3;column-count:3}}@media(min-width:1600px){#stac-browser .cc.catalog .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog .catalogs-container .items .card-columns,#stac-browser .cc.catalog .items-container .catalogs .card-columns,#stac-browser .cc.catalog .items-container .items .card-columns{-moz-column-count:4;column-count:4}}@media(min-width:2500px){#stac-browser .cc.catalog .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog .catalogs-container .items .card-columns,#stac-browser .cc.catalog .items-container .catalogs .card-columns,#stac-browser .cc.catalog .items-container .items .card-columns{-moz-column-count:6;column-count:6}}@media(min-width:768px)and (max-width:991.98px){#stac-browser .cc.collection .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection .catalogs-container .items .card-columns,#stac-browser .cc.collection .items-container .catalogs .card-columns,#stac-browser .cc.collection .items-container .items .card-columns{-moz-column-count:2;column-count:2}}@media(min-width:992px){#stac-browser .cc.collection .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection .catalogs-container .items .card-columns,#stac-browser .cc.collection .items-container .catalogs .card-columns,#stac-browser .cc.collection .items-container .items .card-columns{-moz-column-count:1;column-count:1}}@media(min-width:1600px){#stac-browser .cc.collection .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection .catalogs-container .items .card-columns,#stac-browser .cc.collection .items-container .catalogs .card-columns,#stac-browser .cc.collection .items-container .items .card-columns{-moz-column-count:2;column-count:2}}@media(min-width:2500px){#stac-browser .cc.collection .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection .catalogs-container .items .card-columns,#stac-browser .cc.collection .items-container .catalogs .card-columns,#stac-browser .cc.collection .items-container .items .card-columns{-moz-column-count:3;column-count:3}}@media(min-width:992px){#stac-browser .cc.catalog.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .catalogs-container .items .card-columns,#stac-browser .cc.catalog.mixed .items-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .items-container .items .card-columns{-moz-column-count:1;column-count:1}}@media(min-width:1100px){#stac-browser .cc.catalog.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .catalogs-container .items .card-columns,#stac-browser .cc.catalog.mixed .items-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .items-container .items .card-columns{-moz-column-count:2;column-count:2}}@media(min-width:1600px){#stac-browser .cc.catalog.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .catalogs-container .items .card-columns,#stac-browser .cc.catalog.mixed .items-container .catalogs .card-columns,#stac-browser .cc.catalog.mixed .items-container .items .card-columns{-moz-column-count:3;column-count:3}}#stac-browser .cc.collection.mixed .catalogs-container,#stac-browser .cc.collection.mixed .items-container{max-width:33%}@media(min-width:992px){#stac-browser .cc.collection.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .catalogs-container .items .card-columns,#stac-browser .cc.collection.mixed .items-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .items-container .items .card-columns{-moz-column-count:1;column-count:1}}@media(min-width:1600px){#stac-browser .cc.collection.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .catalogs-container .items .card-columns,#stac-browser .cc.collection.mixed .items-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .items-container .items .card-columns{-moz-column-count:2;column-count:2}}@media(min-width:2500px){#stac-browser .cc.collection.mixed .catalogs-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .catalogs-container .items .card-columns,#stac-browser .cc.collection.mixed .items-container .catalogs .card-columns,#stac-browser .cc.collection.mixed .items-container .items .card-columns{-moz-column-count:3;column-count:3}}#stac-browser .cc .meta{min-width:100%;margin-bottom:1rem}#stac-browser .cc.collection .meta{min-width:33%;margin-bottom:0}@media(min-width:992px){#stac-browser .cc.collection.empty .meta{-moz-column-count:2;column-count:2}#stac-browser .cc.collection.empty .meta>section{-moz-column-break-inside:avoid;break-inside:avoid}}@media(min-width:1100px){#stac-browser .cc.catalog .meta{display:flex;flex-direction:row;flex-wrap:wrap;gap:30px}#stac-browser .cc.catalog .meta>section{flex-basis:0;flex-grow:1;max-width:100%;min-width:40%}}@media(max-width:991.98px){#stac-browser .cc>.row>.catalogs-container,#stac-browser .cc>.row>.items-container,#stac-browser .cc>.row>.meta{min-width:100%}#stac-browser .cc>.row>.meta{order:1;margin-bottom:1rem}#stac-browser .cc>.row>.items-container{order:2}#stac-browser .cc>.row>.catalogs-container{order:3}}#stac-browser .cc .metadata .card-columns{-moz-column-count:1;column-count:1}@media(min-width:2500px){#stac-browser .cc .metadata .card-columns:not(.count-1){-moz-column-count:2;column-count:2}}.popover[data-v-0106123f],.popover[data-v-433de75e]{width:100%;max-width:800px}.lang-item>.dropdown-item[data-v-0106123f]{display:flex}.lang-item>.dropdown-item>.title[data-v-0106123f]{flex:1}h1[data-v-d2d6115c]{word-break:break-word} -/*! - * Bootstrap v4.6.2 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors - * Copyright 2011-2022 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#188191;--secondary:#6c757d;--success:#28a745;--info:#09b3ad;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1100px;--breakpoint-xxl:1600px;--breakpoint-xxxl:2500px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-size:1rem;font-weight:400;line-height:1.5;color:#000;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#188191;text-decoration:none;background-color:transparent}a:hover{color:#0d474f;text-decoration:underline}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-weight:500;line-height:1.2}.h1,h1{font-size:2.2rem}.h2,h2{font-size:1.6rem}.h3,h3{font-size:1.4rem}.h4,h4{font-size:1.2rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:.875em;color:#6c757d}.blockquote-footer:before{content:"— "}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media(min-width:576px){.container,.container-sm{max-width:540px}}@media(min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media(min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media(min-width:1100px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto,.col-xxl,.col-xxl-1,.col-xxl-10,.col-xxl-11,.col-xxl-12,.col-xxl-2,.col-xxl-3,.col-xxl-4,.col-xxl-5,.col-xxl-6,.col-xxl-7,.col-xxl-8,.col-xxl-9,.col-xxl-auto,.col-xxxl,.col-xxxl-1,.col-xxxl-10,.col-xxxl-11,.col-xxxl-12,.col-xxxl-2,.col-xxxl-3,.col-xxxl-4,.col-xxxl-5,.col-xxxl-6,.col-xxxl-7,.col-xxxl-8,.col-xxxl-9,.col-xxxl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-1>*{flex:0 0 100%;max-width:100%}.row-cols-2>*{flex:0 0 50%;max-width:50%}.row-cols-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-4>*{flex:0 0 25%;max-width:25%}.row-cols-5>*{flex:0 0 20%;max-width:20%}.row-cols-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}@media(min-width:576px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-sm-1>*{flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-sm-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-sm-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-sm-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-sm-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}}@media(min-width:768px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-md-1>*{flex:0 0 100%;max-width:100%}.row-cols-md-2>*{flex:0 0 50%;max-width:50%}.row-cols-md-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-md-4>*{flex:0 0 25%;max-width:25%}.row-cols-md-5>*{flex:0 0 20%;max-width:20%}.row-cols-md-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-md-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-md-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-md-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-md-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}}@media(min-width:992px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-lg-1>*{flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-lg-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-lg-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-lg-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-lg-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}}@media(min-width:1100px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-xl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}}@media(min-width:1600px){.col-xxl{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-xxl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xxl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xxl-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xxl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xxl-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xxl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xxl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xxl-3{flex:0 0 25%;max-width:25%}.col-xxl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xxl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xxl-6{flex:0 0 50%;max-width:50%}.col-xxl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xxl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xxl-9{flex:0 0 75%;max-width:75%}.col-xxl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xxl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xxl-12{flex:0 0 100%;max-width:100%}.order-xxl-first{order:-1}.order-xxl-last{order:13}.order-xxl-0{order:0}.order-xxl-1{order:1}.order-xxl-2{order:2}.order-xxl-3{order:3}.order-xxl-4{order:4}.order-xxl-5{order:5}.order-xxl-6{order:6}.order-xxl-7{order:7}.order-xxl-8{order:8}.order-xxl-9{order:9}.order-xxl-10{order:10}.order-xxl-11{order:11}.order-xxl-12{order:12}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}}@media(min-width:2500px){.col-xxxl{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-xxxl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xxxl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xxxl-3>*{flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-xxxl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xxxl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xxxl-6>*{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xxxl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xxxl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xxxl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xxxl-3{flex:0 0 25%;max-width:25%}.col-xxxl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xxxl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xxxl-6{flex:0 0 50%;max-width:50%}.col-xxxl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xxxl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xxxl-9{flex:0 0 75%;max-width:75%}.col-xxxl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xxxl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xxxl-12{flex:0 0 100%;max-width:100%}.order-xxxl-first{order:-1}.order-xxxl-last{order:13}.order-xxxl-0{order:0}.order-xxxl-1{order:1}.order-xxxl-2{order:2}.order-xxxl-3{order:3}.order-xxxl-4{order:4}.order-xxxl-5{order:5}.order-xxxl-6{order:6}.order-xxxl-7{order:7}.order-xxxl-8{order:8}.order-xxxl-9{order:9}.order-xxxl-10{order:10}.order-xxxl-11{order:11}.order-xxxl-12{order:12}.offset-xxxl-0{margin-left:0}.offset-xxxl-1{margin-left:8.33333333%}.offset-xxxl-2{margin-left:16.66666667%}.offset-xxxl-3{margin-left:25%}.offset-xxxl-4{margin-left:33.33333333%}.offset-xxxl-5{margin-left:41.66666667%}.offset-xxxl-6{margin-left:50%}.offset-xxxl-7{margin-left:58.33333333%}.offset-xxxl-8{margin-left:66.66666667%}.offset-xxxl-9{margin-left:75%}.offset-xxxl-10{margin-left:83.33333333%}.offset-xxxl-11{margin-left:91.66666667%}}.table{width:100%;margin-bottom:1rem;color:#000}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#000;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#bedce0}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#87bdc6}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#add3d8}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#baeae8}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#7fd7d4}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#a6e4e1}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:hsla(0,0%,100%,.075)}@media(max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media(max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media(max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media(max-width:1099.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}@media(max-width:1599.98px){.table-responsive-xxl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xxl>.table-bordered{border:0}}@media(max-width:2499.98px){.table-responsive-xxxl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xxxl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#000;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated select.form-control:valid,select.form-control.is-valid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label:before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated select.form-control:invalid,select.form-control.is-invalid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label:before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media(min-width:576px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#000;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#000;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#188191;border-color:#188191}.btn-primary.focus,.btn-primary:focus,.btn-primary:hover{color:#fff;background-color:#136470;border-color:#115a65}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(59,148,162,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#188191;border-color:#188191}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#115a65;border-color:#0f505a}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(59,148,162,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus,.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,6%,54%,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,6%,54%,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success.focus,.btn-success:focus,.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#09b3ad;border-color:#09b3ad}.btn-info.focus,.btn-info:focus,.btn-info:hover{color:#fff;background-color:#078f8a;border-color:#07827e}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(46,190,185,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#09b3ad;border-color:#09b3ad}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#07827e;border-color:#067672}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(46,190,185,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning.focus,.btn-warning:focus,.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger.focus,.btn-danger:focus,.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light.focus,.btn-light:focus,.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem hsla(220,4%,85%,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(220,4%,85%,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark.focus,.btn-dark:focus,.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#188191;border-color:#188191}.btn-outline-primary:hover{color:#fff;background-color:#188191;border-color:#188191}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(24,129,145,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#188191;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#188191;border-color:#188191}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(24,129,145,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#09b3ad;border-color:#09b3ad}.btn-outline-info:hover{color:#fff;background-color:#09b3ad;border-color:#09b3ad}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(9,179,173,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#09b3ad;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#09b3ad;border-color:#09b3ad}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(9,179,173,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#188191;text-decoration:none}.btn-link:hover{color:#0d474f}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.width{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.width{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#000;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media(min-width:1100px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}@media(min-width:1600px){.dropdown-menu-xxl-left{right:auto;left:0}.dropdown-menu-xxl-right{right:0;left:auto}}@media(min-width:2500px){.dropdown-menu-xxxl-left{right:auto;left:0}.dropdown-menu-xxxl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#188191}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label,.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label:after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3),.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label,.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label:after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;border-color:#188191;background-color:#188191}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label:before{border-color:#48cce1}.custom-control-input:not(:disabled):active~.custom-control-label:before{color:#fff;background-color:#74d9e8;border-color:#74d9e8}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before,.custom-control-input[disabled]~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label:before{pointer-events:none;background-color:#fff;border:1px solid #adb5bd}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%23fff' d='m6.564.75-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{border-color:#188191;background-color:#188191}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(24,129,145,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(24,129,145,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(24,129,145,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label:before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label:after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.custom-switch .custom-control-label:after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label:after{background-color:#fff;transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(24,129,145,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(1.5em + .75rem + 2px)}.custom-file-input{z-index:2;margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#48cce1;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]:after{content:attr(data-browse)}.custom-file-label{left:0;z-index:1;height:calc(1.5em + .75rem + 2px);overflow:hidden;font-weight:400;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(24,129,145,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(24,129,145,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(24,129,145,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#188191;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media(prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#74d9e8}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#188191;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media(prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#74d9e8}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#188191;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media(prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#74d9e8}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label:before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:none;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#188191}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media(max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media(min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media(max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media(min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media(max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media(min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media(max-width:1099.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media(min-width:1100px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media(max-width:1599.98px){.navbar-expand-xxl>.container,.navbar-expand-xxl>.container-fluid,.navbar-expand-xxl>.container-lg,.navbar-expand-xxl>.container-md,.navbar-expand-xxl>.container-sm,.navbar-expand-xxl>.container-xl{padding-right:0;padding-left:0}}@media(min-width:1600px){.navbar-expand-xxl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl>.container,.navbar-expand-xxl>.container-fluid,.navbar-expand-xxl>.container-lg,.navbar-expand-xxl>.container-md,.navbar-expand-xxl>.container-sm,.navbar-expand-xxl>.container-xl{flex-wrap:nowrap}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}@media(max-width:2499.98px){.navbar-expand-xxxl>.container,.navbar-expand-xxxl>.container-fluid,.navbar-expand-xxxl>.container-lg,.navbar-expand-xxxl>.container-md,.navbar-expand-xxxl>.container-sm,.navbar-expand-xxxl>.container-xl{padding-right:0;padding-left:0}}@media(min-width:2500px){.navbar-expand-xxxl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xxxl .navbar-nav{flex-direction:row}.navbar-expand-xxxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxxl>.container,.navbar-expand-xxxl>.container-fluid,.navbar-expand-xxxl>.container-lg,.navbar-expand-xxxl>.container-md,.navbar-expand-xxxl>.container-sm,.navbar-expand-xxxl>.container-xl{flex-wrap:nowrap}.navbar-expand-xxxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxxl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media(min-width:576px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media(min-width:576px){.card-columns{-moz-column-count:3;column-count:3;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:flex;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#188191;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0d474f;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#188191;border-color:#188191}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#188191}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#115a65}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#09b3ad}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#07827e}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(9,179,173,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media(min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#0c434b;background-color:#d1e6e9;border-color:#bedce0}.alert-primary hr{border-top-color:#add3d8}.alert-primary .alert-link{color:#051c1f}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#055d5a;background-color:#cef0ef;border-color:#baeae8}.alert-info hr{border-top-color:#a6e4e1}.alert-info .alert-link{color:#022d2b}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:flex;overflow:hidden}.progress-bar{flex-direction:column;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#188191;transition:width .6s ease}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.media{display:flex;align-items:flex-start}.media-body{flex:1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#000;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#188191;border-color:#188191}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width:1100px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width:1600px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media(min-width:2500px){.list-group-horizontal-xxxl{flex-direction:row}.list-group-horizontal-xxxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#0c434b;background-color:#bedce0}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#0c434b;background-color:#add3d8}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#0c434b;border-color:#0c434b}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#055d5a;background-color:#baeae8}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055d5a;background-color:#a6e4e1}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055d5a;border-color:#055d5a}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{flex-basis:350px;max-width:350px;font-size:.875rem;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:flex;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-50px)}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable:before{content:none}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem);height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media(min-width:1100px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=top],.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=top]{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=top] .arrow,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=top] .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=top] .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right],.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right]{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right] .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right] .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=bottom],.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=bottom]{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=bottom] .arrow,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=bottom] .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=bottom] .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left],.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left]{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left] .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left] .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.b-popover-danger.bs-popover-auto[x-placement^=top],.b-popover-dark.bs-popover-auto[x-placement^=top],.b-popover-info.bs-popover-auto[x-placement^=top],.b-popover-light.bs-popover-auto[x-placement^=top],.b-popover-primary.bs-popover-auto[x-placement^=top],.b-popover-secondary.bs-popover-auto[x-placement^=top],.b-popover-success.bs-popover-auto[x-placement^=top],.b-popover-warning.bs-popover-auto[x-placement^=top],.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow:before,.bs-popover-top>.arrow:before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow:after,.bs-popover-top>.arrow:after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.b-popover-danger.bs-popover-auto[x-placement^=right],.b-popover-dark.bs-popover-auto[x-placement^=right],.b-popover-info.bs-popover-auto[x-placement^=right],.b-popover-light.bs-popover-auto[x-placement^=right],.b-popover-primary.bs-popover-auto[x-placement^=right],.b-popover-secondary.bs-popover-auto[x-placement^=right],.b-popover-success.bs-popover-auto[x-placement^=right],.b-popover-warning.bs-popover-auto[x-placement^=right],.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow:before,.bs-popover-right>.arrow:before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow:after,.bs-popover-right>.arrow:after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.b-popover-danger.bs-popover-auto[x-placement^=bottom],.b-popover-dark.bs-popover-auto[x-placement^=bottom],.b-popover-info.bs-popover-auto[x-placement^=bottom],.b-popover-light.bs-popover-auto[x-placement^=bottom],.b-popover-primary.bs-popover-auto[x-placement^=bottom],.b-popover-secondary.bs-popover-auto[x-placement^=bottom],.b-popover-success.bs-popover-auto[x-placement^=bottom],.b-popover-warning.bs-popover-auto[x-placement^=bottom],.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow:before,.bs-popover-bottom>.arrow:before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow:after,.bs-popover-bottom>.arrow:after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.b-popover-danger.bs-popover-auto[x-placement^=left],.b-popover-dark.bs-popover-auto[x-placement^=left],.b-popover-info.bs-popover-auto[x-placement^=left],.b-popover-light.bs-popover-auto[x-placement^=left],.b-popover-primary.bs-popover-auto[x-placement^=left],.b-popover-secondary.bs-popover-auto[x-placement^=left],.b-popover-success.bs-popover-auto[x-placement^=left],.b-popover-warning.bs-popover-auto[x-placement^=left],.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow:before,.bs-popover-left>.arrow:before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow:after,.bs-popover-left>.arrow:after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#000}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='m5.25 0-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='m2.75 0-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@keyframes spinner-border{to{transform:rotate(1turn)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentcolor;border-right-color:transparent;border-radius:50%;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentcolor;border-radius:50%;opacity:0;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#188191!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#115a65!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#09b3ad!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#07827e!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#188191!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#09b3ad!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media(min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media(min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media(min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media(min-width:1100px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media(min-width:1600px){.d-xxl-none{display:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}}@media(min-width:2500px){.d-xxxl-none{display:none!important}.d-xxxl-inline{display:inline!important}.d-xxxl-inline-block{display:inline-block!important}.d-xxxl-block{display:block!important}.d-xxxl-table{display:table!important}.d-xxxl-table-row{display:table-row!important}.d-xxxl-table-cell{display:table-cell!important}.d-xxxl-flex{display:flex!important}.d-xxxl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714286%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:1 1 auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media(min-width:576px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media(min-width:768px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media(min-width:992px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media(min-width:1100px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}@media(min-width:1600px){.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}}@media(min-width:2500px){.flex-xxxl-row{flex-direction:row!important}.flex-xxxl-column{flex-direction:column!important}.flex-xxxl-row-reverse{flex-direction:row-reverse!important}.flex-xxxl-column-reverse{flex-direction:column-reverse!important}.flex-xxxl-wrap{flex-wrap:wrap!important}.flex-xxxl-nowrap{flex-wrap:nowrap!important}.flex-xxxl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xxxl-fill{flex:1 1 auto!important}.flex-xxxl-grow-0{flex-grow:0!important}.flex-xxxl-grow-1{flex-grow:1!important}.flex-xxxl-shrink-0{flex-shrink:0!important}.flex-xxxl-shrink-1{flex-shrink:1!important}.justify-content-xxxl-start{justify-content:flex-start!important}.justify-content-xxxl-end{justify-content:flex-end!important}.justify-content-xxxl-center{justify-content:center!important}.justify-content-xxxl-between{justify-content:space-between!important}.justify-content-xxxl-around{justify-content:space-around!important}.align-items-xxxl-start{align-items:flex-start!important}.align-items-xxxl-end{align-items:flex-end!important}.align-items-xxxl-center{align-items:center!important}.align-items-xxxl-baseline{align-items:baseline!important}.align-items-xxxl-stretch{align-items:stretch!important}.align-content-xxxl-start{align-content:flex-start!important}.align-content-xxxl-end{align-content:flex-end!important}.align-content-xxxl-center{align-content:center!important}.align-content-xxxl-between{align-content:space-between!important}.align-content-xxxl-around{align-content:space-around!important}.align-content-xxxl-stretch{align-content:stretch!important}.align-self-xxxl-auto{align-self:auto!important}.align-self-xxxl-start{align-self:flex-start!important}.align-self-xxxl-end{align-self:flex-end!important}.align-self-xxxl-center{align-self:center!important}.align-self-xxxl-baseline{align-self:baseline!important}.align-self-xxxl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media(min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media(min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media(min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media(min-width:1100px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}@media(min-width:1600px){.float-xxl-left{float:left!important}.float-xxl-right{float:right!important}.float-xxl-none{float:none!important}}@media(min-width:2500px){.float-xxxl-left{float:left!important}.float-xxxl-right{float:right!important}.float-xxxl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports(position:sticky){.sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media(min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media(min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media(min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media(min-width:1100px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}@media(min-width:1600px){.m-xxl-0{margin:0!important}.mt-xxl-0,.my-xxl-0{margin-top:0!important}.mr-xxl-0,.mx-xxl-0{margin-right:0!important}.mb-xxl-0,.my-xxl-0{margin-bottom:0!important}.ml-xxl-0,.mx-xxl-0{margin-left:0!important}.m-xxl-1{margin:.25rem!important}.mt-xxl-1,.my-xxl-1{margin-top:.25rem!important}.mr-xxl-1,.mx-xxl-1{margin-right:.25rem!important}.mb-xxl-1,.my-xxl-1{margin-bottom:.25rem!important}.ml-xxl-1,.mx-xxl-1{margin-left:.25rem!important}.m-xxl-2{margin:.5rem!important}.mt-xxl-2,.my-xxl-2{margin-top:.5rem!important}.mr-xxl-2,.mx-xxl-2{margin-right:.5rem!important}.mb-xxl-2,.my-xxl-2{margin-bottom:.5rem!important}.ml-xxl-2,.mx-xxl-2{margin-left:.5rem!important}.m-xxl-3{margin:1rem!important}.mt-xxl-3,.my-xxl-3{margin-top:1rem!important}.mr-xxl-3,.mx-xxl-3{margin-right:1rem!important}.mb-xxl-3,.my-xxl-3{margin-bottom:1rem!important}.ml-xxl-3,.mx-xxl-3{margin-left:1rem!important}.m-xxl-4{margin:1.5rem!important}.mt-xxl-4,.my-xxl-4{margin-top:1.5rem!important}.mr-xxl-4,.mx-xxl-4{margin-right:1.5rem!important}.mb-xxl-4,.my-xxl-4{margin-bottom:1.5rem!important}.ml-xxl-4,.mx-xxl-4{margin-left:1.5rem!important}.m-xxl-5{margin:3rem!important}.mt-xxl-5,.my-xxl-5{margin-top:3rem!important}.mr-xxl-5,.mx-xxl-5{margin-right:3rem!important}.mb-xxl-5,.my-xxl-5{margin-bottom:3rem!important}.ml-xxl-5,.mx-xxl-5{margin-left:3rem!important}.p-xxl-0{padding:0!important}.pt-xxl-0,.py-xxl-0{padding-top:0!important}.pr-xxl-0,.px-xxl-0{padding-right:0!important}.pb-xxl-0,.py-xxl-0{padding-bottom:0!important}.pl-xxl-0,.px-xxl-0{padding-left:0!important}.p-xxl-1{padding:.25rem!important}.pt-xxl-1,.py-xxl-1{padding-top:.25rem!important}.pr-xxl-1,.px-xxl-1{padding-right:.25rem!important}.pb-xxl-1,.py-xxl-1{padding-bottom:.25rem!important}.pl-xxl-1,.px-xxl-1{padding-left:.25rem!important}.p-xxl-2{padding:.5rem!important}.pt-xxl-2,.py-xxl-2{padding-top:.5rem!important}.pr-xxl-2,.px-xxl-2{padding-right:.5rem!important}.pb-xxl-2,.py-xxl-2{padding-bottom:.5rem!important}.pl-xxl-2,.px-xxl-2{padding-left:.5rem!important}.p-xxl-3{padding:1rem!important}.pt-xxl-3,.py-xxl-3{padding-top:1rem!important}.pr-xxl-3,.px-xxl-3{padding-right:1rem!important}.pb-xxl-3,.py-xxl-3{padding-bottom:1rem!important}.pl-xxl-3,.px-xxl-3{padding-left:1rem!important}.p-xxl-4{padding:1.5rem!important}.pt-xxl-4,.py-xxl-4{padding-top:1.5rem!important}.pr-xxl-4,.px-xxl-4{padding-right:1.5rem!important}.pb-xxl-4,.py-xxl-4{padding-bottom:1.5rem!important}.pl-xxl-4,.px-xxl-4{padding-left:1.5rem!important}.p-xxl-5{padding:3rem!important}.pt-xxl-5,.py-xxl-5{padding-top:3rem!important}.pr-xxl-5,.px-xxl-5{padding-right:3rem!important}.pb-xxl-5,.py-xxl-5{padding-bottom:3rem!important}.pl-xxl-5,.px-xxl-5{padding-left:3rem!important}.m-xxl-n1{margin:-.25rem!important}.mt-xxl-n1,.my-xxl-n1{margin-top:-.25rem!important}.mr-xxl-n1,.mx-xxl-n1{margin-right:-.25rem!important}.mb-xxl-n1,.my-xxl-n1{margin-bottom:-.25rem!important}.ml-xxl-n1,.mx-xxl-n1{margin-left:-.25rem!important}.m-xxl-n2{margin:-.5rem!important}.mt-xxl-n2,.my-xxl-n2{margin-top:-.5rem!important}.mr-xxl-n2,.mx-xxl-n2{margin-right:-.5rem!important}.mb-xxl-n2,.my-xxl-n2{margin-bottom:-.5rem!important}.ml-xxl-n2,.mx-xxl-n2{margin-left:-.5rem!important}.m-xxl-n3{margin:-1rem!important}.mt-xxl-n3,.my-xxl-n3{margin-top:-1rem!important}.mr-xxl-n3,.mx-xxl-n3{margin-right:-1rem!important}.mb-xxl-n3,.my-xxl-n3{margin-bottom:-1rem!important}.ml-xxl-n3,.mx-xxl-n3{margin-left:-1rem!important}.m-xxl-n4{margin:-1.5rem!important}.mt-xxl-n4,.my-xxl-n4{margin-top:-1.5rem!important}.mr-xxl-n4,.mx-xxl-n4{margin-right:-1.5rem!important}.mb-xxl-n4,.my-xxl-n4{margin-bottom:-1.5rem!important}.ml-xxl-n4,.mx-xxl-n4{margin-left:-1.5rem!important}.m-xxl-n5{margin:-3rem!important}.mt-xxl-n5,.my-xxl-n5{margin-top:-3rem!important}.mr-xxl-n5,.mx-xxl-n5{margin-right:-3rem!important}.mb-xxl-n5,.my-xxl-n5{margin-bottom:-3rem!important}.ml-xxl-n5,.mx-xxl-n5{margin-left:-3rem!important}.m-xxl-auto{margin:auto!important}.mt-xxl-auto,.my-xxl-auto{margin-top:auto!important}.mr-xxl-auto,.mx-xxl-auto{margin-right:auto!important}.mb-xxl-auto,.my-xxl-auto{margin-bottom:auto!important}.ml-xxl-auto,.mx-xxl-auto{margin-left:auto!important}}@media(min-width:2500px){.m-xxxl-0{margin:0!important}.mt-xxxl-0,.my-xxxl-0{margin-top:0!important}.mr-xxxl-0,.mx-xxxl-0{margin-right:0!important}.mb-xxxl-0,.my-xxxl-0{margin-bottom:0!important}.ml-xxxl-0,.mx-xxxl-0{margin-left:0!important}.m-xxxl-1{margin:.25rem!important}.mt-xxxl-1,.my-xxxl-1{margin-top:.25rem!important}.mr-xxxl-1,.mx-xxxl-1{margin-right:.25rem!important}.mb-xxxl-1,.my-xxxl-1{margin-bottom:.25rem!important}.ml-xxxl-1,.mx-xxxl-1{margin-left:.25rem!important}.m-xxxl-2{margin:.5rem!important}.mt-xxxl-2,.my-xxxl-2{margin-top:.5rem!important}.mr-xxxl-2,.mx-xxxl-2{margin-right:.5rem!important}.mb-xxxl-2,.my-xxxl-2{margin-bottom:.5rem!important}.ml-xxxl-2,.mx-xxxl-2{margin-left:.5rem!important}.m-xxxl-3{margin:1rem!important}.mt-xxxl-3,.my-xxxl-3{margin-top:1rem!important}.mr-xxxl-3,.mx-xxxl-3{margin-right:1rem!important}.mb-xxxl-3,.my-xxxl-3{margin-bottom:1rem!important}.ml-xxxl-3,.mx-xxxl-3{margin-left:1rem!important}.m-xxxl-4{margin:1.5rem!important}.mt-xxxl-4,.my-xxxl-4{margin-top:1.5rem!important}.mr-xxxl-4,.mx-xxxl-4{margin-right:1.5rem!important}.mb-xxxl-4,.my-xxxl-4{margin-bottom:1.5rem!important}.ml-xxxl-4,.mx-xxxl-4{margin-left:1.5rem!important}.m-xxxl-5{margin:3rem!important}.mt-xxxl-5,.my-xxxl-5{margin-top:3rem!important}.mr-xxxl-5,.mx-xxxl-5{margin-right:3rem!important}.mb-xxxl-5,.my-xxxl-5{margin-bottom:3rem!important}.ml-xxxl-5,.mx-xxxl-5{margin-left:3rem!important}.p-xxxl-0{padding:0!important}.pt-xxxl-0,.py-xxxl-0{padding-top:0!important}.pr-xxxl-0,.px-xxxl-0{padding-right:0!important}.pb-xxxl-0,.py-xxxl-0{padding-bottom:0!important}.pl-xxxl-0,.px-xxxl-0{padding-left:0!important}.p-xxxl-1{padding:.25rem!important}.pt-xxxl-1,.py-xxxl-1{padding-top:.25rem!important}.pr-xxxl-1,.px-xxxl-1{padding-right:.25rem!important}.pb-xxxl-1,.py-xxxl-1{padding-bottom:.25rem!important}.pl-xxxl-1,.px-xxxl-1{padding-left:.25rem!important}.p-xxxl-2{padding:.5rem!important}.pt-xxxl-2,.py-xxxl-2{padding-top:.5rem!important}.pr-xxxl-2,.px-xxxl-2{padding-right:.5rem!important}.pb-xxxl-2,.py-xxxl-2{padding-bottom:.5rem!important}.pl-xxxl-2,.px-xxxl-2{padding-left:.5rem!important}.p-xxxl-3{padding:1rem!important}.pt-xxxl-3,.py-xxxl-3{padding-top:1rem!important}.pr-xxxl-3,.px-xxxl-3{padding-right:1rem!important}.pb-xxxl-3,.py-xxxl-3{padding-bottom:1rem!important}.pl-xxxl-3,.px-xxxl-3{padding-left:1rem!important}.p-xxxl-4{padding:1.5rem!important}.pt-xxxl-4,.py-xxxl-4{padding-top:1.5rem!important}.pr-xxxl-4,.px-xxxl-4{padding-right:1.5rem!important}.pb-xxxl-4,.py-xxxl-4{padding-bottom:1.5rem!important}.pl-xxxl-4,.px-xxxl-4{padding-left:1.5rem!important}.p-xxxl-5{padding:3rem!important}.pt-xxxl-5,.py-xxxl-5{padding-top:3rem!important}.pr-xxxl-5,.px-xxxl-5{padding-right:3rem!important}.pb-xxxl-5,.py-xxxl-5{padding-bottom:3rem!important}.pl-xxxl-5,.px-xxxl-5{padding-left:3rem!important}.m-xxxl-n1{margin:-.25rem!important}.mt-xxxl-n1,.my-xxxl-n1{margin-top:-.25rem!important}.mr-xxxl-n1,.mx-xxxl-n1{margin-right:-.25rem!important}.mb-xxxl-n1,.my-xxxl-n1{margin-bottom:-.25rem!important}.ml-xxxl-n1,.mx-xxxl-n1{margin-left:-.25rem!important}.m-xxxl-n2{margin:-.5rem!important}.mt-xxxl-n2,.my-xxxl-n2{margin-top:-.5rem!important}.mr-xxxl-n2,.mx-xxxl-n2{margin-right:-.5rem!important}.mb-xxxl-n2,.my-xxxl-n2{margin-bottom:-.5rem!important}.ml-xxxl-n2,.mx-xxxl-n2{margin-left:-.5rem!important}.m-xxxl-n3{margin:-1rem!important}.mt-xxxl-n3,.my-xxxl-n3{margin-top:-1rem!important}.mr-xxxl-n3,.mx-xxxl-n3{margin-right:-1rem!important}.mb-xxxl-n3,.my-xxxl-n3{margin-bottom:-1rem!important}.ml-xxxl-n3,.mx-xxxl-n3{margin-left:-1rem!important}.m-xxxl-n4{margin:-1.5rem!important}.mt-xxxl-n4,.my-xxxl-n4{margin-top:-1.5rem!important}.mr-xxxl-n4,.mx-xxxl-n4{margin-right:-1.5rem!important}.mb-xxxl-n4,.my-xxxl-n4{margin-bottom:-1.5rem!important}.ml-xxxl-n4,.mx-xxxl-n4{margin-left:-1.5rem!important}.m-xxxl-n5{margin:-3rem!important}.mt-xxxl-n5,.my-xxxl-n5{margin-top:-3rem!important}.mr-xxxl-n5,.mx-xxxl-n5{margin-right:-3rem!important}.mb-xxxl-n5,.my-xxxl-n5{margin-bottom:-3rem!important}.ml-xxxl-n5,.mx-xxxl-n5{margin-left:-3rem!important}.m-xxxl-auto{margin:auto!important}.mt-xxxl-auto,.my-xxxl-auto{margin-top:auto!important}.mr-xxxl-auto,.mx-xxxl-auto{margin-right:auto!important}.mb-xxxl-auto,.my-xxxl-auto{margin-bottom:auto!important}.ml-xxxl-auto,.mx-xxxl-auto{margin-left:auto!important}}.stretched-link:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:transparent}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media(min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1100px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}@media(min-width:1600px){.text-xxl-left{text-align:left!important}.text-xxl-right{text-align:right!important}.text-xxl-center{text-align:center!important}}@media(min-width:2500px){.text-xxxl-left{text-align:left!important}.text-xxxl-right{text-align:right!important}.text-xxxl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#188191!important}a.text-primary:focus,a.text-primary:hover{color:#0d474f!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#09b3ad!important}a.text-info:focus,a.text-info:hover{color:#056a67!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#000!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd}blockquote,img,pre,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}.bv-no-focus-ring:focus{outline:none}@media(max-width:575.98px){.bv-d-xs-down-none{display:none!important}}@media(max-width:767.98px){.bv-d-sm-down-none{display:none!important}}@media(max-width:991.98px){.bv-d-md-down-none{display:none!important}}@media(max-width:1099.98px){.bv-d-lg-down-none{display:none!important}}@media(max-width:1599.98px){.bv-d-xl-down-none{display:none!important}}@media(max-width:2499.98px){.bv-d-xxl-down-none{display:none!important}}.bv-d-xxxl-down-none{display:none!important}.form-control.focus{color:#495057;background-color:#fff;border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.form-control.focus.is-valid{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.focus.is-invalid{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.b-avatar{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;flex-shrink:0;width:2.5rem;height:2.5rem;font-size:inherit;font-weight:400;line-height:1;max-width:100%;max-height:auto;text-align:center;overflow:visible;position:relative;transition:color .15s ease-in-out,background-color .15s ease-in-out,box-shadow .15s ease-in-out}.b-avatar:focus{outline:0}.b-avatar.btn,.b-avatar[href]{padding:0;border:0}.b-avatar.btn .b-avatar-img img,.b-avatar[href] .b-avatar-img img{transition:transform .15s ease-in-out}.b-avatar.btn:not(:disabled):not(.disabled),.b-avatar[href]:not(:disabled):not(.disabled){cursor:pointer}.b-avatar.btn:not(:disabled):not(.disabled):hover .b-avatar-img img,.b-avatar[href]:not(:disabled):not(.disabled):hover .b-avatar-img img{transform:scale(1.15)}.b-avatar.disabled,.b-avatar:disabled,.b-avatar[disabled]{opacity:.65;pointer-events:none}.b-avatar .b-avatar-custom,.b-avatar .b-avatar-img,.b-avatar .b-avatar-text{border-radius:inherit;width:100%;height:100%;overflow:hidden;display:flex;justify-content:center;align-items:center;-webkit-mask-image:radial-gradient(#fff,#000);mask-image:radial-gradient(#fff,#000)}.b-avatar .b-avatar-text{text-transform:uppercase;white-space:nowrap}.b-avatar[href]{text-decoration:none}.b-avatar>.b-icon{width:60%;height:auto;max-width:100%}.b-avatar .b-avatar-img img{width:100%;height:100%;max-height:auto;border-radius:inherit;-o-object-fit:cover;object-fit:cover}.b-avatar .b-avatar-badge{position:absolute;min-height:1.5em;min-width:1.5em;padding:.25em;line-height:1;border-radius:10em;font-size:70%;font-weight:700;z-index:1}.b-avatar-sm{width:1.5rem;height:1.5rem}.b-avatar-sm .b-avatar-text{font-size:.6rem}.b-avatar-sm .b-avatar-badge{font-size:.42rem}.b-avatar-lg{width:3.5rem;height:3.5rem}.b-avatar-lg .b-avatar-text{font-size:1.4rem}.b-avatar-lg .b-avatar-badge{font-size:.98rem}.b-avatar-group .b-avatar-group-inner{display:flex;flex-wrap:wrap}.b-avatar-group .b-avatar{border:1px solid #dee2e6}.b-avatar-group .btn.b-avatar:hover:not(.disabled):not(disabled),.b-avatar-group a.b-avatar:hover:not(.disabled):not(disabled){z-index:1}.b-calendar{display:inline-flex}.b-calendar .b-calendar-inner{min-width:250px}.b-calendar .b-calendar-header,.b-calendar .b-calendar-nav{margin-bottom:.25rem}.b-calendar .b-calendar-nav .btn{padding:.25rem}.b-calendar output{padding:.25rem;font-size:80%}.b-calendar output.readonly{background-color:#e9ecef;opacity:1}.b-calendar .b-calendar-footer{margin-top:.5rem}.b-calendar .b-calendar-grid{padding:0;margin:0;overflow:hidden}.b-calendar .b-calendar-grid .row{flex-wrap:nowrap}.b-calendar .b-calendar-grid-caption{padding:.25rem}.b-calendar .b-calendar-grid-body .col[data-date] .btn{width:32px;height:32px;font-size:14px;line-height:1;margin:3px auto;padding:9px 0}.b-calendar .btn.disabled,.b-calendar .btn:disabled,.b-calendar .btn[aria-disabled=true]{cursor:default;pointer-events:none}.card-img-left{border-top-left-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-img-right{border-top-right-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.dropdown.dropleft .dropdown-toggle.dropdown-toggle-no-caret:before,.dropdown:not(.dropleft) .dropdown-toggle.dropdown-toggle-no-caret:after{display:none!important}.dropdown .dropdown-menu:focus{outline:none}.b-dropdown-form{display:inline-block;padding:.25rem 1.5rem;width:100%;clear:both;font-weight:400}.b-dropdown-form:focus{outline:1px dotted!important;outline:5px auto -webkit-focus-ring-color!important}.b-dropdown-form.disabled,.b-dropdown-form:disabled{outline:0!important;color:#adb5bd;pointer-events:none}.b-dropdown-text{display:inline-block;padding:.25rem 1.5rem;margin-bottom:0;width:100%;clear:both;font-weight:lighter}.custom-checkbox.b-custom-control-lg,.input-group-lg .custom-checkbox{font-size:1.25rem;line-height:1.5;padding-left:1.875rem}.custom-checkbox.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-checkbox .custom-control-label:before{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;border-radius:.3rem}.custom-checkbox.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-checkbox .custom-control-label:after{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;background-size:50% 50%}.custom-checkbox.b-custom-control-sm,.input-group-sm .custom-checkbox{font-size:.875rem;line-height:1.5;padding-left:1.3125rem}.custom-checkbox.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-checkbox .custom-control-label:before{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;border-radius:.2rem}.custom-checkbox.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-checkbox .custom-control-label:after{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;background-size:50% 50%}.custom-switch.b-custom-control-lg,.input-group-lg .custom-switch{padding-left:2.8125rem}.custom-switch.b-custom-control-lg .custom-control-label,.input-group-lg .custom-switch .custom-control-label{font-size:1.25rem;line-height:1.5}.custom-switch.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-switch .custom-control-label:before{top:.3125rem;height:1.25rem;left:-2.8125rem;width:2.1875rem;border-radius:.625rem}.custom-switch.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-switch .custom-control-label:after{top:calc(.3125rem + 2px);left:calc(-2.8125rem + 2px);width:calc(1.25rem - 4px);height:calc(1.25rem - 4px);border-radius:.625rem;background-size:50% 50%}.custom-switch.b-custom-control-lg .custom-control-input:checked~.custom-control-label:after,.input-group-lg .custom-switch .custom-control-input:checked~.custom-control-label:after{transform:translateX(.9375rem)}.custom-switch.b-custom-control-sm,.input-group-sm .custom-switch{padding-left:1.96875rem}.custom-switch.b-custom-control-sm .custom-control-label,.input-group-sm .custom-switch .custom-control-label{font-size:.875rem;line-height:1.5}.custom-switch.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-switch .custom-control-label:before{top:.21875rem;left:-1.96875rem;width:1.53125rem;height:.875rem;border-radius:.4375rem}.custom-switch.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-switch .custom-control-label:after{top:calc(.21875rem + 2px);left:calc(-1.96875rem + 2px);width:calc(.875rem - 4px);height:calc(.875rem - 4px);border-radius:.4375rem;background-size:50% 50%}.custom-switch.b-custom-control-sm .custom-control-input:checked~.custom-control-label:after,.input-group-sm .custom-switch .custom-control-input:checked~.custom-control-label:after{transform:translateX(.65625rem)}.input-group>.input-group-append:last-child>.btn-group:not(:last-child):not(.dropdown-toggle)>.btn,.input-group>.input-group-append:not(:last-child)>.btn-group>.btn,.input-group>.input-group-prepend>.btn-group>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn-group>.btn,.input-group>.input-group-prepend:first-child>.btn-group:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.btn-group>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.b-form-btn-label-control.form-control{display:flex;align-items:stretch;height:auto;padding:0;background-image:none}.input-group .b-form-btn-label-control.form-control{padding:0}.b-form-btn-label-control.form-control[dir=rtl],[dir=rtl] .b-form-btn-label-control.form-control{flex-direction:row-reverse}.b-form-btn-label-control.form-control[dir=rtl]>label,[dir=rtl] .b-form-btn-label-control.form-control>label{text-align:right}.b-form-btn-label-control.form-control>.btn{line-height:1;font-size:inherit;box-shadow:none!important;border:0}.b-form-btn-label-control.form-control>.btn:disabled{pointer-events:none}.b-form-btn-label-control.form-control.is-valid>.btn{color:#28a745}.b-form-btn-label-control.form-control.is-invalid>.btn{color:#dc3545}.b-form-btn-label-control.form-control>.dropdown-menu{padding:.5rem}.b-form-btn-label-control.form-control>.form-control{height:auto;min-height:calc(1.5em + .75rem);padding-left:.25rem;margin:0;border:0;outline:0;background:transparent;word-break:break-word;font-size:inherit;white-space:normal;cursor:pointer}.b-form-btn-label-control.form-control>.form-control.form-control-sm{min-height:calc(1.5em + .5rem)}.b-form-btn-label-control.form-control>.form-control.form-control-lg{min-height:calc(1.5em + 1rem)}.input-group.input-group-sm .b-form-btn-label-control.form-control>.form-control{min-height:calc(1.5em + .5rem);padding-top:.25rem;padding-bottom:.25rem}.input-group.input-group-lg .b-form-btn-label-control.form-control>.form-control{min-height:calc(1.5em + 1rem);padding-top:.5rem;padding-bottom:.5rem}.b-form-btn-label-control.form-control[aria-disabled=true],.b-form-btn-label-control.form-control[aria-readonly=true]{background-color:#e9ecef;opacity:1}.b-form-btn-label-control.form-control[aria-disabled=true]{pointer-events:none}.b-form-btn-label-control.form-control[aria-disabled=true]>label{cursor:default}.b-form-btn-label-control.btn-group>.dropdown-menu{padding:.5rem}.custom-file-label{white-space:nowrap;overflow-x:hidden}.b-custom-control-lg .custom-file-input,.b-custom-control-lg .custom-file-label,.b-custom-control-lg.custom-file,.input-group-lg .custom-file-input,.input-group-lg .custom-file-label,.input-group-lg.custom-file{font-size:1.25rem;height:calc(1.5em + 1rem + 2px)}.b-custom-control-lg .custom-file-label,.b-custom-control-lg .custom-file-label:after,.input-group-lg .custom-file-label,.input-group-lg .custom-file-label:after{padding:.5rem 1rem;line-height:1.5}.b-custom-control-lg .custom-file-label,.input-group-lg .custom-file-label{border-radius:.3rem}.b-custom-control-lg .custom-file-label:after,.input-group-lg .custom-file-label:after{font-size:inherit;height:calc(1.5em + 1rem);border-radius:0 .3rem .3rem 0}.b-custom-control-sm .custom-file-input,.b-custom-control-sm .custom-file-label,.b-custom-control-sm.custom-file,.input-group-sm .custom-file-input,.input-group-sm .custom-file-label,.input-group-sm.custom-file{font-size:.875rem;height:calc(1.5em + .5rem + 2px)}.b-custom-control-sm .custom-file-label,.b-custom-control-sm .custom-file-label:after,.input-group-sm .custom-file-label,.input-group-sm .custom-file-label:after{padding:.25rem .5rem;line-height:1.5}.b-custom-control-sm .custom-file-label,.input-group-sm .custom-file-label{border-radius:.2rem}.b-custom-control-sm .custom-file-label:after,.input-group-sm .custom-file-label:after{font-size:inherit;height:calc(1.5em + .5rem);border-radius:0 .2rem .2rem 0}.form-control.is-invalid,.form-control.is-valid,.was-validated .form-control:invalid,.was-validated .form-control:valid{background-position:right calc(.375em + .1875rem) center}input[type=color].form-control{height:calc(1.5em + .75rem + 2px);padding:.125rem .25rem}.input-group-sm input[type=color].form-control,input[type=color].form-control.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.125rem .25rem}.input-group-lg input[type=color].form-control,input[type=color].form-control.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.125rem .25rem}input[type=color].form-control:disabled{background-color:#adb5bd;opacity:.65}.input-group>.custom-range{position:relative;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-range,.input-group>.custom-range+.custom-file,.input-group>.custom-range+.custom-range,.input-group>.custom-range+.custom-select,.input-group>.custom-range+.form-control,.input-group>.custom-range+.form-control-plaintext,.input-group>.custom-select+.custom-range,.input-group>.form-control+.custom-range,.input-group>.form-control-plaintext+.custom-range{margin-left:-1px}.input-group>.custom-range:focus{z-index:3}.input-group>.custom-range:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-range:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-range{padding:0 .75rem;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;height:calc(1.5em + .75rem + 2px);border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.input-group>.custom-range{transition:none}}.input-group>.custom-range:focus{color:#495057;background-color:#fff;border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.input-group>.custom-range:disabled,.input-group>.custom-range[readonly]{background-color:#e9ecef}.input-group-lg>.custom-range{height:calc(1.5em + 1rem + 2px);padding:0 1rem;border-radius:.3rem}.input-group-sm>.custom-range{height:calc(1.5em + .5rem + 2px);padding:0 .5rem;border-radius:.2rem}.input-group .custom-range.is-valid,.was-validated .input-group .custom-range:valid{border-color:#28a745}.input-group .custom-range.is-valid:focus,.was-validated .input-group .custom-range:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-range.is-valid:focus::-webkit-slider-thumb,.was-validated .custom-range:valid:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid:focus::-moz-range-thumb,.was-validated .custom-range:valid:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid:focus::-ms-thumb,.was-validated .custom-range:valid:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid::-webkit-slider-thumb,.was-validated .custom-range:valid::-webkit-slider-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-webkit-slider-thumb:active,.was-validated .custom-range:valid::-webkit-slider-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-webkit-slider-runnable-track,.was-validated .custom-range:valid::-webkit-slider-runnable-track{background-color:rgba(40,167,69,.35)}.custom-range.is-valid::-moz-range-thumb,.was-validated .custom-range:valid::-moz-range-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-moz-range-thumb:active,.was-validated .custom-range:valid::-moz-range-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-moz-range-track,.was-validated .custom-range:valid::-moz-range-track{background:rgba(40,167,69,.35)}.custom-range.is-valid~.valid-feedback,.custom-range.is-valid~.valid-tooltip,.was-validated .custom-range:valid~.valid-feedback,.was-validated .custom-range:valid~.valid-tooltip{display:block}.custom-range.is-valid::-ms-thumb,.was-validated .custom-range:valid::-ms-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-ms-thumb:active,.was-validated .custom-range:valid::-ms-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-ms-track-lower,.was-validated .custom-range:valid::-ms-track-lower{background:rgba(40,167,69,.35)}.custom-range.is-valid::-ms-track-upper,.was-validated .custom-range:valid::-ms-track-upper{background:rgba(40,167,69,.35)}.input-group .custom-range.is-invalid,.was-validated .input-group .custom-range:invalid{border-color:#dc3545}.input-group .custom-range.is-invalid:focus,.was-validated .input-group .custom-range:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-range.is-invalid:focus::-webkit-slider-thumb,.was-validated .custom-range:invalid:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid:focus::-moz-range-thumb,.was-validated .custom-range:invalid:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid:focus::-ms-thumb,.was-validated .custom-range:invalid:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid::-webkit-slider-thumb,.was-validated .custom-range:invalid::-webkit-slider-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-webkit-slider-thumb:active,.was-validated .custom-range:invalid::-webkit-slider-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-webkit-slider-runnable-track,.was-validated .custom-range:invalid::-webkit-slider-runnable-track{background-color:rgba(220,53,69,.35)}.custom-range.is-invalid::-moz-range-thumb,.was-validated .custom-range:invalid::-moz-range-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-moz-range-thumb:active,.was-validated .custom-range:invalid::-moz-range-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-moz-range-track,.was-validated .custom-range:invalid::-moz-range-track{background:rgba(220,53,69,.35)}.custom-range.is-invalid~.invalid-feedback,.custom-range.is-invalid~.invalid-tooltip,.was-validated .custom-range:invalid~.invalid-feedback,.was-validated .custom-range:invalid~.invalid-tooltip{display:block}.custom-range.is-invalid::-ms-thumb,.was-validated .custom-range:invalid::-ms-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-ms-thumb:active,.was-validated .custom-range:invalid::-ms-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-ms-track-lower,.was-validated .custom-range:invalid::-ms-track-lower{background:rgba(220,53,69,.35)}.custom-range.is-invalid::-ms-track-upper,.was-validated .custom-range:invalid::-ms-track-upper{background:rgba(220,53,69,.35)}.custom-radio.b-custom-control-lg,.input-group-lg .custom-radio{font-size:1.25rem;line-height:1.5;padding-left:1.875rem}.custom-radio.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-radio .custom-control-label:before{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;border-radius:50%}.custom-radio.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-radio .custom-control-label:after{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;background:no-repeat 50%/50% 50%}.custom-radio.b-custom-control-sm,.input-group-sm .custom-radio{font-size:.875rem;line-height:1.5;padding-left:1.3125rem}.custom-radio.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-radio .custom-control-label:before{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;border-radius:50%}.custom-radio.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-radio .custom-control-label:after{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;background:no-repeat 50%/50% 50%}.b-rating{text-align:center}.b-rating.d-inline-flex{width:auto}.b-rating .b-rating-star,.b-rating .b-rating-value{padding:0 .25em}.b-rating .b-rating-value{min-width:2.5em}.b-rating .b-rating-star{display:inline-flex;justify-content:center;outline:0}.b-rating .b-rating-star .b-rating-icon{display:inline-flex;transition:all .15s ease-in-out}.b-rating.disabled,.b-rating:disabled{background-color:#e9ecef;color:#6c757d}.b-rating:not(.disabled):not(.readonly) .b-rating-star{cursor:pointer}.b-rating:not(.disabled):not(.readonly) .b-rating-star:hover .b-rating-icon,.b-rating:not(.disabled):not(.readonly):focus:not(:hover) .b-rating-star.focused .b-rating-icon{transform:scale(1.5)}.b-rating[dir=rtl] .b-rating-star-half{transform:scaleX(-1)}.b-form-spinbutton{text-align:center;overflow:hidden;background-image:none;padding:0}.b-form-spinbutton[dir=rtl]:not(.flex-column),[dir=rtl] .b-form-spinbutton:not(.flex-column){flex-direction:row-reverse}.b-form-spinbutton output{font-size:inherit;outline:0;border:0;background-color:transparent;width:auto;margin:0;padding:0 .25rem}.b-form-spinbutton output>bdi,.b-form-spinbutton output>div{display:block;min-width:2.25em;height:1.5em}.b-form-spinbutton.flex-column{height:auto;width:auto}.b-form-spinbutton.flex-column output{margin:0 .25rem;padding:.25rem 0}.b-form-spinbutton:not(.d-inline-flex):not(.flex-column){output-width:100%}.b-form-spinbutton.d-inline-flex:not(.flex-column){width:auto}.b-form-spinbutton .btn{line-height:1;box-shadow:none!important}.b-form-spinbutton .btn:disabled{pointer-events:none}.b-form-spinbutton .btn:hover:not(:disabled)>div>.b-icon{transform:scale(1.25)}.b-form-spinbutton.disabled,.b-form-spinbutton.readonly{background-color:#e9ecef}.b-form-spinbutton.disabled{pointer-events:none}.b-form-tags.focus{color:#495057;background-color:#fff;border-color:#48cce1;outline:0;box-shadow:0 0 0 .2rem rgba(24,129,145,.25)}.b-form-tags.focus.is-valid{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.b-form-tags.focus.is-invalid{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.b-form-tags.disabled{background-color:#e9ecef}.b-form-tags-list{margin-top:-.25rem}.b-form-tags-list .b-form-tag,.b-form-tags-list .b-form-tags-field{margin-top:.25rem}.b-form-tags-input{color:#495057}.b-form-tag{font-size:75%;font-weight:400;line-height:1.5;margin-right:.25rem}.b-form-tag.disabled{opacity:.75}.b-form-tag>button.b-form-tag-remove{color:inherit;font-size:125%;line-height:1;float:none;margin-left:.25rem}.form-control-lg .b-form-tag,.form-control-sm .b-form-tag{line-height:1.5}.media-aside{display:flex;margin-right:1rem}.media-aside-right{margin-right:0;margin-left:1rem}.modal-backdrop{opacity:.5}.b-pagination-pills .page-item .page-link{border-radius:50rem!important;margin-left:.25rem;line-height:1}.b-pagination-pills .page-item:first-child .page-link{margin-left:0}.popover.b-popover{display:block;opacity:1;outline:0}.popover.b-popover.fade:not(.show){opacity:0}.popover.b-popover.show{opacity:1}.b-popover-primary.popover{background-color:#d1e6e9;border-color:#bedce0}.b-popover-primary.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-primary.bs-popover-top>.arrow:before{border-top-color:#bedce0}.b-popover-primary.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-primary.bs-popover-top>.arrow:after{border-top-color:#d1e6e9}.b-popover-primary.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-primary.bs-popover-right>.arrow:before{border-right-color:#bedce0}.b-popover-primary.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-primary.bs-popover-right>.arrow:after{border-right-color:#d1e6e9}.b-popover-primary.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-primary.bs-popover-bottom>.arrow:before{border-bottom-color:#bedce0}.b-popover-primary.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-primary.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-primary.bs-popover-bottom .popover-header:before,.b-popover-primary.bs-popover-bottom>.arrow:after{border-bottom-color:#c7e0e4}.b-popover-primary.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-primary.bs-popover-left>.arrow:before{border-left-color:#bedce0}.b-popover-primary.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-primary.bs-popover-left>.arrow:after{border-left-color:#d1e6e9}.b-popover-primary .popover-header{color:#212529;background-color:#c7e0e4;border-bottom-color:#b5d7dc}.b-popover-primary .popover-body{color:#0c434b}.b-popover-secondary.popover{background-color:#e2e3e5;border-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-secondary.bs-popover-top>.arrow:before{border-top-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-secondary.bs-popover-top>.arrow:after{border-top-color:#e2e3e5}.b-popover-secondary.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-secondary.bs-popover-right>.arrow:before{border-right-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-secondary.bs-popover-right>.arrow:after{border-right-color:#e2e3e5}.b-popover-secondary.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-secondary.bs-popover-bottom>.arrow:before{border-bottom-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-secondary.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-secondary.bs-popover-bottom .popover-header:before,.b-popover-secondary.bs-popover-bottom>.arrow:after{border-bottom-color:#dadbde}.b-popover-secondary.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-secondary.bs-popover-left>.arrow:before{border-left-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-secondary.bs-popover-left>.arrow:after{border-left-color:#e2e3e5}.b-popover-secondary .popover-header{color:#212529;background-color:#dadbde;border-bottom-color:#ccced2}.b-popover-secondary .popover-body{color:#383d41}.b-popover-success.popover{background-color:#d4edda;border-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-success.bs-popover-top>.arrow:before{border-top-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-success.bs-popover-top>.arrow:after{border-top-color:#d4edda}.b-popover-success.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-success.bs-popover-right>.arrow:before{border-right-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-success.bs-popover-right>.arrow:after{border-right-color:#d4edda}.b-popover-success.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-success.bs-popover-bottom>.arrow:before{border-bottom-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-success.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-success.bs-popover-bottom .popover-header:before,.b-popover-success.bs-popover-bottom>.arrow:after{border-bottom-color:#c9e8d1}.b-popover-success.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-success.bs-popover-left>.arrow:before{border-left-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-success.bs-popover-left>.arrow:after{border-left-color:#d4edda}.b-popover-success .popover-header{color:#212529;background-color:#c9e8d1;border-bottom-color:#b7e1c1}.b-popover-success .popover-body{color:#155724}.b-popover-info.popover{background-color:#cef0ef;border-color:#baeae8}.b-popover-info.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-info.bs-popover-top>.arrow:before{border-top-color:#baeae8}.b-popover-info.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-info.bs-popover-top>.arrow:after{border-top-color:#cef0ef}.b-popover-info.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-info.bs-popover-right>.arrow:before{border-right-color:#baeae8}.b-popover-info.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-info.bs-popover-right>.arrow:after{border-right-color:#cef0ef}.b-popover-info.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-info.bs-popover-bottom>.arrow:before{border-bottom-color:#baeae8}.b-popover-info.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-info.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-info.bs-popover-bottom .popover-header:before,.b-popover-info.bs-popover-bottom>.arrow:after{border-bottom-color:#c2eceb}.b-popover-info.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-info.bs-popover-left>.arrow:before{border-left-color:#baeae8}.b-popover-info.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-info.bs-popover-left>.arrow:after{border-left-color:#cef0ef}.b-popover-info .popover-header{color:#212529;background-color:#c2eceb;border-bottom-color:#afe6e5}.b-popover-info .popover-body{color:#055d5a}.b-popover-warning.popover{background-color:#fff3cd;border-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-warning.bs-popover-top>.arrow:before{border-top-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-warning.bs-popover-top>.arrow:after{border-top-color:#fff3cd}.b-popover-warning.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-warning.bs-popover-right>.arrow:before{border-right-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-warning.bs-popover-right>.arrow:after{border-right-color:#fff3cd}.b-popover-warning.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-warning.bs-popover-bottom>.arrow:before{border-bottom-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-warning.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-warning.bs-popover-bottom .popover-header:before,.b-popover-warning.bs-popover-bottom>.arrow:after{border-bottom-color:#ffefbe}.b-popover-warning.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-warning.bs-popover-left>.arrow:before{border-left-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-warning.bs-popover-left>.arrow:after{border-left-color:#fff3cd}.b-popover-warning .popover-header{color:#212529;background-color:#ffefbe;border-bottom-color:#ffe9a4}.b-popover-warning .popover-body{color:#856404}.b-popover-danger.popover{background-color:#f8d7da;border-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-danger.bs-popover-top>.arrow:before{border-top-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-danger.bs-popover-top>.arrow:after{border-top-color:#f8d7da}.b-popover-danger.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-danger.bs-popover-right>.arrow:before{border-right-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-danger.bs-popover-right>.arrow:after{border-right-color:#f8d7da}.b-popover-danger.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-danger.bs-popover-bottom>.arrow:before{border-bottom-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-danger.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-danger.bs-popover-bottom .popover-header:before,.b-popover-danger.bs-popover-bottom>.arrow:after{border-bottom-color:#f6cace}.b-popover-danger.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-danger.bs-popover-left>.arrow:before{border-left-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-danger.bs-popover-left>.arrow:after{border-left-color:#f8d7da}.b-popover-danger .popover-header{color:#212529;background-color:#f6cace;border-bottom-color:#f2b4ba}.b-popover-danger .popover-body{color:#721c24}.b-popover-light.popover{background-color:#fefefe;border-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-light.bs-popover-top>.arrow:before{border-top-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-light.bs-popover-top>.arrow:after{border-top-color:#fefefe}.b-popover-light.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-light.bs-popover-right>.arrow:before{border-right-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-light.bs-popover-right>.arrow:after{border-right-color:#fefefe}.b-popover-light.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-light.bs-popover-bottom>.arrow:before{border-bottom-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-light.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-light.bs-popover-bottom .popover-header:before,.b-popover-light.bs-popover-bottom>.arrow:after{border-bottom-color:#f6f6f6}.b-popover-light.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-light.bs-popover-left>.arrow:before{border-left-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-light.bs-popover-left>.arrow:after{border-left-color:#fefefe}.b-popover-light .popover-header{color:#212529;background-color:#f6f6f6;border-bottom-color:#eaeaea}.b-popover-light .popover-body{color:#818182}.b-popover-dark.popover{background-color:#d6d8d9;border-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-dark.bs-popover-top>.arrow:before{border-top-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-dark.bs-popover-top>.arrow:after{border-top-color:#d6d8d9}.b-popover-dark.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-dark.bs-popover-right>.arrow:before{border-right-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-dark.bs-popover-right>.arrow:after{border-right-color:#d6d8d9}.b-popover-dark.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-dark.bs-popover-bottom>.arrow:before{border-bottom-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-dark.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-dark.bs-popover-bottom .popover-header:before,.b-popover-dark.bs-popover-bottom>.arrow:after{border-bottom-color:#ced0d2}.b-popover-dark.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-dark.bs-popover-left>.arrow:before{border-left-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-dark.bs-popover-left>.arrow:after{border-left-color:#d6d8d9}.b-popover-dark .popover-header{color:#212529;background-color:#ced0d2;border-bottom-color:#c1c4c5}.b-popover-dark .popover-body{color:#1b1e21}.b-sidebar-outer{position:fixed;top:0;left:0;right:0;height:0;overflow:visible;z-index:1035}.b-sidebar-backdrop{left:0;z-index:-1;width:100vw;opacity:.6}.b-sidebar,.b-sidebar-backdrop{position:fixed;top:0;height:100vh}.b-sidebar{display:flex;flex-direction:column;width:320px;max-width:100%;max-height:100%;margin:0;outline:0;transform:translateX(0)}.b-sidebar.slide{transition:transform .3s ease-in-out}@media(prefers-reduced-motion:reduce){.b-sidebar.slide{transition:none}}.b-sidebar:not(.b-sidebar-right){left:0;right:auto}.b-sidebar:not(.b-sidebar-right).slide:not(.show){transform:translateX(-100%)}.b-sidebar:not(.b-sidebar-right)>.b-sidebar-header .close{margin-left:auto}.b-sidebar.b-sidebar-right{left:auto;right:0}.b-sidebar.b-sidebar-right.slide:not(.show){transform:translateX(100%)}.b-sidebar.b-sidebar-right>.b-sidebar-header .close{margin-right:auto}.b-sidebar>.b-sidebar-header{font-size:1.5rem;padding:.5rem 1rem;display:flex;flex-direction:row;flex-grow:0;align-items:center}[dir=rtl] .b-sidebar>.b-sidebar-header{flex-direction:row-reverse}.b-sidebar>.b-sidebar-header .close{float:none;font-size:1.5rem}.b-sidebar>.b-sidebar-body{flex-grow:1;height:100%;overflow-y:auto}.b-sidebar>.b-sidebar-footer{flex-grow:0}.b-skeleton-wrapper{cursor:wait}.b-skeleton{position:relative;overflow:hidden;background-color:rgba(0,0,0,.12);cursor:wait;-webkit-mask-image:radial-gradient(#fff,#000);mask-image:radial-gradient(#fff,#000)}.b-skeleton:before{content:" "}.b-skeleton-text{height:1rem;margin-bottom:.25rem;border-radius:.25rem}.b-skeleton-button{width:75px;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem}.b-skeleton-avatar{width:2.5em;height:2.5em;border-radius:50%}.b-skeleton-input{height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;line-height:1.5;border:1px solid #ced4da;border-radius:.25rem}.b-skeleton-icon-wrapper svg{color:rgba(0,0,0,.12)}.b-skeleton-img{height:100%;width:100%}.b-skeleton-animate-wave:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;z-index:0;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.4),transparent);animation:b-skeleton-animate-wave 1.75s linear infinite}@media(prefers-reduced-motion:reduce){.b-skeleton-animate-wave:after{background:none;animation:none}}@keyframes b-skeleton-animate-wave{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}.b-skeleton-animate-fade{animation:b-skeleton-animate-fade .875s ease-in-out infinite alternate}@media(prefers-reduced-motion:reduce){.b-skeleton-animate-fade{animation:none}}@keyframes b-skeleton-animate-fade{0%{opacity:1}to{opacity:.4}}.b-skeleton-animate-throb{animation:b-skeleton-animate-throb .875s ease-in infinite alternate}@media(prefers-reduced-motion:reduce){.b-skeleton-animate-throb{animation:none}}@keyframes b-skeleton-animate-throb{0%{transform:scale(1)}to{transform:scale(.975)}}.table.b-table.b-table-fixed{table-layout:fixed}.table.b-table.b-table-no-border-collapse{border-collapse:separate;border-spacing:0}.table.b-table[aria-busy=true]{opacity:.55}.table.b-table>tbody>tr.b-table-details>td{border-top:none!important}.table.b-table>caption{caption-side:bottom}.table.b-table.b-table-caption-top>caption{caption-side:top!important}.table.b-table>tbody>.table-active,.table.b-table>tbody>.table-active>td,.table.b-table>tbody>.table-active>th{background-color:rgba(0,0,0,.075)}.table.b-table.table-hover>tbody>tr.table-active:hover td,.table.b-table.table-hover>tbody>tr.table-active:hover th{color:#000;background-image:linear-gradient(rgba(0,0,0,.075),rgba(0,0,0,.075));background-repeat:no-repeat}.table.b-table>tbody>.bg-active,.table.b-table>tbody>.bg-active>td,.table.b-table>tbody>.bg-active>th{background-color:hsla(0,0%,100%,.075)!important}.table.b-table.table-hover.table-dark>tbody>tr.bg-active:hover td,.table.b-table.table-hover.table-dark>tbody>tr.bg-active:hover th{color:#fff;background-image:linear-gradient(hsla(0,0%,100%,.075),hsla(0,0%,100%,.075));background-repeat:no-repeat}.b-table-sticky-header,.table-responsive,[class*=table-responsive-]{margin-bottom:1rem}.b-table-sticky-header>.table,.table-responsive>.table,[class*=table-responsive-]>.table{margin-bottom:0}.b-table-sticky-header{overflow-y:auto;max-height:300px}@media print{.b-table-sticky-header{overflow-y:visible!important;max-height:none!important}}@supports(position:sticky){.b-table-sticky-header>.table.b-table>thead>tr>th{position:sticky;top:0;z-index:2}.b-table-sticky-header>.table.b-table>tbody>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>tfoot>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>thead>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tbody>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tfoot>tr>.b-table-sticky-column,.table-responsive>.table.b-table>thead>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tbody>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tfoot>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>thead>tr>.b-table-sticky-column{position:sticky;left:0}.b-table-sticky-header>.table.b-table>thead>tr>.b-table-sticky-column,.table-responsive>.table.b-table>thead>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>thead>tr>.b-table-sticky-column{z-index:5}.b-table-sticky-header>.table.b-table>tbody>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>tfoot>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tbody>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tfoot>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tbody>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tfoot>tr>.b-table-sticky-column{z-index:2}.table.b-table>tbody>tr>.table-b-table-default,.table.b-table>tfoot>tr>.table-b-table-default,.table.b-table>thead>tr>.table-b-table-default{color:#000;background-color:#fff}.table.b-table.table-dark>tbody>tr>.bg-b-table-default,.table.b-table.table-dark>tfoot>tr>.bg-b-table-default,.table.b-table.table-dark>thead>tr>.bg-b-table-default{color:#fff;background-color:#343a40}.table.b-table.table-striped>tbody>tr:nth-of-type(odd)>.table-b-table-default{background-image:linear-gradient(rgba(0,0,0,.05),rgba(0,0,0,.05));background-repeat:no-repeat}.table.b-table.table-striped.table-dark>tbody>tr:nth-of-type(odd)>.bg-b-table-default{background-image:linear-gradient(hsla(0,0%,100%,.05),hsla(0,0%,100%,.05));background-repeat:no-repeat}.table.b-table.table-hover>tbody>tr:hover>.table-b-table-default{color:#000;background-image:linear-gradient(rgba(0,0,0,.075),rgba(0,0,0,.075));background-repeat:no-repeat}.table.b-table.table-hover.table-dark>tbody>tr:hover>.bg-b-table-default{color:#fff;background-image:linear-gradient(hsla(0,0%,100%,.075),hsla(0,0%,100%,.075));background-repeat:no-repeat}}.table.b-table>tfoot>tr>[aria-sort],.table.b-table>thead>tr>[aria-sort]{cursor:pointer;background-image:none;background-repeat:no-repeat;background-size:.65em 1em}.table.b-table>tfoot>tr>[aria-sort]:not(.b-table-sort-icon-left),.table.b-table>thead>tr>[aria-sort]:not(.b-table-sort-icon-left){background-position:right .375rem center;padding-right:calc(.75rem + .65em)}.table.b-table>tfoot>tr>[aria-sort].b-table-sort-icon-left,.table.b-table>thead>tr>[aria-sort].b-table-sort-icon-left{background-position:left .375rem center;padding-left:calc(.75rem + .65em)}.table.b-table>tfoot>tr>[aria-sort=none],.table.b-table>thead>tr>[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>[aria-sort=ascending],.table.b-table>thead>tr>[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>[aria-sort=descending],.table.b-table>thead>tr>[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=none],.table.b-table.table-dark>thead>tr>[aria-sort=none],.table.b-table>.thead-dark>tr>[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=ascending],.table.b-table.table-dark>thead>tr>[aria-sort=ascending],.table.b-table>.thead-dark>tr>[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=descending],.table.b-table.table-dark>thead>tr>[aria-sort=descending],.table.b-table>.thead-dark>tr>[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=none],.table.b-table>thead>tr>.table-dark[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=ascending],.table.b-table>thead>tr>.table-dark[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=descending],.table.b-table>thead>tr>.table-dark[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-sm>tfoot>tr>[aria-sort]:not(.b-table-sort-icon-left),.table.b-table.table-sm>thead>tr>[aria-sort]:not(.b-table-sort-icon-left){background-position:right .15rem center;padding-right:calc(.3rem + .65em)}.table.b-table.table-sm>tfoot>tr>[aria-sort].b-table-sort-icon-left,.table.b-table.table-sm>thead>tr>[aria-sort].b-table-sort-icon-left{background-position:left .15rem center;padding-left:calc(.3rem + .65em)}.table.b-table.b-table-selectable:not(.b-table-selectable-no-click)>tbody>tr{cursor:pointer}.table.b-table.b-table-selectable:not(.b-table-selectable-no-click).b-table-selecting.b-table-select-range>tbody>tr{-webkit-user-select:none;-moz-user-select:none;user-select:none}@media(max-width:575.98px){.table.b-table.b-table-stacked-sm{display:block;width:100%}.table.b-table.b-table-stacked-sm>caption,.table.b-table.b-table-stacked-sm>tbody,.table.b-table.b-table-stacked-sm>tbody>tr,.table.b-table.b-table-stacked-sm>tbody>tr>td,.table.b-table.b-table-stacked-sm>tbody>tr>th{display:block}.table.b-table.b-table-stacked-sm>tfoot,.table.b-table.b-table-stacked-sm>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-sm>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-sm>thead,.table.b-table.b-table-stacked-sm>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-sm>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-sm>caption{caption-side:top!important}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-sm>tbody>tr.bottom-row,.table.b-table.b-table-stacked-sm>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-sm>tbody>tr>:first-child,.table.b-table.b-table-stacked-sm>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-sm>tbody>tr>[rowspan]+th{border-top-width:3px}}@media(max-width:767.98px){.table.b-table.b-table-stacked-md{display:block;width:100%}.table.b-table.b-table-stacked-md>caption,.table.b-table.b-table-stacked-md>tbody,.table.b-table.b-table-stacked-md>tbody>tr,.table.b-table.b-table-stacked-md>tbody>tr>td,.table.b-table.b-table-stacked-md>tbody>tr>th{display:block}.table.b-table.b-table-stacked-md>tfoot,.table.b-table.b-table-stacked-md>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-md>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-md>thead,.table.b-table.b-table-stacked-md>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-md>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-md>caption{caption-side:top!important}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-md>tbody>tr.bottom-row,.table.b-table.b-table-stacked-md>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-md>tbody>tr>:first-child,.table.b-table.b-table-stacked-md>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-md>tbody>tr>[rowspan]+th{border-top-width:3px}}@media(max-width:991.98px){.table.b-table.b-table-stacked-lg{display:block;width:100%}.table.b-table.b-table-stacked-lg>caption,.table.b-table.b-table-stacked-lg>tbody,.table.b-table.b-table-stacked-lg>tbody>tr,.table.b-table.b-table-stacked-lg>tbody>tr>td,.table.b-table.b-table-stacked-lg>tbody>tr>th{display:block}.table.b-table.b-table-stacked-lg>tfoot,.table.b-table.b-table-stacked-lg>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-lg>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-lg>thead,.table.b-table.b-table-stacked-lg>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-lg>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-lg>caption{caption-side:top!important}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-lg>tbody>tr.bottom-row,.table.b-table.b-table-stacked-lg>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-lg>tbody>tr>:first-child,.table.b-table.b-table-stacked-lg>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-lg>tbody>tr>[rowspan]+th{border-top-width:3px}}@media(max-width:1099.98px){.table.b-table.b-table-stacked-xl{display:block;width:100%}.table.b-table.b-table-stacked-xl>caption,.table.b-table.b-table-stacked-xl>tbody,.table.b-table.b-table-stacked-xl>tbody>tr,.table.b-table.b-table-stacked-xl>tbody>tr>td,.table.b-table.b-table-stacked-xl>tbody>tr>th{display:block}.table.b-table.b-table-stacked-xl>tfoot,.table.b-table.b-table-stacked-xl>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xl>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-xl>thead,.table.b-table.b-table-stacked-xl>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xl>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-xl>caption{caption-side:top!important}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-xl>tbody>tr.bottom-row,.table.b-table.b-table-stacked-xl>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-xl>tbody>tr>:first-child,.table.b-table.b-table-stacked-xl>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-xl>tbody>tr>[rowspan]+th{border-top-width:3px}}@media(max-width:1599.98px){.table.b-table.b-table-stacked-xxl{display:block;width:100%}.table.b-table.b-table-stacked-xxl>caption,.table.b-table.b-table-stacked-xxl>tbody,.table.b-table.b-table-stacked-xxl>tbody>tr,.table.b-table.b-table-stacked-xxl>tbody>tr>td,.table.b-table.b-table-stacked-xxl>tbody>tr>th{display:block}.table.b-table.b-table-stacked-xxl>tfoot,.table.b-table.b-table-stacked-xxl>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xxl>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-xxl>thead,.table.b-table.b-table-stacked-xxl>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xxl>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-xxl>caption{caption-side:top!important}.table.b-table.b-table-stacked-xxl>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-xxl>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-xxl>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-xxl>tbody>tr.bottom-row,.table.b-table.b-table-stacked-xxl>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-xxl>tbody>tr>:first-child,.table.b-table.b-table-stacked-xxl>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-xxl>tbody>tr>[rowspan]+th{border-top-width:3px}}@media(max-width:2499.98px){.table.b-table.b-table-stacked-xxxl{display:block;width:100%}.table.b-table.b-table-stacked-xxxl>caption,.table.b-table.b-table-stacked-xxxl>tbody,.table.b-table.b-table-stacked-xxxl>tbody>tr,.table.b-table.b-table-stacked-xxxl>tbody>tr>td,.table.b-table.b-table-stacked-xxxl>tbody>tr>th{display:block}.table.b-table.b-table-stacked-xxxl>tfoot,.table.b-table.b-table-stacked-xxxl>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xxxl>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-xxxl>thead,.table.b-table.b-table-stacked-xxxl>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xxxl>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-xxxl>caption{caption-side:top!important}.table.b-table.b-table-stacked-xxxl>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-xxxl>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-xxxl>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-xxxl>tbody>tr.bottom-row,.table.b-table.b-table-stacked-xxxl>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-xxxl>tbody>tr>:first-child,.table.b-table.b-table-stacked-xxxl>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-xxxl>tbody>tr>[rowspan]+th{border-top-width:3px}}.table.b-table.b-table-stacked{display:block;width:100%}.table.b-table.b-table-stacked>caption,.table.b-table.b-table-stacked>tbody,.table.b-table.b-table-stacked>tbody>tr,.table.b-table.b-table-stacked>tbody>tr>td,.table.b-table.b-table-stacked>tbody>tr>th{display:block}.table.b-table.b-table-stacked>tfoot,.table.b-table.b-table-stacked>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked>thead,.table.b-table.b-table-stacked>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked>caption{caption-side:top!important}.table.b-table.b-table-stacked>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked>tbody>tr.bottom-row,.table.b-table.b-table-stacked>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked>tbody>tr>:first-child,.table.b-table.b-table-stacked>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked>tbody>tr>[rowspan]+th{border-top-width:3px}.b-time{min-width:150px}.b-time output.disabled,.b-time[aria-disabled=true] output,.b-time[aria-readonly=true] output{background-color:#e9ecef;opacity:1}.b-time[aria-disabled=true] output{pointer-events:none}[dir=rtl] .b-time>.d-flex:not(.flex-column){flex-direction:row-reverse}.b-time .b-time-header{margin-bottom:.5rem}.b-time .b-time-header output{padding:.25rem;font-size:80%}.b-time .b-time-footer{margin-top:.5rem}.b-time .b-time-ampm{margin-left:.5rem}.b-toast{display:block;position:relative;max-width:350px;backface-visibility:hidden;background-clip:padding-box;z-index:1;border-radius:.25rem}.b-toast .toast{background-color:hsla(0,0%,100%,.85)}.b-toast:not(:last-child){margin-bottom:.75rem}.b-toast.b-toast-solid .toast{background-color:#fff}.b-toast .toast{opacity:1}.b-toast .toast.fade:not(.show){opacity:0}.b-toast .toast .toast-body{display:block}.b-toast-primary .toast{background-color:rgba(226,239,241,.85);border-color:rgba(190,220,224,.85);color:#0c434b}.b-toast-primary .toast .toast-header{color:#0c434b;background-color:rgba(209,230,233,.85);border-bottom-color:rgba(190,220,224,.85)}.b-toast-primary.b-toast-solid .toast{background-color:#e2eff1}.b-toast-secondary .toast{background-color:hsla(210,7%,94%,.85);border-color:hsla(216,6%,85%,.85);color:#383d41}.b-toast-secondary .toast .toast-header{color:#383d41;background-color:hsla(220,5%,89%,.85);border-bottom-color:hsla(216,6%,85%,.85)}.b-toast-secondary.b-toast-solid .toast{background-color:#eff0f1}.b-toast-success .toast{background-color:rgba(230,245,233,.85);border-color:rgba(195,230,203,.85);color:#155724}.b-toast-success .toast .toast-header{color:#155724;background-color:rgba(212,237,218,.85);border-bottom-color:rgba(195,230,203,.85)}.b-toast-success.b-toast-solid .toast{background-color:#e6f5e9}.b-toast-info .toast{background-color:rgba(226,246,245,.85);border-color:rgba(186,234,232,.85);color:#055d5a}.b-toast-info .toast .toast-header{color:#055d5a;background-color:rgba(206,240,239,.85);border-bottom-color:rgba(186,234,232,.85)}.b-toast-info.b-toast-solid .toast{background-color:#e2f6f5}.b-toast-warning .toast{background-color:rgba(255,249,231,.85);border-color:rgba(255,238,186,.85);color:#856404}.b-toast-warning .toast .toast-header{color:#856404;background-color:rgba(255,243,205,.85);border-bottom-color:rgba(255,238,186,.85)}.b-toast-warning.b-toast-solid .toast{background-color:#fff9e7}.b-toast-danger .toast{background-color:rgba(252,237,238,.85);border-color:rgba(245,198,203,.85);color:#721c24}.b-toast-danger .toast .toast-header{color:#721c24;background-color:rgba(248,215,218,.85);border-bottom-color:rgba(245,198,203,.85)}.b-toast-danger.b-toast-solid .toast{background-color:#fcedee}.b-toast-light .toast{background-color:hsla(0,0%,100%,.85);border-color:rgba(253,253,254,.85);color:#818182}.b-toast-light .toast .toast-header{color:#818182;background-color:hsla(0,0%,100%,.85);border-bottom-color:rgba(253,253,254,.85)}.b-toast-light.b-toast-solid .toast{background-color:#fff}.b-toast-dark .toast{background-color:hsla(180,4%,89%,.85);border-color:hsla(210,4%,78%,.85);color:#1b1e21}.b-toast-dark .toast .toast-header{color:#1b1e21;background-color:hsla(200,4%,85%,.85);border-bottom-color:hsla(210,4%,78%,.85)}.b-toast-dark.b-toast-solid .toast{background-color:#e3e5e5}.b-toaster{z-index:1100}.b-toaster .b-toaster-slot{position:relative;display:block}.b-toaster .b-toaster-slot:empty{display:none!important}.b-toaster.b-toaster-bottom-center,.b-toaster.b-toaster-bottom-full,.b-toaster.b-toaster-bottom-left,.b-toaster.b-toaster-bottom-right,.b-toaster.b-toaster-top-center,.b-toaster.b-toaster-top-full,.b-toaster.b-toaster-top-left,.b-toaster.b-toaster-top-right{position:fixed;left:.5rem;right:.5rem;margin:0;padding:0;height:0;overflow:visible}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{position:absolute;max-width:350px;width:100%;left:0;right:0;padding:0;margin:0}.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot .b-toast,.b-toaster.b-toaster-bottom-full .b-toaster-slot .toast,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot .b-toast,.b-toaster.b-toaster-top-full .b-toaster-slot .toast{width:100%;max-width:100%}.b-toaster.b-toaster-top-center,.b-toaster.b-toaster-top-full,.b-toaster.b-toaster-top-left,.b-toaster.b-toaster-top-right{top:0}.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{top:.5rem}.b-toaster.b-toaster-bottom-center,.b-toaster.b-toaster-bottom-full,.b-toaster.b-toaster-bottom-left,.b-toaster.b-toaster-bottom-right{bottom:0}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot{bottom:.5rem}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{margin-left:auto}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot{margin-right:auto}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-move,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-move,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-move,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-move{transition:transform .175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-to .toast.fade{transition-delay:.175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active{position:absolute;transition-delay:.175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active .toast.fade{transition-delay:0s}.tooltip.b-tooltip{display:block;opacity:.9;outline:0}.tooltip.b-tooltip.fade:not(.show){opacity:0}.tooltip.b-tooltip.show{opacity:.9}.tooltip.b-tooltip.noninteractive{pointer-events:none}.tooltip.b-tooltip .arrow{margin:0 .25rem}.tooltip.b-tooltip.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.bs-tooltip-left .arrow,.tooltip.b-tooltip.bs-tooltip-right .arrow{margin:.25rem 0}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-top .arrow:before{border-top-color:#188191}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-right .arrow:before{border-right-color:#188191}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-bottom .arrow:before{border-bottom-color:#188191}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-left .arrow:before{border-left-color:#188191}.tooltip.b-tooltip-primary .tooltip-inner{color:#fff;background-color:#188191}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-top .arrow:before{border-top-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-right .arrow:before{border-right-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-bottom .arrow:before{border-bottom-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-left .arrow:before{border-left-color:#6c757d}.tooltip.b-tooltip-secondary .tooltip-inner{color:#fff;background-color:#6c757d}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-top .arrow:before{border-top-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-right .arrow:before{border-right-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-bottom .arrow:before{border-bottom-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-left .arrow:before{border-left-color:#28a745}.tooltip.b-tooltip-success .tooltip-inner{color:#fff;background-color:#28a745}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-top .arrow:before{border-top-color:#09b3ad}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-right .arrow:before{border-right-color:#09b3ad}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-bottom .arrow:before{border-bottom-color:#09b3ad}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-left .arrow:before{border-left-color:#09b3ad}.tooltip.b-tooltip-info .tooltip-inner{color:#fff;background-color:#09b3ad}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-top .arrow:before{border-top-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-right .arrow:before{border-right-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-bottom .arrow:before{border-bottom-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-left .arrow:before{border-left-color:#ffc107}.tooltip.b-tooltip-warning .tooltip-inner{color:#212529;background-color:#ffc107}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-top .arrow:before{border-top-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-right .arrow:before{border-right-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-bottom .arrow:before{border-bottom-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-left .arrow:before{border-left-color:#dc3545}.tooltip.b-tooltip-danger .tooltip-inner{color:#fff;background-color:#dc3545}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-top .arrow:before{border-top-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-right .arrow:before{border-right-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-bottom .arrow:before{border-bottom-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-left .arrow:before{border-left-color:#f8f9fa}.tooltip.b-tooltip-light .tooltip-inner{color:#212529;background-color:#f8f9fa}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-top .arrow:before{border-top-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-right .arrow:before{border-right-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-bottom .arrow:before{border-bottom-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-left .arrow:before{border-left-color:#343a40}.tooltip.b-tooltip-dark .tooltip-inner{color:#fff;background-color:#343a40}.b-icon.bi{display:inline-block;overflow:visible;vertical-align:-.15em}.b-icon.b-icon-animation-cylon,.b-icon.b-iconstack .b-icon-animation-cylon>g{transform-origin:center;animation:b-icon-animation-cylon .75s ease-in-out infinite alternate}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-cylon,.b-icon.b-iconstack .b-icon-animation-cylon>g{animation:none}}.b-icon.b-icon-animation-cylon-vertical,.b-icon.b-iconstack .b-icon-animation-cylon-vertical>g{transform-origin:center;animation:b-icon-animation-cylon-vertical .75s ease-in-out infinite alternate}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-cylon-vertical,.b-icon.b-iconstack .b-icon-animation-cylon-vertical>g{animation:none}}.b-icon.b-icon-animation-fade,.b-icon.b-iconstack .b-icon-animation-fade>g{transform-origin:center;animation:b-icon-animation-fade .75s ease-in-out infinite alternate}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-fade,.b-icon.b-iconstack .b-icon-animation-fade>g{animation:none}}.b-icon.b-icon-animation-spin,.b-icon.b-iconstack .b-icon-animation-spin>g{transform-origin:center;animation:b-icon-animation-spin 2s linear infinite normal}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin,.b-icon.b-iconstack .b-icon-animation-spin>g{animation:none}}.b-icon.b-icon-animation-spin-reverse,.b-icon.b-iconstack .b-icon-animation-spin-reverse>g{transform-origin:center;animation:b-icon-animation-spin 2s linear infinite reverse}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-reverse,.b-icon.b-iconstack .b-icon-animation-spin-reverse>g{animation:none}}.b-icon.b-icon-animation-spin-pulse,.b-icon.b-iconstack .b-icon-animation-spin-pulse>g{transform-origin:center;animation:b-icon-animation-spin 1s steps(8) infinite normal}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-pulse,.b-icon.b-iconstack .b-icon-animation-spin-pulse>g{animation:none}}.b-icon.b-icon-animation-spin-reverse-pulse,.b-icon.b-iconstack .b-icon-animation-spin-reverse-pulse>g{transform-origin:center;animation:b-icon-animation-spin 1s steps(8) infinite reverse}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-reverse-pulse,.b-icon.b-iconstack .b-icon-animation-spin-reverse-pulse>g{animation:none}}.b-icon.b-icon-animation-throb,.b-icon.b-iconstack .b-icon-animation-throb>g{transform-origin:center;animation:b-icon-animation-throb .75s ease-in-out infinite alternate}@media(prefers-reduced-motion:reduce){.b-icon.b-icon-animation-throb,.b-icon.b-iconstack .b-icon-animation-throb>g{animation:none}}@keyframes b-icon-animation-cylon{0%{transform:translateX(-25%)}to{transform:translateX(25%)}}@keyframes b-icon-animation-cylon-vertical{0%{transform:translateY(25%)}to{transform:translateY(-25%)}}@keyframes b-icon-animation-fade{0%{opacity:.1}to{opacity:1}}@keyframes b-icon-animation-spin{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes b-icon-animation-throb{0%{opacity:.5;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.btn .b-icon.bi,.dropdown-item .b-icon.bi,.dropdown-toggle .b-icon.bi,.input-group-text .b-icon.bi,.nav-link .b-icon.bi{font-size:125%;vertical-align:text-bottom}body,html{height:100%;width:100%}#stac-browser{display:flex;flex-direction:column;max-width:100%;height:100%;min-height:100%;gap:1rem}@media(min-width:2500px){#stac-browser{max-width:75vw}}#stac-browser>header{padding-top:1rem}#stac-browser>header .lead{color:#6c757d}#stac-browser .logo{display:none}#stac-browser>main{flex:1}#stac-browser>footer{padding-bottom:1rem;text-align:center}#stac-browser .map{height:350px;background-color:transparent;border-radius:.25rem}#stac-browser h1{font-weight:700;display:flex;align-items:center}#stac-browser h2{color:#6c757d;font-weight:600}#stac-browser .maps-preview{position:static}#stac-browser .maps-preview .nav-pills{margin:0;padding:0}#stac-browser .maps-preview .nav-pills>li{margin:0 .5rem}#stac-browser .maps-preview .nav-pills>li:only-child{display:none}#stac-browser .maps-preview .nav-pills>li:first-of-type{margin-top:.5rem}#stac-browser .maps-preview .nav-pills>li:last-of-type{margin-bottom:.5rem}#stac-browser>.popover .items .card-columns.count-1{-moz-column-count:1;column-count:1}#stac-browser .twitter{color:#fff;background-color:#1da1f2;border-color:#1da1f2}#stac-browser .twitter:hover{color:#fff;background-color:#0d8ddc;border-color:#0c85d0}#stac-browser .twitter.focus,#stac-browser .twitter:focus{color:#fff;background-color:#0d8ddc;border-color:#0c85d0;box-shadow:0 0 0 .2rem rgba(63,175,244,.5)}#stac-browser .twitter.disabled,#stac-browser .twitter:disabled{color:#fff;background-color:#1da1f2;border-color:#1da1f2}#stac-browser .twitter:not(:disabled):not(.disabled).active,#stac-browser .twitter:not(:disabled):not(.disabled):active,.show>#stac-browser .twitter.dropdown-toggle{color:#fff;background-color:#0c85d0;border-color:#0b7ec4}#stac-browser .twitter:not(:disabled):not(.disabled).active:focus,#stac-browser .twitter:not(:disabled):not(.disabled):active:focus,.show>#stac-browser .twitter.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(63,175,244,.5)}#stac-browser .icon{max-width:32px;max-height:32px}#stac-browser h1>.icon{max-width:1.2em;max-height:1.2em}#stac-browser .global-error{position:fixed;bottom:0;right:0;z-index:5000;opacity:.9;max-width:50vh;margin:1rem}#stac-browser abbr[data-original-title],#stac-browser abbr[title]{text-decoration:none;border-bottom:1px dotted #000}#stac-browser [class*=col]{position:static}#stac-browser .card{background:transparent}#stac-browser input:invalid{border-color:#dc3545}#stac-browser a.list-group-item{color:#188191;text-decoration:none}#stac-browser a.list-group-item:hover{color:#0d474f;text-decoration:underline}#stac-browser .btn-group .btn-primary{border-color:#0d474f}#stac-browser .btn-group .btn-primary:hover{border-color:#020c0e}#stac-browser .btn.disabled,#stac-browser .btn:disabled{cursor:not-allowed;filter:grayscale(1)}#stac-browser .button-label{display:none}@media(min-width:576px){#stac-browser .button-label.prio{display:inline}}@media(min-width:768px){#stac-browser .button-label{display:inline}} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/css/chunk-vendors.de510de6.css b/cumulus_lambda_functions/uds_api/stac_browser/css/chunk-vendors.de510de6.css deleted file mode 100644 index 4795e72c..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/css/chunk-vendors.de510de6.css +++ /dev/null @@ -1,10 +0,0 @@ -@charset "UTF-8"; -/*! - * Bootstrap v4.6.2 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors - * Copyright 2011-2022 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:.875em;color:#6c757d}.blockquote-footer:before{content:"\2014\00A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-1>*{flex:0 0 100%;max-width:100%}.row-cols-2>*{flex:0 0 50%;max-width:50%}.row-cols-3>*{flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{flex:0 0 25%;max-width:25%}.row-cols-5>*{flex:0 0 20%;max-width:20%}.row-cols-6>*{flex:0 0 16.666667%;max-width:16.666667%}.col-auto{flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.333333%;max-width:8.333333%}.col-2{flex:0 0 16.666667%;max-width:16.666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.333333%;max-width:33.333333%}.col-5{flex:0 0 41.666667%;max-width:41.666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.333333%;max-width:58.333333%}.col-8{flex:0 0 66.666667%;max-width:66.666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.333333%;max-width:83.333333%}.col-11{flex:0 0 91.666667%;max-width:91.666667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-sm-1>*{flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-md-1>*{flex:0 0 100%;max-width:100%}.row-cols-md-2>*{flex:0 0 50%;max-width:50%}.row-cols-md-3>*{flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{flex:0 0 25%;max-width:25%}.row-cols-md-5>*{flex:0 0 20%;max-width:20%}.row-cols-md-6>*{flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-lg-1>*{flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.row-cols-xl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:hsla(0,0%,100%,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated select.form-control:valid,select.form-control.is-valid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label:before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated select.form-control:invalid,select.form-control.is-invalid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label:before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary.focus,.btn-primary:focus,.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus,.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,6%,54%,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,6%,54%,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success.focus,.btn-success:focus,.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info.focus,.btn-info:focus,.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning.focus,.btn-warning:focus,.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger.focus,.btn-danger:focus,.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light.focus,.btn-light:focus,.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem hsla(220,4%,85%,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(220,4%,85%,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark.focus,.btn-dark:focus,.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.width{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.width{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label,.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label:after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3),.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label,.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label:after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label:before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label:before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before,.custom-control-input[disabled]~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label:before{pointer-events:none;background-color:#fff;border:1px solid #adb5bd}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%23fff' d='m6.564.75-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label:before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label:after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label:after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label:after{background-color:#fff;transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(1.5em + .75rem + 2px)}.custom-file-input{z-index:2;margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]:after{content:attr(data-browse)}.custom-file-label{left:0;z-index:1;height:calc(1.5em + .75rem + 2px);overflow:hidden;font-weight:400;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label:before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:none;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-moz-column-count:3;column-count:3;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:flex;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:flex;overflow:hidden}.progress-bar{flex-direction:column;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.media{display:flex;align-items:flex-start}.media-body{flex:1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{flex-basis:350px;max-width:350px;font-size:.875rem;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:flex;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable:before{content:none}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem);height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow:before,.bs-popover-top>.arrow:before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow:after,.bs-popover-top>.arrow:after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow:before,.bs-popover-right>.arrow:before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow:after,.bs-popover-right>.arrow:after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow:before,.bs-popover-bottom>.arrow:before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow:after,.bs-popover-bottom>.arrow:after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow:before,.bs-popover-left>.arrow:before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow:after,.bs-popover-left>.arrow:after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='m5.25 0-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='m2.75 0-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@keyframes spinner-border{to{transform:rotate(1turn)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentcolor;border-right-color:transparent;border-radius:50%;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentcolor;border-radius:50%;opacity:0;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.857143%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:1 1 auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media (min-width:576px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media (min-width:768px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports (position:sticky){.sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:transparent}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd}blockquote,img,pre,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*! - * BootstrapVue Custom CSS (https://bootstrap-vue.org) - */.bv-no-focus-ring:focus{outline:none}@media (max-width:575.98px){.bv-d-xs-down-none{display:none!important}}@media (max-width:767.98px){.bv-d-sm-down-none{display:none!important}}@media (max-width:991.98px){.bv-d-md-down-none{display:none!important}}@media (max-width:1199.98px){.bv-d-lg-down-none{display:none!important}}.bv-d-xl-down-none{display:none!important}.form-control.focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control.focus.is-valid{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.focus.is-invalid{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.b-avatar{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;flex-shrink:0;width:2.5rem;height:2.5rem;font-size:inherit;font-weight:400;line-height:1;max-width:100%;max-height:auto;text-align:center;overflow:visible;position:relative;transition:color .15s ease-in-out,background-color .15s ease-in-out,box-shadow .15s ease-in-out}.b-avatar:focus{outline:0}.b-avatar.btn,.b-avatar[href]{padding:0;border:0}.b-avatar.btn .b-avatar-img img,.b-avatar[href] .b-avatar-img img{transition:transform .15s ease-in-out}.b-avatar.btn:not(:disabled):not(.disabled),.b-avatar[href]:not(:disabled):not(.disabled){cursor:pointer}.b-avatar.btn:not(:disabled):not(.disabled):hover .b-avatar-img img,.b-avatar[href]:not(:disabled):not(.disabled):hover .b-avatar-img img{transform:scale(1.15)}.b-avatar.disabled,.b-avatar:disabled,.b-avatar[disabled]{opacity:.65;pointer-events:none}.b-avatar .b-avatar-custom,.b-avatar .b-avatar-img,.b-avatar .b-avatar-text{border-radius:inherit;width:100%;height:100%;overflow:hidden;display:flex;justify-content:center;align-items:center;-webkit-mask-image:radial-gradient(#fff,#000);mask-image:radial-gradient(#fff,#000)}.b-avatar .b-avatar-text{text-transform:uppercase;white-space:nowrap}.b-avatar[href]{text-decoration:none}.b-avatar>.b-icon{width:60%;height:auto;max-width:100%}.b-avatar .b-avatar-img img{width:100%;height:100%;max-height:auto;border-radius:inherit;-o-object-fit:cover;object-fit:cover}.b-avatar .b-avatar-badge{position:absolute;min-height:1.5em;min-width:1.5em;padding:.25em;line-height:1;border-radius:10em;font-size:70%;font-weight:700;z-index:1}.b-avatar-sm{width:1.5rem;height:1.5rem}.b-avatar-sm .b-avatar-text{font-size:.6rem}.b-avatar-sm .b-avatar-badge{font-size:.42rem}.b-avatar-lg{width:3.5rem;height:3.5rem}.b-avatar-lg .b-avatar-text{font-size:1.4rem}.b-avatar-lg .b-avatar-badge{font-size:.98rem}.b-avatar-group .b-avatar-group-inner{display:flex;flex-wrap:wrap}.b-avatar-group .b-avatar{border:1px solid #dee2e6}.b-avatar-group .btn.b-avatar:hover:not(.disabled):not(disabled),.b-avatar-group a.b-avatar:hover:not(.disabled):not(disabled){z-index:1}.b-calendar{display:inline-flex}.b-calendar .b-calendar-inner{min-width:250px}.b-calendar .b-calendar-header,.b-calendar .b-calendar-nav{margin-bottom:.25rem}.b-calendar .b-calendar-nav .btn{padding:.25rem}.b-calendar output{padding:.25rem;font-size:80%}.b-calendar output.readonly{background-color:#e9ecef;opacity:1}.b-calendar .b-calendar-footer{margin-top:.5rem}.b-calendar .b-calendar-grid{padding:0;margin:0;overflow:hidden}.b-calendar .b-calendar-grid .row{flex-wrap:nowrap}.b-calendar .b-calendar-grid-caption{padding:.25rem}.b-calendar .b-calendar-grid-body .col[data-date] .btn{width:32px;height:32px;font-size:14px;line-height:1;margin:3px auto;padding:9px 0}.b-calendar .btn.disabled,.b-calendar .btn:disabled,.b-calendar .btn[aria-disabled=true]{cursor:default;pointer-events:none}.card-img-left{border-top-left-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-img-right{border-top-right-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.dropdown.dropleft .dropdown-toggle.dropdown-toggle-no-caret:before,.dropdown:not(.dropleft) .dropdown-toggle.dropdown-toggle-no-caret:after{display:none!important}.dropdown .dropdown-menu:focus{outline:none}.b-dropdown-form{display:inline-block;padding:.25rem 1.5rem;width:100%;clear:both;font-weight:400}.b-dropdown-form:focus{outline:1px dotted!important;outline:5px auto -webkit-focus-ring-color!important}.b-dropdown-form.disabled,.b-dropdown-form:disabled{outline:0!important;color:#adb5bd;pointer-events:none}.b-dropdown-text{display:inline-block;padding:.25rem 1.5rem;margin-bottom:0;width:100%;clear:both;font-weight:lighter}.custom-checkbox.b-custom-control-lg,.input-group-lg .custom-checkbox{font-size:1.25rem;line-height:1.5;padding-left:1.875rem}.custom-checkbox.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-checkbox .custom-control-label:before{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;border-radius:.3rem}.custom-checkbox.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-checkbox .custom-control-label:after{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;background-size:50% 50%}.custom-checkbox.b-custom-control-sm,.input-group-sm .custom-checkbox{font-size:.875rem;line-height:1.5;padding-left:1.3125rem}.custom-checkbox.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-checkbox .custom-control-label:before{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;border-radius:.2rem}.custom-checkbox.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-checkbox .custom-control-label:after{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;background-size:50% 50%}.custom-switch.b-custom-control-lg,.input-group-lg .custom-switch{padding-left:2.8125rem}.custom-switch.b-custom-control-lg .custom-control-label,.input-group-lg .custom-switch .custom-control-label{font-size:1.25rem;line-height:1.5}.custom-switch.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-switch .custom-control-label:before{top:.3125rem;height:1.25rem;left:-2.8125rem;width:2.1875rem;border-radius:.625rem}.custom-switch.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-switch .custom-control-label:after{top:calc(.3125rem + 2px);left:calc(-2.8125rem + 2px);width:calc(1.25rem - 4px);height:calc(1.25rem - 4px);border-radius:.625rem;background-size:50% 50%}.custom-switch.b-custom-control-lg .custom-control-input:checked~.custom-control-label:after,.input-group-lg .custom-switch .custom-control-input:checked~.custom-control-label:after{transform:translateX(.9375rem)}.custom-switch.b-custom-control-sm,.input-group-sm .custom-switch{padding-left:1.96875rem}.custom-switch.b-custom-control-sm .custom-control-label,.input-group-sm .custom-switch .custom-control-label{font-size:.875rem;line-height:1.5}.custom-switch.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-switch .custom-control-label:before{top:.21875rem;left:-1.96875rem;width:1.53125rem;height:.875rem;border-radius:.4375rem}.custom-switch.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-switch .custom-control-label:after{top:calc(.21875rem + 2px);left:calc(-1.96875rem + 2px);width:calc(.875rem - 4px);height:calc(.875rem - 4px);border-radius:.4375rem;background-size:50% 50%}.custom-switch.b-custom-control-sm .custom-control-input:checked~.custom-control-label:after,.input-group-sm .custom-switch .custom-control-input:checked~.custom-control-label:after{transform:translateX(.65625rem)}.input-group>.input-group-append:last-child>.btn-group:not(:last-child):not(.dropdown-toggle)>.btn,.input-group>.input-group-append:not(:last-child)>.btn-group>.btn,.input-group>.input-group-prepend>.btn-group>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn-group>.btn,.input-group>.input-group-prepend:first-child>.btn-group:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.btn-group>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.b-form-btn-label-control.form-control{display:flex;align-items:stretch;height:auto;padding:0;background-image:none}.input-group .b-form-btn-label-control.form-control{padding:0}.b-form-btn-label-control.form-control[dir=rtl],[dir=rtl] .b-form-btn-label-control.form-control{flex-direction:row-reverse}.b-form-btn-label-control.form-control[dir=rtl]>label,[dir=rtl] .b-form-btn-label-control.form-control>label{text-align:right}.b-form-btn-label-control.form-control>.btn{line-height:1;font-size:inherit;box-shadow:none!important;border:0}.b-form-btn-label-control.form-control>.btn:disabled{pointer-events:none}.b-form-btn-label-control.form-control.is-valid>.btn{color:#28a745}.b-form-btn-label-control.form-control.is-invalid>.btn{color:#dc3545}.b-form-btn-label-control.form-control>.dropdown-menu{padding:.5rem}.b-form-btn-label-control.form-control>.form-control{height:auto;min-height:calc(1.5em + .75rem);padding-left:.25rem;margin:0;border:0;outline:0;background:transparent;word-break:break-word;font-size:inherit;white-space:normal;cursor:pointer}.b-form-btn-label-control.form-control>.form-control.form-control-sm{min-height:calc(1.5em + .5rem)}.b-form-btn-label-control.form-control>.form-control.form-control-lg{min-height:calc(1.5em + 1rem)}.input-group.input-group-sm .b-form-btn-label-control.form-control>.form-control{min-height:calc(1.5em + .5rem);padding-top:.25rem;padding-bottom:.25rem}.input-group.input-group-lg .b-form-btn-label-control.form-control>.form-control{min-height:calc(1.5em + 1rem);padding-top:.5rem;padding-bottom:.5rem}.b-form-btn-label-control.form-control[aria-disabled=true],.b-form-btn-label-control.form-control[aria-readonly=true]{background-color:#e9ecef;opacity:1}.b-form-btn-label-control.form-control[aria-disabled=true]{pointer-events:none}.b-form-btn-label-control.form-control[aria-disabled=true]>label{cursor:default}.b-form-btn-label-control.btn-group>.dropdown-menu{padding:.5rem}.custom-file-label{white-space:nowrap;overflow-x:hidden}.b-custom-control-lg .custom-file-input,.b-custom-control-lg .custom-file-label,.b-custom-control-lg.custom-file,.input-group-lg .custom-file-input,.input-group-lg .custom-file-label,.input-group-lg.custom-file{font-size:1.25rem;height:calc(1.5em + 1rem + 2px)}.b-custom-control-lg .custom-file-label,.b-custom-control-lg .custom-file-label:after,.input-group-lg .custom-file-label,.input-group-lg .custom-file-label:after{padding:.5rem 1rem;line-height:1.5}.b-custom-control-lg .custom-file-label,.input-group-lg .custom-file-label{border-radius:.3rem}.b-custom-control-lg .custom-file-label:after,.input-group-lg .custom-file-label:after{font-size:inherit;height:calc(1.5em + 1rem);border-radius:0 .3rem .3rem 0}.b-custom-control-sm .custom-file-input,.b-custom-control-sm .custom-file-label,.b-custom-control-sm.custom-file,.input-group-sm .custom-file-input,.input-group-sm .custom-file-label,.input-group-sm.custom-file{font-size:.875rem;height:calc(1.5em + .5rem + 2px)}.b-custom-control-sm .custom-file-label,.b-custom-control-sm .custom-file-label:after,.input-group-sm .custom-file-label,.input-group-sm .custom-file-label:after{padding:.25rem .5rem;line-height:1.5}.b-custom-control-sm .custom-file-label,.input-group-sm .custom-file-label{border-radius:.2rem}.b-custom-control-sm .custom-file-label:after,.input-group-sm .custom-file-label:after{font-size:inherit;height:calc(1.5em + .5rem);border-radius:0 .2rem .2rem 0}.form-control.is-invalid,.form-control.is-valid,.was-validated .form-control:invalid,.was-validated .form-control:valid{background-position:right calc(.375em + .1875rem) center}input[type=color].form-control{height:calc(1.5em + .75rem + 2px);padding:.125rem .25rem}.input-group-sm input[type=color].form-control,input[type=color].form-control.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.125rem .25rem}.input-group-lg input[type=color].form-control,input[type=color].form-control.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.125rem .25rem}input[type=color].form-control:disabled{background-color:#adb5bd;opacity:.65}.input-group>.custom-range{position:relative;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-range,.input-group>.custom-range+.custom-file,.input-group>.custom-range+.custom-range,.input-group>.custom-range+.custom-select,.input-group>.custom-range+.form-control,.input-group>.custom-range+.form-control-plaintext,.input-group>.custom-select+.custom-range,.input-group>.form-control+.custom-range,.input-group>.form-control-plaintext+.custom-range{margin-left:-1px}.input-group>.custom-range:focus{z-index:3}.input-group>.custom-range:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-range:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-range{padding:0 .75rem;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;height:calc(1.5em + .75rem + 2px);border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.input-group>.custom-range{transition:none}}.input-group>.custom-range:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.input-group>.custom-range:disabled,.input-group>.custom-range[readonly]{background-color:#e9ecef}.input-group-lg>.custom-range{height:calc(1.5em + 1rem + 2px);padding:0 1rem;border-radius:.3rem}.input-group-sm>.custom-range{height:calc(1.5em + .5rem + 2px);padding:0 .5rem;border-radius:.2rem}.input-group .custom-range.is-valid,.was-validated .input-group .custom-range:valid{border-color:#28a745}.input-group .custom-range.is-valid:focus,.was-validated .input-group .custom-range:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-range.is-valid:focus::-webkit-slider-thumb,.was-validated .custom-range:valid:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid:focus::-moz-range-thumb,.was-validated .custom-range:valid:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid:focus::-ms-thumb,.was-validated .custom-range:valid:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #9be7ac}.custom-range.is-valid::-webkit-slider-thumb,.was-validated .custom-range:valid::-webkit-slider-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-webkit-slider-thumb:active,.was-validated .custom-range:valid::-webkit-slider-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-webkit-slider-runnable-track,.was-validated .custom-range:valid::-webkit-slider-runnable-track{background-color:rgba(40,167,69,.35)}.custom-range.is-valid::-moz-range-thumb,.was-validated .custom-range:valid::-moz-range-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-moz-range-thumb:active,.was-validated .custom-range:valid::-moz-range-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-moz-range-track,.was-validated .custom-range:valid::-moz-range-track{background:rgba(40,167,69,.35)}.custom-range.is-valid~.valid-feedback,.custom-range.is-valid~.valid-tooltip,.was-validated .custom-range:valid~.valid-feedback,.was-validated .custom-range:valid~.valid-tooltip{display:block}.custom-range.is-valid::-ms-thumb,.was-validated .custom-range:valid::-ms-thumb{background-color:#28a745;background-image:none}.custom-range.is-valid::-ms-thumb:active,.was-validated .custom-range:valid::-ms-thumb:active{background-color:#9be7ac;background-image:none}.custom-range.is-valid::-ms-track-lower,.was-validated .custom-range:valid::-ms-track-lower{background:rgba(40,167,69,.35)}.custom-range.is-valid::-ms-track-upper,.was-validated .custom-range:valid::-ms-track-upper{background:rgba(40,167,69,.35)}.input-group .custom-range.is-invalid,.was-validated .input-group .custom-range:invalid{border-color:#dc3545}.input-group .custom-range.is-invalid:focus,.was-validated .input-group .custom-range:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-range.is-invalid:focus::-webkit-slider-thumb,.was-validated .custom-range:invalid:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid:focus::-moz-range-thumb,.was-validated .custom-range:invalid:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid:focus::-ms-thumb,.was-validated .custom-range:invalid:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem #f6cdd1}.custom-range.is-invalid::-webkit-slider-thumb,.was-validated .custom-range:invalid::-webkit-slider-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-webkit-slider-thumb:active,.was-validated .custom-range:invalid::-webkit-slider-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-webkit-slider-runnable-track,.was-validated .custom-range:invalid::-webkit-slider-runnable-track{background-color:rgba(220,53,69,.35)}.custom-range.is-invalid::-moz-range-thumb,.was-validated .custom-range:invalid::-moz-range-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-moz-range-thumb:active,.was-validated .custom-range:invalid::-moz-range-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-moz-range-track,.was-validated .custom-range:invalid::-moz-range-track{background:rgba(220,53,69,.35)}.custom-range.is-invalid~.invalid-feedback,.custom-range.is-invalid~.invalid-tooltip,.was-validated .custom-range:invalid~.invalid-feedback,.was-validated .custom-range:invalid~.invalid-tooltip{display:block}.custom-range.is-invalid::-ms-thumb,.was-validated .custom-range:invalid::-ms-thumb{background-color:#dc3545;background-image:none}.custom-range.is-invalid::-ms-thumb:active,.was-validated .custom-range:invalid::-ms-thumb:active{background-color:#f6cdd1;background-image:none}.custom-range.is-invalid::-ms-track-lower,.was-validated .custom-range:invalid::-ms-track-lower{background:rgba(220,53,69,.35)}.custom-range.is-invalid::-ms-track-upper,.was-validated .custom-range:invalid::-ms-track-upper{background:rgba(220,53,69,.35)}.custom-radio.b-custom-control-lg,.input-group-lg .custom-radio{font-size:1.25rem;line-height:1.5;padding-left:1.875rem}.custom-radio.b-custom-control-lg .custom-control-label:before,.input-group-lg .custom-radio .custom-control-label:before{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;border-radius:50%}.custom-radio.b-custom-control-lg .custom-control-label:after,.input-group-lg .custom-radio .custom-control-label:after{top:.3125rem;left:-1.875rem;width:1.25rem;height:1.25rem;background:no-repeat 50%/50% 50%}.custom-radio.b-custom-control-sm,.input-group-sm .custom-radio{font-size:.875rem;line-height:1.5;padding-left:1.3125rem}.custom-radio.b-custom-control-sm .custom-control-label:before,.input-group-sm .custom-radio .custom-control-label:before{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;border-radius:50%}.custom-radio.b-custom-control-sm .custom-control-label:after,.input-group-sm .custom-radio .custom-control-label:after{top:.21875rem;left:-1.3125rem;width:.875rem;height:.875rem;background:no-repeat 50%/50% 50%}.b-rating{text-align:center}.b-rating.d-inline-flex{width:auto}.b-rating .b-rating-star,.b-rating .b-rating-value{padding:0 .25em}.b-rating .b-rating-value{min-width:2.5em}.b-rating .b-rating-star{display:inline-flex;justify-content:center;outline:0}.b-rating .b-rating-star .b-rating-icon{display:inline-flex;transition:all .15s ease-in-out}.b-rating.disabled,.b-rating:disabled{background-color:#e9ecef;color:#6c757d}.b-rating:not(.disabled):not(.readonly) .b-rating-star{cursor:pointer}.b-rating:not(.disabled):not(.readonly) .b-rating-star:hover .b-rating-icon,.b-rating:not(.disabled):not(.readonly):focus:not(:hover) .b-rating-star.focused .b-rating-icon{transform:scale(1.5)}.b-rating[dir=rtl] .b-rating-star-half{transform:scaleX(-1)}.b-form-spinbutton{text-align:center;overflow:hidden;background-image:none;padding:0}.b-form-spinbutton[dir=rtl]:not(.flex-column),[dir=rtl] .b-form-spinbutton:not(.flex-column){flex-direction:row-reverse}.b-form-spinbutton output{font-size:inherit;outline:0;border:0;background-color:transparent;width:auto;margin:0;padding:0 .25rem}.b-form-spinbutton output>bdi,.b-form-spinbutton output>div{display:block;min-width:2.25em;height:1.5em}.b-form-spinbutton.flex-column{height:auto;width:auto}.b-form-spinbutton.flex-column output{margin:0 .25rem;padding:.25rem 0}.b-form-spinbutton:not(.d-inline-flex):not(.flex-column){output-width:100%}.b-form-spinbutton.d-inline-flex:not(.flex-column){width:auto}.b-form-spinbutton .btn{line-height:1;box-shadow:none!important}.b-form-spinbutton .btn:disabled{pointer-events:none}.b-form-spinbutton .btn:hover:not(:disabled)>div>.b-icon{transform:scale(1.25)}.b-form-spinbutton.disabled,.b-form-spinbutton.readonly{background-color:#e9ecef}.b-form-spinbutton.disabled{pointer-events:none}.b-form-tags.focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.b-form-tags.focus.is-valid{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.b-form-tags.focus.is-invalid{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.b-form-tags.disabled{background-color:#e9ecef}.b-form-tags-list{margin-top:-.25rem}.b-form-tags-list .b-form-tag,.b-form-tags-list .b-form-tags-field{margin-top:.25rem}.b-form-tags-input{color:#495057}.b-form-tag{font-size:75%;font-weight:400;line-height:1.5;margin-right:.25rem}.b-form-tag.disabled{opacity:.75}.b-form-tag>button.b-form-tag-remove{color:inherit;font-size:125%;line-height:1;float:none;margin-left:.25rem}.form-control-lg .b-form-tag,.form-control-sm .b-form-tag{line-height:1.5}.media-aside{display:flex;margin-right:1rem}.media-aside-right{margin-right:0;margin-left:1rem}.modal-backdrop{opacity:.5}.b-pagination-pills .page-item .page-link{border-radius:50rem!important;margin-left:.25rem;line-height:1}.b-pagination-pills .page-item:first-child .page-link{margin-left:0}.popover.b-popover{display:block;opacity:1;outline:0}.popover.b-popover.fade:not(.show){opacity:0}.popover.b-popover.show{opacity:1}.b-popover-primary.popover{background-color:#cce5ff;border-color:#b8daff}.b-popover-primary.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-primary.bs-popover-top>.arrow:before{border-top-color:#b8daff}.b-popover-primary.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-primary.bs-popover-top>.arrow:after{border-top-color:#cce5ff}.b-popover-primary.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-primary.bs-popover-right>.arrow:before{border-right-color:#b8daff}.b-popover-primary.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-primary.bs-popover-right>.arrow:after{border-right-color:#cce5ff}.b-popover-primary.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-primary.bs-popover-bottom>.arrow:before{border-bottom-color:#b8daff}.b-popover-primary.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-primary.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-primary.bs-popover-bottom .popover-header:before,.b-popover-primary.bs-popover-bottom>.arrow:after{border-bottom-color:#bdddff}.b-popover-primary.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-primary.bs-popover-left>.arrow:before{border-left-color:#b8daff}.b-popover-primary.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-primary.bs-popover-left>.arrow:after{border-left-color:#cce5ff}.b-popover-primary .popover-header{color:#212529;background-color:#bdddff;border-bottom-color:#a3d0ff}.b-popover-primary .popover-body{color:#004085}.b-popover-secondary.popover{background-color:#e2e3e5;border-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-secondary.bs-popover-top>.arrow:before{border-top-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-secondary.bs-popover-top>.arrow:after{border-top-color:#e2e3e5}.b-popover-secondary.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-secondary.bs-popover-right>.arrow:before{border-right-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-secondary.bs-popover-right>.arrow:after{border-right-color:#e2e3e5}.b-popover-secondary.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-secondary.bs-popover-bottom>.arrow:before{border-bottom-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-secondary.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-secondary.bs-popover-bottom .popover-header:before,.b-popover-secondary.bs-popover-bottom>.arrow:after{border-bottom-color:#dadbde}.b-popover-secondary.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-secondary.bs-popover-left>.arrow:before{border-left-color:#d6d8db}.b-popover-secondary.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-secondary.bs-popover-left>.arrow:after{border-left-color:#e2e3e5}.b-popover-secondary .popover-header{color:#212529;background-color:#dadbde;border-bottom-color:#ccced2}.b-popover-secondary .popover-body{color:#383d41}.b-popover-success.popover{background-color:#d4edda;border-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-success.bs-popover-top>.arrow:before{border-top-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-success.bs-popover-top>.arrow:after{border-top-color:#d4edda}.b-popover-success.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-success.bs-popover-right>.arrow:before{border-right-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-success.bs-popover-right>.arrow:after{border-right-color:#d4edda}.b-popover-success.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-success.bs-popover-bottom>.arrow:before{border-bottom-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-success.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-success.bs-popover-bottom .popover-header:before,.b-popover-success.bs-popover-bottom>.arrow:after{border-bottom-color:#c9e8d1}.b-popover-success.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-success.bs-popover-left>.arrow:before{border-left-color:#c3e6cb}.b-popover-success.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-success.bs-popover-left>.arrow:after{border-left-color:#d4edda}.b-popover-success .popover-header{color:#212529;background-color:#c9e8d1;border-bottom-color:#b7e1c1}.b-popover-success .popover-body{color:#155724}.b-popover-info.popover{background-color:#d1ecf1;border-color:#bee5eb}.b-popover-info.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-info.bs-popover-top>.arrow:before{border-top-color:#bee5eb}.b-popover-info.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-info.bs-popover-top>.arrow:after{border-top-color:#d1ecf1}.b-popover-info.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-info.bs-popover-right>.arrow:before{border-right-color:#bee5eb}.b-popover-info.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-info.bs-popover-right>.arrow:after{border-right-color:#d1ecf1}.b-popover-info.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-info.bs-popover-bottom>.arrow:before{border-bottom-color:#bee5eb}.b-popover-info.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-info.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-info.bs-popover-bottom .popover-header:before,.b-popover-info.bs-popover-bottom>.arrow:after{border-bottom-color:#c5e7ed}.b-popover-info.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-info.bs-popover-left>.arrow:before{border-left-color:#bee5eb}.b-popover-info.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-info.bs-popover-left>.arrow:after{border-left-color:#d1ecf1}.b-popover-info .popover-header{color:#212529;background-color:#c5e7ed;border-bottom-color:#b2dfe7}.b-popover-info .popover-body{color:#0c5460}.b-popover-warning.popover{background-color:#fff3cd;border-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-warning.bs-popover-top>.arrow:before{border-top-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-warning.bs-popover-top>.arrow:after{border-top-color:#fff3cd}.b-popover-warning.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-warning.bs-popover-right>.arrow:before{border-right-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-warning.bs-popover-right>.arrow:after{border-right-color:#fff3cd}.b-popover-warning.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-warning.bs-popover-bottom>.arrow:before{border-bottom-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-warning.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-warning.bs-popover-bottom .popover-header:before,.b-popover-warning.bs-popover-bottom>.arrow:after{border-bottom-color:#ffefbe}.b-popover-warning.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-warning.bs-popover-left>.arrow:before{border-left-color:#ffeeba}.b-popover-warning.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-warning.bs-popover-left>.arrow:after{border-left-color:#fff3cd}.b-popover-warning .popover-header{color:#212529;background-color:#ffefbe;border-bottom-color:#ffe9a4}.b-popover-warning .popover-body{color:#856404}.b-popover-danger.popover{background-color:#f8d7da;border-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-danger.bs-popover-top>.arrow:before{border-top-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-danger.bs-popover-top>.arrow:after{border-top-color:#f8d7da}.b-popover-danger.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-danger.bs-popover-right>.arrow:before{border-right-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-danger.bs-popover-right>.arrow:after{border-right-color:#f8d7da}.b-popover-danger.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-danger.bs-popover-bottom>.arrow:before{border-bottom-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-danger.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-danger.bs-popover-bottom .popover-header:before,.b-popover-danger.bs-popover-bottom>.arrow:after{border-bottom-color:#f6cace}.b-popover-danger.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-danger.bs-popover-left>.arrow:before{border-left-color:#f5c6cb}.b-popover-danger.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-danger.bs-popover-left>.arrow:after{border-left-color:#f8d7da}.b-popover-danger .popover-header{color:#212529;background-color:#f6cace;border-bottom-color:#f2b4ba}.b-popover-danger .popover-body{color:#721c24}.b-popover-light.popover{background-color:#fefefe;border-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-light.bs-popover-top>.arrow:before{border-top-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-light.bs-popover-top>.arrow:after{border-top-color:#fefefe}.b-popover-light.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-light.bs-popover-right>.arrow:before{border-right-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-light.bs-popover-right>.arrow:after{border-right-color:#fefefe}.b-popover-light.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-light.bs-popover-bottom>.arrow:before{border-bottom-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-light.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-light.bs-popover-bottom .popover-header:before,.b-popover-light.bs-popover-bottom>.arrow:after{border-bottom-color:#f6f6f6}.b-popover-light.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-light.bs-popover-left>.arrow:before{border-left-color:#fdfdfe}.b-popover-light.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-light.bs-popover-left>.arrow:after{border-left-color:#fefefe}.b-popover-light .popover-header{color:#212529;background-color:#f6f6f6;border-bottom-color:#eaeaea}.b-popover-light .popover-body{color:#818182}.b-popover-dark.popover{background-color:#d6d8d9;border-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=top]>.arrow:before,.b-popover-dark.bs-popover-top>.arrow:before{border-top-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=top]>.arrow:after,.b-popover-dark.bs-popover-top>.arrow:after{border-top-color:#d6d8d9}.b-popover-dark.bs-popover-auto[x-placement^=right]>.arrow:before,.b-popover-dark.bs-popover-right>.arrow:before{border-right-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=right]>.arrow:after,.b-popover-dark.bs-popover-right>.arrow:after{border-right-color:#d6d8d9}.b-popover-dark.bs-popover-auto[x-placement^=bottom]>.arrow:before,.b-popover-dark.bs-popover-bottom>.arrow:before{border-bottom-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=bottom] .popover-header:before,.b-popover-dark.bs-popover-auto[x-placement^=bottom]>.arrow:after,.b-popover-dark.bs-popover-bottom .popover-header:before,.b-popover-dark.bs-popover-bottom>.arrow:after{border-bottom-color:#ced0d2}.b-popover-dark.bs-popover-auto[x-placement^=left]>.arrow:before,.b-popover-dark.bs-popover-left>.arrow:before{border-left-color:#c6c8ca}.b-popover-dark.bs-popover-auto[x-placement^=left]>.arrow:after,.b-popover-dark.bs-popover-left>.arrow:after{border-left-color:#d6d8d9}.b-popover-dark .popover-header{color:#212529;background-color:#ced0d2;border-bottom-color:#c1c4c5}.b-popover-dark .popover-body{color:#1b1e21}.b-sidebar-outer{position:fixed;top:0;left:0;right:0;height:0;overflow:visible;z-index:1035}.b-sidebar-backdrop{left:0;z-index:-1;width:100vw;opacity:.6}.b-sidebar,.b-sidebar-backdrop{position:fixed;top:0;height:100vh}.b-sidebar{display:flex;flex-direction:column;width:320px;max-width:100%;max-height:100%;margin:0;outline:0;transform:translateX(0)}.b-sidebar.slide{transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.b-sidebar.slide{transition:none}}.b-sidebar:not(.b-sidebar-right){left:0;right:auto}.b-sidebar:not(.b-sidebar-right).slide:not(.show){transform:translateX(-100%)}.b-sidebar:not(.b-sidebar-right)>.b-sidebar-header .close{margin-left:auto}.b-sidebar.b-sidebar-right{left:auto;right:0}.b-sidebar.b-sidebar-right.slide:not(.show){transform:translateX(100%)}.b-sidebar.b-sidebar-right>.b-sidebar-header .close{margin-right:auto}.b-sidebar>.b-sidebar-header{font-size:1.5rem;padding:.5rem 1rem;display:flex;flex-direction:row;flex-grow:0;align-items:center}[dir=rtl] .b-sidebar>.b-sidebar-header{flex-direction:row-reverse}.b-sidebar>.b-sidebar-header .close{float:none;font-size:1.5rem}.b-sidebar>.b-sidebar-body{flex-grow:1;height:100%;overflow-y:auto}.b-sidebar>.b-sidebar-footer{flex-grow:0}.b-skeleton-wrapper{cursor:wait}.b-skeleton{position:relative;overflow:hidden;background-color:rgba(0,0,0,.12);cursor:wait;-webkit-mask-image:radial-gradient(#fff,#000);mask-image:radial-gradient(#fff,#000)}.b-skeleton:before{content:" "}.b-skeleton-text{height:1rem;margin-bottom:.25rem;border-radius:.25rem}.b-skeleton-button{width:75px;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem}.b-skeleton-avatar{width:2.5em;height:2.5em;border-radius:50%}.b-skeleton-input{height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;line-height:1.5;border:1px solid #ced4da;border-radius:.25rem}.b-skeleton-icon-wrapper svg{color:rgba(0,0,0,.12)}.b-skeleton-img{height:100%;width:100%}.b-skeleton-animate-wave:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;z-index:0;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.4),transparent);animation:b-skeleton-animate-wave 1.75s linear infinite}@media (prefers-reduced-motion:reduce){.b-skeleton-animate-wave:after{background:none;animation:none}}@keyframes b-skeleton-animate-wave{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}.b-skeleton-animate-fade{animation:b-skeleton-animate-fade .875s ease-in-out infinite alternate}@media (prefers-reduced-motion:reduce){.b-skeleton-animate-fade{animation:none}}@keyframes b-skeleton-animate-fade{0%{opacity:1}to{opacity:.4}}.b-skeleton-animate-throb{animation:b-skeleton-animate-throb .875s ease-in infinite alternate}@media (prefers-reduced-motion:reduce){.b-skeleton-animate-throb{animation:none}}@keyframes b-skeleton-animate-throb{0%{transform:scale(1)}to{transform:scale(.975)}}.table.b-table.b-table-fixed{table-layout:fixed}.table.b-table.b-table-no-border-collapse{border-collapse:separate;border-spacing:0}.table.b-table[aria-busy=true]{opacity:.55}.table.b-table>tbody>tr.b-table-details>td{border-top:none!important}.table.b-table>caption{caption-side:bottom}.table.b-table.b-table-caption-top>caption{caption-side:top!important}.table.b-table>tbody>.table-active,.table.b-table>tbody>.table-active>td,.table.b-table>tbody>.table-active>th{background-color:rgba(0,0,0,.075)}.table.b-table.table-hover>tbody>tr.table-active:hover td,.table.b-table.table-hover>tbody>tr.table-active:hover th{color:#212529;background-image:linear-gradient(rgba(0,0,0,.075),rgba(0,0,0,.075));background-repeat:no-repeat}.table.b-table>tbody>.bg-active,.table.b-table>tbody>.bg-active>td,.table.b-table>tbody>.bg-active>th{background-color:hsla(0,0%,100%,.075)!important}.table.b-table.table-hover.table-dark>tbody>tr.bg-active:hover td,.table.b-table.table-hover.table-dark>tbody>tr.bg-active:hover th{color:#fff;background-image:linear-gradient(hsla(0,0%,100%,.075),hsla(0,0%,100%,.075));background-repeat:no-repeat}.b-table-sticky-header,.table-responsive,[class*=table-responsive-]{margin-bottom:1rem}.b-table-sticky-header>.table,.table-responsive>.table,[class*=table-responsive-]>.table{margin-bottom:0}.b-table-sticky-header{overflow-y:auto;max-height:300px}@media print{.b-table-sticky-header{overflow-y:visible!important;max-height:none!important}}@supports (position:sticky){.b-table-sticky-header>.table.b-table>thead>tr>th{position:sticky;top:0;z-index:2}.b-table-sticky-header>.table.b-table>tbody>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>tfoot>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>thead>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tbody>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tfoot>tr>.b-table-sticky-column,.table-responsive>.table.b-table>thead>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tbody>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tfoot>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>thead>tr>.b-table-sticky-column{position:sticky;left:0}.b-table-sticky-header>.table.b-table>thead>tr>.b-table-sticky-column,.table-responsive>.table.b-table>thead>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>thead>tr>.b-table-sticky-column{z-index:5}.b-table-sticky-header>.table.b-table>tbody>tr>.b-table-sticky-column,.b-table-sticky-header>.table.b-table>tfoot>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tbody>tr>.b-table-sticky-column,.table-responsive>.table.b-table>tfoot>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tbody>tr>.b-table-sticky-column,[class*=table-responsive-]>.table.b-table>tfoot>tr>.b-table-sticky-column{z-index:2}.table.b-table>tbody>tr>.table-b-table-default,.table.b-table>tfoot>tr>.table-b-table-default,.table.b-table>thead>tr>.table-b-table-default{color:#212529;background-color:#fff}.table.b-table.table-dark>tbody>tr>.bg-b-table-default,.table.b-table.table-dark>tfoot>tr>.bg-b-table-default,.table.b-table.table-dark>thead>tr>.bg-b-table-default{color:#fff;background-color:#343a40}.table.b-table.table-striped>tbody>tr:nth-of-type(odd)>.table-b-table-default{background-image:linear-gradient(rgba(0,0,0,.05),rgba(0,0,0,.05));background-repeat:no-repeat}.table.b-table.table-striped.table-dark>tbody>tr:nth-of-type(odd)>.bg-b-table-default{background-image:linear-gradient(hsla(0,0%,100%,.05),hsla(0,0%,100%,.05));background-repeat:no-repeat}.table.b-table.table-hover>tbody>tr:hover>.table-b-table-default{color:#212529;background-image:linear-gradient(rgba(0,0,0,.075),rgba(0,0,0,.075));background-repeat:no-repeat}.table.b-table.table-hover.table-dark>tbody>tr:hover>.bg-b-table-default{color:#fff;background-image:linear-gradient(hsla(0,0%,100%,.075),hsla(0,0%,100%,.075));background-repeat:no-repeat}}.table.b-table>tfoot>tr>[aria-sort],.table.b-table>thead>tr>[aria-sort]{cursor:pointer;background-image:none;background-repeat:no-repeat;background-size:.65em 1em}.table.b-table>tfoot>tr>[aria-sort]:not(.b-table-sort-icon-left),.table.b-table>thead>tr>[aria-sort]:not(.b-table-sort-icon-left){background-position:right .375rem center;padding-right:calc(.75rem + .65em)}.table.b-table>tfoot>tr>[aria-sort].b-table-sort-icon-left,.table.b-table>thead>tr>[aria-sort].b-table-sort-icon-left{background-position:left .375rem center;padding-left:calc(.75rem + .65em)}.table.b-table>tfoot>tr>[aria-sort=none],.table.b-table>thead>tr>[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>[aria-sort=ascending],.table.b-table>thead>tr>[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>[aria-sort=descending],.table.b-table>thead>tr>[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=none],.table.b-table.table-dark>thead>tr>[aria-sort=none],.table.b-table>.thead-dark>tr>[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=ascending],.table.b-table.table-dark>thead>tr>[aria-sort=ascending],.table.b-table>.thead-dark>tr>[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-dark>tfoot>tr>[aria-sort=descending],.table.b-table.table-dark>thead>tr>[aria-sort=descending],.table.b-table>.thead-dark>tr>[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=none],.table.b-table>thead>tr>.table-dark[aria-sort=none]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22zm0 100 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=ascending],.table.b-table>thead>tr>.table-dark[aria-sort=ascending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' opacity='.3' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table>tfoot>tr>.table-dark[aria-sort=descending],.table.b-table>thead>tr>.table-dark[aria-sort=descending]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='101' height='101' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' opacity='.3' d='m51 1 25 23 24 22H1l25-22z'/%3E%3Cpath fill='%23fff' d='m51 101 25-23 24-22H1l25 22z'/%3E%3C/svg%3E")}.table.b-table.table-sm>tfoot>tr>[aria-sort]:not(.b-table-sort-icon-left),.table.b-table.table-sm>thead>tr>[aria-sort]:not(.b-table-sort-icon-left){background-position:right .15rem center;padding-right:calc(.3rem + .65em)}.table.b-table.table-sm>tfoot>tr>[aria-sort].b-table-sort-icon-left,.table.b-table.table-sm>thead>tr>[aria-sort].b-table-sort-icon-left{background-position:left .15rem center;padding-left:calc(.3rem + .65em)}.table.b-table.b-table-selectable:not(.b-table-selectable-no-click)>tbody>tr{cursor:pointer}.table.b-table.b-table-selectable:not(.b-table-selectable-no-click).b-table-selecting.b-table-select-range>tbody>tr{-webkit-user-select:none;-moz-user-select:none;user-select:none}@media (max-width:575.98px){.table.b-table.b-table-stacked-sm{display:block;width:100%}.table.b-table.b-table-stacked-sm>caption,.table.b-table.b-table-stacked-sm>tbody,.table.b-table.b-table-stacked-sm>tbody>tr,.table.b-table.b-table-stacked-sm>tbody>tr>td,.table.b-table.b-table-stacked-sm>tbody>tr>th{display:block}.table.b-table.b-table-stacked-sm>tfoot,.table.b-table.b-table-stacked-sm>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-sm>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-sm>thead,.table.b-table.b-table-stacked-sm>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-sm>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-sm>caption{caption-side:top!important}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-sm>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-sm>tbody>tr.bottom-row,.table.b-table.b-table-stacked-sm>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-sm>tbody>tr>:first-child,.table.b-table.b-table-stacked-sm>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-sm>tbody>tr>[rowspan]+th{border-top-width:3px}}@media (max-width:767.98px){.table.b-table.b-table-stacked-md{display:block;width:100%}.table.b-table.b-table-stacked-md>caption,.table.b-table.b-table-stacked-md>tbody,.table.b-table.b-table-stacked-md>tbody>tr,.table.b-table.b-table-stacked-md>tbody>tr>td,.table.b-table.b-table-stacked-md>tbody>tr>th{display:block}.table.b-table.b-table-stacked-md>tfoot,.table.b-table.b-table-stacked-md>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-md>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-md>thead,.table.b-table.b-table-stacked-md>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-md>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-md>caption{caption-side:top!important}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-md>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-md>tbody>tr.bottom-row,.table.b-table.b-table-stacked-md>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-md>tbody>tr>:first-child,.table.b-table.b-table-stacked-md>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-md>tbody>tr>[rowspan]+th{border-top-width:3px}}@media (max-width:991.98px){.table.b-table.b-table-stacked-lg{display:block;width:100%}.table.b-table.b-table-stacked-lg>caption,.table.b-table.b-table-stacked-lg>tbody,.table.b-table.b-table-stacked-lg>tbody>tr,.table.b-table.b-table-stacked-lg>tbody>tr>td,.table.b-table.b-table-stacked-lg>tbody>tr>th{display:block}.table.b-table.b-table-stacked-lg>tfoot,.table.b-table.b-table-stacked-lg>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-lg>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-lg>thead,.table.b-table.b-table-stacked-lg>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-lg>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-lg>caption{caption-side:top!important}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-lg>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-lg>tbody>tr.bottom-row,.table.b-table.b-table-stacked-lg>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-lg>tbody>tr>:first-child,.table.b-table.b-table-stacked-lg>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-lg>tbody>tr>[rowspan]+th{border-top-width:3px}}@media (max-width:1199.98px){.table.b-table.b-table-stacked-xl{display:block;width:100%}.table.b-table.b-table-stacked-xl>caption,.table.b-table.b-table-stacked-xl>tbody,.table.b-table.b-table-stacked-xl>tbody>tr,.table.b-table.b-table-stacked-xl>tbody>tr>td,.table.b-table.b-table-stacked-xl>tbody>tr>th{display:block}.table.b-table.b-table-stacked-xl>tfoot,.table.b-table.b-table-stacked-xl>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xl>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked-xl>thead,.table.b-table.b-table-stacked-xl>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked-xl>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked-xl>caption{caption-side:top!important}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked-xl>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked-xl>tbody>tr.bottom-row,.table.b-table.b-table-stacked-xl>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked-xl>tbody>tr>:first-child,.table.b-table.b-table-stacked-xl>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked-xl>tbody>tr>[rowspan]+th{border-top-width:3px}}.table.b-table.b-table-stacked{display:block;width:100%}.table.b-table.b-table-stacked>caption,.table.b-table.b-table-stacked>tbody,.table.b-table.b-table-stacked>tbody>tr,.table.b-table.b-table-stacked>tbody>tr>td,.table.b-table.b-table-stacked>tbody>tr>th{display:block}.table.b-table.b-table-stacked>tfoot,.table.b-table.b-table-stacked>tfoot>tr.b-table-bottom-row,.table.b-table.b-table-stacked>tfoot>tr.b-table-top-row,.table.b-table.b-table-stacked>thead,.table.b-table.b-table-stacked>thead>tr.b-table-bottom-row,.table.b-table.b-table-stacked>thead>tr.b-table-top-row{display:none}.table.b-table.b-table-stacked>caption{caption-side:top!important}.table.b-table.b-table-stacked>tbody>tr>[data-label]:before{content:attr(data-label);width:40%;float:left;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal;padding:0 .5rem 0 0;margin:0}.table.b-table.b-table-stacked>tbody>tr>[data-label]:after{display:block;clear:both;content:""}.table.b-table.b-table-stacked>tbody>tr>[data-label]>div{display:inline-block;width:60%;padding:0 0 0 .5rem;margin:0}.table.b-table.b-table-stacked>tbody>tr.bottom-row,.table.b-table.b-table-stacked>tbody>tr.top-row{display:none}.table.b-table.b-table-stacked>tbody>tr>:first-child,.table.b-table.b-table-stacked>tbody>tr>[rowspan]+td,.table.b-table.b-table-stacked>tbody>tr>[rowspan]+th{border-top-width:3px}.b-time{min-width:150px}.b-time output.disabled,.b-time[aria-disabled=true] output,.b-time[aria-readonly=true] output{background-color:#e9ecef;opacity:1}.b-time[aria-disabled=true] output{pointer-events:none}[dir=rtl] .b-time>.d-flex:not(.flex-column){flex-direction:row-reverse}.b-time .b-time-header{margin-bottom:.5rem}.b-time .b-time-header output{padding:.25rem;font-size:80%}.b-time .b-time-footer{margin-top:.5rem}.b-time .b-time-ampm{margin-left:.5rem}.b-toast{display:block;position:relative;max-width:350px;backface-visibility:hidden;background-clip:padding-box;z-index:1;border-radius:.25rem}.b-toast .toast{background-color:hsla(0,0%,100%,.85)}.b-toast:not(:last-child){margin-bottom:.75rem}.b-toast.b-toast-solid .toast{background-color:#fff}.b-toast .toast{opacity:1}.b-toast .toast.fade:not(.show){opacity:0}.b-toast .toast .toast-body{display:block}.b-toast-primary .toast{background-color:rgba(230,242,255,.85);border-color:rgba(184,218,255,.85);color:#004085}.b-toast-primary .toast .toast-header{color:#004085;background-color:rgba(204,229,255,.85);border-bottom-color:rgba(184,218,255,.85)}.b-toast-primary.b-toast-solid .toast{background-color:#e6f2ff}.b-toast-secondary .toast{background-color:hsla(210,7%,94%,.85);border-color:hsla(216,6%,85%,.85);color:#383d41}.b-toast-secondary .toast .toast-header{color:#383d41;background-color:hsla(220,5%,89%,.85);border-bottom-color:hsla(216,6%,85%,.85)}.b-toast-secondary.b-toast-solid .toast{background-color:#eff0f1}.b-toast-success .toast{background-color:rgba(230,245,233,.85);border-color:rgba(195,230,203,.85);color:#155724}.b-toast-success .toast .toast-header{color:#155724;background-color:rgba(212,237,218,.85);border-bottom-color:rgba(195,230,203,.85)}.b-toast-success.b-toast-solid .toast{background-color:#e6f5e9}.b-toast-info .toast{background-color:rgba(229,244,247,.85);border-color:rgba(190,229,235,.85);color:#0c5460}.b-toast-info .toast .toast-header{color:#0c5460;background-color:rgba(209,236,241,.85);border-bottom-color:rgba(190,229,235,.85)}.b-toast-info.b-toast-solid .toast{background-color:#e5f4f7}.b-toast-warning .toast{background-color:rgba(255,249,231,.85);border-color:rgba(255,238,186,.85);color:#856404}.b-toast-warning .toast .toast-header{color:#856404;background-color:rgba(255,243,205,.85);border-bottom-color:rgba(255,238,186,.85)}.b-toast-warning.b-toast-solid .toast{background-color:#fff9e7}.b-toast-danger .toast{background-color:rgba(252,237,238,.85);border-color:rgba(245,198,203,.85);color:#721c24}.b-toast-danger .toast .toast-header{color:#721c24;background-color:rgba(248,215,218,.85);border-bottom-color:rgba(245,198,203,.85)}.b-toast-danger.b-toast-solid .toast{background-color:#fcedee}.b-toast-light .toast{background-color:hsla(0,0%,100%,.85);border-color:rgba(253,253,254,.85);color:#818182}.b-toast-light .toast .toast-header{color:#818182;background-color:hsla(0,0%,100%,.85);border-bottom-color:rgba(253,253,254,.85)}.b-toast-light.b-toast-solid .toast{background-color:#fff}.b-toast-dark .toast{background-color:hsla(180,4%,89%,.85);border-color:hsla(210,4%,78%,.85);color:#1b1e21}.b-toast-dark .toast .toast-header{color:#1b1e21;background-color:hsla(200,4%,85%,.85);border-bottom-color:hsla(210,4%,78%,.85)}.b-toast-dark.b-toast-solid .toast{background-color:#e3e5e5}.b-toaster{z-index:1100}.b-toaster .b-toaster-slot{position:relative;display:block}.b-toaster .b-toaster-slot:empty{display:none!important}.b-toaster.b-toaster-bottom-center,.b-toaster.b-toaster-bottom-full,.b-toaster.b-toaster-bottom-left,.b-toaster.b-toaster-bottom-right,.b-toaster.b-toaster-top-center,.b-toaster.b-toaster-top-full,.b-toaster.b-toaster-top-left,.b-toaster.b-toaster-top-right{position:fixed;left:.5rem;right:.5rem;margin:0;padding:0;height:0;overflow:visible}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{position:absolute;max-width:350px;width:100%;left:0;right:0;padding:0;margin:0}.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot .b-toast,.b-toaster.b-toaster-bottom-full .b-toaster-slot .toast,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot .b-toast,.b-toaster.b-toaster-top-full .b-toaster-slot .toast{width:100%;max-width:100%}.b-toaster.b-toaster-top-center,.b-toaster.b-toaster-top-full,.b-toaster.b-toaster-top-left,.b-toaster.b-toaster-top-right{top:0}.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-full .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{top:.5rem}.b-toaster.b-toaster-bottom-center,.b-toaster.b-toaster-bottom-full,.b-toaster.b-toaster-bottom-left,.b-toaster.b-toaster-bottom-right{bottom:0}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-full .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot{bottom:.5rem}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-right .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-right .b-toaster-slot{margin-left:auto}.b-toaster.b-toaster-bottom-center .b-toaster-slot,.b-toaster.b-toaster-bottom-left .b-toaster-slot,.b-toaster.b-toaster-top-center .b-toaster-slot,.b-toaster.b-toaster-top-left .b-toaster-slot{margin-right:auto}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-move,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-move,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-move,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-move{transition:transform .175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-enter-to .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-active .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-enter-to .toast.fade{transition-delay:.175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active{position:absolute;transition-delay:.175s}.b-toaster.b-toaster-bottom-left .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-bottom-right .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-top-left .b-toast.b-toaster-leave-active .toast.fade,.b-toaster.b-toaster-top-right .b-toast.b-toaster-leave-active .toast.fade{transition-delay:0s}.tooltip.b-tooltip{display:block;opacity:.9;outline:0}.tooltip.b-tooltip.fade:not(.show){opacity:0}.tooltip.b-tooltip.show{opacity:.9}.tooltip.b-tooltip.noninteractive{pointer-events:none}.tooltip.b-tooltip .arrow{margin:0 .25rem}.tooltip.b-tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left] .arrow,.tooltip.b-tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right] .arrow,.tooltip.b-tooltip.bs-tooltip-left .arrow,.tooltip.b-tooltip.bs-tooltip-right .arrow{margin:.25rem 0}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-top .arrow:before{border-top-color:#007bff}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-right .arrow:before{border-right-color:#007bff}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-bottom .arrow:before{border-bottom-color:#007bff}.tooltip.b-tooltip-primary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-primary.bs-tooltip-left .arrow:before{border-left-color:#007bff}.tooltip.b-tooltip-primary .tooltip-inner{color:#fff;background-color:#007bff}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-top .arrow:before{border-top-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-right .arrow:before{border-right-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-bottom .arrow:before{border-bottom-color:#6c757d}.tooltip.b-tooltip-secondary.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-secondary.bs-tooltip-left .arrow:before{border-left-color:#6c757d}.tooltip.b-tooltip-secondary .tooltip-inner{color:#fff;background-color:#6c757d}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-top .arrow:before{border-top-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-right .arrow:before{border-right-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-bottom .arrow:before{border-bottom-color:#28a745}.tooltip.b-tooltip-success.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-success.bs-tooltip-left .arrow:before{border-left-color:#28a745}.tooltip.b-tooltip-success .tooltip-inner{color:#fff;background-color:#28a745}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-top .arrow:before{border-top-color:#17a2b8}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-right .arrow:before{border-right-color:#17a2b8}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-bottom .arrow:before{border-bottom-color:#17a2b8}.tooltip.b-tooltip-info.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-info.bs-tooltip-left .arrow:before{border-left-color:#17a2b8}.tooltip.b-tooltip-info .tooltip-inner{color:#fff;background-color:#17a2b8}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-top .arrow:before{border-top-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-right .arrow:before{border-right-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-bottom .arrow:before{border-bottom-color:#ffc107}.tooltip.b-tooltip-warning.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-warning.bs-tooltip-left .arrow:before{border-left-color:#ffc107}.tooltip.b-tooltip-warning .tooltip-inner{color:#212529;background-color:#ffc107}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-top .arrow:before{border-top-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-right .arrow:before{border-right-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-bottom .arrow:before{border-bottom-color:#dc3545}.tooltip.b-tooltip-danger.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-danger.bs-tooltip-left .arrow:before{border-left-color:#dc3545}.tooltip.b-tooltip-danger .tooltip-inner{color:#fff;background-color:#dc3545}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-top .arrow:before{border-top-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-right .arrow:before{border-right-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-bottom .arrow:before{border-bottom-color:#f8f9fa}.tooltip.b-tooltip-light.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-light.bs-tooltip-left .arrow:before{border-left-color:#f8f9fa}.tooltip.b-tooltip-light .tooltip-inner{color:#212529;background-color:#f8f9fa}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=top] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-top .arrow:before{border-top-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=right] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-right .arrow:before{border-right-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-bottom .arrow:before{border-bottom-color:#343a40}.tooltip.b-tooltip-dark.bs-tooltip-auto[x-placement^=left] .arrow:before,.tooltip.b-tooltip-dark.bs-tooltip-left .arrow:before{border-left-color:#343a40}.tooltip.b-tooltip-dark .tooltip-inner{color:#fff;background-color:#343a40}.b-icon.bi{display:inline-block;overflow:visible;vertical-align:-.15em}.b-icon.b-icon-animation-cylon,.b-icon.b-iconstack .b-icon-animation-cylon>g{transform-origin:center;animation:b-icon-animation-cylon .75s ease-in-out infinite alternate}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-cylon,.b-icon.b-iconstack .b-icon-animation-cylon>g{animation:none}}.b-icon.b-icon-animation-cylon-vertical,.b-icon.b-iconstack .b-icon-animation-cylon-vertical>g{transform-origin:center;animation:b-icon-animation-cylon-vertical .75s ease-in-out infinite alternate}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-cylon-vertical,.b-icon.b-iconstack .b-icon-animation-cylon-vertical>g{animation:none}}.b-icon.b-icon-animation-fade,.b-icon.b-iconstack .b-icon-animation-fade>g{transform-origin:center;animation:b-icon-animation-fade .75s ease-in-out infinite alternate}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-fade,.b-icon.b-iconstack .b-icon-animation-fade>g{animation:none}}.b-icon.b-icon-animation-spin,.b-icon.b-iconstack .b-icon-animation-spin>g{transform-origin:center;animation:b-icon-animation-spin 2s linear infinite normal}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin,.b-icon.b-iconstack .b-icon-animation-spin>g{animation:none}}.b-icon.b-icon-animation-spin-reverse,.b-icon.b-iconstack .b-icon-animation-spin-reverse>g{transform-origin:center;animation:b-icon-animation-spin 2s linear infinite reverse}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-reverse,.b-icon.b-iconstack .b-icon-animation-spin-reverse>g{animation:none}}.b-icon.b-icon-animation-spin-pulse,.b-icon.b-iconstack .b-icon-animation-spin-pulse>g{transform-origin:center;animation:b-icon-animation-spin 1s steps(8) infinite normal}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-pulse,.b-icon.b-iconstack .b-icon-animation-spin-pulse>g{animation:none}}.b-icon.b-icon-animation-spin-reverse-pulse,.b-icon.b-iconstack .b-icon-animation-spin-reverse-pulse>g{transform-origin:center;animation:b-icon-animation-spin 1s steps(8) infinite reverse}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-spin-reverse-pulse,.b-icon.b-iconstack .b-icon-animation-spin-reverse-pulse>g{animation:none}}.b-icon.b-icon-animation-throb,.b-icon.b-iconstack .b-icon-animation-throb>g{transform-origin:center;animation:b-icon-animation-throb .75s ease-in-out infinite alternate}@media (prefers-reduced-motion:reduce){.b-icon.b-icon-animation-throb,.b-icon.b-iconstack .b-icon-animation-throb>g{animation:none}}@keyframes b-icon-animation-cylon{0%{transform:translateX(-25%)}to{transform:translateX(25%)}}@keyframes b-icon-animation-cylon-vertical{0%{transform:translateY(25%)}to{transform:translateY(-25%)}}@keyframes b-icon-animation-fade{0%{opacity:.1}to{opacity:1}}@keyframes b-icon-animation-spin{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes b-icon-animation-throb{0%{opacity:.5;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.btn .b-icon.bi,.dropdown-item .b-icon.bi,.dropdown-toggle .b-icon.bi,.input-group-text .b-icon.bi,.nav-link .b-icon.bi{font-size:125%;vertical-align:text-bottom} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/index.html b/cumulus_lambda_functions/uds_api/stac_browser/index.html deleted file mode 100644 index 053c38d2..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/index.html +++ /dev/null @@ -1 +0,0 @@ -STAC Browser
\ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js deleted file mode 100644 index 9574e0c2..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1228,218],{88896:function(t,e,s){s.r(e),s.d(e,{default:function(){return I}});var i=function(){var t=this,e=t._self._c;return e("main",{staticClass:"search d-flex flex-column"},[t.parent?t.searchLink?e("b-row",[e("b-col",{staticClass:"left"},[e("ItemFilter",{attrs:{stac:t.parent,title:"",value:t.filters,type:"Global"},on:{input:t.setFilters}})],1),e("b-col",{staticClass:"right"},[t.loading?e("Loading",{attrs:{fill:"",top:""}}):t.hasItems||t.hasFilters?t.hasItems?t._e():e("b-alert",{attrs:{variant:"warning",show:""}},[t._v(t._s(t.$t("search.noItemsFound")))]):e("b-alert",{attrs:{variant:"info",show:""}},[t._v(t._s(t.$t("search.modifyCriteria")))]),t.hasItems?[e("div",{attrs:{id:"search-map"}},[e("Map",{attrs:{stac:t.parent,stacLayerData:t.itemCollection,scrollWheelZoom:"",popover:""}})],1),e("Items",{attrs:{stac:t.parent,items:t.apiItems,api:!0,allowFilter:!1,pagination:t.itemPages},on:{paginate:t.paginateItems}})]:t._e()],2)],1):e("b-alert",{attrs:{variant:"danger",show:""}},[t._v(t._s(t.$t("search.notSupported")))]):e("Loading",{attrs:{stretch:""}})],1)},a=[],r=s(52308),n=s(48416),o=s(40848),l=s(31824),h=s(26732),c=s(82124);const p="__search__";var m={name:"Search",components:{ItemFilter:l["default"],Items:r.c,Loading:h.c,Map:()=>s.e(572).then(s.bind(s,572))},props:{loadParent:{type:String,default:null}},data(){return{parent:null,filters:{},selectedItem:null}},computed:{...(0,n.ys)(["apiItems","apiItemsLink","apiItemsPagination","catalogUrl","catalogTitle"]),...(0,n.gV)(["getStac","root","collectionLink","parentLink","fromBrowserPath","getApiItemsLoading"]),pageTitle(){return this.$t("search.title")},loading(){return this.getApiItemsLoading(p)},searchLink(){return this.parent instanceof c.c&&this.parent.getSearchLink()},itemCollection(){return{type:"FeatureCollection",features:this.apiItems,links:[]}},itemPages(){let t=Object.assign({},this.apiItemsPagination);return!t.first&&this.data&&this.apiItemsLink&&(t.first=o.cp.addFiltersToLink(this.apiItemsLink,this.filters)),t},hasFilters(){return o.cp.size(this.filters)>0},hasItems(){return this.apiItems.length>0},pageDescription(){let t=c.c.getDisplayTitle([this.collectionLink,this.parentLink,this.root],this.catalogTitle);return this.$t("search.metaDescription",{title:t})}},watch:{searchLink:{immediate:!0,handler(){this.searchLink&&this.showPage()}}},async created(){let t=this.catalogUrl;this.loadParent?(t=this.fromBrowserPath(this.loadParent),this.parent=this.getStac(t)):this.parent=this.root,this.parent||(await this.$store.dispatch("load",{url:t}),this.root||this.$store.commit("config",{catalogUrl:t}),this.parent=this.getStac(t))},methods:{...(0,n.sR)(["toggleApiItemsLoading"]),async setFilters(t,e=!1){this.filters=t,e?this.$store.commit("resetApiItems"):await this.filterItems(t)},showPage(){this.$store.commit("showPage",{title:this.pageTitle,description:this.pageDescription}),this.$store.commit("setApiItemsLink",this.searchLink)},async paginateItems(t){this.toggleApiItemsLoading(p);try{let e=await this.$store.dispatch("loadApiItems",{link:t,show:!0,filters:this.filters});this.handleResponse(e)}catch(e){this.$root.$emit("error",e,this.$t("errors.loadItems"))}finally{this.toggleApiItemsLoading(p)}},async filterItems(t){this.toggleApiItemsLoading(p);try{let e=await this.$store.dispatch("loadApiItems",{link:this.searchLink,show:!0,filters:t});this.handleResponse(e)}catch(e){this.$root.$emit("error",e,this.$t("errors.loadFilteredItems"))}finally{this.toggleApiItemsLoading(p)}},handleResponse(t){t&&this.$store.commit("showPage",{title:this.pageTitle,url:t.config.url,description:this.pageDescription})}}},g=m,d=s(82528),u=(0,d.c)(g,i,a,!1,null,"7e03b515",null),I=u.exports}}]); -//# sourceMappingURL=1228.8582509b.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js.map deleted file mode 100644 index 0509784e..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1228.8582509b.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1228.8582509b.js","mappings":"sMAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAKC,EAAGF,EAAIG,MAAMD,GAAG,OAAOA,EAAG,OAAO,CAACE,YAAY,6BAA6B,CAAGJ,EAAIK,OAA+CL,EAAIM,WAAiHJ,EAAG,QAAQ,CAACA,EAAG,QAAQ,CAACE,YAAY,QAAQ,CAACF,EAAG,aAAa,CAACK,MAAM,CAAC,KAAOP,EAAIK,OAAO,MAAQ,GAAG,MAAQL,EAAIQ,QAAQ,KAAO,UAAUC,GAAG,CAAC,MAAQT,EAAIU,eAAe,GAAGR,EAAG,QAAQ,CAACE,YAAY,SAAS,CAAEJ,EAAIW,QAAST,EAAG,UAAU,CAACK,MAAM,CAAC,KAAO,GAAG,IAAM,MAAQP,EAAIY,UAAaZ,EAAIa,WAAmHb,EAAIY,SAAgHZ,EAAIc,KAA1GZ,EAAG,UAAU,CAACK,MAAM,CAAC,QAAU,UAAU,KAAO,KAAK,CAACP,EAAIe,GAAGf,EAAIgB,GAAGhB,EAAIiB,GAAG,2BAAhMf,EAAG,UAAU,CAACK,MAAM,CAAC,QAAU,OAAO,KAAO,KAAK,CAACP,EAAIe,GAAGf,EAAIgB,GAAGhB,EAAIiB,GAAG,6BAA6JjB,EAAIY,SAAU,CAACV,EAAG,MAAM,CAACK,MAAM,CAAC,GAAK,eAAe,CAACL,EAAG,MAAM,CAACK,MAAM,CAAC,KAAOP,EAAIK,OAAO,cAAgBL,EAAIkB,eAAe,gBAAkB,GAAG,QAAU,OAAO,GAAGhB,EAAG,QAAQ,CAACK,MAAM,CAAC,KAAOP,EAAIK,OAAO,MAAQL,EAAImB,SAAS,KAAM,EAAK,aAAc,EAAM,WAAanB,EAAIoB,WAAWX,GAAG,CAAC,SAAWT,EAAIqB,kBAAkBrB,EAAIc,MAAM,IAAI,GAA77BZ,EAAG,UAAU,CAACK,MAAM,CAAC,QAAU,SAAS,KAAO,KAAK,CAACP,EAAIe,GAAGf,EAAIgB,GAAGhB,EAAIiB,GAAG,2BAAjIf,EAAG,UAAU,CAACK,MAAM,CAAC,QAAU,OAAy9B,EAC9nC,EACIe,EAAkB,G,kECmCtB,MAAAC,EAAA,aAEA,OACAC,KAAA,SACAC,WAAA,CACAC,WAAA,aACAC,MAAA,IACAC,QAAA,IACAC,IAAAA,IAAA,8BAEAC,MAAA,CACAC,WAAA,CACAC,KAAAC,OACAC,QAAA,OAGAC,IAAAA,GACA,OACA9B,OAAA,KACAG,QAAA,GACA4B,aAAA,KAEA,EACAC,SAAA,KACAC,EAAAA,EAAAA,IAAA,kFACAC,EAAAA,EAAAA,IAAA,yFACAC,SAAAA,GACA,YAAAvB,GAAA,eACA,EACAN,OAAAA,GACA,YAAA8B,mBAAAlB,EACA,EACAjB,UAAAA,GACA,YAAAD,kBAAAqC,EAAAA,GAAA,KAAArC,OAAAsC,eACA,EACAzB,cAAAA,GACA,OACAc,KAAA,oBACAY,SAAA,KAAAzB,SACA0B,MAAA,GAEA,EACAzB,SAAAA,GACA,IAAA0B,EAAAC,OAAAC,OAAA,QAAAC,oBAKA,OAHAH,EAAAI,OAAA,KAAAf,MAAA,KAAAgB,eACAL,EAAAI,MAAAE,EAAAA,GAAAC,iBAAA,KAAAF,aAAA,KAAA3C,UAEAsC,CACA,EACAjC,UAAAA,GACA,OAAAuC,EAAAA,GAAAE,KAAA,KAAA9C,SAAA,CACA,EACAI,QAAAA,GACA,YAAAO,SAAAoC,OAAA,CACA,EACAC,eAAAA,GACA,IAAAC,EAAAf,EAAAA,EAAAgB,gBAAA,MAAAC,eAAA,KAAAC,WAAA,KAAAC,MAAA,KAAAC,cACA,YAAA7C,GAAA,0BAAAwC,SACA,GAEAM,MAAA,CACAzD,WAAA,CACA0D,WAAA,EACAC,OAAAA,GACA,KAAA3D,YACA,KAAA4D,UAEA,IAGA,aAAAC,GACA,IAAAC,EAAA,KAAAC,WACA,KAAAtC,YACAqC,EAAA,KAAAE,gBAAA,KAAAvC,YACA,KAAA1B,OAAA,KAAAkE,QAAAH,IAGA,KAAA/D,OAAA,KAAAwD,KAEA,KAAAxD,eACA,KAAAmE,OAAAC,SAAA,QAAAL,QACA,KAAAP,MACA,KAAAW,OAAAE,OAAA,UAAAL,WAAAD,IAEA,KAAA/D,OAAA,KAAAkE,QAAAH,GAEA,EACAO,QAAA,KACAC,EAAAA,EAAAA,IAAA,2BACA,gBAAAlE,CAAAF,EAAAqE,GAAA,GACA,KAAArE,QAAAA,EACAqE,EACA,KAAAL,OAAAE,OAAA,uBAGA,KAAAI,YAAAtE,EAEA,EACA0D,QAAAA,GACA,KAAAM,OAAAE,OAAA,YACAjB,MAAA,KAAAjB,UACAuC,YAAA,KAAAvB,kBAEA,KAAAgB,OAAAE,OAAA,uBAAApE,WACA,EACA,mBAAAe,CAAA2D,GACA,KAAAC,sBAAA1D,GACA,IACA,IAAA2D,QAAA,KAAAV,OAAAC,SAAA,gBAAAO,OAAAG,MAAA,EAAA3E,QAAA,KAAAA,UACA,KAAA4E,eAAAF,EACA,OAAAG,GACA,KAAAC,MAAAC,MAAA,QAAAF,EAAA,KAAApE,GAAA,oBACA,SACA,KAAAgE,sBAAA1D,EACA,CACA,EACA,iBAAAuD,CAAAtE,GACA,KAAAyE,sBAAA1D,GACA,IACA,IAAA2D,QAAA,KAAAV,OAAAC,SAAA,gBAAAO,KAAA,KAAA1E,WAAA6E,MAAA,EAAA3E,YACA,KAAA4E,eAAAF,EACA,OAAAG,GACA,KAAAC,MAAAC,MAAA,QAAAF,EAAA,KAAApE,GAAA,4BACA,SACA,KAAAgE,sBAAA1D,EACA,CACA,EACA6D,cAAAA,CAAAF,GACAA,GACA,KAAAV,OAAAE,OAAA,YACAjB,MAAA,KAAAjB,UACA4B,IAAAc,EAAAM,OAAApB,IACAW,YAAA,KAAAvB,iBAGA,IC7KqP,I,WCQjPiC,GAAY,OACd,EACA1F,EACAuB,GACA,EACA,KACA,WACA,MAIF,EAAemE,EAAiB,O","sources":["webpack://@radiantearth/stac-browser/./src/views/Search.vue","webpack://@radiantearth/stac-browser/src/views/Search.vue","webpack://@radiantearth/stac-browser/./src/views/Search.vue?713d","webpack://@radiantearth/stac-browser/./src/views/Search.vue?1f58"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _c('main',{staticClass:\"search d-flex flex-column\"},[(!_vm.parent)?_c('Loading',{attrs:{\"stretch\":\"\"}}):(!_vm.searchLink)?_c('b-alert',{attrs:{\"variant\":\"danger\",\"show\":\"\"}},[_vm._v(_vm._s(_vm.$t('search.notSupported')))]):_c('b-row',[_c('b-col',{staticClass:\"left\"},[_c('ItemFilter',{attrs:{\"stac\":_vm.parent,\"title\":\"\",\"value\":_vm.filters,\"type\":\"Global\"},on:{\"input\":_vm.setFilters}})],1),_c('b-col',{staticClass:\"right\"},[(_vm.loading)?_c('Loading',{attrs:{\"fill\":\"\",\"top\":\"\"}}):(!_vm.hasItems && !_vm.hasFilters)?_c('b-alert',{attrs:{\"variant\":\"info\",\"show\":\"\"}},[_vm._v(_vm._s(_vm.$t('search.modifyCriteria')))]):(!_vm.hasItems)?_c('b-alert',{attrs:{\"variant\":\"warning\",\"show\":\"\"}},[_vm._v(_vm._s(_vm.$t('search.noItemsFound')))]):_vm._e(),(_vm.hasItems)?[_c('div',{attrs:{\"id\":\"search-map\"}},[_c('Map',{attrs:{\"stac\":_vm.parent,\"stacLayerData\":_vm.itemCollection,\"scrollWheelZoom\":\"\",\"popover\":\"\"}})],1),_c('Items',{attrs:{\"stac\":_vm.parent,\"items\":_vm.apiItems,\"api\":true,\"allowFilter\":false,\"pagination\":_vm.itemPages},on:{\"paginate\":_vm.paginateItems}})]:_vm._e()],2)],1)],1)\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Search.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Search.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./Search.vue?vue&type=template&id=7e03b515&scoped=true\"\nimport script from \"./Search.vue?vue&type=script&lang=js\"\nexport * from \"./Search.vue?vue&type=script&lang=js\"\nimport style0 from \"./Search.vue?vue&type=style&index=0&id=7e03b515&prod&lang=scss&scoped=true\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7e03b515\",\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_c","_self","staticClass","parent","searchLink","attrs","filters","on","setFilters","loading","hasItems","hasFilters","_e","_v","_s","$t","itemCollection","apiItems","itemPages","paginateItems","staticRenderFns","searchId","name","components","ItemFilter","Items","Loading","Map","props","loadParent","type","String","default","data","selectedItem","computed","mapState","mapGetters","pageTitle","getApiItemsLoading","STAC","getSearchLink","features","links","pages","Object","assign","apiItemsPagination","first","apiItemsLink","Utils","addFiltersToLink","size","length","pageDescription","title","getDisplayTitle","collectionLink","parentLink","root","catalogTitle","watch","immediate","handler","showPage","created","url","catalogUrl","fromBrowserPath","getStac","$store","dispatch","commit","methods","mapMutations","reset","filterItems","description","link","toggleApiItemsLoading","response","show","handleResponse","error","$root","$emit","config","component"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js deleted file mode 100644 index 108d5422..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1284],{51284:function(e,a,t){t.r(a);const r="DD/MM/YYYY",s=Promise.all([t.e(2160),t.e(760)]).then(t.t.bind(t,760,23));a["default"]={format:r,locale:s}}}]); -//# sourceMappingURL=1284.202c3e88.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js.map deleted file mode 100644 index 3bb8c31a..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1284.202c3e88.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1284.202c3e88.js","mappings":"4JAAA,MAAMA,EAAS,aACTC,EAAS,2DACf,cAAgBD,SAAQC,S","sources":["webpack://@radiantearth/stac-browser/./src/locales/it/datepicker.js"],"sourcesContent":["const format = 'DD/MM/YYYY';\nconst locale = import('vue2-datepicker/locale/it');\nexport default {format, locale};\n"],"names":["format","locale"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js deleted file mode 100644 index cf46fd37..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1528],{11528:function(t,e,s){s.r(e),s.d(e,{default:function(){return d}});var i=function(){var t=this,e=t._self._c;return e("b-alert",{staticClass:"deprecation",attrs:{variant:t.variant,show:""}},[e("h3",[t._v(t._s(t.title))]),e("Description",{attrs:{description:t.message,inline:""}}),t.latestLink||t.successorLink||t.predecessorLink?e("ul",[t.latestLink?e("li",[t._v(" "+t._s(t.$t("deprecation.latestVersion"))+" "),e("StacLink",{attrs:{data:t.latestLink,fallbackTitle:t.$t("fallbackTitle")}})],1):t._e(),t.successorLink?e("li",[t._v(" "+t._s(t.$t("deprecation.successorVersion"))+" "),e("StacLink",{attrs:{data:t.successorLink,fallbackTitle:t.$t("fallbackTitle")}})],1):t._e(),t.predecessorLink?e("li",[t._v(" "+t._s(t.$t("deprecation.predecessorVersion"))+" "),e("StacLink",{attrs:{data:t.predecessorLink,fallbackTitle:t.$t("fallbackTitle")}})],1):t._e()]):t._e()],1)},a=[],r=s(44093),n={name:"DeprecationNotice",components:{StacLink:()=>Promise.resolve().then(s.bind(s,22308)),Description:r["default"]},props:{data:{type:Object,default:null}},computed:{message(){let t={type:this.type};return this.isDeprecated?this.$t("deprecation.warning",t):this.$t("deprecation.otherVersionsNotice",t)},latestLink(){return this.data.getStacLinkWithRel("latest-version")},successorLink(){return this.data.getStacLinkWithRel("successor-version")},predecessorLink(){return!this.isDeprecated&&this.data.getStacLinkWithRel("predecessor-version")},variant(){return this.isDeprecated?"warning":"info"},isDeprecated(){return Boolean(this.data.isItem()?this.data.properties.deprecated:this.data.deprecated)},title(){return this.isDeprecated?this.$t("deprecated"):this.latestLink||this.successorLink?this.$t("deprecation.outdatedTitle"):this.$t("deprecation.otherVersionsTitle")},type(){return this.data.isItem()?this.$tc("stacItem"):this.data.isCollection()?this.$tc("stacCollection"):this.data.isCatalog()?this.$tc("stacCatalog"):""}}},c=n,o=s(82528),l=(0,o.c)(c,i,a,!1,null,"0943ed92",null),d=l.exports}}]); -//# sourceMappingURL=1528.1dd4310c.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js.map deleted file mode 100644 index 9e69a04d..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1528.1dd4310c.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1528.1dd4310c.js","mappings":"kMAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAKC,EAAGF,EAAIG,MAAMD,GAAG,OAAOA,EAAG,UAAU,CAACE,YAAY,cAAcC,MAAM,CAAC,QAAUL,EAAIM,QAAQ,KAAO,KAAK,CAACJ,EAAG,KAAK,CAACF,EAAIO,GAAGP,EAAIQ,GAAGR,EAAIS,UAAUP,EAAG,cAAc,CAACG,MAAM,CAAC,YAAcL,EAAIU,QAAQ,OAAS,MAAOV,EAAIW,YAAcX,EAAIY,eAAiBZ,EAAIa,gBAAiBX,EAAG,KAAK,CAAEF,EAAIW,WAAYT,EAAG,KAAK,CAACF,EAAIO,GAAG,IAAIP,EAAIQ,GAAGR,EAAIc,GAAG,8BAA8B,KAAKZ,EAAG,WAAW,CAACG,MAAM,CAAC,KAAOL,EAAIW,WAAW,cAAgBX,EAAIc,GAAG,qBAAqB,GAAGd,EAAIe,KAAMf,EAAIY,cAAeV,EAAG,KAAK,CAACF,EAAIO,GAAG,IAAIP,EAAIQ,GAAGR,EAAIc,GAAG,iCAAiC,KAAKZ,EAAG,WAAW,CAACG,MAAM,CAAC,KAAOL,EAAIY,cAAc,cAAgBZ,EAAIc,GAAG,qBAAqB,GAAGd,EAAIe,KAAMf,EAAIa,gBAAiBX,EAAG,KAAK,CAACF,EAAIO,GAAG,IAAIP,EAAIQ,GAAGR,EAAIc,GAAG,mCAAmC,KAAKZ,EAAG,WAAW,CAACG,MAAM,CAAC,KAAOL,EAAIa,gBAAgB,cAAgBb,EAAIc,GAAG,qBAAqB,GAAGd,EAAIe,OAAOf,EAAIe,MAAM,EACp5B,EACIC,EAAkB,G,WCsBtB,GACAC,KAAA,oBACAC,WAAA,CACAC,SAAAA,IAAA,wCACAC,YAAAA,EAAAA,YAEAC,MAAA,CACAC,KAAA,CACAC,KAAAC,OACAC,QAAA,OAGAC,SAAA,CACAhB,OAAAA,GACA,IAAAiB,EAAA,CAAAJ,KAAA,KAAAA,MACA,YAAAK,aACA,KAAAd,GAAA,sBAAAa,GAIA,KAAAb,GAAA,kCAAAa,EAEA,EACAhB,UAAAA,GACA,YAAAW,KAAAO,mBAAA,iBACA,EACAjB,aAAAA,GACA,YAAAU,KAAAO,mBAAA,oBACA,EACAhB,eAAAA,GAEA,YAAAe,cAAA,KAAAN,KAAAO,mBAAA,sBACA,EACAvB,OAAAA,GACA,YAAAsB,aAAA,gBACA,EACAA,YAAAA,GACA,OAAAE,QAAA,KAAAR,KAAAS,SAAA,KAAAT,KAAAU,WAAAC,WAAA,KAAAX,KAAAW,WACA,EACAxB,KAAAA,GACA,YAAAmB,aACA,KAAAd,GAAA,cAEA,KAAAH,YAAA,KAAAC,cACA,KAAAE,GAAA,6BAGA,KAAAA,GAAA,iCAEA,EACAS,IAAAA,GACA,YAAAD,KAAAS,SACA,KAAAG,IAAA,YAEA,KAAAZ,KAAAa,eACA,KAAAD,IAAA,kBAEA,KAAAZ,KAAAc,YACA,KAAAF,IAAA,eAGA,EAEA,ICvFgQ,I,WCQ5PG,GAAY,OACd,EACAtC,EACAiB,GACA,EACA,KACA,WACA,MAIF,EAAeqB,EAAiB,O","sources":["webpack://@radiantearth/stac-browser/./src/components/DeprecationNotice.vue","webpack://@radiantearth/stac-browser/src/components/DeprecationNotice.vue","webpack://@radiantearth/stac-browser/./src/components/DeprecationNotice.vue?224a","webpack://@radiantearth/stac-browser/./src/components/DeprecationNotice.vue?46d5"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _c('b-alert',{staticClass:\"deprecation\",attrs:{\"variant\":_vm.variant,\"show\":\"\"}},[_c('h3',[_vm._v(_vm._s(_vm.title))]),_c('Description',{attrs:{\"description\":_vm.message,\"inline\":\"\"}}),(_vm.latestLink || _vm.successorLink || _vm.predecessorLink)?_c('ul',[(_vm.latestLink)?_c('li',[_vm._v(\" \"+_vm._s(_vm.$t('deprecation.latestVersion'))+\" \"),_c('StacLink',{attrs:{\"data\":_vm.latestLink,\"fallbackTitle\":_vm.$t('fallbackTitle')}})],1):_vm._e(),(_vm.successorLink)?_c('li',[_vm._v(\" \"+_vm._s(_vm.$t('deprecation.successorVersion'))+\" \"),_c('StacLink',{attrs:{\"data\":_vm.successorLink,\"fallbackTitle\":_vm.$t('fallbackTitle')}})],1):_vm._e(),(_vm.predecessorLink)?_c('li',[_vm._v(\" \"+_vm._s(_vm.$t('deprecation.predecessorVersion'))+\" \"),_c('StacLink',{attrs:{\"data\":_vm.predecessorLink,\"fallbackTitle\":_vm.$t('fallbackTitle')}})],1):_vm._e()]):_vm._e()],1)\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./DeprecationNotice.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./DeprecationNotice.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./DeprecationNotice.vue?vue&type=template&id=0943ed92&scoped=true\"\nimport script from \"./DeprecationNotice.vue?vue&type=script&lang=js\"\nexport * from \"./DeprecationNotice.vue?vue&type=script&lang=js\"\nimport style0 from \"./DeprecationNotice.vue?vue&type=style&index=0&id=0943ed92&prod&lang=scss&scoped=true\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"0943ed92\",\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_c","_self","staticClass","attrs","variant","_v","_s","title","message","latestLink","successorLink","predecessorLink","$t","_e","staticRenderFns","name","components","StacLink","Description","props","data","type","Object","default","computed","vars","isDeprecated","getStacLinkWithRel","Boolean","isItem","properties","deprecated","$tc","isCollection","isCatalog","component"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js deleted file mode 100644 index 48ecf547..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js +++ /dev/null @@ -1,2 +0,0 @@ -(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1584],{41584:function(e){(function(r,t){e.exports=t()})(0,(function(){"use strict";function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e["default"]:e}function r(e,r){return r={exports:{}},e(r,r.exports),r.exports}var t=r((function(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=void 0;var t={months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],weekdaysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],weekdaysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],firstDayOfWeek:0,firstWeekContainsDate:1},a=t;r["default"]=a,e.exports=r.default})),a=e(t),u={formatLocale:a,yearFormat:"YYYY",monthFormat:"MMM",monthBeforeYear:!0};return u}))}}]); -//# sourceMappingURL=1584.a59f0b52.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js.map deleted file mode 100644 index 1f4d3da9..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1584.a59f0b52.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1584.a59f0b52.js","mappings":"qIAAC,SAAUA,EAAQC,GAC6CC,EAAOC,QAAUF,GAGjF,EAJA,CAIEG,GAAM,WAAe,aAEtB,SAASC,EAAeC,GACvB,OAAOA,GAAKA,EAAEC,YAAcC,OAAOC,UAAUC,eAAeC,KAAKL,EAAG,WAAaA,EAAE,WAAaA,CACjG,CAEA,SAASM,EAAqBC,EAAIX,GACjC,OAAOA,EAAS,CAAEC,QAAS,CAAC,GAAKU,EAAGX,EAAQA,EAAOC,SAAUD,EAAOC,OACrE,CAEA,IAAIW,EAAKF,GAAqB,SAAUV,EAAQC,GAEhDK,OAAOO,eAAeZ,EAAS,aAAc,CAC3Ca,OAAO,IAETb,EAAQ,gBAAa,EACrB,IAAIc,EAAS,CACXC,OAAQ,CAAC,UAAW,WAAY,QAAS,QAAS,MAAO,OAAQ,OAAQ,SAAU,YAAa,UAAW,WAAY,YACvHC,YAAa,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,OAC3FC,SAAU,CAAC,SAAU,SAAU,UAAW,YAAa,WAAY,SAAU,YAC7EC,cAAe,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,OAC1DC,YAAa,CAAC,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MAClDC,eAAgB,EAChBC,sBAAuB,GAErBC,EAAWR,EACfd,EAAQ,WAAasB,EACrBvB,EAAOC,QAAUA,EAAQuB,OACzB,IAEIC,EAAOtB,EAAcS,GAErBc,EAAO,CACTC,aAAcF,EACdG,WAAY,OACZC,YAAa,MACbC,iBAAiB,GAGnB,OAAOJ,CAEP,G","sources":["webpack://@radiantearth/stac-browser/./node_modules/vue2-datepicker/locale/en.js"],"sourcesContent":["(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n\ttypeof define === 'function' && define.amd ? define(factory) :\n\t(global = global || self, (global.DatePicker = global.DatePicker || {}, global.DatePicker.lang = global.DatePicker.lang || {}, global.DatePicker.lang.en = factory()));\n}(this, (function () { 'use strict';\n\n\tfunction unwrapExports (x) {\n\t\treturn x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;\n\t}\n\n\tfunction createCommonjsModule(fn, module) {\n\t\treturn module = { exports: {} }, fn(module, module.exports), module.exports;\n\t}\n\n\tvar en = createCommonjsModule(function (module, exports) {\n\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports[\"default\"] = void 0;\n\tvar locale = {\n\t months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n\t monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n\t weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\n\t weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n\t weekdaysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],\n\t firstDayOfWeek: 0,\n\t firstWeekContainsDate: 1\n\t};\n\tvar _default = locale;\n\texports[\"default\"] = _default;\n\tmodule.exports = exports.default;\n\t});\n\n\tvar en$1 = unwrapExports(en);\n\n\tvar lang = {\n\t formatLocale: en$1,\n\t yearFormat: 'YYYY',\n\t monthFormat: 'MMM',\n\t monthBeforeYear: true\n\t};\n\n\treturn lang;\n\n})));\n"],"names":["global","factory","module","exports","this","unwrapExports","x","__esModule","Object","prototype","hasOwnProperty","call","createCommonjsModule","fn","en","defineProperty","value","locale","months","monthsShort","weekdays","weekdaysShort","weekdaysMin","firstDayOfWeek","firstWeekContainsDate","_default","default","en$1","lang","formatLocale","yearFormat","monthFormat","monthBeforeYear"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js deleted file mode 100644 index 61f3437f..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js +++ /dev/null @@ -1,2 +0,0 @@ -(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1752],{1752:function(e,r,t){(function(r,o){e.exports=o(t(72160))})(0,(function(e){"use strict";function r(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e["default"]:e}function t(e,r){return r={exports:{}},e(r,r.exports),r.exports}e=e&&e.hasOwnProperty("default")?e["default"]:e;var o=t((function(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=void 0;var t={months:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthsShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],weekdays:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],weekdaysShort:["dom","lun","mar","mié","jue","vie","sáb"],weekdaysMin:["do","lu","ma","mi","ju","vi","sá"],firstDayOfWeek:1,firstWeekContainsDate:1},o=t;r["default"]=o,e.exports=r.default})),a=r(o),n={formatLocale:a,yearFormat:"YYYY",monthFormat:"MMM",monthBeforeYear:!0};return e.locale("es",n),n}))}}]); -//# sourceMappingURL=1752.6038b67b.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js.map deleted file mode 100644 index 3ff46fd0..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1752.6038b67b.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1752.6038b67b.js","mappings":"wIAAC,SAAUA,EAAQC,GAC6CC,EAAOC,QAAUF,EAAQ,EAAQ,OAGjG,EAJA,CAIEG,GAAM,SAAWC,GAAc,aAIhC,SAASC,EAAeC,GACvB,OAAOA,GAAKA,EAAEC,YAAcC,OAAOC,UAAUC,eAAeC,KAAKL,EAAG,WAAaA,EAAE,WAAaA,CACjG,CAEA,SAASM,EAAqBC,EAAIZ,GACjC,OAAOA,EAAS,CAAEC,QAAS,CAAC,GAAKW,EAAGZ,EAAQA,EAAOC,SAAUD,EAAOC,OACrE,CARAE,EAAaA,GAAcA,EAAWM,eAAe,WAAaN,EAAW,WAAaA,EAU1F,IAAIU,EAAKF,GAAqB,SAAUX,EAAQC,GAEhDM,OAAOO,eAAeb,EAAS,aAAc,CAC3Cc,OAAO,IAETd,EAAQ,gBAAa,EACrB,IAAIe,EAAS,CACXC,OAAQ,CAAC,QAAS,UAAW,QAAS,QAAS,OAAQ,QAAS,QAAS,SAAU,aAAc,UAAW,YAAa,aACzHC,YAAa,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,OAC3FC,SAAU,CAAC,UAAW,QAAS,SAAU,YAAa,SAAU,UAAW,UAC3EC,cAAe,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,OAC1DC,YAAa,CAAC,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MAClDC,eAAgB,EAChBC,sBAAuB,GAErBC,EAAWR,EACff,EAAQ,WAAauB,EACrBxB,EAAOC,QAAUA,EAAQwB,OACzB,IAEIC,EAAOtB,EAAcS,GAErBc,EAAO,CACTC,aAAcF,EACdG,WAAY,OACZC,YAAa,MACbC,iBAAiB,GAInB,OAFA5B,EAAWa,OAAO,KAAMW,GAEjBA,CAEP,G","sources":["webpack://@radiantearth/stac-browser/./node_modules/vue2-datepicker/locale/es.js"],"sourcesContent":["(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue2-datepicker')) :\n\ttypeof define === 'function' && define.amd ? define(['vue2-datepicker'], factory) :\n\t(global = global || self, (global.DatePicker = global.DatePicker || {}, global.DatePicker.lang = global.DatePicker.lang || {}, global.DatePicker.lang.es = factory(global.DatePicker)));\n}(this, (function (DatePicker) { 'use strict';\n\n\tDatePicker = DatePicker && DatePicker.hasOwnProperty('default') ? DatePicker['default'] : DatePicker;\n\n\tfunction unwrapExports (x) {\n\t\treturn x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;\n\t}\n\n\tfunction createCommonjsModule(fn, module) {\n\t\treturn module = { exports: {} }, fn(module, module.exports), module.exports;\n\t}\n\n\tvar es = createCommonjsModule(function (module, exports) {\n\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports[\"default\"] = void 0;\n\tvar locale = {\n\t months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],\n\t monthsShort: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],\n\t weekdays: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],\n\t weekdaysShort: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],\n\t weekdaysMin: ['do', 'lu', 'ma', 'mi', 'ju', 'vi', 'sá'],\n\t firstDayOfWeek: 1,\n\t firstWeekContainsDate: 1\n\t};\n\tvar _default = locale;\n\texports[\"default\"] = _default;\n\tmodule.exports = exports.default;\n\t});\n\n\tvar es$1 = unwrapExports(es);\n\n\tvar lang = {\n\t formatLocale: es$1,\n\t yearFormat: 'YYYY',\n\t monthFormat: 'MMM',\n\t monthBeforeYear: true\n\t};\n\tDatePicker.locale('es', lang);\n\n\treturn lang;\n\n})));\n"],"names":["global","factory","module","exports","this","DatePicker","unwrapExports","x","__esModule","Object","prototype","hasOwnProperty","call","createCommonjsModule","fn","es","defineProperty","value","locale","months","monthsShort","weekdays","weekdaysShort","weekdaysMin","firstDayOfWeek","firstWeekContainsDate","_default","default","es$1","lang","formatLocale","yearFormat","monthFormat","monthBeforeYear"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js deleted file mode 100644 index 98cec2b3..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1788],{81788:function(t,e,a){a.r(e),a.d(e,{default:function(){return p}});var l=function(){var t=this,e=t._self._c;return t.collection?e("section",{staticClass:"parent-collection card-list mb-4"},[e("h2",[t._v(t._s(t.$tc("stacCollection")))]),e("Catalog",{attrs:{catalog:t.collection,showThumbnail:t.showThumbnail}})],1):t._e()},c=[],n=a(54772),o=a(48416),i=a(40848),s={name:"CollectionLink",components:{Catalog:n.c},props:{link:{type:Object,required:!0},showThumbnail:{type:Boolean,default:!1}},computed:{...(0,o.gV)(["getStac"]),collection(){return this.getStac(this.link)}},watch:{link:{immediate:!0,handler(t){i.cp.isObject(t)&&this.$store.dispatch("load",{url:t.href})}}}},r=s,u=a(82528),h=(0,u.c)(r,l,c,!1,null,null,null),p=h.exports}}]); -//# sourceMappingURL=1788.e7c77fdd.js.map \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js.map b/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js.map deleted file mode 100644 index 9c3609d8..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1788.e7c77fdd.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"js/1788.e7c77fdd.js","mappings":"kMAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAKC,EAAGF,EAAIG,MAAMD,GAAG,OAAQF,EAAII,WAAYF,EAAG,UAAU,CAACG,YAAY,oCAAoC,CAACH,EAAG,KAAK,CAACF,EAAIM,GAAGN,EAAIO,GAAGP,EAAIQ,IAAI,sBAAsBN,EAAG,UAAU,CAACO,MAAM,CAAC,QAAUT,EAAII,WAAW,cAAgBJ,EAAIU,kBAAkB,GAAGV,EAAIW,IACnS,EACIC,EAAkB,G,iCCUtB,GACAC,KAAA,iBACAC,WAAA,CACAC,QAAAA,EAAAA,GAEAC,MAAA,CACAC,KAAA,CACAC,KAAAC,OACAC,UAAA,GAEAV,cAAA,CACAQ,KAAAG,QACAC,SAAA,IAGAC,SAAA,KACAC,EAAAA,EAAAA,IAAA,aACApB,UAAAA,GACA,YAAAqB,QAAA,KAAAR,KACA,GAEAS,MAAA,CACAT,KAAA,CACAU,WAAA,EACAC,OAAAA,CAAAC,GACAC,EAAAA,GAAAC,SAAAF,IACA,KAAAG,OAAAC,SAAA,QAAAC,IAAAL,EAAAM,MAEA,KCxC6P,I,WCOzPC,GAAY,OACd,EACArC,EACAa,GACA,EACA,KACA,KACA,MAIF,EAAewB,EAAiB,O","sources":["webpack://@radiantearth/stac-browser/./src/components/CollectionLink.vue","webpack://@radiantearth/stac-browser/src/components/CollectionLink.vue","webpack://@radiantearth/stac-browser/./src/components/CollectionLink.vue?69e3","webpack://@radiantearth/stac-browser/./src/components/CollectionLink.vue?dc86"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return (_vm.collection)?_c('section',{staticClass:\"parent-collection card-list mb-4\"},[_c('h2',[_vm._v(_vm._s(_vm.$tc('stacCollection')))]),_c('Catalog',{attrs:{\"catalog\":_vm.collection,\"showThumbnail\":_vm.showThumbnail}})],1):_vm._e()\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./CollectionLink.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./CollectionLink.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./CollectionLink.vue?vue&type=template&id=4be67dea\"\nimport script from \"./CollectionLink.vue?vue&type=script&lang=js\"\nexport * from \"./CollectionLink.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_c","_self","collection","staticClass","_v","_s","$tc","attrs","showThumbnail","_e","staticRenderFns","name","components","Catalog","props","link","type","Object","required","Boolean","default","computed","mapGetters","getStac","watch","immediate","handler","newLink","Utils","isObject","$store","dispatch","url","href","component"],"sourceRoot":""} \ No newline at end of file diff --git a/cumulus_lambda_functions/uds_api/stac_browser/js/1824.9102e1c7.js b/cumulus_lambda_functions/uds_api/stac_browser/js/1824.9102e1c7.js deleted file mode 100644 index ee9cbd4f..00000000 --- a/cumulus_lambda_functions/uds_api/stac_browser/js/1824.9102e1c7.js +++ /dev/null @@ -1,2 +0,0 @@ -(self["webpackChunk_radiantearth_stac_browser"]=self["webpackChunk_radiantearth_stac_browser"]||[]).push([[1824],{62052:function(t,e,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&r(e,t,n);return i(e,t),e},s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const a=s(n(91940)),u=s(n(30160)),c=o(n(45980));function l(t,e){const n=[];f(t,"schema",t.$refs._root$Ref.path+"#","#",0,n,t.$refs,e),h(n)}function f(t,e,n,r,i,o,s,c){const l=null===e?t:t[e];if(l&&"object"===typeof l&&!ArrayBuffer.isView(l))if(a.default.isAllowed$Ref(l))p(t,e,n,r,i,o,s,c);else{const t=Object.keys(l).sort(((t,e)=>"definitions"===t?-1:"definitions"===e?1:t.length-e.length));for(const e of t){const t=u.default.join(n,e),h=u.default.join(r,e),d=l[e];a.default.isAllowed$Ref(d)?p(l,e,n,h,i,o,s,c):f(l,e,t,h,i,o,s,c)}}}function p(t,e,n,r,i,o,s,l){const p=null===e?t:t[e],h=c.resolve(n,p.$ref),g=s._resolve(h,r,l);if(null===g)return;const m=u.default.parse(r).length,b=c.stripHash(g.path),y=c.getHash(g.path),w=b!==s._root$Ref.path,x=a.default.isExtended$Ref(p);i+=g.indirections;const _=d(o,t,e);if(_){if(!(m<_.depth||i<_.indirections))return;v(o,_)}o.push({$ref:p,parent:t,key:e,pathFromRoot:r,depth:m,file:b,hash:y,value:g.value,circular:g.circular,extended:x,external:w,indirections:i}),_&&!w||f(g.value,null,g.path,r,i+1,o,s,l)}function h(t){let e,n,r;t.sort(((t,e)=>{if(t.file!==e.file)return t.file!1);if(("ignore"===c.dereference.circular||!i.has(t))&&t&&"object"===typeof t&&!ArrayBuffer.isView(t)&&!v(n)){if(r.add(t),i.add(t),a.default.isAllowed$Ref(t,c))l=h(t,e,n,r,i,o,s,c),f.circular=l.circular,f.value=l.value;else for(const g of Object.keys(t)){const m=u.default.join(e,g),b=u.default.join(n,g);if(v(b))continue;const y=t[g];let w=!1;a.default.isAllowed$Ref(y,c)?(l=h(y,m,b,r,i,o,s,c),w=l.circular,t[g]!==l.value&&(t[g]=l.value,c.dereference.onDereference&&c.dereference.onDereference(y.$ref,t[g]))):r.has(y)?w=d(m,s,c):(l=p(y,m,b,r,i,o,s,c),w=l.circular,t[g]!==l.value&&(t[g]=l.value)),f.circular=f.circular||w}r.delete(t)}return f}function h(t,e,n,r,i,o,s,u){const c=l.resolve(e,t.$ref),f=o.get(c);if(f){const e=Object.keys(t);if(e.length>1){const n={};for(const r of e)"$ref"===r||r in f.value||(n[r]=t[r]);return{circular:f.circular,value:Object.assign({},f.value,n)}}return f}const h=s._resolve(c,e,u);if(null===h)return{circular:!1,value:null};const v=h.circular;let g=v||r.has(h.value);g&&d(e,s,u);let m=a.default.dereference(t,h.value);if(!g){const t=p(m,h.path,n,r,i,o,s,u);g=t.circular,m=t.value}g&&!v&&"ignore"===u.dereference.circular&&(m=t),v&&(m.$ref=n);const b={circular:g,value:m};return 1===Object.keys(t).length&&o.set(c,b),b}function d(t,e,n){if(e.circular=!0,!n.dereference.circular)throw c.ono.reference(`Circular $ref pointer found at ${t}`);return!0}e["default"]=f},18356:function(t,e,n){"use strict";var r=n(33296)["Buffer"],i=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),o=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),s=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&i(e,t,n);return o(e,t),e},a=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))},u=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.dereference=e.bundle=e.resolve=e.parse=e.$RefParser=e.UnmatchedResolverError=e.UnmatchedParserError=e.ParserError=e.ResolverError=e.MissingPointerError=e.InvalidPointerError=e.JSONParserError=void 0;const c=u(n(53316)),l=u(n(11480)),f=u(n(49181)),p=u(n(22512)),h=u(n(62052)),d=u(n(63580)),v=s(n(45980)),g=n(76536);Object.defineProperty(e,"JSONParserError",{enumerable:!0,get:function(){return g.JSONParserError}}),Object.defineProperty(e,"InvalidPointerError",{enumerable:!0,get:function(){return g.InvalidPointerError}}),Object.defineProperty(e,"MissingPointerError",{enumerable:!0,get:function(){return g.MissingPointerError}}),Object.defineProperty(e,"ResolverError",{enumerable:!0,get:function(){return g.ResolverError}}),Object.defineProperty(e,"ParserError",{enumerable:!0,get:function(){return g.ParserError}}),Object.defineProperty(e,"UnmatchedParserError",{enumerable:!0,get:function(){return g.UnmatchedParserError}}),Object.defineProperty(e,"UnmatchedResolverError",{enumerable:!0,get:function(){return g.UnmatchedResolverError}});const m=n(42856),b=u(n(49968));class y{constructor(){this.schema=null,this.$refs=new c.default}parse(){return a(this,arguments,void 0,(function*(){const t=(0,f.default)(arguments);let e;if(!t.path&&!t.schema){const e=(0,m.ono)(`Expected a file path, URL, or object. Got ${t.path||t.schema}`);return(0,b.default)(t.callback,Promise.reject(e))}this.schema=null,this.$refs=new c.default;let n="http";if(v.isFileSystemPath(t.path)&&(t.path=v.fromFileSystemPath(t.path),n="file"),t.path=v.resolve(v.cwd(),t.path),t.schema&&"object"===typeof t.schema){const r=this.$refs._add(t.path);r.value=t.schema,r.pathType=n,e=Promise.resolve(t.schema)}else e=(0,l.default)(t.path,this.$refs,t.options);try{const n=yield e;if(null===n||"object"!==typeof n||r.isBuffer(n)){if(t.options.continueOnError)return this.schema=null,(0,b.default)(t.callback,Promise.resolve(this.schema));throw m.ono.syntax(`"${this.$refs._root$Ref.path||n}" is not a valid JSON Schema`)}return this.schema=n,(0,b.default)(t.callback,Promise.resolve(this.schema))}catch(i){return t.options.continueOnError&&(0,g.isHandledError)(i)?(this.$refs._$refs[v.stripHash(t.path)]&&this.$refs._$refs[v.stripHash(t.path)].addError(i),(0,b.default)(t.callback,Promise.resolve(null))):(0,b.default)(t.callback,Promise.reject(i))}}))}static parse(){const t=new y;return t.parse.apply(t,arguments)}resolve(){return a(this,arguments,void 0,(function*(){const t=(0,f.default)(arguments);try{return yield this.parse(t.path,t.schema,t.options),yield(0,p.default)(this,t.options),w(this),(0,b.default)(t.callback,Promise.resolve(this.$refs))}catch(e){return(0,b.default)(t.callback,Promise.reject(e))}}))}static resolve(){const t=new y;return t.resolve.apply(t,arguments)}static bundle(){const t=new y;return t.bundle.apply(t,arguments)}bundle(){return a(this,arguments,void 0,(function*(){const t=(0,f.default)(arguments);try{return yield this.resolve(t.path,t.schema,t.options),(0,h.default)(this,t.options),w(this),(0,b.default)(t.callback,Promise.resolve(this.schema))}catch(e){return(0,b.default)(t.callback,Promise.reject(e))}}))}static dereference(){const t=new y;return t.dereference.apply(t,arguments)}dereference(){return a(this,arguments,void 0,(function*(){const t=(0,f.default)(arguments);try{return yield this.resolve(t.path,t.schema,t.options),(0,d.default)(this,t.options),w(this),(0,b.default)(t.callback,Promise.resolve(this.schema))}catch(e){return(0,b.default)(t.callback,Promise.reject(e))}}))}}function w(t){const e=g.JSONParserErrorGroup.getParserErrors(t);if(e.length>0)throw new g.JSONParserErrorGroup(t)}e.$RefParser=y,e["default"]=y,e.parse=y.parse,e.resolve=y.resolve,e.bundle=y.bundle,e.dereference=y.dereference},49181:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const r=n(74380);function i(t){let e,n,i,o;const s=Array.prototype.slice.call(t);return"function"===typeof s[s.length-1]&&(o=s.pop()),"string"===typeof s[0]?(e=s[0],"object"===typeof s[2]?(n=s[1],i=s[2]):(n=void 0,i=s[1])):(e="",n=s[0],i=s[1]),i=(0,r.getNewOptions)(i),{path:e,schema:n,options:i,callback:o}}e["default"]=i},74380:function(t,e,n){"use strict";var r=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.getNewOptions=void 0;const i=r(n(47848)),o=r(n(59584)),s=r(n(24504)),a=r(n(55228)),u=r(n(41408)),c=r(n(48752)),l=r(n(59907)),f=()=>{const t={parse:{json:i.default,yaml:o.default,text:s.default,binary:a.default},resolve:{file:u.default,http:c.default,external:!0},continueOnError:!1,dereference:{circular:!0,excludedPathMatcher:()=>!1}};return(0,l.default)(t)},p=t=>{const e=f();return t&&h(e,t),e};function h(t,e){if(d(e)){const n=Object.keys(e);for(let r=0;r0?i:r;l.sort(o);try{const e=yield l.run(o,"parse",t,n);if(!e.plugin.allowEmpty&&v(e.result))throw u.ono.syntax(`Error parsing "${t.url}" as ${e.plugin.name}. \nParsed value is empty`);return e}catch(s){throw!s&&e.continueOnError?new f.UnmatchedParserError(t.url):s&&s.message&&s.message.startsWith("Error parsing")?s:s&&"error"in s?s.error instanceof f.ParserError?s.error:new f.ParserError(s.error.message,t.url):u.ono.syntax(`Unable to parse ${t.url}`)}}))}function v(t){return void 0===t||"object"===typeof t&&0===Object.keys(t).length||"string"===typeof t&&0===t.trim().length||r.isBuffer(t)&&0===t.length}e["default"]=p},55228:function(t,e,n){"use strict";var r=n(33296)["Buffer"];Object.defineProperty(e,"__esModule",{value:!0});const i=/\.(jpeg|jpg|gif|png|bmp|ico)$/i;e["default"]={order:400,allowEmpty:!0,canParse(t){return r.isBuffer(t.data)&&i.test(t.url)},parse(t){return r.isBuffer(t.data)?t.data:r.from(t.data)}}},47848:function(t,e,n){"use strict";var r=n(33296)["Buffer"],i=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0});const o=n(76536);e["default"]={order:100,allowEmpty:!0,canParse:".json",parse(t){return i(this,void 0,void 0,(function*(){let e=t.data;if(r.isBuffer(e)&&(e=e.toString()),"string"!==typeof e)return e;if(0!==e.trim().length)try{return JSON.parse(e)}catch(n){throw new o.ParserError(n.message,t.url)}}))}}},24504:function(t,e,n){"use strict";var r=n(33296)["Buffer"];Object.defineProperty(e,"__esModule",{value:!0});const i=n(76536),o=/\.(txt|htm|html|md|xml|js|min|map|css|scss|less|svg)$/i;e["default"]={order:300,allowEmpty:!0,encoding:"utf8",canParse(t){return("string"===typeof t.data||r.isBuffer(t.data))&&o.test(t.url)},parse(t){if("string"===typeof t.data)return t.data;if(r.isBuffer(t.data))return t.data.toString(this.encoding);throw new i.ParserError("data is not text",t.url)}}},59584:function(t,e,n){"use strict";var r=n(33296)["Buffer"],i=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))},o=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const s=n(76536),a=o(n(73344)),u=n(73344);e["default"]={order:200,allowEmpty:!0,canParse:[".yaml",".yml",".json"],parse(t){return i(this,void 0,void 0,(function*(){let e=t.data;if(r.isBuffer(e)&&(e=e.toString()),"string"!==typeof e)return e;try{return a.default.load(e,{schema:u.JSON_SCHEMA})}catch(n){throw new s.ParserError(n.message,t.url)}}))}}},30160:function(t,e,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&r(e,t,n);return i(e,t),e},s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const a=s(n(91940)),u=o(n(45980)),c=n(76536),l=/\//g,f=/~/g,p=/~1/g,h=/~0/g;class d{constructor(t,e,n){this.$ref=t,this.path=e,this.originalPath=n||e,this.value=void 0,this.circular=!1,this.indirections=0}resolve(t,e,n){const r=d.parse(this.path,this.originalPath);this.value=m(t);for(let i=0;it));"errors"in t&&Array.isArray(t.errors)?this.errors.push(...t.errors.map(o.normalizeError).filter((({footprint:t})=>!e.includes(t)))):"footprint"in t&&e.includes(t.footprint)||this.errors.push((0,o.normalizeError)(t))}exists(t,e){try{return this.resolve(t,e),!0}catch(n){return!1}}get(t,e){var n;return null===(n=this.resolve(t,e))||void 0===n?void 0:n.value}resolve(t,e,n,r){const a=new i.default(this,t,n);try{return a.resolve(this.value,e,r)}catch(u){if(!e||!e.continueOnError||!(0,o.isHandledError)(u))throw u;return null===u.path&&(u.path=(0,s.safePointerToPath)((0,s.getHash)(r))),u instanceof o.InvalidPointerError&&(u.source=decodeURI((0,s.stripHash)(r))),this.addError(u),null}}set(t,e){const n=new i.default(this,t);this.value=n.set(this.value,e)}static is$Ref(t){return t&&"object"===typeof t&&"string"===typeof t.$ref&&t.$ref.length>0}static isExternal$Ref(t){return a.is$Ref(t)&&"#"!==t.$ref[0]}static isAllowed$Ref(t,e){if(this.is$Ref(t)){if("#/"===t.$ref.substring(0,2)||"#"===t.$ref)return!0;if("#"!==t.$ref[0]&&(!e||e.resolve.external))return!0}}static isExtended$Ref(t){return a.is$Ref(t)&&Object.keys(t).length>1}static dereference(t,e){if(e&&"object"===typeof e&&a.isExtended$Ref(t)){const n={};for(const e of Object.keys(t))"$ref"!==e&&(n[e]=t[e]);for(const t of Object.keys(e))t in n||(n[t]=e[t]);return n}return e}}e["default"]=a},53316:function(t,e,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&r(e,t,n);return i(e,t),e},s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const a=n(42856),u=s(n(91940)),c=o(n(45980)),l=/^win/.test(globalThis.process?globalThis.process.platform:""),f=t=>l?t.replace(/\\/g,"/"):t;class p{paths(...t){const e=h(this._$refs,t);return e.map((t=>f(t.decoded)))}values(...t){const e=this._$refs,n=h(e,t);return n.reduce(((t,n)=>(t[f(n.decoded)]=e[n.encoded].value,t)),{})}exists(t,e){try{return this._resolve(t,"",e),!0}catch(n){return!1}}get(t,e){return this._resolve(t,"",e).value}set(t,e){const n=c.resolve(this._root$Ref.path,t),r=c.stripHash(n),i=this._$refs[r];if(!i)throw(0,a.ono)(`Error resolving $ref pointer "${t}". \n"${r}" not found.`);i.set(n,e)}_get$Ref(t){t=c.resolve(this._root$Ref.path,t);const e=c.stripHash(t);return this._$refs[e]}_add(t){const e=c.stripHash(t),n=new u.default(this);return n.path=e,this._$refs[e]=n,this._root$Ref=this._root$Ref||n,n}_resolve(t,e,n){const r=c.resolve(this._root$Ref.path,t),i=c.stripHash(r),o=this._$refs[i];if(!o)throw(0,a.ono)(`Error resolving $ref pointer "${t}". \n"${i}" not found.`);return o.resolve(r,n,t,e)}constructor(){this._$refs={},this.toJSON=this.values,this.circular=!1,this._$refs={},this._root$Ref=null}}function h(t,e){let n=Object.keys(t);return e=Array.isArray(e[0])?e[0]:Array.prototype.slice.call(e),e.length>0&&e[0]&&(n=n.filter((n=>e.includes(t[n].pathType)))),n.map((e=>({encoded:e,decoded:"file"===t[e].pathType?c.toFileSystemPath(e,!0):e})))}e["default"]=p},22512:function(t,e,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&r(e,t,n);return i(e,t),e},s=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))},a=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const u=a(n(91940)),c=a(n(30160)),l=a(n(11480)),f=o(n(45980)),p=n(76536);function h(t,e){if(!e.resolve.external)return Promise.resolve();try{const n=d(t.schema,t.$refs._root$Ref.path+"#",t.$refs,e);return Promise.all(n)}catch(n){return Promise.reject(n)}}function d(t,e,n,r,i){i||(i=new Set);let o=[];if(t&&"object"===typeof t&&!ArrayBuffer.isView(t)&&!i.has(t))if(i.add(t),u.default.isExternal$Ref(t))o.push(v(t,e,n,r));else for(const s of Object.keys(t)){const a=c.default.join(e,s),l=t[s];u.default.isExternal$Ref(l)?o.push(v(l,a,n,r)):o=o.concat(d(l,a,n,r,i))}return o}function v(t,e,n,r){return s(this,void 0,void 0,(function*(){const i=f.resolve(e,t.$ref),o=f.stripHash(i);if(t=n._$refs[o],t)return Promise.resolve(t.value);try{const t=yield(0,l.default)(i,n,r),e=d(t,o+"#",n,r);return Promise.all(e)}catch(s){if(!(null===r||void 0===r?void 0:r.continueOnError)||!(0,p.isHandledError)(s))throw s;return n._$refs[o]&&(s.source=decodeURI(f.stripHash(e)),s.path=f.safePointerToPath(f.getHash(e))),[]}}))}e["default"]=h},41408:function(t,e,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&r(e,t,n);return i(e,t),e},s=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))},a=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const u=a(n(33392)),c=n(42856),l=o(n(45980)),f=n(76536);e["default"]={order:100,canRead(t){return l.isFileSystemPath(t.url)},read(t){return s(this,void 0,void 0,(function*(){let e;try{e=l.toFileSystemPath(t.url)}catch(n){throw new f.ResolverError(c.ono.uri(n,`Malformed URI: ${t.url}`),t.url)}try{const t=yield u.default.readFile(e);return t}catch(n){throw new f.ResolverError((0,c.ono)(n,`Error opening file "${e}"`),e)}}))}}},48752:function(t,e,n){"use strict";var r=n(33296)["Buffer"],i=this&&this.__createBinding||(Object.create?function(t,e,n,r){void 0===r&&(r=n);var i=Object.getOwnPropertyDescriptor(e,n);i&&!("get"in i?!e.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,i)}:function(t,e,n,r){void 0===r&&(r=n),t[r]=e[n]}),o=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t["default"]=e}),s=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)"default"!==n&&Object.prototype.hasOwnProperty.call(t,n)&&i(e,t,n);return o(e,t),e},a=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0});const u=n(42856),c=s(n(45980)),l=n(76536);function f(t,e,n){return a(this,void 0,void 0,(function*(){t=c.parse(t);const i=n||[];i.push(t.href);try{const n=yield p(t,e);if(n.status>=400)throw(0,u.ono)({status:n.status},`HTTP ERROR ${n.status}`);if(n.status>=300){if(!Number.isNaN(e.redirects)&&i.length>e.redirects)throw new l.ResolverError((0,u.ono)({status:n.status},`Error downloading ${i[0]}. \nToo many redirects: \n ${i.join(" \n ")}`));if("location"in n.headers&&n.headers.location){const r=c.resolve(t,n.headers.location);return f(r,e,i)}throw(0,u.ono)({status:n.status},`HTTP ${n.status} redirect with no location header`)}if(n.body){const t=yield n.arrayBuffer();return r.from(t)}return r.alloc(0)}catch(o){throw new l.ResolverError((0,u.ono)(o,`Error downloading ${t.href}`),t.href)}}))}function p(t,e){return a(this,void 0,void 0,(function*(){let r,i;if(e.timeout&&(r=new AbortController,i=setTimeout((()=>r.abort()),e.timeout)),!n.g.fetch){const{default:t,Request:e,Headers:r}=yield Promise.resolve().then((()=>s(n(6e3))));n.g.fetch=t,n.g.Request=e,n.g.Headers=r}const o=yield fetch(t,{method:"GET",headers:e.headers||{},credentials:e.withCredentials?"include":"same-origin",signal:r?r.signal:null});return i&&clearTimeout(i),o}))}e["default"]={order:200,headers:null,timeout:5e3,redirects:5,withCredentials:!1,canRead(t){return c.isHttp(t.url)},read(t){const e=c.parse(t.url);return"undefined"===typeof window||e.protocol||(e.protocol=c.parse(location.href).protocol),f(e,this)}}},76536:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.normalizeError=e.isHandledError=e.InvalidPointerError=e.MissingPointerError=e.UnmatchedResolverError=e.ResolverError=e.UnmatchedParserError=e.ParserError=e.JSONParserErrorGroup=e.JSONParserError=void 0;const r=n(42856),i=n(45980);class o extends Error{constructor(t,e){super(),this.code="EUNKNOWN",this.name="JSONParserError",this.message=t,this.source=e,this.path=null,r.Ono.extend(this)}get footprint(){return`${this.path}+${this.source}+${this.code}+${this.message}`}}e.JSONParserError=o;class s extends Error{constructor(t){super(),this.files=t,this.name="JSONParserErrorGroup",this.message=`${this.errors.length} error${this.errors.length>1?"s":""} occurred while reading '${(0,i.toFileSystemPath)(t.$refs._root$Ref.path)}'`,r.Ono.extend(this)}static getParserErrors(t){const e=[];for(const n of Object.values(t.$refs._$refs))n.errors&&e.push(...n.errors);return e}get errors(){return s.getParserErrors(this.files)}}e.JSONParserErrorGroup=s;class a extends o{constructor(t,e){super(`Error parsing ${e}: ${t}`,e),this.code="EPARSER",this.name="ParserError"}}e.ParserError=a;class u extends o{constructor(t){super(`Could not find parser for "${t}"`,t),this.code="EUNMATCHEDPARSER",this.name="UnmatchedParserError"}}e.UnmatchedParserError=u;class c extends o{constructor(t,e){super(t.message||`Error reading file "${e}"`,e),this.code="ERESOLVER",this.name="ResolverError","code"in t&&(this.ioErrorCode=String(t.code))}}e.ResolverError=c;class l extends o{constructor(t){super(`Could not find resolver for "${t}"`,t),this.code="EUNMATCHEDRESOLVER",this.name="UnmatchedResolverError"}}e.UnmatchedResolverError=l;class f extends o{constructor(t,e){super(`Token "${t}" does not exist.`,(0,i.stripHash)(e)),this.code="EUNMATCHEDRESOLVER",this.name="MissingPointerError"}}e.MissingPointerError=f;class p extends o{constructor(t,e){super(`Invalid $ref pointer "${t}". Pointers must begin with "#/"`,(0,i.stripHash)(e)),this.code="EUNMATCHEDRESOLVER",this.name="InvalidPointerError"}}function h(t){return t instanceof o||t instanceof s}function d(t){return null===t.path&&(t.path=[]),t}e.InvalidPointerError=p,e.isHandledError=h,e.normalizeError=d},49968:function(t,e,n){"use strict";var r=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=r(n(46464));function o(t,e){return t?void e.then((function(e){(0,i.default)((function(){t(null,e)}))}),(function(e){(0,i.default)((function(){t(e)}))})):e}e["default"]=o},46464:function(t,e){"use strict";function n(){return"object"===typeof process&&"function"===typeof process.nextTick?process.nextTick:"function"===typeof setImmediate?setImmediate:function(t){setTimeout(t,0)}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n()},75016:function(t,e){"use strict";var n=this&&this.__awaiter||function(t,e,n,r){function i(t){return t instanceof n?t:new n((function(e){e(t)}))}return new(n||(n=Promise))((function(n,o){function s(t){try{u(r.next(t))}catch(e){o(e)}}function a(t){try{u(r["throw"](t))}catch(e){o(e)}}function u(t){t.done?n(t.value):i(t.value).then(s,a)}u((r=r.apply(t,e||[])).next())}))};function r(t){return Object.keys(t).filter((e=>"object"===typeof t[e])).map((e=>(t[e].name=e,t[e])))}function i(t,e,n){return t.filter((t=>!!a(t,e,n)))}function o(t){for(const e of t)e.order=e.order||Number.MAX_SAFE_INTEGER;return t.sort(((t,e)=>t.order-e.order))}function s(t,e,r,i){return n(this,void 0,void 0,(function*(){let n,o,s=0;return new Promise(((u,c)=>{function l(){if(n=t[s++],!n)return c(o);try{const o=a(n,e,r,f,i);if(o&&"function"===typeof o.then)o.then(p,h);else if(void 0!==o)p(o);else if(s===t.length)throw new Error("No promise has been returned or callback has been called.")}catch(u){h(u)}}function f(t,e){t?h(t):p(e)}function p(t){u({plugin:n,result:t})}function h(t){o={plugin:n,error:t},l()}l()}))}))}function a(t,e,n,r,i){const o=t[e];if("function"===typeof o)return o.apply(t,[n,r,i]);if(!r){if(o instanceof RegExp)return o.test(n.url);if("string"===typeof o)return o===n.extension;if(Array.isArray(o))return-1!==o.indexOf(n.extension)}return o}Object.defineProperty(e,"__esModule",{value:!0}),e.run=e.sort=e.filter=e.all=void 0,e.all=r,e.filter=i,e.sort=o,e.run=s},45980:function(t,e,n){"use strict";var r="/";Object.defineProperty(e,"__esModule",{value:!0}),e.safePointerToPath=e.toFileSystemPath=e.fromFileSystemPath=e.isFileSystemPath=e.isHttp=e.stripHash=e.getHash=e.stripQuery=e.getExtension=e.getProtocol=e.cwd=e.resolve=e.parse=void 0;const i=/^win/.test(globalThis.process?globalThis.process.platform:""),o=/\//g,s=/^(\w{2,}):\/\//i,a=/~1/g,u=/~0/g,c=n(71504),l=(0,c.join)(r,"..",".."),f=[/\?/g,"%3F",/#/g,"%23"],p=[/%23/g,"#",/%24/g,"$",/%26/g,"&",/%2C/g,",",/%40/g,"@"],h=t=>new URL(t);function d(t,e){const n=new URL(e,new URL(t,"resolve://"));if("resolve:"===n.protocol){const{pathname:t,search:e,hash:r}=n;return t+e+r}return n.toString()}function v(){if("undefined"!==typeof window)return location.href;const t=process.cwd(),e=t.slice(-1);return"/"===e||"\\"===e?t:t+"/"}function g(t){const e=s.exec(t);if(e)return e[1].toLowerCase()}function m(t){const e=t.lastIndexOf(".");return e>=0?b(t.substr(e).toLowerCase()):""}function b(t){const e=t.indexOf("?");return e>=0&&(t=t.substr(0,e)),t}function y(t){const e=t.indexOf("#");return e>=0?t.substr(e):"#"}function w(t){const e=t.indexOf("#");return e>=0&&(t=t.substr(0,e)),t}function x(t){const e=g(t);return"http"===e||"https"===e||void 0===e&&"undefined"!==typeof window}function _(t){if("undefined"!==typeof window||process.browser)return!1;const e=g(t);return void 0===e||"file"===e}function O(t){if(i){const e=t.toUpperCase().includes(l.replace(/\\/g,"\\").toUpperCase()),n=t.toUpperCase().includes(l.replace(/\\/g,"/").toUpperCase());t=e||n?t.replace(/\\/g,"/"):`${l}/${t}`.replace(/\\/g,"/")}t=encodeURI(t);for(let e=0;edecodeURIComponent(t).replace(a,"/").replace(u,"~")))}e.parse=h,e.resolve=d,e.cwd=v,e.getProtocol=g,e.getExtension=m,e.stripQuery=b,e.getHash=y,e.stripHash=w,e.isHttp=x,e.isFileSystemPath=_,e.fromFileSystemPath=O,e.toFileSystemPath=j,e.safePointerToPath=S},42856:function(t,e,n){"use strict";n.r(e),n.d(e,{Ono:function(){return j},default:function(){return C},ono:function(){return k}});const r=!1,i=!1,o=/\r?\n/,s=/\bono[ @]/;function a(t){return Boolean(t&&t.configurable&&"function"===typeof t.get)}function u(t){return Boolean(!t||t.writable||"function"===typeof t.set)}function c(t,e){let n=f(t.stack),r=e?e.stack:void 0;return n&&r?n+"\n\n"+r:n||r}function l(t,e,n){n?Object.defineProperty(e,"stack",{get:()=>{let r=t.get.apply(e);return c({stack:r},n)},enumerable:!1,configurable:!0}):p(e,t)}function f(t){if(t){let e,n=t.split(o);for(let t=0;t0)return n.join("\n")}return t}function p(t,e){Object.defineProperty(t,"stack",{get:()=>f(e.get.apply(t)),enumerable:!1,configurable:!0})}const h=["function","symbol","undefined"],d=["constructor","prototype","__proto__"],v=Object.getPrototypeOf({});function g(){let t={},e=this;for(let n of m(e))if("string"===typeof n){let r=e[n],i=typeof r;h.includes(i)||(t[n]=r)}return t}function m(t,e=[]){let n=[];while(t&&t!==v)n=n.concat(Object.getOwnPropertyNames(t),Object.getOwnPropertySymbols(t)),t=Object.getPrototypeOf(t);let r=new Set(n);for(let i of e.concat(d))r.delete(i);return r}const b=["name","message","stack"];function y(t,e,n){let r=t;return w(r,e),e&&"object"===typeof e&&x(r,e),r.toJSON=g,i&&i(r),n&&"object"===typeof n&&Object.assign(r,n),r}function w(t,e){let n=Object.getOwnPropertyDescriptor(t,"stack");a(n)?l(n,t,e):u(n)&&(t.stack=c(t,e))}function x(t,e){let n=m(e,b),r=t,i=e;for(let s of n)if(void 0===r[s])try{r[s]=i[s]}catch(o){}}function _(t){return t=t||{},{concatMessages:void 0===t.concatMessages||Boolean(t.concatMessages),format:void 0===t.format?r:"function"===typeof t.format&&t.format}}function O(t,e){let n,r,i,o="";return"string"===typeof t[0]?i=t:"string"===typeof t[1]?(t[0]instanceof Error?n=t[0]:r=t[0],i=t.slice(1)):(n=t[0],r=t[1],i=t.slice(2)),i.length>0&&(o=e.format?e.format.apply(void 0,i):i.join(" ")),e.concatMessages&&n&&n.message&&(o+=(o?" \n":"")+n.message),{originalError:n,props:r,message:o}}const j=S;function S(t,e){function n(...n){let{originalError:r,props:i,message:o}=O(n,e),s=new t(o);return y(s,r,i)}return e=_(e),n[Symbol.species]=t,n}S.toJSON=function(t){return g.call(t)},S.extend=function(t,e,n){return n||e instanceof Error?y(t,e,n):e?y(t,void 0,e):y(t)};const k=A;A.error=new j(Error),A.eval=new j(EvalError),A.range=new j(RangeError),A.reference=new j(ReferenceError),A.syntax=new j(SyntaxError),A.type=new j(TypeError),A.uri=new j(URIError);const E=A;function A(...t){let e=t[0];if("object"===typeof e&&"string"===typeof e.name)for(let n of Object.values(E))if("function"===typeof n&&"ono"===n.name){let r=n[Symbol.species];if(r&&r!==Error&&(e instanceof r||e.name===r.name))return n.apply(void 0,t)}return A.error.apply(void 0,t)}n(93912);t=n.hmd(t);var C=k;"object"===typeof t.exports&&(t.exports=Object.assign(t.exports.default,t.exports))},31824:function(t,e,n){"use strict";n.r(e),n.d(e,{default:function(){return mt}});var r,i=function(){var t=this,e=t._self._c;return e("b-form",{staticClass:"filter mb-4",on:{submit:function(e){return e.stopPropagation(),e.preventDefault(),t.onSubmit.apply(null,arguments)},reset:t.onReset}},[e("b-card",{attrs:{"no-body":"",title:t.title}},[e("b-card-body",[t.loaded?t._e():e("Loading",{attrs:{fill:""}}),t.title?e("b-card-title",{attrs:{title:t.title}}):t._e(),t.canFilterExtents?e("b-form-group",{attrs:{label:t.$t("search.temporalExtent"),"label-for":"datetime",description:t.$t("search.dateDescription")}},[e("date-picker",{attrs:{range:"",id:"datetime",lang:t.datepickerLang,format:t.datepickerFormat,value:t.query.datetime,"input-class":"form-control mx-input"},on:{input:t.setDateTime}})],1):t._e(),t.canFilterExtents?e("b-form-group",{attrs:{label:t.$t("search.spatialExtent"),"label-for":"provideBBox"}},[e("b-form-checkbox",{attrs:{id:"provideBBox",value:"1"},on:{change:function(e){return t.setBBox()}},model:{value:t.provideBBox,callback:function(e){t.provideBBox=e},expression:"provideBBox"}},[t._v(t._s(t.$t("search.filterBySpatialExtent")))]),t.provideBBox?e("Map",{staticClass:"mb-4",attrs:{stac:t.stac,selectBounds:"",scrollWheelZoom:""},on:{bounds:t.setBBox}}):t._e()],1):t._e(),t.conformances.Collections?e("b-form-group",{attrs:{label:t.$tc("stacCollection",t.collections.length),"label-for":"collections"}},[t.collections.length>0?e("multiselect",{attrs:{id:"collections",value:t.selectedCollections,placeholder:t.$t("search.selectCollections"),tagPlaceholder:t.$t("search.addCollections"),selectLabel:t.$t("multiselect.selectLabel"),selectedLabel:t.$t("multiselect.selectedLabel"),deselectLabel:t.$t("multiselect.deselectLabel"),limitText:t.limitText,options:t.collections,multiple:"","track-by":"value",label:"text"},on:{input:t.setCollections}}):e("multiselect",{attrs:{id:"collections",value:t.selectedCollections,multiple:"",taggable:"",options:t.query.collections,placeholder:t.$t("search.enterCollections"),tagPlaceholder:t.$t("search.addCollections"),selectLabel:t.$t("multiselect.selectLabel"),selectedLabel:t.$t("multiselect.selectedLabel"),deselectLabel:t.$t("multiselect.deselectLabel"),limitText:t.limitText},on:{input:t.setCollections,tag:t.addCollection},scopedSlots:t._u([{key:"noOptions",fn:function(){return[t._v(t._s(t.$t("search.noOptions")))]},proxy:!0}],null,!1,1486998433)})],1):t._e(),t.conformances.Items?e("b-form-group",{attrs:{label:t.$t("search.itemIds"),"label-for":"ids"}},[e("multiselect",{attrs:{id:"ids",value:t.query.ids,multiple:"",taggable:"",options:t.query.ids,placeholder:t.$t("search.enterItemIds"),tagPlaceholder:t.$t("search.addItemIds"),noOptions:t.$t("search.addItemIds")},on:{input:t.setIds,tag:t.addId},scopedSlots:t._u([{key:"noOptions",fn:function(){return[t._v(t._s(t.$t("search.noOptions")))]},proxy:!0}],null,!1,1486998433)})],1):t._e(),t.cql&&Array.isArray(t.queryables)&&t.queryables.length>0?e("div",{staticClass:"additional-filters"},[e("b-form-group",{attrs:{label:t.$t("search.additionalFilters"),"label-for":"availableFields"}},[e("b-form-radio-group",{attrs:{id:"logical",options:t.andOrOptions,name:"logical",size:"sm"},model:{value:t.filtersAndOr,callback:function(e){t.filtersAndOr=e},expression:"filtersAndOr"}}),e("b-dropdown",{staticClass:"queryables mt-2 mb-3",attrs:{size:"sm",text:t.$t("search.addFilter"),block:"",variant:"primary","menu-class":"w-100"}},[t._l(t.sortedQueryables,(function(n){return[n.supported?e("b-dropdown-item",{key:n.id,on:{click:function(e){return t.additionalFieldSelected(n)}}},[t._v(" "+t._s(n.title)+" "),e("b-badge",{staticClass:"ml-2",attrs:{variant:"dark"}},[t._v(t._s(n.id))])],1):t._e()]}))],2),t._l(t.filters,(function(n,r){return e("QueryableInput",{key:n.id,attrs:{value:n.value,operator:n.operator,queryable:n.queryable,index:r,cql:t.cql},on:{"update:value":function(e){return t.$set(n,"value",e)},"update:operator":function(e){return t.$set(n,"operator",e)},"remove-queryable":function(e){return t.removeQueryable(r)}}})}))],2)],1):t._e(),e("hr"),t.canSort?e("b-form-group",{attrs:{label:t.$t("sort.title"),"label-for":"sort",description:t.$t("search.notFullySupported")}},[e("multiselect",{attrs:{id:"sort",value:t.sortTerm,options:t.sortOptions,"track-by":"value",label:"text",placeholder:t.$t("default"),selectLabel:t.$t("multiselect.selectLabel"),selectedLabel:t.$t("multiselect.selectedLabel"),deselectLabel:t.$t("multiselect.deselectLabel")},on:{input:t.sortFieldSet}}),t.sortTerm&&t.sortTerm.value?e("SortButtons",{staticClass:"mt-1",attrs:{value:t.sortOrder,enforce:""},on:{input:t.sortDirectionSet}}):t._e()],1):t._e(),e("b-form-group",{attrs:{label:t.$t("search.itemsPerPage"),"label-for":"limit",description:t.$t("search.itemsPerPageDescription",{maxItems:t.maxItems})}},[e("b-form-input",{attrs:{id:"limit",value:t.query.limit,min:"1",max:t.maxItems,type:"number",placeholder:t.$t("defaultWithValue",{value:t.itemsPerPage})},on:{change:t.setLimit}})],1)],1),e("b-card-footer",[e("b-button",{attrs:{type:"submit",variant:"primary"}},[t._v(t._s(t.$t("search.buttons.filter")))]),e("b-button",{staticClass:"ml-3",attrs:{type:"reset",variant:"danger"}},[t._v(t._s(t.$t("search.buttons.reset")))])],1)],1)],1)},o=[],s=(n(83248),n(15928)),a=n(79808),u=n(24692),c=n(96296),l=n(60592),f=n(46584),p=n(16300),h=n(77548),d=n(99628),v=n(97637),g=n(95756),m=n(18952),b=n(33284),y=n(94768),w=n(26596),x=n(25800),_=n(78248),O=(0,v.a8)(_.ov,d.sT),j=(0,h.SU)({name:d.sT,mixins:[_.qq],inject:{getBvGroup:{from:"getBvRadioGroup",default:function(){return function(){return null}}}},props:O,computed:{bvGroup:function(){return this.getBvGroup()}}}),S=n(37536),k=n(10208),E=n(61272),A=n(54724),C=n(29596),P=n(35136),$=n(46128);function I(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function T(t){for(var e=1;en.e(2760).then(n.bind(n,82760)),Loading:J.c,Map:()=>n.e(572).then(n.bind(n,572)),SortButtons:()=>n.e(996).then(n.bind(n,80996)),Multiselect:K()},mixins:[lt,Y.c],props:{stac:{type:Object,required:!0},title:{type:String,required:!0},value:{type:Object,default:()=>({})}},data(){return Object.assign({maxItems:1e4,loaded:!1},pt())},computed:{...(0,z.ys)(["itemsPerPage","uiLanguage","queryables","apiCollections"]),...(0,z.gV)(["hasMoreCollections","root"]),andOrOptions(){return[{value:"and",text:this.$i18n.t("search.logical.and")},{value:"or",text:this.$i18n.t("search.logical.or")}]},sortOptions(){return[{value:null,text:this.$t("default")},{value:"properties.datetime",text:this.$t("search.sortOptions.datetime")},{value:"id",text:this.$t("search.sortOptions.id")},{value:"properties.title",text:this.$t("search.sortOptions.title")}]},collections(){return this.hasMoreCollections||!this.conformances.Collections?[]:this.apiCollections.map((t=>({value:t.id,text:t.title||t.id}))).sort(((t,e)=>t.text.localeCompare(e.text,this.uiLanguage)))},sortedQueryables(){return this.queryables.slice(0).sort(((t,e)=>t.title.localeCompare(e.title)))}},watch:{value:{immediate:!0,handler(t){let e=Object.assign(ft(),t);Array.isArray(e.datetime)&&(e.datetime=e.datetime.map(W.cp.dateFromUTC)),this.query=e}}},created(){let t=[];this.cql&&t.push(this.$store.dispatch("loadQueryables",{stac:this.stac,refParser:n(18356)}).catch((t=>console.error(t)))),this.conformances.Collections&&0===this.apiCollections.length&&t.push(this.$store.dispatch("loadNextApiCollections",{stac:this.root,show:!0}).catch((t=>console.error(t)))),Promise.all(t).finally((()=>this.loaded=!0))},methods:{limitText(t){return this.$t("multiselect.andMore",{count:t})},sortFieldSet(t){this.sortTerm=t},sortDirectionSet(t){this.sortOrder=t},buildFilter(){const t=this.filters.map((t=>new t.operator(t.queryable,t.value))),e=Z.create(this.filtersAndOr,t);return new rt(e)},removeQueryable(t){this.filters.splice(t,1)},additionalFieldSelected(t){this.filters.push({value:ot.cp.create(t.defaultValue),operator:it.gv,queryable:t})},onSubmit(){this.canSort&&this.sortTerm&&this.sortOrder&&this.$set(this.query,"sortby",this.formatSort()),this.filters.length>0&&this.$set(this.query,"filters",this.buildFilter()),this.$emit("input",this.query,!1)},async onReset(){Object.assign(this,pt()),this.$emit("input",{},!0)},setLimit(t){t=Number.parseInt(t,10),t>this.maxItems?t=this.maxItems:t<0&&(t=null),this.$set(this.query,"limit",t)},setBBox(t){let e=null;if(this.provideBBox)if(W.cp.isObject(t)&&"function"===typeof t.toBBoxString){const n=85.06,r=180;e=[Math.max(t.getWest(),-r),Math.max(t.getSouth(),-n),Math.min(t.getEast(),r),Math.min(t.getNorth(),n)]}else Array.isArray(t)&&4===t.length&&(e=t);this.$set(this.query,"bbox",e)},setDateTime(t){t=t.find((t=>t instanceof Date))?t.map(W.cp.dateToUTC):null,this.$set(this.query,"datetime",t)},addCollection(t){this.selectedCollections.push(t),this.query.collections.push(t)},setCollections(t){this.selectedCollections=t,this.$set(this.query,"collections",t.map((t=>t.value)))},addId(t){this.query.ids.push(t)},setIds(t){this.$set(this.query,"ids",t)},formatSort(){if(this.sortTerm&&this.sortTerm.value&&this.sortOrder){let t=this.sortOrder<0?"-":"";return`${t}${this.sortTerm.value}`}return null}}},dt=ht,vt=n(82528),gt=(0,vt.c)(dt,i,o,!1,null,null,null),mt=gt.exports},36801:function(t,e,n){"use strict";var r=n(48416);e.c={components:{DatePicker:()=>n.e(2160).then(n.bind(n,72160))},data(){return{datepickerLang:null,datepickerFormat:"YYYY-MM-DD"}},computed:{...(0,r.ys)(["uiLanguage"])},watch:{uiLanguage:{immediate:!0,async handler(t){if(!t)return;const e=(await n(85388)(`./${t}/datepicker.js`)).default;e.locale instanceof Promise?this.datepickerLang=(await e.locale).default:this.datepickerLang=e.locale,this.datepickerFormat=e.format}}}}},91880:function(t,e,n){"use strict";n.d(e,{cp:function(){return i}});var r=n(40848);class i{constructor(t){this.value=t}static create(t){return t instanceof Date?new o(t):"string"===typeof t?new s(t):new i(t)}toJSON(){return this.value}toText(){return this.value}}class o extends i{constructor(t){super(t)}toJSON(){return{timestamp:this.toTimestamp()}}toText(){return`TIMESTAMP('${this.toTimestamp()}')`}toTimestamp(){return r.cp.dateToUTC(this.value).toISOString()}}class s extends i{constructor(t){super(t)}toJSON(){return this.value}toText(){return`'${this.value.replace("'","''")}'`}}},16300:function(t,e,n){"use strict";n.d(e,{U:function(){return w}});var r,i=n(77548),o=n(99628),s=n(98092),a=n(95756),u=n(77928),c=n(94768),l=function(t,e){for(var n=0;n-1:(0,c.i)(e,t)},isRadio:function(){return!1}},watch:g({},m,(function(t,e){(0,c.i)(t,e)||this.setIndeterminate(t)})),mounted:function(){this.setIndeterminate(this[m])},methods:{computedLocalCheckedWatcher:function(t,e){if(!(0,c.i)(t,e)){this.$emit(h.Y5,t);var n=this.$refs.input;n&&this.$emit(b,n.indeterminate)}},handleChange:function(t){var e=this,n=t.target,r=n.checked,i=n.indeterminate,o=this.value,a=this.uncheckedValue,c=this.computedLocalChecked;if((0,u.c7)(c)){var f=l(c,o);r&&f<0?c=c.concat(o):!r&&f>-1&&(c=c.slice(0,f).concat(c.slice(f+1)))}else c=r?o:a;this.computedLocalChecked=c,this.$nextTick((function(){e.$emit(s.Cu,c),e.isGroup&&e.bvGroup.$emit(s.Cu,c),e.$emit(b,i)}))},setIndeterminate:function(t){(0,u.c7)(this.computedLocalChecked)&&(t=!1);var e=this.$refs.input;e&&(e.indeterminate=t,this.$emit(b,t))}}})},96296:function(t,e,n){"use strict";n.d(e,{E:function(){return c}});var r=n(77548),i=n(76516),o=n(99628),s=n(95756),a=n(97637),u=(0,a.a8)({id:(0,a.K2)(s.nV),inline:(0,a.K2)(s.aM,!1),novalidate:(0,a.K2)(s.aM,!1),validated:(0,a.K2)(s.aM,!1)},o.U$),c=(0,r.SU)({name:o.U$,functional:!0,props:u,render:function(t,e){var n=e.props,r=e.data,o=e.children;return t("form",(0,i.k)(r,{class:{"form-inline":n.inline,"was-validated":n.validated},attrs:{id:n.id,novalidate:n.novalidate}}),o)}})},10208:function(t,e,n){"use strict";n.d(e,{o:function(){return s},u:function(){return a}});var r=n(77548),i=n(95756),o=n(97637),s=(0,o.a8)({plain:(0,o.K2)(i.aM,!1)},"formControls"),a=(0,r.SU)({props:s,computed:{custom:function(){return!this.plain}}})},61272:function(t,e,n){"use strict";n.d(e,{g:function(){return h},o:function(){return p}});var r=n(77548),i=n(95756),o=n(39580),s=n(33284),a=n(77928),u=n(25800),c=n(97637),l=n(83478),f='Setting prop "options" to an object is deprecated. Use the array format instead.',p=(0,c.a8)({disabledField:(0,c.K2)(i.nV,"disabled"),htmlField:(0,c.K2)(i.nV,"html"),options:(0,c.K2)(i.mO,[]),textField:(0,c.K2)(i.nV,"text"),valueField:(0,c.K2)(i.nV,"value")},"formOptionControls"),h=(0,r.SU)({props:p,computed:{formOptions:function(){return this.normalizeOptions(this.options)}},methods:{normalizeOption:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if((0,a.oF)(t)){var n=(0,o._)(t,this.valueField),r=(0,o._)(t,this.textField);return{value:(0,a.Y3)(n)?e||r:n,text:(0,s.o)(String((0,a.Y3)(r)?e:r)),html:(0,o._)(t,this.htmlField),disabled:Boolean((0,o._)(t,this.disabledField))}}return{value:e||t,text:(0,s.o)(String(t)),disabled:!1}},normalizeOptions:function(t){var e=this;return(0,a.c7)(t)?t.map((function(t){return e.normalizeOption(t)})):(0,a.oF)(t)?((0,l.mo)(f,this.$options.name),(0,u.C_)(t).map((function(n){return e.normalizeOption(t[n]||{},n)}))):[]}}})},78248:function(t,e,n){"use strict";n.d(e,{Y5:function(){return A},ov:function(){return C},qq:function(){return P}});var r,i,o=n(77548),s=n(95756),a=n(98092),u=n(53377),c=n(77928),l=n(94768),f=n(26596),p=n(25800),h=n(97637),d=n(48256),v=n(37536),g=n(10208),m=n(54724),b=n(29596),y=n(35136),w=n(46128);function x(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function _(t){for(var e=1;e=55296&&r<=56319&&e+1=56320&&n<=57343)?1024*(r-55296)+n-56320+65536:r}function Q(t){var e=/^\n* /;return e.test(t)}var X=1,Z=2,tt=3,et=4,nt=5;function rt(t,e,n,r,i,o,s,a){var u,c=0,f=null,p=!1,h=!1,d=-1!==r,v=-1,g=Y(W(t,0))&&J(W(t,t.length-1));if(e||s)for(u=0;u=65536?u+=2:u++){if(c=W(t,u),!H(c))return nt;g=g&&z(c,f,a),f=c}else{for(u=0;u=65536?u+=2:u++){if(c=W(t,u),c===l)p=!0,d&&(h=h||u-v-1>r&&" "!==t[v+1],v=u);else if(!H(c))return nt;g=g&&z(c,f,a),f=c}h=h||d&&u-v-1>r&&" "!==t[v+1]}return p||h?n>9&&Q(t)?nt:s?o===N?nt:Z:h?et:tt:!g||s||i(t)?o===N?nt:Z:X}function it(t,e,n,r,o){t.dump=function(){if(0===e.length)return t.quotingType===N?'""':"''";if(!t.noCompatMode&&(-1!==L.indexOf(e)||M.test(e)))return t.quotingType===N?'"'+e+'"':"'"+e+"'";var s=t.indent*Math.max(1,n),a=-1===t.lineWidth?-1:Math.max(Math.min(t.lineWidth,40),t.lineWidth-s),u=r||t.flowLevel>-1&&n>=t.flowLevel;function c(e){return G(t,e)}switch(rt(e,u,t.indent,a,c,t.quotingType,t.forceQuotes&&!r,o)){case X:return e;case Z:return"'"+e.replace(/'/g,"''")+"'";case tt:return"|"+ot(e,t.indent)+st(V(e,s));case et:return">"+ot(e,t.indent)+st(V(at(e,a),s));case nt:return'"'+ct(e,a)+'"';default:throw new i("impossible error: invalid scalar style")}}()}function ot(t,e){var n=Q(t)?String(e):"",r="\n"===t[t.length-1],i=r&&("\n"===t[t.length-2]||"\n"===t),o=i?"+":r?"":"-";return n+o+"\n"}function st(t){return"\n"===t[t.length-1]?t.slice(0,-1):t}function at(t,e){var n,r,i=/(\n+)([^\n]*)/g,o=function(){var n=t.indexOf("\n");return n=-1!==n?n:t.length,i.lastIndex=n,ut(t.slice(0,n),e)}(),s="\n"===t[0]||" "===t[0];while(r=i.exec(t)){var a=r[1],u=r[2];n=" "===u[0],o+=a+(s||n||""===u?"":"\n")+ut(u,e),s=n}return o}function ut(t,e){if(""===t||" "===t[0])return t;var n,r,i=/ [^ ]/g,o=0,s=0,a=0,u="";while(n=i.exec(t))a=n.index,a-o>e&&(r=s>o?s:a,u+="\n"+t.slice(o,r),o=r+1),s=a;return u+="\n",t.length-o>e&&s>o?u+=t.slice(o,s)+"\n"+t.slice(s+1):u+=t.slice(o),u.slice(1)}function ct(t){for(var e,n="",r=0,i=0;i=65536?i+=2:i++)r=W(t,i),e=T[r],!e&&H(r)?(n+=t[i],r>=65536&&(n+=t[i+1])):n+=e||F(r);return n}function lt(t,e,n){var r,i,o,s="",a=t.tag;for(r=0,i=n.length;r1024&&(a+="? "),a+=t.dump+(t.condenseFlow?'"':"")+":"+(t.condenseFlow?"":" "),vt(t,e,s,!1,!1)&&(a+=t.dump,u+=a));t.tag=c,t.dump="{"+u+"}"}function ht(t,e,n,r){var o,s,a,u,c,f,p="",h=t.tag,d=Object.keys(n);if(!0===t.sortKeys)d.sort();else if("function"===typeof t.sortKeys)d.sort(t.sortKeys);else if(t.sortKeys)throw new i("sortKeys must be a boolean or a function");for(o=0,s=d.length;o1024,c&&(t.dump&&l===t.dump.charCodeAt(0)?f+="?":f+="? "),f+=t.dump,c&&(f+=U(t,e)),vt(t,e+1,u,!0,c)&&(t.dump&&l===t.dump.charCodeAt(0)?f+=":":f+=": ",f+=t.dump,p+=f));t.tag=h,t.dump=p||"{}"}function dt(t,e,n){var r,o,u,c,l,f;for(o=n?t.explicitTypes:t.implicitTypes,u=0,c=o.length;u tag resolver accepts not "'+f+'" style');r=l.represent[f](e,f)}t.dump=r}return!0}return!1}function vt(t,e,n,r,o,a,u){t.tag=null,t.dump=n,dt(t,n,!1)||dt(t,n,!0);var c,l=s.call(t.dump),f=r;r&&(r=t.flowLevel<0||t.flowLevel>e);var p,h,d="[object Object]"===l||"[object Array]"===l;if(d&&(p=t.duplicates.indexOf(n),h=-1!==p),(null!==t.tag&&"?"!==t.tag||h||2!==t.indent&&e>0)&&(o=!1),h&&t.usedDuplicates[p])t.dump="*ref_"+p;else{if(d&&h&&!t.usedDuplicates[p]&&(t.usedDuplicates[p]=!0),"[object Object]"===l)r&&0!==Object.keys(t.dump).length?(ht(t,e,t.dump,o),h&&(t.dump="&ref_"+p+t.dump)):(pt(t,e,t.dump),h&&(t.dump="&ref_"+p+" "+t.dump));else if("[object Array]"===l)r&&0!==t.dump.length?(t.noArrayIndent&&!u&&e>0?ft(t,e-1,t.dump,o):ft(t,e,t.dump,o),h&&(t.dump="&ref_"+p+t.dump)):(lt(t,e,t.dump),h&&(t.dump="&ref_"+p+" "+t.dump));else{if("[object String]"!==l){if("[object Undefined]"===l)return!1;if(t.skipInvalid)return!1;throw new i("unacceptable kind of an object to dump "+l)}"?"!==t.tag&&it(t,t.dump,e,a,f)}null!==t.tag&&"?"!==t.tag&&(c=encodeURI("!"===t.tag[0]?t.tag.slice(1):t.tag).replace(/!/g,"%21"),c="!"===t.tag[0]?"!"+c:"tag:yaml.org,2002:"===c.slice(0,18)?"!!"+c.slice(18):"!<"+c+">",t.dump=c+" "+t.dump)}return!0}function gt(t,e){var n,r,i=[],o=[];for(mt(t,i,o),n=0,r=o.length;n>10),56320+(t-65536&1023))}for(var P=new Array(256),$=new Array(256),I=0;I<256;I++)P[I]=A(I)?1:0,$[I]=A(I);function T(t,e){this.input=t,this.filename=e["filename"]||null,this.schema=e["schema"]||s,this.onWarning=e["onWarning"]||null,this.legacy=e["legacy"]||!1,this.json=e["json"]||!1,this.listener=e["listener"]||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function L(t,e){var n={name:t.filename,buffer:t.input.slice(0,-1),position:t.position,line:t.line,column:t.position-t.lineStart};return n.snippet=o(n),new i(e,n)}function M(t,e){throw L(t,e)}function R(t,e){t.onWarning&&t.onWarning.call(null,L(t,e))}var F={YAML:function(t,e,n){var r,i,o;null!==t.version&&M(t,"duplication of %YAML directive"),1!==n.length&&M(t,"YAML directive accepts exactly one argument"),r=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===r&&M(t,"ill-formed argument of the YAML directive"),i=parseInt(r[1],10),o=parseInt(r[2],10),1!==i&&M(t,"unacceptable YAML version of the document"),t.version=n[0],t.checkLineBreaks=o<2,1!==o&&2!==o&&R(t,"unsupported YAML version of the document")},TAG:function(t,e,n){var r,i;2!==n.length&&M(t,"TAG directive accepts exactly two arguments"),r=n[0],i=n[1],b.test(r)||M(t,"ill-formed tag handle (first argument) of the TAG directive"),a.call(t.tagMap,r)&&M(t,'there is a previously declared suffix for "'+r+'" tag handle'),y.test(i)||M(t,"ill-formed tag prefix (second argument) of the TAG directive");try{i=decodeURIComponent(i)}catch(o){M(t,"tag prefix is malformed: "+i)}t.tagMap[r]=i}};function D(t,e,n,r){var i,o,s,a;if(e1&&(t.result+=r.repeat("\n",e-1))}function H(t,e,n){var r,i,o,s,a,u,c,l,f,p=t.kind,h=t.result;if(f=t.input.charCodeAt(t.position),O(f)||j(f)||35===f||38===f||42===f||33===f||124===f||62===f||39===f||34===f||37===f||64===f||96===f)return!1;if((63===f||45===f)&&(i=t.input.charCodeAt(t.position+1),O(i)||n&&j(i)))return!1;t.kind="scalar",t.result="",o=s=t.position,a=!1;while(0!==f){if(58===f){if(i=t.input.charCodeAt(t.position+1),O(i)||n&&j(i))break}else if(35===f){if(r=t.input.charCodeAt(t.position-1),O(r))break}else{if(t.position===t.lineStart&&G(t)||n&&j(f))break;if(x(f)){if(u=t.line,c=t.lineStart,l=t.lineIndent,U(t,!1,-1),t.lineIndent>=e){a=!0,f=t.input.charCodeAt(t.position);continue}t.position=s,t.line=u,t.lineStart=c,t.lineIndent=l;break}}a&&(D(t,o,s,!1),q(t,t.line-u),o=s=t.position,a=!1),_(f)||(s=t.position+1),f=t.input.charCodeAt(++t.position)}return D(t,o,s,!1),!!t.result||(t.kind=p,t.result=h,!1)}function K(t,e){var n,r,i;if(n=t.input.charCodeAt(t.position),39!==n)return!1;t.kind="scalar",t.result="",t.position++,r=i=t.position;while(0!==(n=t.input.charCodeAt(t.position)))if(39===n){if(D(t,r,t.position,!0),n=t.input.charCodeAt(++t.position),39!==n)return!0;r=t.position,t.position++,i=t.position}else x(n)?(D(t,r,i,!0),q(t,U(t,!1,e)),r=i=t.position):t.position===t.lineStart&&G(t)?M(t,"unexpected end of the document within a single quoted scalar"):(t.position++,i=t.position);M(t,"unexpected end of the stream within a single quoted scalar")}function z(t,e){var n,r,i,o,s,a;if(a=t.input.charCodeAt(t.position),34!==a)return!1;t.kind="scalar",t.result="",t.position++,n=r=t.position;while(0!==(a=t.input.charCodeAt(t.position))){if(34===a)return D(t,n,t.position,!0),t.position++,!0;if(92===a){if(D(t,n,t.position,!0),a=t.input.charCodeAt(++t.position),x(a))U(t,!1,e);else if(a<256&&P[a])t.result+=$[a],t.position++;else if((s=k(a))>0){for(i=s,o=0;i>0;i--)a=t.input.charCodeAt(++t.position),(s=S(a))>=0?o=(o<<4)+s:M(t,"expected hexadecimal character");t.result+=C(o),t.position++}else M(t,"unknown escape sequence");n=r=t.position}else x(a)?(D(t,n,r,!0),q(t,U(t,!1,e)),n=r=t.position):t.position===t.lineStart&&G(t)?M(t,"unexpected end of the document within a double quoted scalar"):(t.position++,r=t.position)}M(t,"unexpected end of the stream within a double quoted scalar")}function Y(t,e){var n,r,i,o,s,a,c,l,f,p,h,d,v,g=!0,m=t.tag,b=t.anchor,y=Object.create(null);if(v=t.input.charCodeAt(t.position),91===v)a=93,f=!1,o=[];else{if(123!==v)return!1;a=125,f=!0,o={}}null!==t.anchor&&(t.anchorMap[t.anchor]=o),v=t.input.charCodeAt(++t.position);while(0!==v){if(U(t,!0,e),v=t.input.charCodeAt(t.position),v===a)return t.position++,t.tag=m,t.anchor=b,t.kind=f?"mapping":"sequence",t.result=o,!0;g?44===v&&M(t,"expected the node content, but found ','"):M(t,"missed comma between flow collection entries"),h=p=d=null,c=l=!1,63===v&&(s=t.input.charCodeAt(t.position+1),O(s)&&(c=l=!0,t.position++,U(t,!0,e))),n=t.line,r=t.lineStart,i=t.position,et(t,e,u,!1,!0),h=t.tag,p=t.result,U(t,!0,e),v=t.input.charCodeAt(t.position),!l&&t.line!==n||58!==v||(c=!0,v=t.input.charCodeAt(++t.position),U(t,!0,e),et(t,e,u,!1,!0),d=t.result),f?B(t,o,y,h,p,d,n,r,i):c?o.push(B(t,null,y,h,p,d,n,r,i)):o.push(p),U(t,!0,e),v=t.input.charCodeAt(t.position),44===v?(g=!0,v=t.input.charCodeAt(++t.position)):g=!1}M(t,"unexpected end of the stream within a flow collection")}function J(t,e){var n,i,o,s,a=p,u=!1,c=!1,l=e,f=0,v=!1;if(s=t.input.charCodeAt(t.position),124===s)i=!1;else{if(62!==s)return!1;i=!0}t.kind="scalar",t.result="";while(0!==s)if(s=t.input.charCodeAt(++t.position),43===s||45===s)p===a?a=43===s?d:h:M(t,"repeat of a chomping mode identifier");else{if(!((o=E(s))>=0))break;0===o?M(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):c?M(t,"repeat of an indentation width identifier"):(l=e+o-1,c=!0)}if(_(s)){do{s=t.input.charCodeAt(++t.position)}while(_(s));if(35===s)do{s=t.input.charCodeAt(++t.position)}while(!x(s)&&0!==s)}while(0!==s){V(t),t.lineIndent=0,s=t.input.charCodeAt(t.position);while((!c||t.lineIndentl&&(l=t.lineIndent),x(s))f++;else{if(t.lineIndente)&&0!==i)M(t,"bad indentation of a sequence entry");else if(t.lineIndente)&&(y&&(s=t.line,a=t.lineStart,u=t.position),et(t,e,f,!0,i)&&(y?m=t.result:b=t.result),y||(B(t,d,v,g,m,b,s,a,u),g=m=b=null),U(t,!0,-1),l=t.input.charCodeAt(t.position)),(t.line===o||t.lineIndent>e)&&0!==l)M(t,"bad indentation of a mapping entry");else if(t.lineIndente?y=1:t.lineIndent===e?y=0:t.lineIndente?y=1:t.lineIndent===e?y=0:t.lineIndent tag; it should be "scalar", not "'+t.kind+'"'),h=0,d=t.implicitTypes.length;h"),null!==t.result&&g.kind!==t.kind&&M(t,"unacceptable node kind for !<"+t.tag+'> tag; it should be "'+g.kind+'", not "'+t.kind+'"'),g.resolve(t.result,t.tag)?(t.result=g.construct(t.result,t.tag),null!==t.anchor&&(t.anchorMap[t.anchor]=t.result)):M(t,"cannot resolve a node with !<"+t.tag+"> explicit tag")}return null!==t.listener&&t.listener("close",t),null!==t.tag||null!==t.anchor||x}function nt(t){var e,n,r,i,o=t.position,s=!1;t.version=null,t.checkLineBreaks=t.legacy,t.tagMap=Object.create(null),t.anchorMap=Object.create(null);while(0!==(i=t.input.charCodeAt(t.position))){if(U(t,!0,-1),i=t.input.charCodeAt(t.position),t.lineIndent>0||37!==i)break;s=!0,i=t.input.charCodeAt(++t.position),e=t.position;while(0!==i&&!O(i))i=t.input.charCodeAt(++t.position);n=t.input.slice(e,t.position),r=[],n.length<1&&M(t,"directive name must not be less than one character in length");while(0!==i){while(_(i))i=t.input.charCodeAt(++t.position);if(35===i){do{i=t.input.charCodeAt(++t.position)}while(0!==i&&!x(i));break}if(x(i))break;e=t.position;while(0!==i&&!O(i))i=t.input.charCodeAt(++t.position);r.push(t.input.slice(e,t.position))}0!==i&&V(t),a.call(F,n)?F[n](t,n,r):R(t,'unknown document directive "'+n+'"')}U(t,!0,-1),0===t.lineIndent&&45===t.input.charCodeAt(t.position)&&45===t.input.charCodeAt(t.position+1)&&45===t.input.charCodeAt(t.position+2)?(t.position+=3,U(t,!0,-1)):s&&M(t,"directives end mark is expected"),et(t,t.lineIndent-1,f,!1,!0),U(t,!0,-1),t.checkLineBreaks&&g.test(t.input.slice(o,t.position))&&R(t,"non-ASCII line breaks are interpreted as content"),t.documents.push(t.result),t.position===t.lineStart&&G(t)?46===t.input.charCodeAt(t.position)&&(t.position+=3,U(t,!0,-1)):t.positiona&&(o=" ... ",e=r-a+o.length),n-r>a&&(s=" ...",n=r+a-s.length),{str:o+t.slice(e,n).replace(/\t/g,"→")+s,pos:r-e+o.length}}function o(t,e){return r.repeat(" ",e-t.length)+t}function s(t,e){if(e=Object.create(e||null),!t.buffer)return null;e.maxLength||(e.maxLength=79),"number"!==typeof e.indent&&(e.indent=1),"number"!==typeof e.linesBefore&&(e.linesBefore=3),"number"!==typeof e.linesAfter&&(e.linesAfter=2);var n,s=/\r?\n|\r|\0/g,a=[0],u=[],c=-1;while(n=s.exec(t.buffer))u.push(n.index),a.push(n.index+n[0].length),t.position<=n.index&&c<0&&(c=a.length-2);c<0&&(c=a.length-1);var l,f,p="",h=Math.min(t.line+e.linesAfter,u.length).toString().length,d=e.maxLength-(e.indent+h+3);for(l=1;l<=e.linesBefore;l++){if(c-l<0)break;f=i(t.buffer,a[c-l],u[c-l],t.position-(a[c]-a[c-l]),d),p=r.repeat(" ",e.indent)+o((t.line-l+1).toString(),h)+" | "+f.str+"\n"+p}for(f=i(t.buffer,a[c],u[c],t.position,d),p+=r.repeat(" ",e.indent)+o((t.line+1).toString(),h)+" | "+f.str+"\n",p+=r.repeat("-",e.indent+h+3+f.pos)+"^\n",l=1;l<=e.linesAfter;l++){if(c+l>=u.length)break;f=i(t.buffer,a[c+l],u[c+l],t.position-(a[c]-a[c+l]),d),p+=r.repeat(" ",e.indent)+o((t.line+l+1).toString(),h)+" | "+f.str+"\n"}return p.replace(/\n$/,"")}t.exports=s},50688:function(t,e,n){"use strict";var r=n(4640),i=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],o=["scalar","sequence","mapping"];function s(t){var e={};return null!==t&&Object.keys(t).forEach((function(n){t[n].forEach((function(t){e[String(t)]=n}))})),e}function a(t,e){if(e=e||{},Object.keys(e).forEach((function(e){if(-1===i.indexOf(e))throw new r('Unknown option "'+e+'" is met in definition of "'+t+'" YAML type.')})),this.options=e,this.tag=t,this.kind=e["kind"]||null,this.resolve=e["resolve"]||function(){return!0},this.construct=e["construct"]||function(t){return t},this.instanceOf=e["instanceOf"]||null,this.predicate=e["predicate"]||null,this.represent=e["represent"]||null,this.representName=e["representName"]||null,this.defaultStyle=e["defaultStyle"]||null,this.multi=e["multi"]||!1,this.styleAliases=s(e["styleAliases"]||null),-1===o.indexOf(this.kind))throw new r('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}t.exports=a},63504:function(t,e,n){"use strict";var r=n(50688),i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";function o(t){if(null===t)return!1;var e,n,r=0,o=t.length,s=i;for(n=0;n64)){if(e<0)return!1;r+=6}return r%8===0}function s(t){var e,n,r=t.replace(/[\r\n=]/g,""),o=r.length,s=i,a=0,u=[];for(e=0;e>16&255),u.push(a>>8&255),u.push(255&a)),a=a<<6|s.indexOf(r.charAt(e));return n=o%4*6,0===n?(u.push(a>>16&255),u.push(a>>8&255),u.push(255&a)):18===n?(u.push(a>>10&255),u.push(a>>2&255)):12===n&&u.push(a>>4&255),new Uint8Array(u)}function a(t){var e,n,r="",o=0,s=t.length,a=i;for(e=0;e>18&63],r+=a[o>>12&63],r+=a[o>>6&63],r+=a[63&o]),o=(o<<8)+t[e];return n=s%3,0===n?(r+=a[o>>18&63],r+=a[o>>12&63],r+=a[o>>6&63],r+=a[63&o]):2===n?(r+=a[o>>10&63],r+=a[o>>4&63],r+=a[o<<2&63],r+=a[64]):1===n&&(r+=a[o>>2&63],r+=a[o<<4&63],r+=a[64],r+=a[64]),r}function u(t){return"[object Uint8Array]"===Object.prototype.toString.call(t)}t.exports=new r("tag:yaml.org,2002:binary",{kind:"scalar",resolve:o,construct:s,predicate:u,represent:a})},43136:function(t,e,n){"use strict";var r=n(50688);function i(t){if(null===t)return!1;var e=t.length;return 4===e&&("true"===t||"True"===t||"TRUE"===t)||5===e&&("false"===t||"False"===t||"FALSE"===t)}function o(t){return"true"===t||"True"===t||"TRUE"===t}function s(t){return"[object Boolean]"===Object.prototype.toString.call(t)}t.exports=new r("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:o,predicate:s,represent:{lowercase:function(t){return t?"true":"false"},uppercase:function(t){return t?"TRUE":"FALSE"},camelcase:function(t){return t?"True":"False"}},defaultStyle:"lowercase"})},78760:function(t,e,n){"use strict";var r=n(5728),i=n(50688),o=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function s(t){return null!==t&&!(!o.test(t)||"_"===t[t.length-1])}function a(t){var e,n;return e=t.replace(/_/g,"").toLowerCase(),n="-"===e[0]?-1:1,"+-".indexOf(e[0])>=0&&(e=e.slice(1)),".inf"===e?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===e?NaN:n*parseFloat(e,10)}var u=/^[-+]?[0-9]+e/;function c(t,e){var n;if(isNaN(t))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===t)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===t)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(r.isNegativeZero(t))return"-0.0";return n=t.toString(10),u.test(n)?n.replace("e",".e"):n}function l(t){return"[object Number]"===Object.prototype.toString.call(t)&&(t%1!==0||r.isNegativeZero(t))}t.exports=new i("tag:yaml.org,2002:float",{kind:"scalar",resolve:s,construct:a,predicate:l,represent:c,defaultStyle:"lowercase"})},36664:function(t,e,n){"use strict";var r=n(5728),i=n(50688);function o(t){return 48<=t&&t<=57||65<=t&&t<=70||97<=t&&t<=102}function s(t){return 48<=t&&t<=55}function a(t){return 48<=t&&t<=57}function u(t){if(null===t)return!1;var e,n=t.length,r=0,i=!1;if(!n)return!1;if(e=t[r],"-"!==e&&"+"!==e||(e=t[++r]),"0"===e){if(r+1===n)return!0;if(e=t[++r],"b"===e){for(r++;r=0?"0b"+t.toString(2):"-0b"+t.toString(2).slice(1)},octal:function(t){return t>=0?"0o"+t.toString(8):"-0o"+t.toString(8).slice(1)},decimal:function(t){return t.toString(10)},hexadecimal:function(t){return t>=0?"0x"+t.toString(16).toUpperCase():"-0x"+t.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},24424:function(t,e,n){"use strict";var r=n(50688);t.exports=new r("tag:yaml.org,2002:map",{kind:"mapping",construct:function(t){return null!==t?t:{}}})},36120:function(t,e,n){"use strict";var r=n(50688);function i(t){return"<<"===t||null===t}t.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},96976:function(t,e,n){"use strict";var r=n(50688);function i(t){if(null===t)return!0;var e=t.length;return 1===e&&"~"===t||4===e&&("null"===t||"Null"===t||"NULL"===t)}function o(){return null}function s(t){return null===t}t.exports=new r("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:o,predicate:s,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"})},8888:function(t,e,n){"use strict";var r=n(50688),i=Object.prototype.hasOwnProperty,o=Object.prototype.toString;function s(t){if(null===t)return!0;var e,n,r,s,a,u=[],c=t;for(e=0,n=c.length;e-1}function qt(t,e){var n=this.__data__,r=oe(n,t);return r<0?n.push([t,e]):n[r][1]=e,this}function Ht(t){var e=-1,n=t?t.length:0;this.clear();while(++e-1&&t%1==0&&t-1&&t%1==0&&t<=o}function Ke(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function ze(t){return!!t&&"object"==typeof t}function Ye(t){return Ve(t)?re(t):pe(t)}function Je(){return[]}function We(){return!1}t.exports=Fe},6e3:function(t,e,n){"use strict";var r=function(){if("undefined"!==typeof self)return self;if("undefined"!==typeof window)return window;if("undefined"!==typeof n.g)return n.g;throw new Error("unable to locate global object")},i=r();t.exports=e=i.fetch,i.fetch&&(e["default"]=i.fetch.bind(i)),e.Headers=i.Headers,e.Request=i.Request,e.Response=i.Response},71504:function(t){"use strict";function e(t){if("string"!==typeof t)throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}function n(t,e){for(var n,r="",i=0,o=-1,s=0,a=0;a<=t.length;++a){if(a2){var u=r.lastIndexOf("/");if(u!==r.length-1){-1===u?(r="",i=0):(r=r.slice(0,u),i=r.length-1-r.lastIndexOf("/")),o=a,s=0;continue}}else if(2===r.length||1===r.length){r="",i=0,o=a,s=0;continue}e&&(r.length>0?r+="/..":r="..",i=2)}else r.length>0?r+="/"+t.slice(o+1,a):r=t.slice(o+1,a),i=a-o-1;o=a,s=0}else 46===n&&-1!==s?++s:s=-1}return r}function r(t,e){var n=e.dir||e.root,r=e.base||(e.name||"")+(e.ext||"");return n?n===e.root?n+r:n+t+r:r}var i={resolve:function(){for(var t,r="",i=!1,o=arguments.length-1;o>=-1&&!i;o--){var s;o>=0?s=arguments[o]:(void 0===t&&(t=process.cwd()),s=t),e(s),0!==s.length&&(r=s+"/"+r,i=47===s.charCodeAt(0))}return r=n(r,!i),i?r.length>0?"/"+r:"/":r.length>0?r:"."},normalize:function(t){if(e(t),0===t.length)return".";var r=47===t.charCodeAt(0),i=47===t.charCodeAt(t.length-1);return t=n(t,!r),0!==t.length||r||(t="."),t.length>0&&i&&(t+="/"),r?"/"+t:t},isAbsolute:function(t){return e(t),t.length>0&&47===t.charCodeAt(0)},join:function(){if(0===arguments.length)return".";for(var t,n=0;n0&&(void 0===t?t=r:t+="/"+r)}return void 0===t?".":i.normalize(t)},relative:function(t,n){if(e(t),e(n),t===n)return"";if(t=i.resolve(t),n=i.resolve(n),t===n)return"";for(var r=1;rl){if(47===n.charCodeAt(a+p))return n.slice(a+p+1);if(0===p)return n.slice(a+p)}else s>l&&(47===t.charCodeAt(r+p)?f=p:0===p&&(f=0));break}var h=t.charCodeAt(r+p),d=n.charCodeAt(a+p);if(h!==d)break;47===h&&(f=p)}var v="";for(p=r+f+1;p<=o;++p)p!==o&&47!==t.charCodeAt(p)||(0===v.length?v+="..":v+="/..");return v.length>0?v+n.slice(a+f):(a+=f,47===n.charCodeAt(a)&&++a,n.slice(a))},_makeLong:function(t){return t},dirname:function(t){if(e(t),0===t.length)return".";for(var n=t.charCodeAt(0),r=47===n,i=-1,o=!0,s=t.length-1;s>=1;--s)if(n=t.charCodeAt(s),47===n){if(!o){i=s;break}}else o=!1;return-1===i?r?"/":".":r&&1===i?"//":t.slice(0,i)},basename:function(t,n){if(void 0!==n&&"string"!==typeof n)throw new TypeError('"ext" argument must be a string');e(t);var r,i=0,o=-1,s=!0;if(void 0!==n&&n.length>0&&n.length<=t.length){if(n.length===t.length&&n===t)return"";var a=n.length-1,u=-1;for(r=t.length-1;r>=0;--r){var c=t.charCodeAt(r);if(47===c){if(!s){i=r+1;break}}else-1===u&&(s=!1,u=r+1),a>=0&&(c===n.charCodeAt(a)?-1===--a&&(o=r):(a=-1,o=u))}return i===o?o=u:-1===o&&(o=t.length),t.slice(i,o)}for(r=t.length-1;r>=0;--r)if(47===t.charCodeAt(r)){if(!s){i=r+1;break}}else-1===o&&(s=!1,o=r+1);return-1===o?"":t.slice(i,o)},extname:function(t){e(t);for(var n=-1,r=0,i=-1,o=!0,s=0,a=t.length-1;a>=0;--a){var u=t.charCodeAt(a);if(47!==u)-1===i&&(o=!1,i=a+1),46===u?-1===n?n=a:1!==s&&(s=1):-1!==n&&(s=-1);else if(!o){r=a+1;break}}return-1===n||-1===i||0===s||1===s&&n===i-1&&n===r+1?"":t.slice(n,i)},format:function(t){if(null===t||"object"!==typeof t)throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof t);return r("/",t)},parse:function(t){e(t);var n={root:"",dir:"",base:"",ext:"",name:""};if(0===t.length)return n;var r,i=t.charCodeAt(0),o=47===i;o?(n.root="/",r=1):r=0;for(var s=-1,a=0,u=-1,c=!0,l=t.length-1,f=0;l>=r;--l)if(i=t.charCodeAt(l),47!==i)-1===u&&(c=!1,u=l+1),46===i?-1===s?s=l:1!==f&&(f=1):-1!==s&&(f=-1);else if(!c){a=l+1;break}return-1===s||-1===u||0===f||1===f&&s===u-1&&s===a+1?-1!==u&&(n.base=n.name=0===a&&o?t.slice(1,u):t.slice(a,u)):(0===a&&o?(n.name=t.slice(1,s),n.base=t.slice(1,u)):(n.name=t.slice(a,s),n.base=t.slice(a,u)),n.ext=t.slice(s,u)),a>0?n.dir=t.slice(0,a-1):o&&(n.dir="/"),n},sep:"/",delimiter:":",win32:null,posix:null};i.posix=i,t.exports=i},12724:function(t){!function(e,n){t.exports=n()}(0,(function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="/",e(e.s=89)}([function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(35),i=Function.prototype,o=i.call,s=r&&i.bind.bind(o,o);t.exports=r?s:function(t){return function(){return o.apply(t,arguments)}}},function(t,e,n){var r=n(59),i=r.all;t.exports=r.IS_HTMLDDA?function(t){return"function"==typeof t||t===i}:function(t){return"function"==typeof t}},function(t,e,n){var r=n(4),i=n(43).f,o=n(30),s=n(11),a=n(33),u=n(95),c=n(66);t.exports=function(t,e){var n,l,f,p,h,d=t.target,v=t.global,g=t.stat;if(n=v?r:g?r[d]||a(d,{}):(r[d]||{}).prototype)for(l in e){if(p=e[l],t.dontCallGetSet?(h=i(n,l),f=h&&h.value):f=n[l],!c(v?l:d+(g?".":"#")+l,t.forced)&&void 0!==f){if(typeof p==typeof f)continue;u(p,f)}(t.sham||f&&f.sham)&&o(p,"sham",!0),s(n,l,p,t)}}},function(t,e,n){(function(e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e&&e)||function(){return this}()||Function("return this")()}).call(e,n(139))},function(t,e,n){var r=n(0);t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e,n){var r=n(8),i=String,o=TypeError;t.exports=function(t){if(r(t))return t;throw o(i(t)+" is not an object")}},function(t,e,n){var r=n(1),i=n(14),o=r({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,e){return o(i(t),e)}},function(t,e,n){var r=n(2),i=n(59),o=i.all;t.exports=i.IS_HTMLDDA?function(t){return"object"==typeof t?null!==t:r(t)||t===o}:function(t){return"object"==typeof t?null!==t:r(t)}},function(t,e,n){var r=n(4),i=n(47),o=n(7),s=n(75),a=n(72),u=n(76),c=i("wks"),l=r.Symbol,f=l&&l.for,p=u?l:l&&l.withoutSetter||s;t.exports=function(t){if(!o(c,t)||!a&&"string"!=typeof c[t]){var e="Symbol."+t;a&&o(l,t)?c[t]=l[t]:c[t]=u&&f?f(e):p(e)}return c[t]}},function(t,e,n){var r=n(123);t.exports=function(t){return r(t.length)}},function(t,e,n){var r=n(2),i=n(13),o=n(104),s=n(33);t.exports=function(t,e,n,a){a||(a={});var u=a.enumerable,c=void 0!==a.name?a.name:e;if(r(n)&&o(n,c,a),a.global)u?t[e]=n:s(e,n);else{try{a.unsafe?t[e]&&(u=!0):delete t[e]}catch(t){}u?t[e]=n:i.f(t,e,{value:n,enumerable:!1,configurable:!a.nonConfigurable,writable:!a.nonWritable})}return t}},function(t,e,n){var r=n(35),i=Function.prototype.call;t.exports=r?i.bind(i):function(){return i.apply(i,arguments)}},function(t,e,n){var r=n(5),i=n(62),o=n(77),s=n(6),a=n(50),u=TypeError,c=Object.defineProperty,l=Object.getOwnPropertyDescriptor;e.f=r?o?function(t,e,n){if(s(t),e=a(e),s(n),"function"==typeof t&&"prototype"===e&&"value"in n&&"writable"in n&&!n.writable){var r=l(t,e);r&&r.writable&&(t[e]=n.value,n={configurable:"configurable"in n?n.configurable:r.configurable,enumerable:"enumerable"in n?n.enumerable:r.enumerable,writable:!1})}return c(t,e,n)}:c:function(t,e,n){if(s(t),e=a(e),s(n),i)try{return c(t,e,n)}catch(t){}if("get"in n||"set"in n)throw u("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(24),i=Object;t.exports=function(t){return i(r(t))}},function(t,e,n){var r=n(1),i=r({}.toString),o=r("".slice);t.exports=function(t){return o(i(t),8,-1)}},function(t,e,n){var r=n(0),i=n(9),o=n(23),s=i("species");t.exports=function(t){return o>=51||!r((function(){var e=[],n=e.constructor={};return n[s]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},function(t,e,n){var r=n(4),i=n(2),o=function(t){return i(t)?t:void 0};t.exports=function(t,e){return arguments.length<2?o(r[t]):r[t]&&r[t][e]}},function(t,e,n){var r=n(15);t.exports=Array.isArray||function(t){return"Array"==r(t)}},function(t,e,n){var r=n(39),i=n(24);t.exports=function(t){return r(i(t))}},function(t,e,n){var r=n(29),i=String;t.exports=function(t){if("Symbol"===r(t))throw TypeError("Cannot convert a Symbol value to a string");return i(t)}},function(t,e,n){var r=n(100),i=n(1),o=n(39),s=n(14),a=n(10),u=n(28),c=i([].push),l=function(t){var e=1==t,n=2==t,i=3==t,l=4==t,f=6==t,p=7==t,h=5==t||f;return function(d,v,g,m){for(var b,y,w=s(d),x=o(w),_=r(v,g),O=a(x),j=0,S=m||u,k=e?S(d,O):n||p?S(d,0):void 0;O>j;j++)if((h||j in x)&&(b=x[j],y=_(b,j,w),t))if(e)k[j]=y;else if(y)switch(t){case 3:return!0;case 5:return b;case 6:return j;case 2:c(k,b)}else switch(t){case 4:return!1;case 7:c(k,b)}return f?-1:i||l?l:k}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterReject:l(7)}},function(t,e){var n=TypeError;t.exports=function(t){if(t>9007199254740991)throw n("Maximum allowed index exceeded");return t}},function(t,e,n){var r,i,o=n(4),s=n(97),a=o.process,u=o.Deno,c=a&&a.versions||u&&u.version,l=c&&c.v8;l&&(r=l.split("."),i=r[0]>0&&r[0]<4?1:+(r[0]+r[1])),!i&&s&&(!(r=s.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=s.match(/Chrome\/(\d+)/))&&(i=+r[1]),t.exports=i},function(t,e,n){var r=n(40),i=TypeError;t.exports=function(t){if(r(t))throw i("Can't call method on "+t);return t}},function(t,e,n){var r=n(2),i=n(74),o=TypeError;t.exports=function(t){if(r(t))return t;throw o(i(t)+" is not a function")}},function(t,e,n){"use strict";var r=n(0);t.exports=function(t,e){var n=[][t];return!!n&&r((function(){n.call(null,e||function(){return 1},1)}))}},function(t,e,n){"use strict";var r=n(5),i=n(18),o=TypeError,s=Object.getOwnPropertyDescriptor,a=r&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(t){return t instanceof TypeError}}();t.exports=a?function(t,e){if(i(t)&&!s(t,"length").writable)throw o("Cannot set read only .length");return t.length=e}:function(t,e){return t.length=e}},function(t,e,n){var r=n(94);t.exports=function(t,e){return new(r(t))(0===e?0:e)}},function(t,e,n){var r=n(51),i=n(2),o=n(15),s=n(9),a=s("toStringTag"),u=Object,c="Arguments"==o(function(){return arguments}()),l=function(t,e){try{return t[e]}catch(t){}};t.exports=r?o:function(t){var e,n,r;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=l(e=u(t),a))?n:c?o(e):"Object"==(r=o(e))&&i(e.callee)?"Arguments":r}},function(t,e,n){var r=n(5),i=n(13),o=n(31);t.exports=r?function(t,e,n){return i.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){"use strict";var r=n(50),i=n(13),o=n(31);t.exports=function(t,e,n){var s=r(e);s in t?i.f(t,s,o(0,n)):t[s]=n}},function(t,e,n){var r=n(4),i=Object.defineProperty;t.exports=function(t,e){try{i(r,t,{value:e,configurable:!0,writable:!0})}catch(n){r[t]=e}return e}},function(t,e){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,e,n){var r=n(0);t.exports=!r((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},function(t,e,n){var r=n(5),i=n(7),o=Function.prototype,s=r&&Object.getOwnPropertyDescriptor,a=i(o,"name"),u=a&&"something"===function(){}.name,c=a&&(!r||r&&s(o,"name").configurable);t.exports={EXISTS:a,PROPER:u,CONFIGURABLE:c}},function(t,e,n){var r=n(15),i=n(1);t.exports=function(t){if("Function"===r(t))return i(t)}},function(t,e){t.exports={}},function(t,e,n){var r=n(1),i=n(0),o=n(15),s=Object,a=r("".split);t.exports=i((function(){return!s("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?a(t,""):s(t)}:s},function(t,e){t.exports=function(t){return null===t||void 0===t}},function(t,e,n){var r=n(17),i=n(2),o=n(44),s=n(76),a=Object;t.exports=s?function(t){return"symbol"==typeof t}:function(t){var e=r("Symbol");return i(e)&&o(e.prototype,a(t))}},function(t,e,n){var r,i=n(6),o=n(107),s=n(34),a=n(38),u=n(101),c=n(60),l=n(70),f=l("IE_PROTO"),p=function(){},h=function(t){return"\n\n\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./ItemFilter.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./ItemFilter.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./ItemFilter.vue?vue&type=template&id=3ca3d571\"\nimport script from \"./ItemFilter.vue?vue&type=script&lang=js\"\nexport * from \"./ItemFilter.vue?vue&type=script&lang=js\"\nimport style1 from \"./ItemFilter.vue?vue&type=style&index=1&id=3ca3d571&prod&lang=scss\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import { mapState } from \"vuex\";\n\nexport default {\n components: {\n DatePicker: () => import('vue2-datepicker')\n },\n data() {\n return {\n datepickerLang: null,\n datepickerFormat: 'YYYY-MM-DD'\n };\n },\n computed: {\n ...mapState(['uiLanguage'])\n },\n watch: {\n uiLanguage: {\n immediate: true,\n async handler(locale) {\n if (!locale) {\n return;\n }\n const options = (await import(`../locales/${locale}/datepicker.js`)).default;\n if (options.locale instanceof Promise) {\n this.datepickerLang = (await options.locale).default;\n }\n else {\n this.datepickerLang = options.locale;\n }\n this.datepickerFormat = options.format;\n }\n }\n }\n};","import Utils from \"../../utils\";\n\nexport default class CqlValue {\n\n constructor(value) {\n this.value = value;\n }\n\n static create(value) {\n if (value instanceof Date) {\n return new CqlTimestamp(value);\n }\n else if (typeof value === 'string') {\n return new CqlString(value);\n }\n else {\n return new CqlValue(value);\n }\n }\n\n toJSON() {\n return this.value;\n }\n\n toText() {\n return this.value;\n }\n\n}\n\nexport class CqlTimestamp extends CqlValue {\n\n constructor(value) {\n super(value);\n }\n\n toJSON() {\n return { timestamp: this.toTimestamp() };\n }\n\n toText() {\n return `TIMESTAMP('${this.toTimestamp()}')`;\n }\n\n toTimestamp() {\n return Utils.dateToUTC(this.value).toISOString();\n }\n\n}\n\nexport class CqlString extends CqlValue {\n\n constructor(value) {\n super(value);\n }\n\n toJSON() {\n return this.value;\n }\n\n toText() {\n return `'${this.value.replace(\"'\", \"''\")}'`;\n }\n\n}\n","var _objectSpread2;\n\nfunction ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nimport { extend } from '../../vue';\nimport { NAME_FORM_CHECKBOX } from '../../constants/components';\nimport { EVENT_NAME_CHANGE, MODEL_EVENT_NAME_PREFIX } from '../../constants/events';\nimport { PROP_TYPE_ANY, PROP_TYPE_BOOLEAN } from '../../constants/props';\nimport { isArray } from '../../utils/inspect';\nimport { looseEqual } from '../../utils/loose-equal';\nimport { looseIndexOf } from '../../utils/loose-index-of';\nimport { sortKeys } from '../../utils/object';\nimport { makeProp, makePropsConfigurable } from '../../utils/props';\nimport { MODEL_EVENT_NAME, formRadioCheckMixin, props as formRadioCheckProps } from '../../mixins/form-radio-check'; // --- Constants ---\n\nvar MODEL_PROP_NAME_INDETERMINATE = 'indeterminate';\nvar MODEL_EVENT_NAME_INDETERMINATE = MODEL_EVENT_NAME_PREFIX + MODEL_PROP_NAME_INDETERMINATE; // --- Props ---\n\nexport var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread({}, formRadioCheckProps), {}, (_objectSpread2 = {}, _defineProperty(_objectSpread2, MODEL_PROP_NAME_INDETERMINATE, makeProp(PROP_TYPE_BOOLEAN, false)), _defineProperty(_objectSpread2, \"switch\", makeProp(PROP_TYPE_BOOLEAN, false)), _defineProperty(_objectSpread2, \"uncheckedValue\", makeProp(PROP_TYPE_ANY, false)), _defineProperty(_objectSpread2, \"value\", makeProp(PROP_TYPE_ANY, true)), _objectSpread2))), NAME_FORM_CHECKBOX); // --- Main component ---\n// @vue/component\n\nexport var BFormCheckbox = /*#__PURE__*/extend({\n name: NAME_FORM_CHECKBOX,\n mixins: [formRadioCheckMixin],\n inject: {\n getBvGroup: {\n from: 'getBvCheckGroup',\n default: function _default() {\n return function () {\n return null;\n };\n }\n }\n },\n props: props,\n computed: {\n bvGroup: function bvGroup() {\n return this.getBvGroup();\n },\n isChecked: function isChecked() {\n var value = this.value,\n checked = this.computedLocalChecked;\n return isArray(checked) ? looseIndexOf(checked, value) > -1 : looseEqual(checked, value);\n },\n isRadio: function isRadio() {\n return false;\n }\n },\n watch: _defineProperty({}, MODEL_PROP_NAME_INDETERMINATE, function (newValue, oldValue) {\n if (!looseEqual(newValue, oldValue)) {\n this.setIndeterminate(newValue);\n }\n }),\n mounted: function mounted() {\n // Set initial indeterminate state\n this.setIndeterminate(this[MODEL_PROP_NAME_INDETERMINATE]);\n },\n methods: {\n computedLocalCheckedWatcher: function computedLocalCheckedWatcher(newValue, oldValue) {\n if (!looseEqual(newValue, oldValue)) {\n this.$emit(MODEL_EVENT_NAME, newValue);\n var $input = this.$refs.input;\n\n if ($input) {\n this.$emit(MODEL_EVENT_NAME_INDETERMINATE, $input.indeterminate);\n }\n }\n },\n handleChange: function handleChange(_ref) {\n var _this = this;\n\n var _ref$target = _ref.target,\n checked = _ref$target.checked,\n indeterminate = _ref$target.indeterminate;\n var value = this.value,\n uncheckedValue = this.uncheckedValue; // Update `computedLocalChecked`\n\n var localChecked = this.computedLocalChecked;\n\n if (isArray(localChecked)) {\n var index = looseIndexOf(localChecked, value);\n\n if (checked && index < 0) {\n // Add value to array\n localChecked = localChecked.concat(value);\n } else if (!checked && index > -1) {\n // Remove value from array\n localChecked = localChecked.slice(0, index).concat(localChecked.slice(index + 1));\n }\n } else {\n localChecked = checked ? value : uncheckedValue;\n }\n\n this.computedLocalChecked = localChecked; // Fire events in a `$nextTick()` to ensure the `v-model` is updated\n\n this.$nextTick(function () {\n // Change is only emitted on user interaction\n _this.$emit(EVENT_NAME_CHANGE, localChecked); // If this is a child of a group, we emit a change event on it as well\n\n\n if (_this.isGroup) {\n _this.bvGroup.$emit(EVENT_NAME_CHANGE, localChecked);\n }\n\n _this.$emit(MODEL_EVENT_NAME_INDETERMINATE, indeterminate);\n });\n },\n setIndeterminate: function setIndeterminate(state) {\n // Indeterminate only supported in single checkbox mode\n if (isArray(this.computedLocalChecked)) {\n state = false;\n }\n\n var $input = this.$refs.input;\n\n if ($input) {\n $input.indeterminate = state; // Emit update event to prop\n\n this.$emit(MODEL_EVENT_NAME_INDETERMINATE, state);\n }\n }\n }\n});","import { looseEqual } from './loose-equal'; // Assumes that the first argument is an array\n\nexport var looseIndexOf = function looseIndexOf(array, value) {\n for (var i = 0; i < array.length; i++) {\n if (looseEqual(array[i], value)) {\n return i;\n }\n }\n\n return -1;\n};","import { extend, mergeData } from '../../vue';\nimport { NAME_FORM } from '../../constants/components';\nimport { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props';\nimport { makeProp, makePropsConfigurable } from '../../utils/props'; // --- Props ---\n\nexport var props = makePropsConfigurable({\n id: makeProp(PROP_TYPE_STRING),\n inline: makeProp(PROP_TYPE_BOOLEAN, false),\n novalidate: makeProp(PROP_TYPE_BOOLEAN, false),\n validated: makeProp(PROP_TYPE_BOOLEAN, false)\n}, NAME_FORM); // --- Main component ---\n// @vue/component\n\nexport var BForm = /*#__PURE__*/extend({\n name: NAME_FORM,\n functional: true,\n props: props,\n render: function render(h, _ref) {\n var props = _ref.props,\n data = _ref.data,\n children = _ref.children;\n return h('form', mergeData(data, {\n class: {\n 'form-inline': props.inline,\n 'was-validated': props.validated\n },\n attrs: {\n id: props.id,\n novalidate: props.novalidate\n }\n }), children);\n }\n});","import { extend } from '../vue';\nimport { PROP_TYPE_BOOLEAN } from '../constants/props';\nimport { makeProp, makePropsConfigurable } from '../utils/props'; // --- Props ---\n\nexport var props = makePropsConfigurable({\n plain: makeProp(PROP_TYPE_BOOLEAN, false)\n}, 'formControls'); // --- Mixin ---\n// @vue/component\n\nexport var formCustomMixin = extend({\n props: props,\n computed: {\n custom: function custom() {\n return !this.plain;\n }\n }\n});","import { extend } from '../vue';\nimport { PROP_TYPE_ARRAY_OBJECT, PROP_TYPE_STRING } from '../constants/props';\nimport { get } from '../utils/get';\nimport { stripTags } from '../utils/html';\nimport { isArray, isPlainObject, isUndefined } from '../utils/inspect';\nimport { keys } from '../utils/object';\nimport { makeProp, makePropsConfigurable } from '../utils/props';\nimport { warn } from '../utils/warn'; // --- Constants ---\n\nvar OPTIONS_OBJECT_DEPRECATED_MSG = 'Setting prop \"options\" to an object is deprecated. Use the array format instead.'; // --- Props ---\n\nexport var props = makePropsConfigurable({\n disabledField: makeProp(PROP_TYPE_STRING, 'disabled'),\n htmlField: makeProp(PROP_TYPE_STRING, 'html'),\n options: makeProp(PROP_TYPE_ARRAY_OBJECT, []),\n textField: makeProp(PROP_TYPE_STRING, 'text'),\n valueField: makeProp(PROP_TYPE_STRING, 'value')\n}, 'formOptionControls'); // --- Mixin ---\n// @vue/component\n\nexport var formOptionsMixin = extend({\n props: props,\n computed: {\n formOptions: function formOptions() {\n return this.normalizeOptions(this.options);\n }\n },\n methods: {\n normalizeOption: function normalizeOption(option) {\n var key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n // When the option is an object, normalize it\n if (isPlainObject(option)) {\n var value = get(option, this.valueField);\n var text = get(option, this.textField);\n return {\n value: isUndefined(value) ? key || text : value,\n text: stripTags(String(isUndefined(text) ? key : text)),\n html: get(option, this.htmlField),\n disabled: Boolean(get(option, this.disabledField))\n };\n } // Otherwise create an `