diff --git a/ChartsDemo.zip b/ChartsDemo.zip new file mode 100644 index 0000000..8e0e40f Binary files /dev/null and b/ChartsDemo.zip differ diff --git a/app/ptmvision/api.py b/app/ptmvision/api.py index c026351..47bef17 100644 --- a/app/ptmvision/api.py +++ b/app/ptmvision/api.py @@ -21,10 +21,12 @@ load_dotenv() """ Definition of session keys """ -MODIFICATIONS_DATA = "7421BE93662C5" +MODIFICATIONS_DATA1 = "7421BE93662C5" +MODIFICATIONS_DATA2 = "7421BE93662D6" SESSION_STATE = "E3D6FB747F7ED" STATE_HAS_DATA = "has_data" STATE_PROTEIN_SELECTED = "protein_selected" +DUAL_MODE = False # Dual mode variable (default disabled, enabled) #BASEPATH = "./app/ptmvision" # Uncomment for local development. BASEPATH = "/app/ptmvision" # Uncomment for deployment. @@ -36,7 +38,7 @@ app.config["SESSION_USE_SIGNER"] = True app.config["SECRET_KEY"] = os.getenv("SIGNER") app.config["SESSION_CACHELIB"] = FileSystemCache(cache_dir = BASEPATH + '/session/', threshold=0) -app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024 # Limit content lengths to 50 MB. +app.config["MAX_CONTENT_LENGTH"] = 100 * 1024 * 1024 # Limit content lengths to 100 MB. """ Set API parameters. """ DEBUG = os.getenv("DEBUG") @@ -44,21 +46,48 @@ """ Start session """ Session(app) -@app.route("/example_session", methods=["GET"]) -def example_session(): +@app.route("/dualmodestatus", methods=["GET","POST"]) +def get_dual_mode(): """ - Route to load an example session from a JSON file. + Function for loading the status of the dualMode variable + from the client side. """ - try : + + global DUAL_MODE + + try: + [session.pop(key) for key in list(session.keys())] + + current_dual_state = request.get_json() + + state_dual_mode = current_dual_state["dualMode"] + + if state_dual_mode == "true": + DUAL_MODE = True + else: + DUAL_MODE = False + + return "Ok ", 200 + except Exception as e: + return "Failes POST request for dual mode status " + _format_exception(e), 500 + + +""" +@app.route("/example_session", methods=["GET"]) +def example_session(): + + #Route to load an example session from a JSON file. + + try: [session.pop(key) for key in list(session.keys())] with open( BASEPATH + "/static/resources/example_session/" + request.args.get("fileIdentifier") + ".zlib", "rb" ) as example_session_data : session_data = zlib.decompress( base64.b64decode( example_session_data.read( ) ) ).decode( ) - session[MODIFICATIONS_DATA] = json.loads(session_data) + session[MODIFICATIONS_DATA1] = json.loads(session_data) _set_session_state(**{STATE_HAS_DATA: True}) return "Ok", 200 except Exception as e : return "[Status 500] Failed request to start an example session: " + _format_exception(e), 500 - +""" @app.route("/resource", methods=["GET"]) def get_resource(): @@ -70,35 +99,38 @@ def get_resource(): except Exception as e: return "[Status 500] Failed request to retrieve resource: " + _format_exception(e), 500 - +""" @app.route("/download_session", methods=["GET"]) def download_session(): - """ + Route to download the current session as a zlib file. - """ - if MODIFICATIONS_DATA not in session: + + if MODIFICATIONS_DATA1 not in session: return "[Status 404] Failed request to download session data: The resource is not available. Was a PTMVision session started?", 404 try : zlib_compress = zlib.compressobj( 6, zlib.DEFLATED, zlib.MAX_WBITS ) - compressed_session_bytes = zlib_compress.compress( bytes(json.dumps(session[MODIFICATIONS_DATA]), "utf-8") ) + zlib_compress.flush( ) + compressed_session_bytes = zlib_compress.compress( bytes(json.dumps(session[MODIFICATIONS_DATA1]), "utf-8") ) + zlib_compress.flush( ) encoded_session = base64.b64encode( compressed_session_bytes ).decode("ascii") return encoded_session, 200 except Exception as e : return "[Status 500] Failed request to download session data: " + _format_exception(e), 500 +""" - +""" @app.route("/restart_session", methods=["POST"]) def restart_session(): - """ + Route to restart a previous session. - """ + try : [session.pop(key) for key in list(session.keys())] session_data = zlib.decompress( base64.b64decode( request.data ) ).decode( ) - session[MODIFICATIONS_DATA] = json.loads(session_data) + session[MODIFICATIONS_DATA1] = json.loads(session_data) return "Ok", 200 except Exception as e : return "[Status 500] Failed request to restart session: " + _format_exception(e), 500 +""" + @app.route("/process_search_engine_output", methods=["POST"]) def process_search_engine_output(): @@ -108,23 +140,58 @@ def process_search_engine_output(): try: [session.pop(key) for key in list(session.keys())] json_request_data = _request_to_json(request.data) + if DUAL_MODE: + """ + case: Dual mode enabled - 2 datasets as input + """ + + # data 1 + json_user_data1 = utils.parse_user_input( + StringIO(json_request_data["content1"]), + json_request_data["contentType1"], + json_request_data["massShiftTolerance1"], + json_request_data["excludeClasses1"], + json_request_data["filename1"] + ) + session[MODIFICATIONS_DATA1] = json_user_data1 + + # data 2 + json_user_data2 = utils.parse_user_input( + StringIO(json_request_data["content2"]), + json_request_data["contentType2"], + json_request_data["massShiftTolerance2"], + json_request_data["excludeClasses2"], + json_request_data["filename2"] + ) + session[MODIFICATIONS_DATA2] = json_user_data2 + + # update session state + _set_session_state(**{STATE_HAS_DATA: True}) + + return "Ok", 200 + else: + """ + case: Dual mode disabled - only 1 dataset as input + """ + json_user_data1 = utils.parse_user_input( + StringIO(json_request_data["content1"]), + json_request_data["contentType1"], + json_request_data["massShiftTolerance1"], + json_request_data["excludeClasses1"], + json_request_data["filename1"] + ) + session[MODIFICATIONS_DATA1] = json_user_data1 + + # update session state + _set_session_state(**{STATE_HAS_DATA: True}) + # Uncomment for local development. + """ + if DEBUG : + with open( "./dump.json", "w+" ) as dumpfile : + dumpfile.write( json.dumps( session[MODIFICATIONS_DATA1], indent = 3 ) ) + """ + return "Ok", 200 - json_user_data = utils.parse_user_input( - StringIO(json_request_data["content"]), - json_request_data["contentType"], - json_request_data["massShiftTolerance"], - json_request_data["excludeClasses"], - json_request_data["filename"] - ) - session[MODIFICATIONS_DATA] = json_user_data - _set_session_state(**{STATE_HAS_DATA: True}) - # Uncomment for local development. - """ - if DEBUG : - with open( "./dump.json", "w+" ) as dumpfile : - dumpfile.write( json.dumps( session[MODIFICATIONS_DATA], indent = 3 ) ) - """ - return "Ok", 200 except Exception as e: return "[Status 500] Failed request to process search engine output: " + _format_exception(e), 500 @@ -136,11 +203,97 @@ def available_proteins(): """ try : protein_entries = [] - if MODIFICATIONS_DATA in session: + if MODIFICATIONS_DATA1 in session: + protein_identifiers_unannotated = list( + filter( + lambda _ : "annotation" not in session[MODIFICATIONS_DATA1]["proteins"][ _ ], + list( session[MODIFICATIONS_DATA1]["proteins"].keys( ) ) + ) + ) + if len( protein_identifiers_unannotated ) > 0 : + annotations = _map_uniprot_identifiers( + protein_identifiers_unannotated, + "UniProtKB" + ) + + # remove IDs that were demerged in UniProt into multiple + # we don't know the original sequence, site mapping would go wrong + seen = set() + duplicates = set(item["from"] for item in annotations["results"] if item["from"] in seen or seen.add(item["from"])) + + # remove them from results + annotations["results"] = [item for item in annotations["results"] if item["from"] not in duplicates] + + if "failedIds" in annotations : + annotations["failedIds"].extend(list(duplicates)) + else : + annotations["failedIds"] = list(duplicates) + + # Number of demerged proteins / failed IDs to show user + #if env_parameters["DEBUG"] : + #n_demerged = len(duplicates) + #n_failed = len(annotations["failedIds"]) + + if "results" in annotations : + for entry in annotations[ "results" ] : + _add_annotation_to_protein( entry["from"], entry["to"] ) + if "failedIds" in annotations : + for failed_id in annotations[ "failedIds" ] : + _add_annotation_to_protein( failed_id, { }, failed = True ) + + # Construct entry per protein in input data. + for protein_identifier in list( session[MODIFICATIONS_DATA1]["proteins"].keys( ) ): + protein_name = _get_protein_name(session[MODIFICATIONS_DATA1]["proteins"][protein_identifier]["annotation"]) + protein_length = _get_protein_length(session[MODIFICATIONS_DATA1]["proteins"][protein_identifier]["annotation"]) + position_modification_data = session[MODIFICATIONS_DATA1]["proteins"][ + protein_identifier + ]["positions"] + modified_positions = [] + modifications = [] + for modified_position in position_modification_data: + modified_positions.append(int(modified_position)) + for modification in position_modification_data[modified_position][ + "modifications" + ]: + modifications.append(modification["display_name"]) + modified_positions = sorted(modified_positions) + modifications = list(set(modifications)) + protein_entry = { + "id": protein_identifier, + "name": protein_name, + "length": protein_length, + "modified_positions": len(modified_positions), + "unique_modifications": len(modifications), + "modifications": "$".join(modifications), + } + protein_entries.append(protein_entry) + # Uncomment for local development. + """ + if DEBUG : + with open( "./dump.json", "w+" ) as dumpfile : + dumpfile.write( json.dumps( session[MODIFICATIONS_DATA1], indent = 3 ) ) + """ + + return protein_entries, 200 + else : + raise Exception("Faulty session data.") + except Exception as e: + return "[Status 500] Failed request to get available proteins: " + _format_exception(e), 500 + + +@app.route("/available_proteins2", methods=["GET"]) +def available_proteins2(): + """ + Route to retrieve all available proteins of the session as a JSON. + For dataset 2 (Dual mode) + """ + try : + protein_entries = [] + if MODIFICATIONS_DATA2 in session: protein_identifiers_unannotated = list( filter( - lambda _ : "annotation" not in session[MODIFICATIONS_DATA]["proteins"][ _ ], - list( session[MODIFICATIONS_DATA]["proteins"].keys( ) ) + lambda _ : "annotation" not in session[MODIFICATIONS_DATA2]["proteins"][ _ ], + list( session[MODIFICATIONS_DATA2]["proteins"].keys( ) ) ) ) if len( protein_identifiers_unannotated ) > 0 : @@ -175,10 +328,10 @@ def available_proteins(): _add_annotation_to_protein( failed_id, { }, failed = True ) # Construct entry per protein in input data. - for protein_identifier in list( session[MODIFICATIONS_DATA]["proteins"].keys( ) ): - protein_name = _get_protein_name(session[MODIFICATIONS_DATA]["proteins"][protein_identifier]["annotation"]) - protein_length = _get_protein_length(session[MODIFICATIONS_DATA]["proteins"][protein_identifier]["annotation"]) - position_modification_data = session[MODIFICATIONS_DATA]["proteins"][ + for protein_identifier in list( session[MODIFICATIONS_DATA2]["proteins"].keys( ) ): + protein_name = _get_protein_name(session[MODIFICATIONS_DATA2]["proteins"][protein_identifier]["annotation"]) + protein_length = _get_protein_length(session[MODIFICATIONS_DATA2]["proteins"][protein_identifier]["annotation"]) + position_modification_data = session[MODIFICATIONS_DATA2]["proteins"][ protein_identifier ]["positions"] modified_positions = [] @@ -205,7 +358,7 @@ def available_proteins(): """ if DEBUG : with open( "./dump.json", "w+" ) as dumpfile : - dumpfile.write( json.dumps( session[MODIFICATIONS_DATA], indent = 3 ) ) + dumpfile.write( json.dumps( session[MODIFICATIONS_DATA1], indent = 3 ) ) """ return protein_entries, 200 @@ -215,6 +368,7 @@ def available_proteins(): return "[Status 500] Failed request to get available proteins: " + _format_exception(e), 500 + @app.route("/overview_data", methods=["GET"]) def overview_data(): """ @@ -224,11 +378,73 @@ def overview_data(): modifications = { } # Stores all present modifications together with their count. modification_co_occurrence = { } # Maps pairs of modification display names to their co-occurrence count. modification_classification_counts = { } - if MODIFICATIONS_DATA in session: - protein_identifiers = [_ for _ in session[MODIFICATIONS_DATA]["proteins"]] + if MODIFICATIONS_DATA1 in session: + protein_identifiers = [_ for _ in session[MODIFICATIONS_DATA1]["proteins"]] + dataset_size = len( protein_identifiers ) + for protein_identifier in protein_identifiers: + modification_data = session[MODIFICATIONS_DATA1]["proteins"][ + protein_identifier + ]["positions"] + for position in modification_data: + modifications_at_position = [] + for modification in modification_data[position][ + "modifications" + ]: + modification_name = modification["display_name"] + modifications.setdefault( modification_name, deepcopy( modification ) ) + + modifications[ modification_name ].setdefault( "count", 0 ) + modifications[ modification_name ][ "count" ] += 1 + + modifications[ modification_name ].setdefault( "occurrence", [ ] ) + modifications[ modification_name ][ "occurrence" ].append( protein_identifier ) + + modifications_at_position.append(modification_name) + + modification_classification = modification[ "modification_classification" ] + modification_classification_counts.setdefault( modification_classification, 0 ) + modification_classification_counts[ modification_classification ] += 1 + + for modifications_pair_tuple in combinations(set(modifications_at_position), 2): + modifications_pair = "@".join( sorted( list( modifications_pair_tuple ) ) ) + modification_co_occurrence.setdefault( modifications_pair, 0 ) + modification_co_occurrence[ modifications_pair ] += 1 + + for modification_name, modification in modifications.items( ) : + modification[ "frequency" ] = round( ( len( set( modifications[ modification_name ][ "occurrence" ] ) ) / dataset_size ) * 100, 2 ) + del modifications[ modification_name ][ "occurrence" ] + + modifications_by_mass_shift = sorted( list( modifications.keys( ) ), key = lambda k : -modifications[k][ "mass_shift" ] if type( modifications[k][ "mass_shift" ] ) != str else 0.0 ) + modifications_by_count = sorted( list( modifications.keys( ) ), key = lambda k : -modifications[k][ "count" ] ) + + return [ + modifications, + [ modifications_by_mass_shift, modifications_by_count ], + modification_co_occurrence, + modification_classification_counts, + session[MODIFICATIONS_DATA1][ "meta_data" ] + ], 200 + else : + raise Exception("Faulty session data.") + except Exception as e: + return "[Status 500] Failed request to get global/sample-level PTM data: " + _format_exception(e), 500 + + + +@app.route("/overview_data2", methods=["GET"]) +def overview_data2(): + """ + Route to retrieve overview data of the session as a JSON. + """ + try : + modifications = { } # Stores all present modifications together with their count. + modification_co_occurrence = { } # Maps pairs of modification display names to their co-occurrence count. + modification_classification_counts = { } + if MODIFICATIONS_DATA2 in session: + protein_identifiers = [_ for _ in session[MODIFICATIONS_DATA2]["proteins"]] dataset_size = len( protein_identifiers ) for protein_identifier in protein_identifiers: - modification_data = session[MODIFICATIONS_DATA]["proteins"][ + modification_data = session[MODIFICATIONS_DATA2]["proteins"][ protein_identifier ]["positions"] for position in modification_data: @@ -268,7 +484,7 @@ def overview_data(): [ modifications_by_mass_shift, modifications_by_count ], modification_co_occurrence, modification_classification_counts, - session[MODIFICATIONS_DATA][ "meta_data" ] + session[MODIFICATIONS_DATA2][ "meta_data" ] ], 200 else : raise Exception("Faulty session data.") @@ -287,40 +503,40 @@ def protein_data(): if json_request_data["pdb_text"] != None: structure, pdb_text = utils.parse_structure(json_request_data["pdb_text"]) else: - if "structure" in session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ] : - pdb_text = utils._brotli_decompress( session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ]["structure"] ) + if "structure" in session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ] : + pdb_text = utils._brotli_decompress( session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ]["structure"] ) structure = utils.parse_structure( pdb_text ) else : structure, pdb_text = utils.get_structure(json_request_data["uniprot_pa"]) # Extract protein sequence from structure and store it in session data. - session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ]["sequence"] = utils.get_sequence_from_structure(structure) - session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ][ "structure" ] = utils._brotly_compress(pdb_text) + session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ]["sequence"] = utils.get_sequence_from_structure(structure) + session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ][ "structure" ] = utils._brotly_compress(pdb_text) if structure != None: # Extract annotations for protein from UniProt. # NOTE: Temp. deprecated code segment; Data is (experimental) queried from UniProt by initial analysis for all proteins. This may be re-used if performance issues occur. - # if not "annotation" in session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ] : + # if not "annotation" in session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ] : # annotation = _map_uniprot_identifiers( # [json_request_data["uniprot_pa"]], "UniProtKB" # ) - # session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = { } - # session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = annotation[ "results" ][ 0 ][ "to" ] + # session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = { } + # session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = annotation[ "results" ][ 0 ][ "to" ] # Compute contacts from structure and store them in session data. - if not "contacts" in session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ] : - session[MODIFICATIONS_DATA]["meta_data"]["distance_cutoff"] = float(json_request_data["cutoff"]) - session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ] = { } + if not "contacts" in session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ] : + session[MODIFICATIONS_DATA1]["meta_data"]["distance_cutoff"] = float(json_request_data["cutoff"]) + session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ] = { } distance_matrix = utils.get_distance_matrix(structure) contacts = utils.get_contacts( distance_matrix, - session[MODIFICATIONS_DATA]["meta_data"]["distance_cutoff"], + session[MODIFICATIONS_DATA1]["meta_data"]["distance_cutoff"], ) for source_index, contacts_list in contacts.items( ) : - session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ] = { } + session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ] = { } for contact_index in contacts_list : - session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ][ contact_index + 1 ] = round( distance_matrix[ source_index, contact_index ], 4 ) + session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ][ contact_index + 1 ] = round( distance_matrix[ source_index, contact_index ], 4 ) # Construct response - response = deepcopy( session[MODIFICATIONS_DATA]["proteins"][ json_request_data["uniprot_pa"] ] ) + response = deepcopy( session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ] ) response[ "structure" ] = utils._brotli_decompress( response[ "structure" ] ) - response[ "meta" ] = session[MODIFICATIONS_DATA]["meta_data"] + response[ "meta" ] = session[MODIFICATIONS_DATA1]["meta_data"] _set_session_state(**{STATE_PROTEIN_SELECTED: json_request_data["uniprot_pa"]}) return response, 200 else : @@ -329,6 +545,61 @@ def protein_data(): return "[Status 500] Failed request to get protein PTM data: " + _format_exception(e), 500 +@app.route("/protein_data2", methods=["POST"]) +def protein_data2(): + """ + Route to retrieve data for a specific protein of the second Dataset. + """ + try : + json_request_data = _request_to_json(request.data) + # Try to fetch PDB format structure for UniProt identifier from AlphaFold database. + if json_request_data["pdb_text"] != None: + structure, pdb_text = utils.parse_structure(json_request_data["pdb_text"]) + else: + if "structure" in session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ] : + pdb_text = utils._brotli_decompress( session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ]["structure"] ) + structure = utils.parse_structure( pdb_text ) + else : + structure, pdb_text = utils.get_structure(json_request_data["uniprot_pa"]) + # Extract protein sequence from structure and store it in session data. + session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ]["sequence"] = utils.get_sequence_from_structure(structure) + session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ][ "structure" ] = utils._brotly_compress(pdb_text) + if structure != None: + # Extract annotations for protein from UniProt. + # NOTE: Temp. deprecated code segment; Data is (experimental) queried from UniProt by initial analysis for all proteins. This may be re-used if performance issues occur. + # if not "annotation" in session[MODIFICATIONS_DATA1]["proteins"][ json_request_data["uniprot_pa"] ] : + # annotation = _map_uniprot_identifiers( + # [json_request_data["uniprot_pa"]], "UniProtKB" + # ) + # session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = { } + # session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ]["annotation"] = annotation[ "results" ][ 0 ][ "to" ] + # Compute contacts from structure and store them in session data. + if not "contacts" in session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ] : + session[MODIFICATIONS_DATA2]["meta_data"]["distance_cutoff"] = float(json_request_data["cutoff"]) + session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ] = { } + distance_matrix = utils.get_distance_matrix(structure) + contacts = utils.get_contacts( + distance_matrix, + session[MODIFICATIONS_DATA2]["meta_data"]["distance_cutoff"], + ) + for source_index, contacts_list in contacts.items( ) : + session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ] = { } + for contact_index in contacts_list : + session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ][ "contacts" ][ source_index + 1 ][ contact_index + 1 ] = round( distance_matrix[ source_index, contact_index ], 4 ) + # Construct response + response = deepcopy( session[MODIFICATIONS_DATA2]["proteins"][ json_request_data["uniprot_pa"] ] ) + response[ "structure" ] = utils._brotli_decompress( response[ "structure" ] ) + response[ "meta" ] = session[MODIFICATIONS_DATA2]["meta_data"] + _set_session_state(**{STATE_PROTEIN_SELECTED: json_request_data["uniprot_pa"]}) + return response, 200 + else : + return "Error in request '/get_protein_data': No protein structure available.", 303 + except Exception as e: + return "[Status 500] Failed request to get protein PTM data: " + _format_exception(e), 500 + + + + @app.route("/session_state", methods=["GET"]) def session_state(): """ @@ -418,10 +689,10 @@ def _add_annotation_to_protein(protein_identifier: str, annotation: dict, failed failed (bool, optional): Flag indicating if annotation failed. Defaults to False. """ if failed: - session[MODIFICATIONS_DATA]["proteins"][protein_identifier]["annotation"] = None + session[MODIFICATIONS_DATA1]["proteins"][protein_identifier]["annotation"] = None else: if protein_identifier != annotation["primaryAccession"]: - _rename_dictionary_entry(session[MODIFICATIONS_DATA]["proteins"], protein_identifier, annotation["primaryAccession"]) + _rename_dictionary_entry(session[MODIFICATIONS_DATA1]["proteins"], protein_identifier, annotation["primaryAccession"]) protein_identifier = annotation["primaryAccession"] # Adjust annotation. annotation.pop("entryType", None) @@ -431,7 +702,7 @@ def _add_annotation_to_protein(protein_identifier: str, annotation: dict, failed annotation.pop("uniProtKBCrossReferences", None) annotation.pop("extraAttributes", None) # Set annotation to protein entry. - session[MODIFICATIONS_DATA]["proteins"][protein_identifier]["annotation"] = annotation + session[MODIFICATIONS_DATA1]["proteins"][protein_identifier]["annotation"] = annotation def _get_protein_name(annotation: dict) -> str: @@ -500,7 +771,7 @@ def _format_exception(e: str) -> str: """ if DEBUG: # Print the exception traceback with color highlighting - print("\u001b[31m" + "".join(traceback.format_exception(e)) + "\u001b[0m") + print("\u001b[31m" + "" + str(e) + "\u001b[0m") # Format only the exception message - return "".join(traceback.format_exception_only(e)).strip() \ No newline at end of file + return "".join(str(e)) \ No newline at end of file diff --git a/app/ptmvision/static/css/dual-mode-button-style.css b/app/ptmvision/static/css/dual-mode-button-style.css new file mode 100644 index 0000000..480aa3d --- /dev/null +++ b/app/ptmvision/static/css/dual-mode-button-style.css @@ -0,0 +1,41 @@ +/* Buttons in the sidebar menu */ + +/* Container for the view select buttons */ +#ViewSelector{ + background-color: gray; + font-size: 0px; + display: flex; + align-items: center; + justify-content: center; +} + +#LeftViewSelButton{ + border-top-left-radius: 50%; + border-bottom-left-radius: 50%; + width: 10%; +} + +#RightViewSelButton{ + border-top-right-radius: 50%; + border-bottom-right-radius: 50%; + width: 10%; +} + +#MiddleViewSelButton{ + width: 15%; +} + + +.ViewSelButtons{ + background-color: rgb(129, 131, 155); + margin-left: -1px; + margin-right: -1px; + border-style: solid; + border-color: black; + color: black; + font-size: large; +} + +.ViewSelButtons:hover{ + background-color: rgb(159, 162, 196); +} diff --git a/app/ptmvision/static/css/molview-style.css b/app/ptmvision/static/css/molview-style.css new file mode 100644 index 0000000..017ba7c --- /dev/null +++ b/app/ptmvision/static/css/molview-style.css @@ -0,0 +1,19 @@ +/* +Styling of molecule view window (kekulejs) +(window opens when pressing on supported ptms in PTMCounts) +*/ +body { + background-color: rgb(126, 149, 153); +} + +.Centered { + + color: #1d1d1d; +} + +#molDisplay{ + border: 2px solid black; + height: 300px; + width: 400px; + background-color: #FFFFFF; +} \ No newline at end of file diff --git a/app/ptmvision/static/css/nav-style.css b/app/ptmvision/static/css/nav-style.css index d2633f5..0cdfacd 100644 --- a/app/ptmvision/static/css/nav-style.css +++ b/app/ptmvision/static/css/nav-style.css @@ -3,6 +3,11 @@ Stylesheet specific for the navigation bar */ +#Navigation img{ + margin-top: 0.5%; +} + + /*The button (image) for opening the navigation menu*/ #NavOpenButton{ position: fixed; @@ -15,12 +20,13 @@ Stylesheet specific for the navigation bar position: fixed; z-index: 2; width: 0; - background-color: #fca75d; + background-color: gray; overflow-x: hidden; height: 50%; width: 0%; transition: 0.1s; border-radius: 5px; + margin-top: 0.5%; } @@ -30,7 +36,7 @@ Stylesheet specific for the navigation bar text-indent: 5%; /*indent for text, looks nicer*/ } -#NavBlock p{ +#NavBlock h3{ text-indent: 5%; /*indent for text, looks nicer*/ } @@ -53,6 +59,16 @@ Stylesheet specific for the navigation bar list-style-type: none; } +/*"Select Mode heading, default hidden because of default disabled dual mode"*/ +#NavBlock #ViewSelectorHeading{ + display: none; +} + +/*View selector buttons, default hidden*/ +#NavBlock #ViewSelector{ + display: none; +} + /*Button to close the navigation menu*/ #NavCloseButton{ color: white; diff --git a/app/ptmvision/static/css/ptmvision-header.css b/app/ptmvision/static/css/ptmvision-header.css new file mode 100644 index 0000000..9106c98 --- /dev/null +++ b/app/ptmvision/static/css/ptmvision-header.css @@ -0,0 +1,73 @@ +header{ + background-color: gray; + height: 60px; + display: flex; + flex-wrap: nowrap; + position: sticky; + z-index: 1000; + top: 0; +} + +.headerSec { + display: flex; + height: 60px; + align-self: center; + + /*text setting: */ + font-family: 'Courier New', Courier, monospace; + color: white; + font-weight: bold; + +} + +#HeaderSecLogo { + width: 15%; + justify-content: center; + align-items: stretch; +} + +#HeaderSecError { + + color: #FFFFFF; + /*Settings for the positioning of the error msg*/ + justify-content: center; + align-items: center; +} + + +#HeaderSecSpace { + width: 450%; +} + +#HeaderSecDataset { + justify-content: flex-end; + + /*Settings for the positioning of the dataset display*/ + justify-content: center; + align-items: center; +} + +#HeaderSecDSTextContainer{ + display: none; +} + +#HeaderSecDualStatus { + /*container settings: */ + display: flex; + flex-direction: column; + +} + +#HeaderSecDualStatus p{ + /*alignment settings: */ + align-self: center; + justify-content: center; +} + +#DualModeStatusDot{ + height: 15px; + width: 80%; + background-color: #FF3636; /*turn this green or red depending if dual mode is enabled or disabled*/ + align-self: center; + +} \ No newline at end of file diff --git a/app/ptmvision/static/css/style.css b/app/ptmvision/static/css/ptmvision-mainstyle.css similarity index 82% rename from app/ptmvision/static/css/style.css rename to app/ptmvision/static/css/ptmvision-mainstyle.css index aa0a08b..49f0ca0 100644 --- a/app/ptmvision/static/css/style.css +++ b/app/ptmvision/static/css/ptmvision-mainstyle.css @@ -9,7 +9,7 @@ section{ width: 95%; } -/*TODO: Buttons for changing datasets/comparative view*/ +/*TODO: Buttons for changing datasets/comparative view -> in dual-mode-button-style.css */ #ModeSelector{ border: 1px solid black; } @@ -23,10 +23,6 @@ section{ background-color: #bdbdbd; } -#input-data-set-2{ - display: none; -} - #OverviewSection{ background-color: #bdbdbd; @@ -43,7 +39,7 @@ section{ display: none; } -#ModificationDetailsSection{ +#DashboardSection{ background-color: #bdbdbd; display: none; } @@ -52,6 +48,18 @@ section{ display: flex; } +#input-data-set-1{ + width: 45%; +} + +#input-data-set-2{ + display: none; + width: 45%; +} + +#input-data-y-line{ + display: none; +} /* Important for correct select 2 multi select display: diff --git a/app/ptmvision/static/css/ptmvision-overview.css b/app/ptmvision/static/css/ptmvision-overview.css new file mode 100644 index 0000000..257bda6 --- /dev/null +++ b/app/ptmvision/static/css/ptmvision-overview.css @@ -0,0 +1,14 @@ +#Overview{ + /*Main Overview Chart*/ + height: 600px; + border-style: solid; + border-color: black; + border-width: 1px; +} + +#PTMCountMolView{ + /*PTM Count with click interaction*/ + border-style: solid; + border-color: black; + border-width: 1px; +} \ No newline at end of file diff --git a/app/ptmvision/static/css/ptmvision-protein.css b/app/ptmvision/static/css/ptmvision-protein.css new file mode 100644 index 0000000..105fc8e --- /dev/null +++ b/app/ptmvision/static/css/ptmvision-protein.css @@ -0,0 +1,15 @@ +/* +Style of the proteinview table +*/ + +#ProtTable{ + height: 500px; + overflow: hidden; +} + +#ProteinTreemap{ + height: 800px; + border-style: solid; + border-color: black; + border-width: 1px; +} \ No newline at end of file diff --git a/app/ptmvision/static/js/axios-1.1.2.min.js b/app/ptmvision/static/js/axios-1.1.2.min.js deleted file mode 100644 index 79aa153..0000000 --- a/app/ptmvision/static/js/axios-1.1.2.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{},s=i.allOwnKeys,a=void 0!==s&&s;if(null!=t)if("object"!==e(t)&&(t=[t]),l(t))for(r=0,o=t.length;r3&&void 0!==arguments[3]?arguments[3]:{},i=r.allOwnKeys;return S(t,(function(t,r){n&&m(t)?e[r]=o(t,n):e[r]=t}),{allOwnKeys:i}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,s,u={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)s=o[i],r&&!r(s,e,t)||u[s]||(t[s]=e[s],u[s]=!0);e=!1!==n&&a(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:u,kindOfTest:c,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(l(e))return e;var t=e.length;if(!v(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:T,hasOwnProperty:x,hasOwnProp:x,reduceDescriptors:N,freezeMethods:function(e){N(e,(function(t,n){var r=e[n];m(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not read-only method '"+n+"'")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return l(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[_-\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t}};function _(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}P.inherits(_,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var B=_.prototype,D={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){D[e]={value:e}})),Object.defineProperties(_,D),Object.defineProperty(B,"isAxiosError",{value:!0}),_.from=function(e,t,n,r,o,i){var s=Object.create(B);return P.toFlatObject(e,s,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),_.call(s,e.message,t,n,r,o),s.cause=e,s.name=e.name,i&&Object.assign(s,i),s};var F="object"==("undefined"==typeof self?"undefined":e(self))?self.FormData:window.FormData;function U(e){return P.isPlainObject(e)||P.isArray(e)}function k(e){return P.endsWith(e,"[]")?e.slice(0,-2):e}function L(e,t,n){return e?e.concat(t).map((function(e,t){return e=k(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}var q=P.toFlatObject(P,{},null,(function(e){return/^is[A-Z]/.test(e)}));function z(t,n,r){if(!P.isObject(t))throw new TypeError("target must be an object");n=n||new(F||FormData);var o,i=(r=P.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!P.isUndefined(t[e])}))).metaTokens,s=r.visitor||l,a=r.dots,u=r.indexes,c=(r.Blob||"undefined"!=typeof Blob&&Blob)&&((o=n)&&P.isFunction(o.append)&&"FormData"===o[Symbol.toStringTag]&&o[Symbol.iterator]);if(!P.isFunction(s))throw new TypeError("visitor must be a function");function f(e){if(null===e)return"";if(P.isDate(e))return e.toISOString();if(!c&&P.isBlob(e))throw new _("Blob is not supported. Use a Buffer instead.");return P.isArrayBuffer(e)||P.isTypedArray(e)?c&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function l(t,r,o){var s=t;if(t&&!o&&"object"===e(t))if(P.endsWith(r,"{}"))r=i?r:r.slice(0,-2),t=JSON.stringify(t);else if(P.isArray(t)&&function(e){return P.isArray(e)&&!e.some(U)}(t)||P.isFileList(t)||P.endsWith(r,"[]")&&(s=P.toArray(t)))return r=k(r),s.forEach((function(e,t){!P.isUndefined(e)&&n.append(!0===u?L([r],t,a):null===u?r:r+"[]",f(e))})),!1;return!!U(t)||(n.append(L(o,r,a),f(t)),!1)}var d=[],h=Object.assign(q,{defaultVisitor:l,convertValue:f,isVisitable:U});if(!P.isObject(t))throw new TypeError("data must be an object");return function e(t,r){if(!P.isUndefined(t)){if(-1!==d.indexOf(t))throw Error("Circular reference detected in "+r.join("."));d.push(t),P.forEach(t,(function(t,o){!0===(!P.isUndefined(t)&&s.call(n,t,P.isString(o)?o.trim():o,r,h))&&e(t,r?r.concat(o):[o])})),d.pop()}}(t),n}function I(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function M(e,t){this._pairs=[],e&&z(e,this,t)}var J=M.prototype;function H(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function V(e,t,n){if(!t)return e;var r=e.indexOf("#");-1!==r&&(e=e.slice(0,r));var o=n&&n.encode||H,i=P.isURLSearchParams(t)?t.toString():new M(t,n).toString(o);return i&&(e+=(-1===e.indexOf("?")?"?":"&")+i),e}J.append=function(e,t){this._pairs.push([e,t])},J.toString=function(e){var t=e?function(t){return e.call(this,t,I)}:I;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var W,K=function(){function e(){t(this,e),this.handlers=[]}return r(e,[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){P.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),X={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},$="undefined"!=typeof URLSearchParams?URLSearchParams:M,Q=FormData,G=("undefined"==typeof navigator||"ReactNative"!==(W=navigator.product)&&"NativeScript"!==W&&"NS"!==W)&&"undefined"!=typeof window&&"undefined"!=typeof document,Y={isBrowser:!0,classes:{URLSearchParams:$,FormData:Q,Blob:Blob},isStandardBrowserEnv:G,protocols:["http","https","file","blob","url","data"]};function Z(e){function t(e,n,r,o){var i=e[o++],s=Number.isFinite(+i),a=o>=e.length;return i=!i&&P.isArray(r)?r.length:i,a?(P.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!s):(r[i]&&P.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&P.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t0;)if(t===(n=r[o]).toLowerCase())return n;return null}function le(e,t){e&&this.set(e),this[se]=t||null}function de(e,t){var n=0,r=function(e,t){e=e||10;var n,r=new Array(e),o=new Array(e),i=0,s=0;return t=void 0!==t?t:1e3,function(a){var u=Date.now(),c=o[s];n||(n=u),r[i]=a,o[i]=u;for(var f=s,l=0;f!==i;)l+=r[f++],f%=e;if((i=(i+1)%e)===s&&(s=(s+1)%e),!(u-n-1,i=P.isObject(e);if(i&&P.isHTMLForm(e)&&(e=new FormData(e)),P.isFormData(e))return o&&o?JSON.stringify(Z(e)):e;if(P.isArrayBuffer(e)||P.isBuffer(e)||P.isStream(e)||P.isFile(e)||P.isBlob(e))return e;if(P.isArrayBufferView(e))return e.buffer;if(P.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return z(e,new Y.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return Y.isNode&&P.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=P.isFileList(e))||r.indexOf("multipart/form-data")>-1){var s=this.env&&this.env.FormData;return z(n?{"files[]":e}:e,s&&new s,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(P.isString(e))try{return(t||JSON.parse)(e),P.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||be.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(e&&P.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw _.from(e,_.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:Y.classes.FormData,Blob:Y.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};function ge(e,t){var n=this||be,r=t||n,o=le.from(r.headers),i=r.data;return P.forEach(e,(function(e){i=e.call(n,i,o.normalize(),t?t.status:void 0)})),o.normalize(),i}function Ee(e){return!(!e||!e.__CANCEL__)}function we(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new re}function Oe(e){return we(e),e.headers=le.from(e.headers),e.data=ge.call(e,e.transformRequest),(e.adapter||be.adapter)(e).then((function(t){return we(e),t.data=ge.call(e,e.transformResponse,t),t.headers=le.from(t.headers),t}),(function(t){return Ee(t)||(we(e),t&&t.response&&(t.response.data=ge.call(e,e.transformResponse,t.response),t.response.headers=le.from(t.response.headers))),Promise.reject(t)}))}function Re(e,t){t=t||{};var n={};function r(e,t){return P.isPlainObject(e)&&P.isPlainObject(t)?P.merge(e,t):P.isPlainObject(t)?P.merge({},t):P.isArray(t)?t.slice():t}function o(n){return P.isUndefined(t[n])?P.isUndefined(e[n])?void 0:r(void 0,e[n]):r(e[n],t[n])}function i(e){if(!P.isUndefined(t[e]))return r(void 0,t[e])}function s(n){return P.isUndefined(t[n])?P.isUndefined(e[n])?void 0:r(void 0,e[n]):r(void 0,t[n])}function a(n){return n in t?r(e[n],t[n]):n in e?r(void 0,e[n]):void 0}var u={url:i,method:i,data:i,baseURL:s,transformRequest:s,transformResponse:s,paramsSerializer:s,timeout:s,timeoutMessage:s,withCredentials:s,adapter:s,responseType:s,xsrfCookieName:s,xsrfHeaderName:s,onUploadProgress:s,onDownloadProgress:s,decompress:s,maxContentLength:s,maxBodyLength:s,beforeRedirect:s,transport:s,httpAgent:s,httpsAgent:s,cancelToken:s,socketPath:s,responseEncoding:s,validateStatus:a};return P.forEach(Object.keys(e).concat(Object.keys(t)),(function(e){var t=u[e]||o,r=t(e);P.isUndefined(r)&&t!==a||(n[e]=r)})),n}P.forEach(["delete","get","head"],(function(e){be.headers[e]={}})),P.forEach(["post","put","patch"],(function(e){be.headers[e]=P.merge(ve)}));var Se="1.1.2",Ae={};["object","boolean","number","function","string","symbol"].forEach((function(t,n){Ae[t]=function(r){return e(r)===t||"a"+(n<1?"n ":" ")+t}}));var je={};Ae.transitional=function(e,t,n){function r(e,t){return"[Axios v1.1.2] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,o,i){if(!1===e)throw new _(r(o," has been removed"+(t?" in "+t:"")),_.ERR_DEPRECATED);return t&&!je[o]&&(je[o]=!0,console.warn(r(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,o,i)}};var Te={assertOptions:function(t,n,r){if("object"!==e(t))throw new _("options must be an object",_.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(t),i=o.length;i-- >0;){var s=o[i],a=n[s];if(a){var u=t[s],c=void 0===u||a(u,s,t);if(!0!==c)throw new _("option "+s+" must be "+c,_.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new _("Unknown option "+s,_.ERR_BAD_OPTION)}},validators:Ae},xe=Te.validators,Ce=function(){function e(n){t(this,e),this.defaults=n,this.interceptors={request:new K,response:new K}}return r(e,[{key:"request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n=(t=Re(this.defaults,t)).transitional;void 0!==n&&Te.assertOptions(n,{silentJSONParsing:xe.transitional(xe.boolean),forcedJSONParsing:xe.transitional(xe.boolean),clarifyTimeoutError:xe.transitional(xe.boolean)},!1),t.method=(t.method||this.defaults.method||"get").toLowerCase();var r=t.headers&&P.merge(t.headers.common,t.headers[t.method]);r&&P.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete t.headers[e]})),t.headers=new le(t.headers,r);var o=[],i=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(i=i&&e.synchronous,o.unshift(e.fulfilled,e.rejected))}));var s,a=[];this.interceptors.response.forEach((function(e){a.push(e.fulfilled,e.rejected)}));var u,c=0;if(!i){var f=[Oe.bind(this),void 0];for(f.unshift.apply(f,o),f.push.apply(f,a),u=f.length,s=Promise.resolve(t);c0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},n((function(e,t,n){o.reason||(o.reason=new re(e,t,n),r(o.reason))}))}return r(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Pe=function e(t){var n=new Ce(t),r=o(Ce.prototype.request,n);return P.extend(r,Ce.prototype,n,{allOwnKeys:!0}),P.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(Re(t,n))},r}(be);return Pe.Axios=Ce,Pe.CanceledError=re,Pe.CancelToken=Ne,Pe.isCancel=Ee,Pe.VERSION=Se,Pe.toFormData=z,Pe.AxiosError=_,Pe.Cancel=Pe.CanceledError,Pe.all=function(e){return Promise.all(e)},Pe.spread=function(e){return function(t){return e.apply(null,t)}},Pe.isAxiosError=function(e){return P.isObject(e)&&!0===e.isAxiosError},Pe.formToJSON=function(e){return Z(P.isHTMLForm(e)?new FormData(e):e)},Pe})); -//# sourceMappingURL=axios.min.js.map diff --git a/app/ptmvision/static/js/3Dmol-2.1.0.min.js b/app/ptmvision/static/js/lib-3Dmol-2.1.0.min.js similarity index 100% rename from app/ptmvision/static/js/3Dmol-2.1.0.min.js rename to app/ptmvision/static/js/lib-3Dmol-2.1.0.min.js diff --git a/app/ptmvision/static/js/lib-axios-1.13.5.min.js b/app/ptmvision/static/js/lib-axios-1.13.5.min.js new file mode 100644 index 0000000..d97e5ed --- /dev/null +++ b/app/ptmvision/static/js/lib-axios-1.13.5.min.js @@ -0,0 +1,5 @@ +/*! Axios v1.13.5 Copyright (c) 2026 Matt Zabriskie and contributors */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,function(){"use strict";function e(e,t){this.v=e,this.k=t}function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n3?(o=h===r)&&(s=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=p&&((o=n<2&&pr||r>h)&&(i[4]=n,i[5]=r,d.n=h,u=0))}if(o||n>1)return a;throw l=!0,r}return function(o,f,h){if(c>1)throw TypeError("Generator is already running");for(l&&1===f&&p(f,h),u=f,s=h;(t=u<2?e:s)||!l;){i||(u?u<3?(u>1&&(d.n=-1),p(u,s)):d.n=s:d.v=s);try{if(c=2,i){if(u||(o="next"),t=i[o]){if(!(t=t.call(i,s)))throw TypeError("iterator result is not an object");if(!t.done)return t;s=t.value,u<2&&(u=0)}else 1===u&&(t=i.return)&&t.call(i),u<2&&(s=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((t=(l=d.n<0)?s:n.call(r,d))!==a)break}catch(t){i=e,u=1,s=t}finally{c=1}}return{value:t,done:l}}}(n,o,i),!0),c}var a={};function u(){}function s(){}function c(){}t=Object.getPrototypeOf;var f=[][r]?t(t([][r]())):(g(t={},r,function(){return this}),t),l=c.prototype=u.prototype=Object.create(f);function d(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,c):(e.__proto__=c,g(e,o,"GeneratorFunction")),e.prototype=Object.create(l),e}return s.prototype=c,g(l,"constructor",c),g(c,"constructor",s),s.displayName="GeneratorFunction",g(c,o,"GeneratorFunction"),g(l),g(l,o,"Generator"),g(l,r,function(){return this}),g(l,"toString",function(){return"[object Generator]"}),(m=function(){return{w:i,m:d}})()}function g(e,t,n,r){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}g=function(e,t,n,r){function i(t,n){g(e,t,function(e){return this._invoke(t,n,e)})}t?o?o(e,t,{value:n,enumerable:!r,configurable:!r,writable:!r}):e[t]=n:(i("next",0),i("throw",1),i("return",2))},g(e,t,n,r)}function w(e){if(null!=e){var t=e["function"==typeof Symbol&&Symbol.iterator||"@@iterator"],n=0;if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length))return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}throw new TypeError(typeof e+" is not iterable")}function O(e,t){return O=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},O(e,t)}function E(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,a,u=[],s=!0,c=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;s=!1}else for(;!(s=(r=i.call(n)).done)&&(u.push(r.value),u.length!==t);s=!0);}catch(e){c=!0,o=e}finally{try{if(!s&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return u}}(e,t)||A(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function R(e){return function(e){if(Array.isArray(e))return t(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||A(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function S(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}function T(e){return T="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},T(e)}function A(e,n){if(e){if("string"==typeof e)return t(e,n);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?t(e,n):void 0}}function j(e){return function(){return new k(e.apply(this,arguments))}}function k(t){var n,r;function o(n,r){try{var a=t[n](r),u=a.value,s=u instanceof e;Promise.resolve(s?u.v:u).then(function(e){if(s){var r="return"===n?"return":"next";if(!u.k||e.done)return o(r,e);e=t[r](e).value}i(a.done?"return":"normal",e)},function(e){o("throw",e)})}catch(e){i("throw",e)}}function i(e,t){switch(e){case"return":n.resolve({value:t,done:!0});break;case"throw":n.reject(t);break;default:n.resolve({value:t,done:!1})}(n=n.next)?o(n.key,n.arg):r=null}this._invoke=function(e,t){return new Promise(function(i,a){var u={key:e,arg:t,resolve:i,reject:a,next:null};r?r=r.next=u:(n=r=u,o(e,t))})},"function"!=typeof t.return&&(this.return=void 0)}function P(e){var t="function"==typeof Map?new Map:void 0;return P=function(e){if(null===e||!function(e){try{return-1!==Function.toString.call(e).indexOf("[native code]")}catch(t){return"function"==typeof e}}(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return function(e,t,n){if(y())return Reflect.construct.apply(null,arguments);var r=[null];r.push.apply(r,t);var o=new(e.bind.apply(e,r));return n&&O(o,n.prototype),o}(e,arguments,p(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),O(n,e)},P(e)}function _(e,t){return function(){return e.apply(t,arguments)}}k.prototype["function"==typeof Symbol&&Symbol.asyncIterator||"@@asyncIterator"]=function(){return this},k.prototype.next=function(e){return this._invoke("next",e)},k.prototype.throw=function(e){return this._invoke("throw",e)},k.prototype.return=function(e){return this._invoke("return",e)};var x,N=Object.prototype.toString,C=Object.getPrototypeOf,U=Symbol.iterator,F=Symbol.toStringTag,D=(x=Object.create(null),function(e){var t=N.call(e);return x[t]||(x[t]=t.slice(8,-1).toLowerCase())}),L=function(e){return e=e.toLowerCase(),function(t){return D(t)===e}},B=function(e){return function(t){return T(t)===e}},I=Array.isArray,q=B("undefined");function M(e){return null!==e&&!q(e)&&null!==e.constructor&&!q(e.constructor)&&J(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var z=L("ArrayBuffer");var H=B("string"),J=B("function"),W=B("number"),K=function(e){return null!==e&&"object"===T(e)},V=function(e){if("object"!==D(e))return!1;var t=C(e);return!(null!==t&&t!==Object.prototype&&null!==Object.getPrototypeOf(t)||F in e||U in e)},G=L("Date"),X=L("File"),$=L("Blob"),Q=L("FileList"),Y=L("URLSearchParams"),Z=E(["ReadableStream","Request","Response","Headers"].map(L),4),ee=Z[0],te=Z[1],ne=Z[2],re=Z[3];function oe(e,t){var n,r,o=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).allOwnKeys,i=void 0!==o&&o;if(null!=e)if("object"!==T(e)&&(e=[e]),I(e))for(n=0,r=e.length;n0;)if(t===(n=r[o]).toLowerCase())return n;return null}var ae="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,ue=function(e){return!q(e)&&e!==ae};var se,ce=(se="undefined"!=typeof Uint8Array&&C(Uint8Array),function(e){return se&&e instanceof se}),fe=L("HTMLFormElement"),le=function(){var e=Object.prototype.hasOwnProperty;return function(t,n){return e.call(t,n)}}(),de=L("RegExp"),pe=function(e,t){var n=Object.getOwnPropertyDescriptors(e),r={};oe(n,function(n,o){var i;!1!==(i=t(n,o,e))&&(r[o]=i||n)}),Object.defineProperties(e,r)};var he,ye,ve,be,me=L("AsyncFunction"),ge=(he="function"==typeof setImmediate,ye=J(ae.postMessage),he?setImmediate:ye?(ve="axios@".concat(Math.random()),be=[],ae.addEventListener("message",function(e){var t=e.source,n=e.data;t===ae&&n===ve&&be.length&&be.shift()()},!1),function(e){be.push(e),ae.postMessage(ve,"*")}):function(e){return setTimeout(e)}),we="undefined"!=typeof queueMicrotask?queueMicrotask.bind(ae):"undefined"!=typeof process&&process.nextTick||ge,Oe={isArray:I,isArrayBuffer:z,isBuffer:M,isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||J(e.append)&&("formdata"===(t=D(e))||"object"===t&&J(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&z(e.buffer)},isString:H,isNumber:W,isBoolean:function(e){return!0===e||!1===e},isObject:K,isPlainObject:V,isEmptyObject:function(e){if(!K(e)||M(e))return!1;try{return 0===Object.keys(e).length&&Object.getPrototypeOf(e)===Object.prototype}catch(e){return!1}},isReadableStream:ee,isRequest:te,isResponse:ne,isHeaders:re,isUndefined:q,isDate:G,isFile:X,isBlob:$,isRegExp:de,isFunction:J,isStream:function(e){return K(e)&&J(e.pipe)},isURLSearchParams:Y,isTypedArray:ce,isFileList:Q,forEach:oe,merge:function e(){for(var t=ue(this)&&this||{},n=t.caseless,r=t.skipUndefined,o={},i=function(t,i){if("__proto__"!==i&&"constructor"!==i&&"prototype"!==i){var a=n&&ie(o,i)||i;V(o[a])&&V(t)?o[a]=e(o[a],t):V(t)?o[a]=e({},t):I(t)?o[a]=t.slice():r&&q(t)||(o[a]=t)}},a=0,u=arguments.length;a3&&void 0!==arguments[3]?arguments[3]:{}).allOwnKeys}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,"constructor",{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,a,u={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],r&&!r(a,e,t)||u[a]||(t[a]=e[a],u[a]=!0);e=!1!==n&&C(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:D,kindOfTest:L,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(I(e))return e;var t=e.length;if(!W(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[U]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:fe,hasOwnProperty:le,hasOwnProp:le,reduceDescriptors:pe,freezeMethods:function(e){pe(e,function(t,n){if(J(e)&&-1!==["arguments","caller","callee"].indexOf(n))return!1;var r=e[n];J(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+n+"'")}))})},toObjectSet:function(e,t){var n={},r=function(e){e.forEach(function(e){n[e]=!0})};return I(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n})},noop:function(){},toFiniteNumber:function(e,t){return null!=e&&Number.isFinite(e=+e)?e:t},findKey:ie,global:ae,isContextDefined:ue,isSpecCompliantForm:function(e){return!!(e&&J(e.append)&&"FormData"===e[F]&&e[U])},toJSONObject:function(e){var t=new Array(10),n=function(e,r){if(K(e)){if(t.indexOf(e)>=0)return;if(M(e))return e;if(!("toJSON"in e)){t[r]=e;var o=I(e)?[]:{};return oe(e,function(e,t){var i=n(e,r+1);!q(i)&&(o[t]=i)}),t[r]=void 0,o}}return e};return n(e,0)},isAsyncFn:me,isThenable:function(e){return e&&(K(e)||J(e))&&J(e.then)&&J(e.catch)},setImmediate:ge,asap:we,isIterable:function(e){return null!=e&&J(e[U])}},Ee=function(e){function t(e,n,r,o,i){var a;return c(this,t),(a=s(this,t,[e])).name="AxiosError",a.isAxiosError=!0,n&&(a.code=n),r&&(a.config=r),o&&(a.request=o),i&&(a.response=i,a.status=i.status),a}return h(t,e),l(t,[{key:"toJSON",value:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:Oe.toJSONObject(this.config),code:this.code,status:this.status}}}],[{key:"from",value:function(e,n,r,o,i,a){var u=new t(e.message,n||e.code,r,o,i);return u.cause=e,u.name=e.name,a&&Object.assign(u,a),u}}])}(P(Error));Ee.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE",Ee.ERR_BAD_OPTION="ERR_BAD_OPTION",Ee.ECONNABORTED="ECONNABORTED",Ee.ETIMEDOUT="ETIMEDOUT",Ee.ERR_NETWORK="ERR_NETWORK",Ee.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS",Ee.ERR_DEPRECATED="ERR_DEPRECATED",Ee.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE",Ee.ERR_BAD_REQUEST="ERR_BAD_REQUEST",Ee.ERR_CANCELED="ERR_CANCELED",Ee.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT",Ee.ERR_INVALID_URL="ERR_INVALID_URL";var Re=Ee;function Se(e){return Oe.isPlainObject(e)||Oe.isArray(e)}function Te(e){return Oe.endsWith(e,"[]")?e.slice(0,-2):e}function Ae(e,t,n){return e?e.concat(t).map(function(e,t){return e=Te(e),!n&&t?"["+e+"]":e}).join(n?".":""):t}var je=Oe.toFlatObject(Oe,{},null,function(e){return/^is[A-Z]/.test(e)});function ke(e,t,n){if(!Oe.isObject(e))throw new TypeError("target must be an object");t=t||new FormData;var r=(n=Oe.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!Oe.isUndefined(t[e])})).metaTokens,o=n.visitor||c,i=n.dots,a=n.indexes,u=(n.Blob||"undefined"!=typeof Blob&&Blob)&&Oe.isSpecCompliantForm(t);if(!Oe.isFunction(o))throw new TypeError("visitor must be a function");function s(e){if(null===e)return"";if(Oe.isDate(e))return e.toISOString();if(Oe.isBoolean(e))return e.toString();if(!u&&Oe.isBlob(e))throw new Re("Blob is not supported. Use a Buffer instead.");return Oe.isArrayBuffer(e)||Oe.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function c(e,n,o){var u=e;if(e&&!o&&"object"===T(e))if(Oe.endsWith(n,"{}"))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(Oe.isArray(e)&&function(e){return Oe.isArray(e)&&!e.some(Se)}(e)||(Oe.isFileList(e)||Oe.endsWith(n,"[]"))&&(u=Oe.toArray(e)))return n=Te(n),u.forEach(function(e,r){!Oe.isUndefined(e)&&null!==e&&t.append(!0===a?Ae([n],r,i):null===a?n:n+"[]",s(e))}),!1;return!!Se(e)||(t.append(Ae(o,n,i),s(e)),!1)}var f=[],l=Object.assign(je,{defaultVisitor:c,convertValue:s,isVisitable:Se});if(!Oe.isObject(e))throw new TypeError("data must be an object");return function e(n,r){if(!Oe.isUndefined(n)){if(-1!==f.indexOf(n))throw Error("Circular reference detected in "+r.join("."));f.push(n),Oe.forEach(n,function(n,i){!0===(!(Oe.isUndefined(n)||null===n)&&o.call(t,n,Oe.isString(i)?i.trim():i,r,l))&&e(n,r?r.concat(i):[i])}),f.pop()}}(e),t}function Pe(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function _e(e,t){this._pairs=[],e&&ke(e,this,t)}var xe=_e.prototype;function Ne(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function Ce(e,t,n){if(!t)return e;var r,o=n&&n.encode||Ne,i=Oe.isFunction(n)?{serialize:n}:n,a=i&&i.serialize;if(r=a?a(t,i):Oe.isURLSearchParams(t)?t.toString():new _e(t,i).toString(o)){var u=e.indexOf("#");-1!==u&&(e=e.slice(0,u)),e+=(-1===e.indexOf("?")?"?":"&")+r}return e}xe.append=function(e,t){this._pairs.push([e,t])},xe.toString=function(e){var t=e?function(t){return e.call(this,t,Pe)}:Pe;return this._pairs.map(function(e){return t(e[0])+"="+t(e[1])},"").join("&")};var Ue=function(){return l(function e(){c(this,e),this.handlers=[]},[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){Oe.forEach(this.handlers,function(t){null!==t&&e(t)})}}])}(),Fe={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},De={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:_e,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},protocols:["http","https","file","blob","url","data"]},Le="undefined"!=typeof window&&"undefined"!=typeof document,Be="object"===("undefined"==typeof navigator?"undefined":T(navigator))&&navigator||void 0,Ie=Le&&(!Be||["ReactNative","NativeScript","NS"].indexOf(Be.product)<0),qe="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,Me=Le&&window.location.href||"http://localhost",ze=b(b({},Object.freeze({__proto__:null,hasBrowserEnv:Le,hasStandardBrowserWebWorkerEnv:qe,hasStandardBrowserEnv:Ie,navigator:Be,origin:Me})),De);function He(e){function t(e,n,r,o){var i=e[o++];if("__proto__"===i)return!0;var a=Number.isFinite(+i),u=o>=e.length;return i=!i&&Oe.isArray(r)?r.length:i,u?(Oe.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!a):(r[i]&&Oe.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&Oe.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=Oe.isObject(e);if(i&&Oe.isHTMLForm(e)&&(e=new FormData(e)),Oe.isFormData(e))return o?JSON.stringify(He(e)):e;if(Oe.isArrayBuffer(e)||Oe.isBuffer(e)||Oe.isStream(e)||Oe.isFile(e)||Oe.isBlob(e)||Oe.isReadableStream(e))return e;if(Oe.isArrayBufferView(e))return e.buffer;if(Oe.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return ke(e,new ze.classes.URLSearchParams,b({visitor:function(e,t,n,r){return ze.isNode&&Oe.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=Oe.isFileList(e))||r.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return ke(n?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(Oe.isString(e))try{return(t||JSON.parse)(e),Oe.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||Je.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(Oe.isResponse(e)||Oe.isReadableStream(e))return e;if(e&&Oe.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(o){if("SyntaxError"===e.name)throw Re.from(e,Re.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:ze.classes.FormData,Blob:ze.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};Oe.forEach(["delete","get","head","post","put","patch"],function(e){Je.headers[e]={}});var We=Je,Ke=Oe.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),Ve=Symbol("internals");function Ge(e){return e&&String(e).trim().toLowerCase()}function Xe(e){return!1===e||null==e?e:Oe.isArray(e)?e.map(Xe):String(e)}function $e(e,t,n,r,o){return Oe.isFunction(r)?r.call(this,t,n):(o&&(t=n),Oe.isString(t)?Oe.isString(r)?-1!==t.indexOf(r):Oe.isRegExp(r)?r.test(t):void 0:void 0)}var Qe=function(){return l(function e(t){c(this,e),t&&this.set(t)},[{key:"set",value:function(e,t,n){var r=this;function o(e,t,n){var o=Ge(t);if(!o)throw new Error("header name must be a non-empty string");var i=Oe.findKey(r,o);(!i||void 0===r[i]||!0===n||void 0===n&&!1!==r[i])&&(r[i||t]=Xe(e))}var i=function(e,t){return Oe.forEach(e,function(e,n){return o(e,n,t)})};if(Oe.isPlainObject(e)||e instanceof this.constructor)i(e,t);else if(Oe.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim()))i(function(e){var t,n,r,o={};return e&&e.split("\n").forEach(function(e){r=e.indexOf(":"),t=e.substring(0,r).trim().toLowerCase(),n=e.substring(r+1).trim(),!t||o[t]&&Ke[t]||("set-cookie"===t?o[t]?o[t].push(n):o[t]=[n]:o[t]=o[t]?o[t]+", "+n:n)}),o}(e),t);else if(Oe.isObject(e)&&Oe.isIterable(e)){var a,u,s,c={},f=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=A(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw i}}}}(e);try{for(f.s();!(s=f.n()).done;){var l=s.value;if(!Oe.isArray(l))throw TypeError("Object iterator must return a key-value pair");c[u=l[0]]=(a=c[u])?Oe.isArray(a)?[].concat(R(a),[l[1]]):[a,l[1]]:l[1]}}catch(e){f.e(e)}finally{f.f()}i(c,t)}else null!=e&&o(t,e,n);return this}},{key:"get",value:function(e,t){if(e=Ge(e)){var n=Oe.findKey(this,e);if(n){var r=this[n];if(!t)return r;if(!0===t)return function(e){for(var t,n=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=r.exec(e);)n[t[1]]=t[2];return n}(r);if(Oe.isFunction(t))return t.call(this,r,n);if(Oe.isRegExp(t))return t.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=Ge(e)){var n=Oe.findKey(this,e);return!(!n||void 0===this[n]||t&&!$e(0,this[n],n,t))}return!1}},{key:"delete",value:function(e,t){var n=this,r=!1;function o(e){if(e=Ge(e)){var o=Oe.findKey(n,e);!o||t&&!$e(0,n[o],o,t)||(delete n[o],r=!0)}}return Oe.isArray(e)?e.forEach(o):o(e),r}},{key:"clear",value:function(e){for(var t=Object.keys(this),n=t.length,r=!1;n--;){var o=t[n];e&&!$e(0,this[o],o,e,!0)||(delete this[o],r=!0)}return r}},{key:"normalize",value:function(e){var t=this,n={};return Oe.forEach(this,function(r,o){var i=Oe.findKey(n,o);if(i)return t[i]=Xe(r),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n})}(o):String(o).trim();a!==o&&delete t[o],t[a]=Xe(r),n[a]=!0}),this}},{key:"concat",value:function(){for(var e,t=arguments.length,n=new Array(t),r=0;r1?n-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:3,r=0,o=function(e,t){e=e||10;var n,r=new Array(e),o=new Array(e),i=0,a=0;return t=void 0!==t?t:1e3,function(u){var s=Date.now(),c=o[a];n||(n=s),r[i]=u,o[i]=s;for(var f=a,l=0;f!==i;)l+=r[f++],f%=e;if((i=(i+1)%e)===a&&(a=(a+1)%e),!(s-n1&&void 0!==arguments[1]?arguments[1]:Date.now();o=i,n=null,r&&(clearTimeout(r),r=null),e.apply(void 0,R(t))};return[function(){for(var e=Date.now(),t=e-o,u=arguments.length,s=new Array(u),c=0;c=i?a(s,e):(n=s,r||(r=setTimeout(function(){r=null,a(n)},i-t)))},function(){return n&&a(n)}]}(function(n){var i=n.loaded,a=n.lengthComputable?n.total:void 0,u=i-r,s=o(u);r=i;var c=d({loaded:i,total:a,progress:a?i/a:void 0,bytes:u,rate:s||void 0,estimated:s&&a&&i<=a?(a-i)/s:void 0,event:n,lengthComputable:null!=a},t?"download":"upload",!0);e(c)},n)},ot=function(e,t){var n=null!=e;return[function(r){return t[0]({lengthComputable:n,total:e,loaded:r})},t[1]]},it=function(e){return function(){for(var t=arguments.length,n=new Array(t),r=0;r1?t-1:0),r=1;r1?"since :\n"+s.map(Pt).join("\n"):" "+Pt(s[0]):"as no adapter specified";throw new Re("There is no suitable adapter to dispatch the request "+c,"ERR_NOT_SUPPORT")}return r},adapters:kt};function Nt(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new tt(null,e)}function Ct(e){return Nt(e),e.headers=Ye.from(e.headers),e.data=Ze.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),xt.getAdapter(e.adapter||We.adapter,e)(e).then(function(t){return Nt(e),t.data=Ze.call(e,e.transformResponse,t),t.headers=Ye.from(t.headers),t},function(t){return et(t)||(Nt(e),t&&t.response&&(t.response.data=Ze.call(e,e.transformResponse,t.response),t.response.headers=Ye.from(t.response.headers))),Promise.reject(t)})}var Ut="1.13.5",Ft={};["object","boolean","number","function","string","symbol"].forEach(function(e,t){Ft[e]=function(n){return T(n)===e||"a"+(t<1?"n ":" ")+e}});var Dt={};Ft.transitional=function(e,t,n){function r(e,t){return"[Axios v"+Ut+"] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,o,i){if(!1===e)throw new Re(r(o," has been removed"+(t?" in "+t:"")),Re.ERR_DEPRECATED);return t&&!Dt[o]&&(Dt[o]=!0,console.warn(r(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,o,i)}},Ft.spelling=function(e){return function(t,n){return console.warn("".concat(n," is likely a misspelling of ").concat(e)),!0}};var Lt={assertOptions:function(e,t,n){if("object"!==T(e))throw new Re("options must be an object",Re.ERR_BAD_OPTION_VALUE);for(var r=Object.keys(e),o=r.length;o-- >0;){var i=r[o],a=t[i];if(a){var u=e[i],s=void 0===u||a(u,i,e);if(!0!==s)throw new Re("option "+i+" must be "+s,Re.ERR_BAD_OPTION_VALUE)}else if(!0!==n)throw new Re("Unknown option "+i,Re.ERR_BAD_OPTION)}},validators:Ft},Bt=Lt.validators,It=function(){return l(function e(t){c(this,e),this.defaults=t||{},this.interceptors={request:new Ue,response:new Ue}},[{key:"request",value:(e=a(m().m(function e(t,n){var r,o,i;return m().w(function(e){for(;;)switch(e.p=e.n){case 0:return e.p=0,e.n=1,this._request(t,n);case 1:return e.a(2,e.v);case 2:if(e.p=2,(i=e.v)instanceof Error){r={},Error.captureStackTrace?Error.captureStackTrace(r):r=new Error,o=r.stack?r.stack.replace(/^.+\n/,""):"";try{i.stack?o&&!String(i.stack).endsWith(o.replace(/^.+\n.+\n/,""))&&(i.stack+="\n"+o):i.stack=o}catch(e){}}throw i;case 3:return e.a(2)}},e,this,[[0,2]])})),function(t,n){return e.apply(this,arguments)})},{key:"_request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n=t=ft(this.defaults,t),r=n.transitional,o=n.paramsSerializer,i=n.headers;void 0!==r&&Lt.assertOptions(r,{silentJSONParsing:Bt.transitional(Bt.boolean),forcedJSONParsing:Bt.transitional(Bt.boolean),clarifyTimeoutError:Bt.transitional(Bt.boolean),legacyInterceptorReqResOrdering:Bt.transitional(Bt.boolean)},!1),null!=o&&(Oe.isFunction(o)?t.paramsSerializer={serialize:o}:Lt.assertOptions(o,{encode:Bt.function,serialize:Bt.function},!0)),void 0!==t.allowAbsoluteUrls||(void 0!==this.defaults.allowAbsoluteUrls?t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:t.allowAbsoluteUrls=!0),Lt.assertOptions(t,{baseUrl:Bt.spelling("baseURL"),withXsrfToken:Bt.spelling("withXSRFToken")},!0),t.method=(t.method||this.defaults.method||"get").toLowerCase();var a=i&&Oe.merge(i.common,i[t.method]);i&&Oe.forEach(["delete","get","head","post","put","patch","common"],function(e){delete i[e]}),t.headers=Ye.concat(a,i);var u=[],s=!0;this.interceptors.request.forEach(function(e){if("function"!=typeof e.runWhen||!1!==e.runWhen(t)){s=s&&e.synchronous;var n=t.transitional||Fe;n&&n.legacyInterceptorReqResOrdering?u.unshift(e.fulfilled,e.rejected):u.push(e.fulfilled,e.rejected)}});var c,f=[];this.interceptors.response.forEach(function(e){f.push(e.fulfilled,e.rejected)});var l,d=0;if(!s){var p=[Ct.bind(this),void 0];for(p.unshift.apply(p,u),p.push.apply(p,f),l=p.length,c=Promise.resolve(t);d0;)r._listeners[t](e);r._listeners=null}}),this.promise.then=function(e){var t,n=new Promise(function(e){r.subscribe(e),t=e}).then(e);return n.cancel=function(){r.unsubscribe(t)},n},t(function(e,t,o){r.reason||(r.reason=new tt(e,t,o),n(r.reason))})}return l(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}},{key:"toAbortSignal",value:function(){var e=this,t=new AbortController,n=function(e){t.abort(e)};return this.subscribe(n),t.signal.unsubscribe=function(){return e.unsubscribe(n)},t.signal}}],[{key:"source",value:function(){var t;return{token:new e(function(e){t=e}),cancel:t}}}])}(),zt=Mt;var Ht={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(Ht).forEach(function(e){var t=E(e,2),n=t[0],r=t[1];Ht[r]=n});var Jt=Ht;var Wt=function e(t){var n=new qt(t),r=_(qt.prototype.request,n);return Oe.extend(r,qt.prototype,n,{allOwnKeys:!0}),Oe.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(ft(t,n))},r}(We);return Wt.Axios=qt,Wt.CanceledError=tt,Wt.CancelToken=zt,Wt.isCancel=et,Wt.VERSION=Ut,Wt.toFormData=ke,Wt.AxiosError=Re,Wt.Cancel=Wt.CanceledError,Wt.all=function(e){return Promise.all(e)},Wt.spread=function(e){return function(t){return e.apply(null,t)}},Wt.isAxiosError=function(e){return Oe.isObject(e)&&!0===e.isAxiosError},Wt.mergeConfig=ft,Wt.AxiosHeaders=Ye,Wt.formToJSON=function(e){return He(Oe.isHTMLForm(e)?new FormData(e):e)},Wt.getAdapter=xt.getAdapter,Wt.HttpStatusCode=Jt,Wt.default=Wt,Wt}); +//# sourceMappingURL=axios.min.js.map diff --git a/app/ptmvision/static/js/echarts-6.0.0.min.js b/app/ptmvision/static/js/lib-echarts-6.0.0.min.js similarity index 100% rename from app/ptmvision/static/js/echarts-6.0.0.min.js rename to app/ptmvision/static/js/lib-echarts-6.0.0.min.js diff --git a/app/ptmvision/static/js/jquery-3.7.1.min.js b/app/ptmvision/static/js/lib-jquery-3.7.1.min.js similarity index 100% rename from app/ptmvision/static/js/jquery-3.7.1.min.js rename to app/ptmvision/static/js/lib-jquery-3.7.1.min.js diff --git a/app/ptmvision/static/js/lib-kekule.min.js b/app/ptmvision/static/js/lib-kekule.min.js new file mode 100755 index 0000000..8cbd62c --- /dev/null +++ b/app/ptmvision/static/js/lib-kekule.min.js @@ -0,0 +1 @@ +!function(){var e;"object"==typeof self?e=self:"object"==typeof window&&window&&window.document?e=window:"object"==typeof global&&(e=global),e.__$kekule_single_min_bundle__=!0}(),function(e){var t;t="object"==typeof window&&window&&window.document?window:"object"==typeof global?global:"object"==typeof self?self:e||{},Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){t||(t=0);var r=this.length;for(t<0&&(t=r+t);t=0)r&&r(null);else{if(o){var i=function(e){if(o)try{var t=0===e.indexOf("file:///")?e.substr(8):e,r=d.readFileSync(t,"utf8");return u.runInThisContext(r,{filename:e}),!0}catch(e){return e}}(t);return r&&r(!0===i?null:i),i}try{var n=e.createElement("script");return n.src=t,n.onerror=function(e){r&&r(new Error("Loading script file "+t+" failed"))},n.onload=n.onreadystatechange=function(e){if(!n._loaded){var i=n.readyState;void 0!==i&&"loaded"!==i&&"complete"!==i||(n._loaded=!0,n.onload=n.onreadystatechange=null,c.push(t),r&&r(null))}},(e.getElementsByTagName("head")[0]||e.body).appendChild(n),n}catch(e){r&&r(e)}}}function p(e,t,r){!function e(t,r,i,n){if(r.length<=0)return void(i&&(n.length?i(new Error(n.join(", "))):i(null)));var o=r.shift();h(t,o,function(o){o&&n.push(o?o.message||o:""),e(t,r,i,n)})}(e,[].concat(t),r,[])}function g(e,t,i){if(o)p(r,e,function(e){(this.Kekule||l.Kekule)&&(this.Kekule||l.Kekule)._loaded(),i&&i(e)});else if(function(){if(o)return!0;var e=r&&r.readyState,t=window.attachEvent&&!window.opera;return"complete"===e||"loaded"===e||"interactive"===e&&!t}()||t)p(r,e,function(e){e||Kekule._loaded(),i&&i(e)});else try{for(var n=0,a=e.length;n<\/script>');r.write(' + + + - - - + - + - + - - - - - + + + - - - - - - --> - - - + + + + + + + + + - - - - --> + + + + - + + + +
+ +
+ +

+
+
+ +
+
+ +
+

+ Selected View +

+
+

+ Comparative +

+
+
+
+
+ +

Dual Mode

+
+ +
+
+
+