diff --git a/README.md b/README.md index 7e66ccf..015a4dd 100644 --- a/README.md +++ b/README.md @@ -332,3 +332,4 @@ A widely adopted UUID spec (and used by postgres), [rfc4122](https://datatracker + diff --git a/bloom_env.yaml b/bloom_env.yaml index 3228e63..18654db 100644 --- a/bloom_env.yaml +++ b/bloom_env.yaml @@ -22,6 +22,7 @@ dependencies: - jq - fd-find - rclone + - gunicorn - pip: - zebra_day==0.3.4 - fedex_tracking_day==0.2.8 diff --git a/bloom_lims/bin/kill_proc_holding_port.sh b/bloom_lims/bin/kill_proc_holding_port.sh new file mode 100644 index 0000000..7fa2c6d --- /dev/null +++ b/bloom_lims/bin/kill_proc_holding_port.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Function to kill processes by port +kill_processes_by_port() { + local port=$1 + local pids=$(lsof -t -i:$port) + + if [ -z "$pids" ]; then + echo "No processes found running on port $port." + return + fi + + echo "Found processes running on port $port: $pids" + + echo "Sending SIGTERM to processes..." + for pid in $pids; do + sudo kill -TERM $pid + done + + # Wait for a moment to ensure processes receive the signal + sleep 2 + + echo "Sending SIGKILL to processes..." + for pid in $pids; do + sudo kill -KILL $pid + done + + # Wait for a moment to ensure processes are killed + sleep 2 + + echo "Using fuser to release port $port..." + sudo fuser -k $port/tcp + + # Verify if any processes are still running on the port + remaining_pids=$(lsof -t -i:$port) + if [ -z "$remaining_pids" ]; then + echo "All processes on port $port have been terminated." + else + echo "Processes still running on port $port: $remaining_pids" + echo "Attempting to kill remaining processes with SIGKILL..." + for pid in $remaining_pids; do + sudo kill -KILL $pid + done + fi + + # Final check + final_pids=$(lsof -t -i:$port) + if [ -z "$final_pids" ]; then + echo "All processes on port $port have been terminated." + else + echo "Failed to terminate processes: $final_pids" + fi +} + +# Check if a port is provided +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Run the function to kill processes on the specified port +kill_processes_by_port $1 diff --git a/bloom_lims/bobjs.py b/bloom_lims/bobjs.py index b2e7f48..fdec2fb 100644 --- a/bloom_lims/bobjs.py +++ b/bloom_lims/bobjs.py @@ -701,9 +701,27 @@ def query_instance_by_component_v2( # should abstract to not assume properties key - def get_unique_property_values(self, property_key): + def get_unique_property_values(self, property_key, super_type=None, btype=None, b_sub_type=None, version=None): + json_path = property_key.split("->") + + # Start building the query with the base table query = self.session.query( + self.Base.classes.generic_instance + ) + + # Add filters based on the provided arguments + if super_type: + query = query.filter(self.Base.classes.generic_instance.super_type == super_type) + if btype: + query = query.filter(self.Base.classes.generic_instance.btype == btype) + if b_sub_type: + query = query.filter(self.Base.classes.generic_instance.b_sub_type == b_sub_type) + if version: + query = query.filter(self.Base.classes.generic_instance.version == version) + + # Add the JSON path extraction and distinct filtering after the base filters + query = query.with_entities( func.distinct( func.jsonb_extract_path_text( self.Base.classes.generic_instance.json_addl['properties'], *json_path @@ -713,11 +731,17 @@ def get_unique_property_values(self, property_key): func.jsonb_extract_path_text( self.Base.classes.generic_instance.json_addl['properties'], *json_path ).isnot(None) - ).all() + ) + + # Execute the query and get the results + results = query.all() + + # Extract unique values from the query results + unique_values = [value[0] for value in results if value[0] is not None] - unique_values = [value[0] for value in query if value[0] is not None] return unique_values + def query_template_by_component_v2( self, super_type=None, btype=None, b_sub_type=None, version=None ): @@ -2862,7 +2886,7 @@ def create_file( else: logging.warning(f"No data provided for file creation: {file_data, url}") new_file.bstatus = "no file data provided" - self.session.commit() + self.session.commit() if create_locked: self.lock_file(new_file.euid) diff --git a/bloom_lims/config/file/file_set.json b/bloom_lims/config/file/file_set.json index 5f32d45..8cc30f9 100644 --- a/bloom_lims/config/file/file_set.json +++ b/bloom_lims/config/file/file_set.json @@ -3,12 +3,27 @@ "1.0": { "description": "Generic File Set", "properties": { - "name": "A Generic File Set", + "name": "", "comments": "", "lab_code":"", "tag":"", - "description":"" + "tags":[], + "duration":"", + "ref_type":"", + "description":"", + "creating_user": "", + "rclone_config": {} }, + "ui_form_properties" : [ + {"property_key":"name","form_label":"Set Name", "required": false, "value_type": "string"}, + {"property_key":"description","form_label":"Set Description", "required": false, "value_type":"string"}, + {"property_key":"tag","form_label":"Tag", "required": false, "value_type":"uid-interactive"}, + {"property_key":"tags","form_label":"Tags", "required": false, "value_type":"list"}, + {"property_key": "creating_user", "form_label":"Creating User", "required":false, "value_type":"uid-static"}, + {"property_key": "ref_type", "form_label":"Ref Type", "required":false, "value_type":"uid-static"}, + {"property_key": "comments", "form_label": "Comments", "required": false, "value_type": "string"} + ], + "controlled_properties": { }, "expected_inputs": [], "expected_outputs": [], "singleton": "0", diff --git a/bloom_shell.py b/bloom_shell.py index 014888c..f38ecf6 100755 --- a/bloom_shell.py +++ b/bloom_shell.py @@ -5,8 +5,8 @@ import sys import os -bobdb = BloomObj(BLOOMdb3()) +bob = BloomObj(BLOOMdb3()) from IPython import embed -embed() \ No newline at end of file +embed() diff --git a/main.py b/main.py index 0e37830..7ccc473 100644 --- a/main.py +++ b/main.py @@ -1165,6 +1165,38 @@ async def get_stats(days): ) return HTMLResponse(content=content) +@app.post("/save_json_addl_key") +async def save_json_addl_key(request: Request): + try: + # Extract JSON data from the request + data = await request.json() + euid = data.get("euid") + json_addl_key = data.get('json_addl_key') + json_data = data.get("json_data") + + if not euid or not json_addl_key or not json_data: + logging.error("EUID, JSON key, or JSON data missing") + raise HTTPException(status_code=400, detail="EUID, JSON key, or JSON data missing") + + + bobdb = BloomObj(BLOOMdb3(app_username=request.session["user_data"]["email"])) + obj = bobdb.get_by_euid(euid) + + if not obj: + raise HTTPException(status_code=404, detail="Object not found") + + # Update the json_addl['properties'] + obj.json_addl[json_addl_key] = json_data + flag_modified(obj, "json_addl") + bobdb.session.commit() + + # Redirect to euid_details page + return RedirectResponse(url=f"/euid_details?euid={euid}", status_code=303) + + except Exception as e: + logging.error(f"Error saving JSON properties: {e}") + raise HTTPException(status_code=500, detail="An error occurred while saving JSON properties") + @app.get("/object_templates_summary", response_class=HTMLResponse) async def object_templates_summary(request: Request, _auth=Depends(require_auth)): @@ -1208,9 +1240,10 @@ async def euid_details( ) try: + # Fetch the object using euid obj = bobdb.get_by_euid(euid) - relationship_data = await get_relationship_data(obj) if obj else {} + relationship_data = get_relationship_data(obj) if obj else {} if not obj: raise HTTPException(status_code=404, detail="Object not found") @@ -1221,7 +1254,6 @@ async def euid_details( for column in obj.__table__.columns if hasattr(obj, column.key) } - obj_dict["parent_template_euid"] = obj.parent_template.euid if hasattr(obj, "parent_template") else "" audit_logs = bobdb.query_audit_log_by_euid(euid) user_data = request.session.get("user_data", {}) @@ -1230,6 +1262,8 @@ async def euid_details( content = templates.get_template("euid_details.html").render( request=request, object=obj_dict, + jaddl_prop=obj.json_addl.get("properties", {}), + jaddl_controlled_prop=obj.json_addl.get("controlled_properties", {}), style=style, relationships=relationship_data, audit_logs=audit_logs, @@ -1239,18 +1273,17 @@ async def euid_details( return HTMLResponse(content=content) except Exception as e: - if not is_deleted: - # Retry with is_deleted set to True - return await euid_details( - request=request, - euid=euid, - _uuid=_uuid, - is_deleted=True, - _auth=_auth, - ) - else: - # Re-raise the exception if already tried with is_deleted = True - raise e + #if not is_deleted: + # # Retry with is_deleted set to True + # return await euid_details( + # request=request, + # euid=euid, + # _uuid=_uuid, + # is_deleted=True, + # _auth=_auth, + # ) + #else: + raise e @app.get("/un_delete_by_uuid") @@ -1603,7 +1636,7 @@ async def user_home(request: Request): fedex_version = os.popen("pip freeze | grep fedex_tracking_day | cut -d = -f 3").readline().rstrip() zebra_printer_version = os.popen("pip freeze | grep zebra-day | cut -d = -f 3").readline().rstrip() - + # HARDCODED THE BUCKET PREFIX INT to 0 here and elsewhere using the same pattern. Reconsider the zero detection (and prob remove it) content = templates.get_template("user_home.html").render( request=request, user_data=user_data, @@ -1612,7 +1645,7 @@ async def user_home(request: Request): style=style, dest_section=dest_section, whitelisted_domains=" , ".join(os.environ.get("SUPABASE_WHITELIST_DOMAINS", "all").split(",")), - s3_bucket_prefix=os.environ.get("BLOOM_DEWEY_S3_BUCKET_PREFIX", "NEEDS TO BE SET!"), + s3_bucket_prefix=os.environ.get("BLOOM_DEWEY_S3_BUCKET_PREFIX", "NEEDS TO BE SET!")+"0", supabase_url=os.environ.get("SUPABASE_URL", "NEEDS TO BE SET!"), printer_info=printer_info, github_tag=github_tag, @@ -1866,13 +1899,27 @@ async def dewey(request: Request, _auth=Depends(require_auth)): bobdb = BloomObj(BLOOMdb3(app_username=request.session["user_data"]["email"])) f_templates = bobdb.query_template_by_component_v2("file","file","generic","1.0") + fset_templates = bobdb.query_template_by_component_v2("file","file_set","generic","1.0") + # these should only be 1 if len(f_templates) > 1: logging.error(f"Multiple file templates found for file/file/generic/1.0") raise HTTPException(status_code=500, detail="Multiple file templates found for file/file/generic/1.0") + if len(fset_templates) > 1: + logging.error(f"Multiple file set templates found for file/file_set/generic/1.0") + raise HTTPException(status_code=500, detail="Multiple file set templates found for file/file_set/generic/1.0") + f_template = f_templates[0] ui_form_properties = f_template.json_addl.get("ui_form_properties", []) ui_form_fields = generate_ui_form_fields(ui_form_properties, f_template.json_addl.get("controlled_properties", {}), bobject=bobdb) + ui_form_fields_query = generate_ui_form_fields(ui_form_properties, f_template.json_addl.get("controlled_properties", {}), bobject=bobdb, form_type="query", super_type="file", btype="file", version=None) + + + fset_template = fset_templates[0] + ui_form_properties_fset = fset_template.json_addl.get("ui_form_properties", []) + ui_form_fields_fset = generate_ui_form_fields(ui_form_properties_fset, fset_template.json_addl.get("controlled_properties", {}), bobject=bobdb) + ui_form_fields_query_fset = generate_ui_form_fields(ui_form_properties_fset, fset_template.json_addl.get("controlled_properties", {}), bobject=bobdb, form_type="query", super_type="file", btype="file_set", version=None) + content = templates.get_template("dewey.html").render( request=request, @@ -1881,11 +1928,13 @@ async def dewey(request: Request, _auth=Depends(require_auth)): upload_group_key=upload_group_key, udat=user_data, ui_fields=ui_form_fields, + ui_search_fields=ui_form_fields_query, controlled_properties=f_template.json_addl.get("controlled_properties", {}), has_ui_form_properties=bool(ui_form_properties), searchable_properties=sorted(f_template.json_addl['properties'].keys()), s3_bucket_prefix=os.environ.get("BLOOM_DEWEY_S3_BUCKET_PREFIX", "NEEDS TO BE SET!")+"0", - + ui_fields_fset=ui_form_fields_fset, + ui_search_fields_fset=ui_form_fields_query_fset, ) return HTMLResponse(content=content) @@ -2232,7 +2281,7 @@ async def delete_temp_file( async def search_files( request: Request, euid: str = Form(None), - is_greedy: str = Form(...), + is_greedy: str = Form("yes"), patient_id: List[str] = Form(None), clinician_id: List[str] = Form(None), relevant_datetime_start: str = Form(None), @@ -2249,65 +2298,40 @@ async def search_files( created_datetime_end: str = Form(None), creating_user: List[str] = Form(None), ): - search_criteria = {} - - if euid: - search_criteria["euid"] = euid - - if patient_id: - if len(patient_id) == 1 and patient_id[0] == ".na": - patient_id = "" - elif len(patient_id) == 1 and patient_id[0] == "": - patient_id = None - search_criteria["patient_id"] = patient_id - - if clinician_id: - if len(clinician_id) == 1 and clinician_id[0] == ".na": - clinician_id = "" - elif len(clinician_id) == 1 and clinician_id[0] == "": - clinician_id = None - search_criteria["clinician_id"] = clinician_id + + form_data = { + "euid": euid, + "patient_id": patient_id, + "clinician_id": clinician_id, + "lab_code": lab_code, + "purpose": purpose, + "purpose_subtype": purpose_subtype, + "category": category, + "sub_category": sub_category, + "sub_category_2": sub_category_2, + "study_id": study_id, + "comments": comments, + "creating_user": creating_user, + } + + search_fields = [ + "euid", "patient_id", "clinician_id", "lab_code", "purpose", "purpose_subtype", + "category", "sub_category", "sub_category_2", "study_id", "comments", "creating_user" + ] + + search_criteria = create_search_criteria(form_data, search_fields) + if relevant_datetime_start or relevant_datetime_end: search_criteria["relevant_datetime"] = { "start": relevant_datetime_start, "end": relevant_datetime_end } - if lab_code: - if len(lab_code) == 1 and lab_code[0] == ".na": - lab_code = "" - elif len(lab_code) == 1 and lab_code[0] == "": - lab_code = None - - search_criteria["lab_code"] = lab_code - if purpose: - search_criteria["purpose"] = purpose - if purpose_subtype: - search_criteria["purpose_subtype"] = purpose_subtype - if category: - search_criteria["category"] = category - if sub_category: - search_criteria["sub_category"] = sub_category - if sub_category_2: - search_criteria["sub_category_2"] = sub_category_2 - if study_id: - if len(study_id) == 1 and study_id[0] == ".na": - study_id = "" - elif len(study_id) == 1 and study_id[0] == "": - study_id = None - search_criteria["study_id"] = study_id - if comments: - search_criteria["comments"] = comments - if creating_user: - if len(creating_user) == 1 and creating_user[0] == ".na": - creating_user = "" - elif len(creating_user) == 1 and creating_user[0] == "": - creating_user = None - search_criteria["creating_user"] = creating_user greedy = is_greedy == "yes" try: bfi = BloomFile(BLOOMdb3(app_username=request.session["user_data"]["email"])) + bobdb = BloomObj(BLOOMdb3(app_username=request.session["user_data"]["email"])) # Prepare additional filters for created_datetime and relevant_datetime query = bfi.session.query(bfi.Base.classes.generic_instance) @@ -2360,13 +2384,23 @@ async def search_files( user_data = request.session.get("user_data", {}) style = {"skin_css": user_data.get("style_css", "static/skins/bloom.css")} + fset_templates = bobdb.query_template_by_component_v2("file","file_set","generic","1.0") + + fset_template = fset_templates[0] + ui_form_properties_fset = fset_template.json_addl.get("ui_form_properties", []) + ui_form_fields_fset = generate_ui_form_fields(ui_form_properties_fset, fset_template.json_addl.get("controlled_properties", {}), bobject=bobdb) + ui_form_fields_query_fset = generate_ui_form_fields(ui_form_properties_fset, fset_template.json_addl.get("controlled_properties", {}), bobject=bobdb, form_type="query", super_type="file", btype="file_set", version=None) content = templates.get_template("search_results.html").render( request=request, columns=columns, table_data=table_data, + n_results=len(table_data), + s3_bucket_prefix=os.environ.get("BLOOM_DEWEY_S3_BUCKET_PREFIX", "NEEDS TO BE SET!")+"0", style=style, udat=user_data, + ui_fields_fset=ui_form_fields_fset, + ui_search_fields_fset=ui_form_fields_query_fset, ) return HTMLResponse(content=content) @@ -2406,9 +2440,9 @@ async def get_node_property(request: Request, euid: str, key: str): @app.post("/create_file_set") async def create_file_set( request: Request, - file_set_name: str = Form(...), - file_set_description: str = Form(...), - file_set_tag: str = Form(...), + name: str = Form(...), + description: str = Form(...), + tag: str = Form(...), comments: str = Form(None), file_euids: str = Form(...), ref_type: str = Form("na"), @@ -2432,13 +2466,14 @@ async def create_file_set( bfr = BloomFileReference(BLOOMdb3(app_username=request.session["user_data"]["email"])) file_set_metadata = { - "name": file_set_name, - "description": file_set_description, - "tag": file_set_tag, + "name": name, + "description": description, + "tag": tag, "comments": comments, "ref_type": ref_type, "duration": duration, - "rclone_config": rclone_config + "rclone_config": rclone_config, + "creating_user": request.session["user_data"]["email"], } # Create the file set @@ -2472,37 +2507,61 @@ async def create_file_set( except Exception as e: raise (e) + + +def create_search_criteria(form_data, fields): + search_criteria = {} + for field in fields: + field_value = form_data.get(field) + if field_value: + if isinstance(field_value, list): + if len(field_value) == 1 and field_value[0] in [".na"]: + field_value = "" + if len(field_value) == 1 and field_value[0] in [""]: + continue + search_criteria[field] = field_value + else: + if field_value in [".na"]: + field_value = "" + if field_value in [""]: + continue + search_criteria[field] = field_value + return search_criteria + # The following is very redundant to the file_search and probably should be refactored @app.post("/search_file_sets", response_class=HTMLResponse) async def search_file_sets( request: Request, - file_set_name: str = Form(None), - file_set_description: str = Form(None), - file_set_tag: str = Form(None), + name: str = Form(None), + description: str = Form(None), + tag: List[str] = Form(None), comments: str = Form(None), file_euids: str = Form(None), is_greedy: str = Form("yes"), + ref_type: List[str] = Form(None), + creating_user: List[str] = Form(None), ): - search_criteria = {} - if file_set_name: - search_criteria["name"] = file_set_name - if file_set_description: - search_criteria["description"] = file_set_description - if file_set_tag: - search_criteria["tag"] = file_set_tag - if comments: - search_criteria["comments"] = comments + form_data = { + "name": name, + "description": description, + "tag": tag, + "comments": comments, + "file_euids": file_euids, + "ref_type": ref_type, + "creating_user": creating_user, + } - q_ds = {"properties": search_criteria} + search_fields = ["name", "description", "tag", "comments", "ref_type", "creating_user"] + search_criteria = create_search_criteria(form_data, search_fields) greedy = is_greedy == "yes" try: bfs = BloomFileSet(BLOOMdb3(app_username=request.session["user_data"]["email"])) file_sets = bfs.search_objs_by_addl_metadata( - q_ds, greedy, "file_set", super_type="file" + {'properties':search_criteria}, greedy, "file_set", super_type="file" ) # Fetch details for each EUID @@ -2535,12 +2594,14 @@ async def search_file_sets( user_data = request.session.get("user_data", {}) style = {"skin_css": user_data.get("style_css", "static/skins/bloom.css")} + num_results = len(table_data) content = templates.get_template("file_set_search_results.html").render( request=request, table_data=table_data, columns=columns, style=style, udat=user_data, + num_results=num_results, ) return HTMLResponse(content=content) @@ -2695,7 +2756,7 @@ def generate_form_fields(template_data: Dict) -> List[FormField]: -def generate_ui_form_fields(ui_form_properties: List[Dict], controlled_properties: Dict, form_type: str = 'create', bobject=None) -> List[FormField]: +def generate_ui_form_fields(ui_form_properties: List[Dict], controlled_properties: Dict, form_type: str = 'create', bobject=None, super_type: str = None, btype: str = None, b_type: str = None, b_sub_type: str = None, version: str = None ) -> List[FormField]: form_fields = [] for prop in ui_form_properties: @@ -2705,21 +2766,35 @@ def generate_ui_form_fields(ui_form_properties: List[Dict], controlled_propertie value_type = prop.get("value_type", "string") if property_key in controlled_properties: - cp = controlled_properties[property_key] - if cp["type"] == "dependent string": - form_fields.append(FormField( - name=property_key, - type="select", - label=form_label, - options=[], - required=required - )) + if form_type == 'create': + cp = controlled_properties[property_key] + if cp["type"] == "dependent string": + form_fields.append(FormField( + name=property_key, + type="select", + label=form_label, + options=[], + required=required + )) + else: + form_fields.append(FormField( + name=property_key, + type="select", + label=form_label, + options=cp.get("enum", []), + required=required + )) else: + unique_values = sorted(bobject.get_unique_property_values(property_key, super_type=super_type, btype=btype, b_sub_type=b_sub_type, version=version)) + if '' not in unique_values: + unique_values.insert(0, '') + form_fields.append(FormField( name=property_key, type="select", label=form_label, - options=cp.get("enum", []), + options=unique_values, + multiple=True, required=required )) elif value_type == "uid-interactive": diff --git a/run_bloomui.sh b/run_bloomui.sh index d1e53df..5a49e8a 100644 --- a/run_bloomui.sh +++ b/run_bloomui.sh @@ -1,4 +1,4 @@ -#source this +#!/bin/bash # Check if $1 is null and set host accordingly if [ -z "$1" ]; then @@ -7,5 +7,27 @@ else host="$1" fi -# Run uvicorn with the specified host -uvicorn main:app --reload --log-level trace --port 8911 --timeout-keep-alive 303 --host $host +# Detect the number of CPU cores +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + num_cores=$(nproc) +elif [[ "$OSTYPE" == "darwin"* ]]; then + num_cores=$(sysctl -n hw.ncpu) +else + echo "Unsupported OS type: $OSTYPE" + exit 1 +fi + +# Calculate the number of workers (2 * number of cores) - 1 +num_workers=$(( (num_cores * 2) - 1 )) + +# Run Uvicorn for development or Gunicorn for production +if [ -z "$2" ]; then + echo "Running in dev mode with 1 worker on $host" + sleep 2 + uvicorn main:app --reload --log-level trace --port 8911 --timeout-keep-alive 303 --host $host +else + echo "RUNNING IN PRODUCTION MODE" + echo "Running with $num_workers workers on $host" + sleep 4 + gunicorn main:app -w $num_workers -k uvicorn.workers.UvicornWorker --log-level trace --timeout 303 --bind $host:8911 +fi diff --git a/setup.py b/setup.py index b6c494b..b6a35b9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="bloom_lims", - version="0.10.4", + version="0.10.5", packages=find_packages(), install_requires=[ # Add dependencies here, diff --git a/static/skins/bloom.css b/static/skins/bloom.css index a4339b4..78a8643 100644 --- a/static/skins/bloom.css +++ b/static/skins/bloom.css @@ -37,7 +37,7 @@ --property-button-color: #898989; --form-container-input-color: #ccc; --info-box-border-color: rgb(22, 191, 191); - --hilight-color: yellow; + --hilight-color: rgb(2, 130, 147); --a: rgb(109, 241, 241); --a-visited: rgb(193, 33, 221); --a-hover: rgb(246, 11, 254); /* Change to your preferred color */ @@ -50,4 +50,29 @@ --workflow-non-step-bg-color: #02849e; --accordion-prefix: lightgray; --disabled-action-btn: #acb6b7; +} + + +input[type="date" i] { + font-family: inherit; + padding-inline-start: 1px; + cursor: default; + padding: inherit; + border: inherit; + overflow: hidden; +} + +input[type="file" i] { + font-family: inherit; + appearance: none; + background-color: inherit; + cursor: default; + align-items: baseline; + color: inherit; + text-overflow: ellipsis; + text-align: start !important; + padding: initial; + border: inherit; + white-space: pre; + overflow: hidden !important; } \ No newline at end of file diff --git a/static/skins/fdx_a.css b/static/skins/fdx_a.css index 81b1748..bef3d32 100644 --- a/static/skins/fdx_a.css +++ b/static/skins/fdx_a.css @@ -32,7 +32,7 @@ --property-button-color: #ff3856; /* updated to the vivid red */ --form-container-input-color: #36525c; /* updated to the deep blue-gray */ --info-box-border-color: #3a47ff; /* updated to the strong blue */ - --hilight-color: yellow; /* unchanged */ + --hilight-color: rgb(182, 232, 234); /* unchanged */ --a: #3a47ff; /* updated to match the strong blue */ --a-visited: #8c96ee; /* updated to the soft violet */ --a-hover: #ff3856; /* updated to the vivid red */ @@ -46,3 +46,4 @@ --accordion-prefix: #8c96ee; /* updated to the soft violet */ --disabled-action-btn: #acb6b7; /* unchanged */ } + diff --git a/static/skins/json_editor.css b/static/skins/json_editor.css new file mode 100644 index 0000000..a137d5c --- /dev/null +++ b/static/skins/json_editor.css @@ -0,0 +1,80 @@ +/* Custom JSON Editor Styles */ +.jsoneditor { + background-color: var(--background-color); + color: var(--text-color); + font-family: var(--font-family); +} + +.jsoneditor .jsoneditor-tree { + background-color: var(--background-color); + color: var(--text-color); +} + +.jsoneditor .jsoneditor-tree .jsoneditor-field, +.jsoneditor .jsoneditor-tree .jsoneditor-value, +.jsoneditor .jsoneditor-tree .jsoneditor-readonly { + color: var(--text-color); +} + +.jsoneditor .jsoneditor-tree .jsoneditor-selected { + background-color: magenta; /* Highlight color changed to magenta */ +} + +.jsoneditor .jsoneditor-menu { + background-color: var(--panel-bg-color); + color: var(--text-color); +} + +.jsoneditor .jsoneditor-menu button { + background-color: var(--button-bg-color); + color: var(--text-color); + border: 1px solid var(--button-border-color); + border-radius: var(--border-radius); +} + +.jsoneditor .jsoneditor-menu button:hover { + background-color: var(--hilight-color); +} + +#jsoneditor { + width: 100%; + height: 500px; +} + +.jsoneditor-popover { + background-color: var(--panel-bg-color); + color: var(--text-color); +} + +.jsoneditor-schema-error { + color: var(--primary-color); +} + +div.jsoneditor td, +div.jsoneditor th, +div.jsoneditor textarea { + background-color: var(--background-color); + color: var(--text-color); + border: 1px solid var(--form-border-color); +} + +div.jsoneditor-field, +div.jsoneditor-value { + color: var(--text-color); +} + +pre.jsoneditor-preview { + background-color: var(--background-color); + color: var(--text-color); +} + +.jsoneditor-popover, .jsoneditor-schema-error, div.jsoneditor td, div.jsoneditor textarea, div.jsoneditor th, div.jsoneditor-field, div.jsoneditor-value, pre.jsoneditor-preview { + font-family: var(--font-family); + font-size: 10pt; + color: #1a1a1a; +} + +div.jsoneditor-field.jsoneditor-highlight, div.jsoneditor-field[contenteditable=true]:focus, div.jsoneditor-field[contenteditable=true]:hover, div.jsoneditor-value.jsoneditor-highlight, div.jsoneditor-value[contenteditable=true]:focus, div.jsoneditor-value[contenteditable=true]:hover { + background-color: var(--hilight-color); + +} \ No newline at end of file diff --git a/static/skins/vlight.css b/static/skins/vlight.css index 938209b..b56c0f4 100644 --- a/static/skins/vlight.css +++ b/static/skins/vlight.css @@ -32,7 +32,7 @@ --property-button-color: #898989; /* Medium grey */ --form-container-input-color: #cccccc; /* Soft grey */ --info-box-border-color: #b2d8d8; /* Soft teal */ - --hilight-color: #ffff99; /* Light yellow */ + --hilight-color: #f9de75; /* Light yellow */ --a: #6db3f2; /* Light blue */ --a-visited: #b4a7d6; /* Soft purple */ --a-hover: #f4cccc; /* Soft pink */ @@ -46,3 +46,4 @@ --accordion-prefix: #d0e0e3; /* Very light teal */ --disabled-action-btn: #d9d9d9; /* Light grey */ } + diff --git a/templates/create_file_set.html b/templates/create_file_set.html index 0cb2331..9080dd0 100644 --- a/templates/create_file_set.html +++ b/templates/create_file_set.html @@ -1,19 +1,40 @@
-{% else %} -

No object found for the provided EUID.

-{% endif %} - -
-
-
-
-
-SOFTdelete object - -- or -- - unSOFTdelete object + var editor, editorTemplate; + $(document).ready(function() { + var container = document.getElementById('json-editor'); + var options = {}; + editor = new JSONEditor(container, options); + editor.set({{ jaddl_prop|tojson }}); + + var containerTemplate = document.getElementById('json-editor-template'); + editorTemplate = new JSONEditor(containerTemplate, options); + editorTemplate.set({{ jaddl_controlled_prop|tojson }}); + }); + + + SOFTdelete object - -- or -- - unSOFTdelete object diff --git a/templates/file_search.html b/templates/file_search.html index 4a3df2f..257eb9c 100644 --- a/templates/file_search.html +++ b/templates/file_search.html @@ -2,8 +2,8 @@