diff --git a/epdb/models.py b/epdb/models.py index 88ed9855..a6d4cee1 100644 --- a/epdb/models.py +++ b/epdb/models.py @@ -944,7 +944,7 @@ class SimpleAmbitRule(SimpleRule): @property def as_svg(self): - return IndigoUtils.smirks_to_svg(self.smirks, True) + return IndigoUtils.smirks_to_svg(self.smirks, True, width=800, height=400) class SimpleRDKitRule(SimpleRule): @@ -1203,6 +1203,12 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin): for n in self.root_nodes: queue.append(n) + # Add unconnected nodes + for n in self.nodes: + if len(n.out_edges.all()) == 0: + if n not in queue: + queue.append(n) + while len(queue): current = queue.pop() processed.add(current) @@ -1341,12 +1347,12 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin): pw = Pathway() pw.package = package - if name is None: + if name is None or name.strip() == '': name = f"Pathway {Pathway.objects.filter(package=package).count() + 1}" pw.name = name - if description is not None: + if description is not None and description.strip() != '': pw.description = description pw.save() diff --git a/epdb/views.py b/epdb/views.py index ab9d4b7d..453eb397 100644 --- a/epdb/views.py +++ b/epdb/views.py @@ -4,6 +4,9 @@ from typing import List, Dict, Any from django.conf import settings as s from django.contrib.auth import get_user_model +from django.db.models import F, Value +from django.db.models.fields import CharField +from django.db.models.functions import Concat from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest from django.shortcuts import render, redirect from django.views.decorators.csrf import csrf_exempt @@ -436,8 +439,16 @@ def scenarios(request): if request.GET.get('all'): return JsonResponse({ "objects": [ - {"name": pw.name, "url": pw.url, "reviewed": True} - for pw in reviewed_scenario_qs + {"name": s.name, "url": s.full_url, "reviewed": True} + for s in reviewed_scenario_qs.annotate( + full_url=Concat( + Value(s.SERVER_URL + '/package/'), + F("package__uuid"), + Value("/scenario/"), + F("uuid"), + output_field=CharField(), + ) + ) ] }) @@ -517,6 +528,7 @@ def search(request): packages = PackageManager.get_reviewed_packages() search_result = SearchManager.search(packages, searchterm, mode) + return JsonResponse(search_result, safe=False) context = get_base_context(request) @@ -542,6 +554,7 @@ def search(request): packages = PackageManager.get_reviewed_packages() context['search_result'] = SearchManager.search(packages, searchterm, mode) + context['search_result']['searchterm'] = searchterm return render(request, 'search.html', context) @@ -706,7 +719,7 @@ def package_model(request, package_uuid, model_uuid): elif request.method == 'POST': if hidden := request.POST.get('hidden', None): - if hidden == 'delete-model': + if hidden == 'delete': current_model.delete() return redirect(current_package.url + '/model') else: @@ -757,7 +770,7 @@ def package(request, package_uuid): logger.debug(f"{k}\t{v}") if hidden := request.POST.get('hidden', None): - if hidden == 'delete-package': + if hidden == 'delete': logger.debug(current_package.delete()) return redirect(s.SERVER_URL + '/package') else: @@ -897,7 +910,7 @@ def package_compound(request, package_uuid, compound_uuid): elif request.method == 'POST': if hidden := request.POST.get('hidden', None): - if hidden == 'delete-compound': + if hidden == 'delete': current_compound.delete() return redirect(current_package.url + '/compound') else: @@ -940,6 +953,7 @@ def package_compound_structures(request, package_uuid, compound_uuid): context['meta']['current_package'] = current_package context['object_type'] = 'structure' + context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound, 'structure') reviewed_compound_structure_qs = CompoundStructure.objects.none() unreviewed_compound_structure_qs = CompoundStructure.objects.none() @@ -979,15 +993,38 @@ def package_compound_structure(request, package_uuid, compound_uuid, structure_u context['title'] = f'enviPath - {current_package.name} - {current_compound.name} - {current_structure.name}' context['meta']['current_package'] = current_package - context['object_type'] = 'compound' + context['object_type'] = 'structure' context['compound_structure'] = current_structure context['current_object'] = current_structure + context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound, 'structure', current_structure) return render(request, 'objects/compound_structure.html', context) elif request.method == 'POST': + if hidden := request.POST.get('hidden', None): + if hidden == 'delete': + # Check if we have to delete the compound as no structure is left + if len(current_structure.compound.structures.all()) == 1: + # This will delete the structure as well + current_compound.delete() + return redirect(current_package.url + '/compound') + else: + if current_structure.normalized_structure: + current_compound.delete() + return redirect(current_package.url + '/compound') + else: + if current_compound.default_structure == current_structure: + current_structure.delete() + current_compound.default_structure = current_compound.structures.all().first() + return redirect(current_compound.url + '/structure') + else: + current_structure.delete() + return redirect(current_compound.url + '/structure') + else: + return HttpResponseBadRequest() + selected_scenarios = request.POST.getlist('selected-scenarios') if selected_scenarios: @@ -1109,7 +1146,7 @@ def package_rule(request, package_uuid, rule_uuid): elif request.method == 'POST': if hidden := request.POST.get('hidden', None): - if hidden == 'delete-rule': + if hidden == 'delete': current_rule.delete() return redirect(current_package.url + '/rule') else: @@ -1212,7 +1249,7 @@ def package_reaction(request, package_uuid, reaction_uuid): elif request.method == 'POST': if hidden := request.POST.get('hidden', None): - if hidden == 'delete-reaction': + if hidden == 'delete': current_reaction.delete() return redirect(current_package.url + '/reaction') else: @@ -1281,8 +1318,8 @@ def package_pathways(request, package_uuid): log_post_params(request) - name = request.POST.get('name', 'Pathway ' + str(Pathway.objects.filter(package=current_package).count())) - description = request.POST.get('description', s.DEFAULT_VALUES['description']) + name = request.POST.get('name') + description = request.POST.get('description') pw_mode = request.POST.get('predict', 'predict') smiles = request.POST.get('smiles') @@ -1303,7 +1340,14 @@ def package_pathways(request, package_uuid): return error(request, "Pathway prediction failed!", f'Pathway prediction failed as received mode "{pw_mode}" is none of {modes}') + prediction_setting = request.POST.get('prediction-setting', None) + if prediction_setting: + prediction_setting = SettingManager.get_setting_by_url(current_user, prediction_setting) + else: + prediction_setting = current_user.prediction_settings() + pw = Pathway.create(current_package, stand_smiles, name=name, description=description) + # set mode pw.kv.update({'mode': pw_mode}) pw.save() @@ -1316,12 +1360,11 @@ def package_pathways(request, package_uuid): if pw_mode == 'incremental': limit = 1 - pred_setting = current_user.prediction_settings() - pw.setting = pred_setting + pw.setting = prediction_setting pw.save() from .tasks import predict - predict.delay(pw.pk, pred_setting.pk, limit=limit) + predict.delay(pw.pk, prediction_setting.pk, limit=limit) return redirect(pw.url) @@ -1371,7 +1414,7 @@ def package_pathway(request, package_uuid, pathway_uuid): elif request.method == 'POST': if hidden := request.POST.get('hidden', None): - if hidden == 'delete-pathway': + if hidden == 'delete': current_pathway.delete() return redirect(current_package.url + '/pathway') else: @@ -1514,7 +1557,7 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid): log_post_params(request) if hidden := request.POST.get('hidden', None): - if hidden == 'delete-node': + if hidden == 'delete': # pre_delete signal will take care of edge deletion current_node.delete() @@ -1616,7 +1659,7 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid): 'title'] = f'enviPath - {current_package.name} - {current_pathway.name} - {current_edge.edge_label.name}' context['meta']['current_package'] = current_package - context['object_type'] = 'reaction' + context['object_type'] = 'edge' context['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway, 'edge', current_edge) context['edge'] = current_edge context['current_object'] = current_edge @@ -1628,7 +1671,7 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid): log_post_params(request) if hidden := request.POST.get('hidden', None): - if hidden == 'delete-edge': + if hidden == 'delete': current_edge.delete() return redirect(current_pathway.url) @@ -1896,7 +1939,7 @@ def group(request, group_uuid): log_post_params(request) if hidden := request.POST.get('hidden', None): - if hidden == 'delete-group': + if hidden == 'delete': current_group.delete() return redirect(s.SERVER_URL + '/group') else: diff --git a/templates/actions/objects/compound.html b/templates/actions/objects/compound.html index 77cfb4cd..88ecb27c 100644 --- a/templates/actions/objects/compound.html +++ b/templates/actions/objects/compound.html @@ -12,7 +12,7 @@ Set Scenarios