<jupyter_start><jupyter_text>MongoDB metadata query GUIHere's a GUI for handling query submission to a metadata database and presentation of result datasets that match submitted queries.The query string must be a Python dictionary containing key-value pairs, where the keys are any element of Iris cube metadata, and the values are any valid value for the item of metadata specified in the key. For example:```python{"standard_name": "air_temperature"}   Standard name lookup.{"mime_type": "pp"}   Input file type.```Nested queries are also possible, such as querying specific elements of a cube's attributes dictionary. Lists and other non-string values can be passed:```python{"attributes.um_version": "10.6"}   UM Version cube attribute.{"attributes.STASH": [1, 3, 463]}   STASH is represented as a 3-item list [model, section, item].```Queries can also be made against multiple attributes simultaneously:```python{"standard_name": "air_temperature", "mime_type": "pp"}   PP files with a standard name of `air_temperature`.```<jupyter_code>from ast import literal_eval
import os
import warnings

from ipywidgets import interact
import ipywidgets as widgets
import iris
import pymongo

import metadatabase
def authenticate(b):
    """Handle connecting to a mongoDB Atlas database"""
    conn_str = f"mongodb+srv://iris:{pw.<EMAIL>}@iris-<EMAIL>.mongodb.net/?retryWrites=true&w=majority"
    try:
        global client
        client = metadatabase.client.Client(host=conn_str)
        db_names = list(client.collection_names.keys())
        db.set_trait("options", db_names)
        query.set_trait("disabled", False)
        search.set_trait("disabled", False)
    except Exception as e:
        with login_output:
            print(f"Login unsuccessful. Original error was:\n{e}")
    else:
        with login_output:
            print("Successfully logged in.")
# Login handler widgets.
pw = widgets.Password(
    value="",
    placeholder="mongoDB Atlas password...",
    description="Password:",
    disabled=False
)

login = widgets.Button(description="Log In")
login.on_click(authenticate)

login_output = widgets.Output()<jupyter_output><empty_output><jupyter_text>AuthenticateIn order to submit queries you must first authenticate with mongoDB Atlas.<jupyter_code># Display login handler widgets.
widgets.VBox([widgets.HBox([pw, login]), login_output])
# Callback functions.
def on_select_change(event):
    """Callback to update collection selector when database selector changes."""
    coll.set_trait("options", client.collection_names[event["new"]])

def handle_query(db_name, coll_name, query_dict):
    """
    Submit a query to the mongoDB client, using values taken from the widgets.
    Print the result files located, then the files loaded as Iris cubes. 
    
    """
    result = client.query(db_name, coll_name, query_dict)
    # Handle multiple matches arising from multiple matching datasets in the same file.
    result = list(set(result))
    
    # Display list of filenames.
    with list_out:
        list_out.clear_output()
        if len(result):
            print("Matching files:")
            display(result)
            print("\nIris Cubes:")
        else:
            print("No results.")
            
    # Display CubeList.
    if len(result):
        with cl_out:
            cl_out.clear_output()
            print("...")
        cl = iris.load(result)
        with cl_out:
            cl_out.clear_output(wait=True)
            display(cl)
    
def on_button_click(b):
    """Handle the 'search' button being clicked."""
    db_name = db.value
    coll_name = coll.value
    query_dict = literal_eval(query.value)
    handle_query(db_name, coll_name, query_dict)
# Set up widgets and callbacks.
db = widgets.Dropdown(
    options=["Log in first..."],
    description='DB:',
    disabled=False
)
db.observe(on_select_change, names="value")

coll = widgets.Dropdown(
    options=["Log in first..."],
    description="Collection:",
    disabled=False
)

query = widgets.Text(
    value="",
    placeholder='e.g. {"standard_name": "air_temperature"}',
    description='query =',
    disabled=True
)

search = widgets.Button(description="Search", disabled=True)
search.on_click(on_button_click)

list_out = widgets.Output()
cl_out = widgets.Output()<jupyter_output><empty_output><jupyter_text>QuerySelect a database (`DB`) and collection, then enter your query as a Python dict. Hit `Search` to run the query.<jupyter_code># Set up output display.
widgets.VBox([widgets.HBox([widgets.VBox([db, coll]), query, search]), list_out, cl_out])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11009771986970684, 'fraction_numerical': 0.001954397394136808, 'mean_word_length': 3.853530031612223, 'pattern_counts': {'":': 7, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '630134', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1379, 'n_tokens_neox': 1334, 'n_words': 401, 'path': 'eg/query-gui-mongodb.ipynb', 'repo_name': 'informatics-lab/metadatabase', 'size': '4614'}