forked from enviPath/enviPy
feature/additional_information (#30)
Fixes #12 Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#30
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import User, Group, UserPackagePermission, GroupPackagePermission, Setting, SimpleAmbitRule
|
from .models import User, Group, UserPackagePermission, GroupPackagePermission, Setting, SimpleAmbitRule, Scenario
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(admin.ModelAdmin):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
@ -22,15 +22,19 @@ class GroupPackagePermissionAdmin(admin.ModelAdmin):
|
|||||||
class SettingAdmin(admin.ModelAdmin):
|
class SettingAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
|
||||||
admin.site.register(Group, GroupAdmin)
|
|
||||||
admin.site.register(UserPackagePermission, UserPackagePermissionAdmin)
|
|
||||||
admin.site.register(GroupPackagePermission, GroupPackagePermissionAdmin)
|
|
||||||
admin.site.register(Setting, SettingAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleAmbitRuleAdmin(admin.ModelAdmin):
|
class SimpleAmbitRuleAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(User, UserAdmin)
|
||||||
|
admin.site.register(Group, GroupAdmin)
|
||||||
|
admin.site.register(UserPackagePermission, UserPackagePermissionAdmin)
|
||||||
|
admin.site.register(GroupPackagePermission, GroupPackagePermissionAdmin)
|
||||||
|
admin.site.register(Setting, SettingAdmin)
|
||||||
admin.site.register(SimpleAmbitRule, SimpleAmbitRuleAdmin)
|
admin.site.register(SimpleAmbitRule, SimpleAmbitRuleAdmin)
|
||||||
|
admin.site.register(Scenario, ScenarioAdmin)
|
||||||
|
|||||||
376
epdb/logic.py
376
epdb/logic.py
@ -255,6 +255,382 @@ class PackageManager(object):
|
|||||||
else:
|
else:
|
||||||
_ = perm_cls.objects.update_or_create(defaults={'permission': new_perm}, **data)
|
_ = perm_cls.objects.update_or_create(defaults={'permission': new_perm}, **data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@transaction.atomic
|
||||||
|
def import_package(data: dict, owner: User, keep_ids=False):
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
from datetime import datetime
|
||||||
|
from collections import defaultdict
|
||||||
|
from .models import Package, Compound, CompoundStructure, SimpleRule, SimpleAmbitRule, SimpleRDKitRule, \
|
||||||
|
ParallelRule, SequentialRule, SequentialRuleOrdering, Reaction, Pathway, Node, Edge, Scenario
|
||||||
|
from envipy_additional_information import AdditionalInformationConverter
|
||||||
|
|
||||||
|
pack = Package()
|
||||||
|
pack.uuid = UUID(data['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
pack.name = '{} - {}'.format(data['name'], datetime.now().strftime('%Y-%m-%d %H:%M'))
|
||||||
|
pack.reviewed = True if data['reviewStatus'] == 'reviewed' else False
|
||||||
|
pack.description = data['description']
|
||||||
|
pack.save()
|
||||||
|
|
||||||
|
up = UserPackagePermission()
|
||||||
|
up.user = owner
|
||||||
|
up.package = pack
|
||||||
|
up.permission = up.ALL[0]
|
||||||
|
up.save()
|
||||||
|
|
||||||
|
# Stores old_id to new_id
|
||||||
|
mapping = {}
|
||||||
|
# Stores new_scen_id to old_parent_scen_id
|
||||||
|
parent_mapping = {}
|
||||||
|
# Mapping old scen_id to old_obj_id
|
||||||
|
scen_mapping = defaultdict(list)
|
||||||
|
|
||||||
|
# Store Scenarios
|
||||||
|
for scenario in data['scenarios']:
|
||||||
|
scen = Scenario()
|
||||||
|
scen.package = pack
|
||||||
|
scen.uuid = UUID(scenario['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
scen.name = scenario['name']
|
||||||
|
scen.description = scenario['description']
|
||||||
|
scen.scenario_type = scenario['type']
|
||||||
|
scen.scenario_date = scenario['date']
|
||||||
|
scen.additional_information = dict()
|
||||||
|
scen.save()
|
||||||
|
|
||||||
|
mapping[scenario['id']] = scen.uuid
|
||||||
|
|
||||||
|
new_add_inf = defaultdict(list)
|
||||||
|
# TODO Store AI...
|
||||||
|
for ex in scenario.get('additionalInformationCollection', {}).get('additionalInformation', []):
|
||||||
|
name = ex['name']
|
||||||
|
addinf_data = ex['data']
|
||||||
|
|
||||||
|
# park the parent scen id for now and link it later
|
||||||
|
if name == 'referringscenario':
|
||||||
|
parent_mapping[scen.uuid] = addinf_data
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Broken eP Data
|
||||||
|
if name == 'initialmasssediment' and addinf_data == 'missing data':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO Enzymes arent ready yet
|
||||||
|
if name == 'enzyme':
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = AdditionalInformationConverter.convert(name, addinf_data)
|
||||||
|
except:
|
||||||
|
logger.error(f"Failed to convert {name} with {addinf_data}")
|
||||||
|
|
||||||
|
new_add_inf[name].append(res.model_dump_json())
|
||||||
|
|
||||||
|
scen.additional_information = new_add_inf
|
||||||
|
scen.save()
|
||||||
|
|
||||||
|
print('Scenarios imported...')
|
||||||
|
|
||||||
|
# Store compounds and its structures
|
||||||
|
for compound in data['compounds']:
|
||||||
|
comp = Compound()
|
||||||
|
comp.package = pack
|
||||||
|
comp.uuid = UUID(compound['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
comp.name = compound['name']
|
||||||
|
comp.description = compound['description']
|
||||||
|
comp.aliases = compound['aliases']
|
||||||
|
comp.save()
|
||||||
|
|
||||||
|
mapping[compound['id']] = comp.uuid
|
||||||
|
|
||||||
|
for scen in compound['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(comp)
|
||||||
|
|
||||||
|
default_structure = None
|
||||||
|
|
||||||
|
for structure in compound['structures']:
|
||||||
|
struc = CompoundStructure()
|
||||||
|
# struc.object_url = Command.get_id(structure, keep_ids)
|
||||||
|
struc.compound = comp
|
||||||
|
struc.uuid = UUID(structure['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
struc.name = structure['name']
|
||||||
|
struc.description = structure['description']
|
||||||
|
struc.smiles = structure['smiles']
|
||||||
|
struc.save()
|
||||||
|
|
||||||
|
for scen in structure['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(struc)
|
||||||
|
|
||||||
|
mapping[structure['id']] = struc.uuid
|
||||||
|
|
||||||
|
if structure['id'] == compound['defaultStructure']['id']:
|
||||||
|
default_structure = struc
|
||||||
|
|
||||||
|
struc.save()
|
||||||
|
|
||||||
|
|
||||||
|
if default_structure is None:
|
||||||
|
raise ValueError('No default structure set')
|
||||||
|
|
||||||
|
comp.default_structure = default_structure
|
||||||
|
comp.save()
|
||||||
|
|
||||||
|
print('Compounds imported...')
|
||||||
|
|
||||||
|
# Store simple and parallel-rules
|
||||||
|
par_rules = []
|
||||||
|
seq_rules = []
|
||||||
|
|
||||||
|
for rule in data['rules']:
|
||||||
|
if rule['identifier'] == 'parallel-rule':
|
||||||
|
par_rules.append(rule)
|
||||||
|
continue
|
||||||
|
if rule['identifier'] == 'sequential-rule':
|
||||||
|
seq_rules.append(rule)
|
||||||
|
continue
|
||||||
|
r = SimpleAmbitRule()
|
||||||
|
r.uuid = UUID(rule['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
r.package = pack
|
||||||
|
r.name = rule['name']
|
||||||
|
r.description = rule['description']
|
||||||
|
r.smirks = rule['smirks']
|
||||||
|
r.reactant_filter_smarts = rule.get('reactantFilterSmarts', None)
|
||||||
|
r.product_filter_smarts = rule.get('productFilterSmarts', None)
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
mapping[rule['id']] = r.uuid
|
||||||
|
|
||||||
|
for scen in rule['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(r)
|
||||||
|
|
||||||
|
print("Par: ", len(par_rules))
|
||||||
|
print("Seq: ", len(seq_rules))
|
||||||
|
|
||||||
|
for par_rule in par_rules:
|
||||||
|
r = ParallelRule()
|
||||||
|
r.package = pack
|
||||||
|
r.uuid = UUID(par_rule['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
r.name = par_rule['name']
|
||||||
|
r.description = par_rule['description']
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
mapping[par_rule['id']] = r.uuid
|
||||||
|
|
||||||
|
for scen in par_rule['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(r)
|
||||||
|
|
||||||
|
for simple_rule in par_rule['simpleRules']:
|
||||||
|
if simple_rule['id'] in mapping:
|
||||||
|
r.simple_rules.add(SimpleRule.objects.get(uuid=mapping[simple_rule['id']]))
|
||||||
|
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
for seq_rule in seq_rules:
|
||||||
|
r = SequentialRule()
|
||||||
|
r.package = pack
|
||||||
|
r.uuid = UUID(seq_rule['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
r.name = seq_rule['name']
|
||||||
|
r.description = seq_rule['description']
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
mapping[seq_rule['id']] = r.uuid
|
||||||
|
|
||||||
|
for scen in seq_rule['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(r)
|
||||||
|
|
||||||
|
for i, simple_rule in enumerate(seq_rule['simpleRules']):
|
||||||
|
sro = SequentialRuleOrdering()
|
||||||
|
sro.simple_rule = simple_rule
|
||||||
|
sro.sequential_rule = r
|
||||||
|
sro.order_index = i
|
||||||
|
sro.save()
|
||||||
|
# r.simple_rules.add(SimpleRule.objects.get(uuid=mapping[simple_rule['id']]))
|
||||||
|
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
print('Rules imported...')
|
||||||
|
|
||||||
|
for reaction in data['reactions']:
|
||||||
|
r = Reaction()
|
||||||
|
r.package = pack
|
||||||
|
r.uuid = UUID(reaction['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
r.name = reaction['name']
|
||||||
|
r.description = reaction['description']
|
||||||
|
r.medlinereferences = reaction['medlinereferences'],
|
||||||
|
r.multi_step = True if reaction['multistep'] == 'true' else False
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
mapping[reaction['id']] = r.uuid
|
||||||
|
|
||||||
|
for scen in reaction['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(r)
|
||||||
|
|
||||||
|
for educt in reaction['educts']:
|
||||||
|
r.educts.add(CompoundStructure.objects.get(uuid=mapping[educt['id']]))
|
||||||
|
|
||||||
|
for product in reaction['products']:
|
||||||
|
r.products.add(CompoundStructure.objects.get(uuid=mapping[product['id']]))
|
||||||
|
|
||||||
|
if 'rules' in reaction:
|
||||||
|
for rule in reaction['rules']:
|
||||||
|
try:
|
||||||
|
r.rules.add(Rule.objects.get(uuid=mapping[rule['id']]))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Rule with id {rule['id']} not found!")
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
print('Reactions imported...')
|
||||||
|
|
||||||
|
for pathway in data['pathways']:
|
||||||
|
pw = Pathway()
|
||||||
|
pw.package = pack
|
||||||
|
pw.uuid = UUID(pathway['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
pw.name = pathway['name']
|
||||||
|
pw.description = pathway['description']
|
||||||
|
pw.save()
|
||||||
|
|
||||||
|
mapping[pathway['id']] = pw.uuid
|
||||||
|
for scen in pathway['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(pw)
|
||||||
|
|
||||||
|
out_nodes_mapping = defaultdict(set)
|
||||||
|
|
||||||
|
root_node = None
|
||||||
|
|
||||||
|
for node in pathway['nodes']:
|
||||||
|
n = Node()
|
||||||
|
n.uuid = UUID(node['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
n.name = node['name']
|
||||||
|
n.pathway = pw
|
||||||
|
n.depth = node['depth']
|
||||||
|
n.default_node_label = CompoundStructure.objects.get(uuid=mapping[node['defaultNodeLabel']['id']])
|
||||||
|
n.save()
|
||||||
|
|
||||||
|
mapping[node['id']] = n.uuid
|
||||||
|
|
||||||
|
for scen in node['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(n)
|
||||||
|
|
||||||
|
for node_label in node['nodeLabels']:
|
||||||
|
n.node_labels.add(CompoundStructure.objects.get(uuid=mapping[node_label['id']]))
|
||||||
|
|
||||||
|
n.save()
|
||||||
|
|
||||||
|
for out_edge in node['outEdges']:
|
||||||
|
out_nodes_mapping[n.uuid].add(out_edge)
|
||||||
|
|
||||||
|
for edge in pathway['edges']:
|
||||||
|
e = Edge()
|
||||||
|
e.uuid = UUID(edge['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||||
|
e.name = edge['name']
|
||||||
|
e.pathway = pw
|
||||||
|
e.description = edge['description']
|
||||||
|
e.edge_label = Reaction.objects.get(uuid=mapping[edge['edgeLabel']['id']])
|
||||||
|
e.save()
|
||||||
|
|
||||||
|
mapping[edge['id']] = e.uuid
|
||||||
|
|
||||||
|
for scen in edge['scenarios']:
|
||||||
|
scen_mapping[scen['id']].append(e)
|
||||||
|
|
||||||
|
for start_node in edge['startNodes']:
|
||||||
|
e.start_nodes.add(Node.objects.get(uuid=mapping[start_node]))
|
||||||
|
|
||||||
|
for end_node in edge['endNodes']:
|
||||||
|
e.end_nodes.add(Node.objects.get(uuid=mapping[end_node]))
|
||||||
|
|
||||||
|
e.save()
|
||||||
|
|
||||||
|
for k, v in out_nodes_mapping.items():
|
||||||
|
n = Node.objects.get(uuid=k)
|
||||||
|
for v1 in v:
|
||||||
|
n.out_edges.add(Edge.objects.get(uuid=mapping[v1]))
|
||||||
|
n.save()
|
||||||
|
|
||||||
|
print('Pathways imported...')
|
||||||
|
|
||||||
|
# Linking Phase
|
||||||
|
for child, parent in parent_mapping.items():
|
||||||
|
child_obj = Scenario.objects.get(uuid=child)
|
||||||
|
parent_obj = Scenario.objects.get(uuid=mapping[parent])
|
||||||
|
child_obj.parent = parent_obj
|
||||||
|
child_obj.save()
|
||||||
|
|
||||||
|
for scen_id, objects in scen_mapping.items():
|
||||||
|
scen = Scenario.objects.get(uuid=mapping[scen_id])
|
||||||
|
for o in objects:
|
||||||
|
o.scenarios.add(scen)
|
||||||
|
o.save()
|
||||||
|
|
||||||
|
print("Scenarios linked...")
|
||||||
|
|
||||||
|
print('Import statistics:')
|
||||||
|
print('Package {} stored'.format(pack.url))
|
||||||
|
print('Imported {} compounds'.format(Compound.objects.filter(package=pack).count()))
|
||||||
|
print('Imported {} rules'.format(Rule.objects.filter(package=pack).count()))
|
||||||
|
print('Imported {} reactions'.format(Reaction.objects.filter(package=pack).count()))
|
||||||
|
print('Imported {} pathways'.format(Pathway.objects.filter(package=pack).count()))
|
||||||
|
print('Imported {} Scenarios'.format(Scenario.objects.filter(package=pack).count()))
|
||||||
|
|
||||||
|
print("Fixing Node depths...")
|
||||||
|
total_pws = Pathway.objects.filter(package=pack).count()
|
||||||
|
for p, pw in enumerate(Pathway.objects.filter(package=pack)):
|
||||||
|
print(pw.url)
|
||||||
|
in_count = defaultdict(lambda: 0)
|
||||||
|
out_count = defaultdict(lambda: 0)
|
||||||
|
|
||||||
|
for e in pw.edges:
|
||||||
|
# TODO check if this will remain
|
||||||
|
for react in e.start_nodes.all():
|
||||||
|
out_count[str(react.uuid)] += 1
|
||||||
|
|
||||||
|
for prod in e.end_nodes.all():
|
||||||
|
in_count[str(prod.uuid)] += 1
|
||||||
|
|
||||||
|
root_nodes = []
|
||||||
|
for n in pw.nodes:
|
||||||
|
num_parents = in_count[str(n.uuid)]
|
||||||
|
if num_parents == 0:
|
||||||
|
# must be a root node or unconnected node
|
||||||
|
if n.depth != 0:
|
||||||
|
n.depth = 0
|
||||||
|
n.save()
|
||||||
|
|
||||||
|
# Only root node may have children
|
||||||
|
if out_count[str(n.uuid)] > 0:
|
||||||
|
root_nodes.append(n)
|
||||||
|
|
||||||
|
levels = [root_nodes]
|
||||||
|
seen = set()
|
||||||
|
# Do a bfs to determine depths starting with level 0 a.k.a. root nodes
|
||||||
|
for i, level_nodes in enumerate(levels):
|
||||||
|
new_level = []
|
||||||
|
for n in level_nodes:
|
||||||
|
for e in n.out_edges.all():
|
||||||
|
for prod in e.end_nodes.all():
|
||||||
|
if str(prod.uuid) not in seen:
|
||||||
|
old_depth = prod.depth
|
||||||
|
if old_depth != i + 1:
|
||||||
|
print(f'updating depth from {old_depth} to {i + 1}')
|
||||||
|
prod.depth = i + 1
|
||||||
|
prod.save()
|
||||||
|
|
||||||
|
new_level.append(prod)
|
||||||
|
|
||||||
|
seen.add(str(n.uuid))
|
||||||
|
|
||||||
|
if new_level:
|
||||||
|
levels.append(new_level)
|
||||||
|
|
||||||
|
print(f'{p + 1}/{total_pws} fixed.')
|
||||||
|
|
||||||
|
return pack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SettingManager(object):
|
class SettingManager(object):
|
||||||
setting_pattern = re.compile(r".*/setting/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
setting_pattern = re.compile(r".*/setting/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
|
||||||
from datetime import datetime
|
|
||||||
from uuid import UUID
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from django.conf import settings as s
|
from django.conf import settings as s
|
||||||
from epdb.models import *
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
||||||
|
from epdb.models import UserSettingPermission, MLRelativeReasoning, EnviFormer, Permission, User
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@ -52,283 +52,7 @@ class Command(BaseCommand):
|
|||||||
return anon, admin, g, jebus
|
return anon, admin, g, jebus
|
||||||
|
|
||||||
def import_package(self, data, owner):
|
def import_package(self, data, owner):
|
||||||
# Start import
|
return PackageManager.import_package(data, owner, keep_ids=True)
|
||||||
pack = Package()
|
|
||||||
pack.uuid = UUID(data['id'].split('/')[-1])
|
|
||||||
pack.name = '{} - {}'.format(data['name'], datetime.now().strftime('%Y-%m-%d %H:%M'))
|
|
||||||
pack.reviewed = True if data['reviewStatus'] == 'reviewed' else False
|
|
||||||
pack.description = data['description']
|
|
||||||
pack.save()
|
|
||||||
|
|
||||||
up = UserPackagePermission()
|
|
||||||
up.user = owner
|
|
||||||
up.package = pack
|
|
||||||
up.permission = up.ALL[0]
|
|
||||||
up.save()
|
|
||||||
|
|
||||||
# Stores old_id to new_id
|
|
||||||
mapping = {}
|
|
||||||
|
|
||||||
# Store compounds and its structures
|
|
||||||
for compound in data['compounds']:
|
|
||||||
comp = Compound()
|
|
||||||
comp.package = pack
|
|
||||||
comp.uuid = UUID(compound['id'].split('/')[-1])
|
|
||||||
comp.name = compound['name']
|
|
||||||
comp.description = compound['description']
|
|
||||||
comp.aliases = compound['aliases']
|
|
||||||
comp.save()
|
|
||||||
|
|
||||||
mapping[compound['id']] = comp.uuid
|
|
||||||
|
|
||||||
default_structure = None
|
|
||||||
|
|
||||||
for structure in compound['structures']:
|
|
||||||
struc = CompoundStructure()
|
|
||||||
# struc.object_url = Command.get_id(structure, keep_ids)
|
|
||||||
struc.compound = comp
|
|
||||||
struc.uuid = UUID(structure['id'].split('/')[-1])
|
|
||||||
struc.name = structure['name']
|
|
||||||
struc.description = structure['description']
|
|
||||||
struc.smiles = structure['smiles']
|
|
||||||
struc.save()
|
|
||||||
mapping[structure['id']] = struc.uuid
|
|
||||||
|
|
||||||
if structure['id'] == compound['defaultStructure']['id']:
|
|
||||||
default_structure = struc
|
|
||||||
|
|
||||||
struc.save()
|
|
||||||
|
|
||||||
if default_structure is None:
|
|
||||||
raise ValueError('No default structure set')
|
|
||||||
|
|
||||||
comp.default_structure = default_structure
|
|
||||||
comp.save()
|
|
||||||
|
|
||||||
print('Compounds imported...')
|
|
||||||
|
|
||||||
# Store simple and parallel-rules
|
|
||||||
par_rules = []
|
|
||||||
seq_rules = []
|
|
||||||
|
|
||||||
for rule in data['rules']:
|
|
||||||
if rule['identifier'] == 'parallel-rule':
|
|
||||||
par_rules.append(rule)
|
|
||||||
continue
|
|
||||||
if rule['identifier'] == 'sequential-rule':
|
|
||||||
seq_rules.append(rule)
|
|
||||||
continue
|
|
||||||
r = SimpleAmbitRule()
|
|
||||||
r.uuid = UUID(rule['id'].split('/')[-1])
|
|
||||||
r.package = pack
|
|
||||||
r.name = rule['name']
|
|
||||||
r.description = rule['description']
|
|
||||||
r.smirks = rule['smirks']
|
|
||||||
r.reactant_filter_smarts = rule.get('reactantFilterSmarts', None)
|
|
||||||
r.product_filter_smarts = rule.get('productFilterSmarts', None)
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
mapping[rule['id']] = r.uuid
|
|
||||||
|
|
||||||
print("Par: ", len(par_rules))
|
|
||||||
print("Seq: ", len(seq_rules))
|
|
||||||
|
|
||||||
for par_rule in par_rules:
|
|
||||||
r = ParallelRule()
|
|
||||||
r.package = pack
|
|
||||||
r.uuid = UUID(par_rule['id'].split('/')[-1])
|
|
||||||
r.name = par_rule['name']
|
|
||||||
r.description = par_rule['description']
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
mapping[par_rule['id']] = r.uuid
|
|
||||||
|
|
||||||
for simple_rule in par_rule['simpleRules']:
|
|
||||||
if simple_rule['id'] in mapping:
|
|
||||||
r.simple_rules.add(SimpleRule.objects.get(uuid=mapping[simple_rule['id']]))
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
for seq_rule in seq_rules:
|
|
||||||
r = SequentialRule()
|
|
||||||
r.package = pack
|
|
||||||
r.uuid = UUID(seq_rule['id'].split('/')[-1])
|
|
||||||
r.name = seq_rule['name']
|
|
||||||
r.description = seq_rule['description']
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
mapping[seq_rule['id']] = r.uuid
|
|
||||||
|
|
||||||
# m1 = Membership(
|
|
||||||
# ... person=ringo,
|
|
||||||
# ... group=beatles,
|
|
||||||
# ... date_joined=date(1962, 8, 16),
|
|
||||||
# ... invite_reason="Needed a new drummer.",
|
|
||||||
# ... )
|
|
||||||
# >>> m1.save()
|
|
||||||
|
|
||||||
for i, simple_rule in enumerate(seq_rule['simpleRules']):
|
|
||||||
sro = SequentialRuleOrdering()
|
|
||||||
sro.simple_rule = simple_rule
|
|
||||||
sro.sequential_rule = r
|
|
||||||
sro.order_index = i
|
|
||||||
sro.save()
|
|
||||||
# r.simple_rules.add(SimpleRule.objects.get(uuid=mapping[simple_rule['id']]))
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
print('Rules imported...')
|
|
||||||
|
|
||||||
for reaction in data['reactions']:
|
|
||||||
r = Reaction()
|
|
||||||
r.package = pack
|
|
||||||
r.uuid = UUID(reaction['id'].split('/')[-1])
|
|
||||||
r.name = reaction['name']
|
|
||||||
r.description = reaction['description']
|
|
||||||
r.medlinereferences = reaction['medlinereferences'],
|
|
||||||
r.multi_step = True if reaction['multistep'] == 'true' else False
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
mapping[reaction['id']] = r.uuid
|
|
||||||
|
|
||||||
for educt in reaction['educts']:
|
|
||||||
r.educts.add(CompoundStructure.objects.get(uuid=mapping[educt['id']]))
|
|
||||||
|
|
||||||
for product in reaction['products']:
|
|
||||||
r.products.add(CompoundStructure.objects.get(uuid=mapping[product['id']]))
|
|
||||||
|
|
||||||
if 'rules' in reaction:
|
|
||||||
for rule in reaction['rules']:
|
|
||||||
try:
|
|
||||||
r.rules.add(Rule.objects.get(uuid=mapping[rule['id']]))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Rule with id {rule['id']} not found!")
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
print('Reactions imported...')
|
|
||||||
|
|
||||||
for pathway in data['pathways']:
|
|
||||||
pw = Pathway()
|
|
||||||
pw.package = pack
|
|
||||||
pw.uuid = UUID(pathway['id'].split('/')[-1])
|
|
||||||
pw.name = pathway['name']
|
|
||||||
pw.description = pathway['description']
|
|
||||||
pw.save()
|
|
||||||
|
|
||||||
mapping[pathway['id']] = pw.uuid
|
|
||||||
|
|
||||||
out_nodes_mapping = defaultdict(set)
|
|
||||||
|
|
||||||
root_node = None
|
|
||||||
|
|
||||||
for node in pathway['nodes']:
|
|
||||||
n = Node()
|
|
||||||
n.uuid = UUID(node['id'].split('/')[-1])
|
|
||||||
n.name = node['name']
|
|
||||||
n.pathway = pw
|
|
||||||
n.depth = node['depth']
|
|
||||||
n.default_node_label = CompoundStructure.objects.get(uuid=mapping[node['defaultNodeLabel']['id']])
|
|
||||||
n.save()
|
|
||||||
|
|
||||||
mapping[node['id']] = n.uuid
|
|
||||||
|
|
||||||
for node_label in node['nodeLabels']:
|
|
||||||
n.node_labels.add(CompoundStructure.objects.get(uuid=mapping[node_label['id']]))
|
|
||||||
|
|
||||||
n.save()
|
|
||||||
|
|
||||||
for out_edge in node['outEdges']:
|
|
||||||
out_nodes_mapping[n.uuid].add(out_edge)
|
|
||||||
|
|
||||||
for edge in pathway['edges']:
|
|
||||||
e = Edge()
|
|
||||||
e.uuid = UUID(edge['id'].split('/')[-1])
|
|
||||||
e.name = edge['name']
|
|
||||||
e.pathway = pw
|
|
||||||
e.description = edge['description']
|
|
||||||
e.edge_label = Reaction.objects.get(uuid=mapping[edge['edgeLabel']['id']])
|
|
||||||
e.save()
|
|
||||||
|
|
||||||
mapping[edge['id']] = e.uuid
|
|
||||||
|
|
||||||
for start_node in edge['startNodes']:
|
|
||||||
e.start_nodes.add(Node.objects.get(uuid=mapping[start_node]))
|
|
||||||
|
|
||||||
for end_node in edge['endNodes']:
|
|
||||||
e.end_nodes.add(Node.objects.get(uuid=mapping[end_node]))
|
|
||||||
|
|
||||||
e.save()
|
|
||||||
|
|
||||||
for k, v in out_nodes_mapping.items():
|
|
||||||
n = Node.objects.get(uuid=k)
|
|
||||||
for v1 in v:
|
|
||||||
n.out_edges.add(Edge.objects.get(uuid=mapping[v1]))
|
|
||||||
n.save()
|
|
||||||
|
|
||||||
print('Pathways imported...')
|
|
||||||
|
|
||||||
print('Import statistics:')
|
|
||||||
print('Package {} stored'.format(pack.url))
|
|
||||||
print('Imported {} compounds'.format(Compound.objects.filter(package=pack).count()))
|
|
||||||
print('Imported {} rules'.format(Rule.objects.filter(package=pack).count()))
|
|
||||||
print('Imported {} reactions'.format(Reaction.objects.filter(package=pack).count()))
|
|
||||||
print('Imported {} pathways'.format(Pathway.objects.filter(package=pack).count()))
|
|
||||||
|
|
||||||
print("Fixing Node depths...")
|
|
||||||
total_pws = Pathway.objects.filter(package=pack).count()
|
|
||||||
for p, pw in enumerate(Pathway.objects.filter(package=pack)):
|
|
||||||
print(pw.url)
|
|
||||||
in_count = defaultdict(lambda: 0)
|
|
||||||
out_count = defaultdict(lambda: 0)
|
|
||||||
|
|
||||||
for e in pw.edges:
|
|
||||||
# TODO check if this will remain
|
|
||||||
for react in e.start_nodes.all():
|
|
||||||
out_count[str(react.uuid)] += 1
|
|
||||||
|
|
||||||
for prod in e.end_nodes.all():
|
|
||||||
in_count[str(prod.uuid)] += 1
|
|
||||||
|
|
||||||
root_nodes = []
|
|
||||||
for n in pw.nodes:
|
|
||||||
num_parents = in_count[str(n.uuid)]
|
|
||||||
if num_parents == 0:
|
|
||||||
# must be a root node or unconnected node
|
|
||||||
if n.depth != 0:
|
|
||||||
n.depth = 0
|
|
||||||
n.save()
|
|
||||||
|
|
||||||
# Only root node may have children
|
|
||||||
if out_count[str(n.uuid)] > 0:
|
|
||||||
root_nodes.append(n)
|
|
||||||
|
|
||||||
levels = [root_nodes]
|
|
||||||
seen = set()
|
|
||||||
# Do a bfs to determine depths starting with level 0 a.k.a. root nodes
|
|
||||||
for i, level_nodes in enumerate(levels):
|
|
||||||
new_level = []
|
|
||||||
for n in level_nodes:
|
|
||||||
for e in n.out_edges.all():
|
|
||||||
for prod in e.end_nodes.all():
|
|
||||||
if str(prod.uuid) not in seen:
|
|
||||||
old_depth = prod.depth
|
|
||||||
if old_depth != i + 1:
|
|
||||||
print(f'updating depth from {old_depth} to {i + 1}')
|
|
||||||
prod.depth = i + 1
|
|
||||||
prod.save()
|
|
||||||
|
|
||||||
new_level.append(prod)
|
|
||||||
|
|
||||||
seen.add(str(n.uuid))
|
|
||||||
|
|
||||||
if new_level:
|
|
||||||
levels.append(new_level)
|
|
||||||
|
|
||||||
print(f'{p + 1}/{total_pws} fixed.')
|
|
||||||
|
|
||||||
return pack
|
|
||||||
|
|
||||||
def create_default_setting(self, owner, packages):
|
def create_default_setting(self, owner, packages):
|
||||||
s = SettingManager.create_setting(
|
s = SettingManager.create_setting(
|
||||||
@ -344,8 +68,6 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
# Create users
|
# Create users
|
||||||
@ -356,12 +78,13 @@ class Command(BaseCommand):
|
|||||||
'EAWAG-BBD.json',
|
'EAWAG-BBD.json',
|
||||||
'EAWAG-SOIL.json',
|
'EAWAG-SOIL.json',
|
||||||
'EAWAG-SLUDGE.json',
|
'EAWAG-SLUDGE.json',
|
||||||
|
'EAWAG-SEDIMENT.json',
|
||||||
]
|
]
|
||||||
|
|
||||||
mapping = {}
|
mapping = {}
|
||||||
for p in packages:
|
for p in packages:
|
||||||
print(f"Importing {p}...")
|
print(f"Importing {p}...")
|
||||||
package_data = json.loads(open(s.BASE_DIR / 'fixtures' / p).read())
|
package_data = json.loads(open(s.BASE_DIR / 'fixtures' / 'packages' / '2025-07-18' / p).read())
|
||||||
imported_package = self.import_package(package_data, admin)
|
imported_package = self.import_package(package_data, admin)
|
||||||
mapping[p.replace('.json', '')] = imported_package
|
mapping[p.replace('.json', '')] = imported_package
|
||||||
|
|
||||||
@ -378,7 +101,8 @@ class Command(BaseCommand):
|
|||||||
usp.save()
|
usp.save()
|
||||||
|
|
||||||
# Create Model Package
|
# Create Model Package
|
||||||
pack = PackageManager.create_package(admin, "Public Prediction Models", "Package to make Prediction Models publicly available")
|
pack = PackageManager.create_package(admin, "Public Prediction Models",
|
||||||
|
"Package to make Prediction Models publicly available")
|
||||||
pack.reviewed = True
|
pack.reviewed = True
|
||||||
pack.save()
|
pack.save()
|
||||||
|
|
||||||
|
|||||||
27
epdb/management/commands/import_package.py
Normal file
27
epdb/management/commands/import_package.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from epdb.logic import PackageManager
|
||||||
|
from epdb.models import *
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--data',
|
||||||
|
type=str,
|
||||||
|
help='Path of the Package to import.',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--owner',
|
||||||
|
type=str,
|
||||||
|
help='Username of the desired Owner.',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
owner = User.objects.get(username=options['owner'])
|
||||||
|
package_data = json.load(open(options['data']))
|
||||||
|
PackageManager.import_package(package_data, owner)
|
||||||
@ -468,10 +468,10 @@ class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
|
|
||||||
# I think this only affects Django Admin which we are barely using
|
# I think this only affects Django Admin which we are barely using
|
||||||
# # https://github.com/django-polymorphic/django-polymorphic/issues/229
|
# # https://github.com/django-polymorphic/django-polymorphic/issues/229
|
||||||
_non_polymorphic = models.Manager()
|
# _non_polymorphic = models.Manager()
|
||||||
|
#
|
||||||
class Meta:
|
# class Meta:
|
||||||
base_manager_name = '_non_polymorphic'
|
# base_manager_name = '_non_polymorphic'
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def apply(self, *args, **kwargs):
|
def apply(self, *args, **kwargs):
|
||||||
@ -1431,14 +1431,13 @@ class PluginModel(EPModel):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# # # TODO fully implement AdditionalInformation
|
|
||||||
# # # TODO consider Scenario, BaseScenario, RelatedScenario
|
|
||||||
class Scenario(EnviPathModel):
|
class Scenario(EnviPathModel):
|
||||||
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
||||||
type = models.CharField(max_length=256, null=False, blank=False, default='No date')
|
scenario_date = models.CharField(max_length=256, null=False, blank=False, default='No date')
|
||||||
type = models.CharField(max_length=256, null=False, blank=False, default='Not specified')
|
scenario_type = models.CharField(max_length=256, null=False, blank=False, default='Not specified')
|
||||||
|
|
||||||
|
# for Referring Scenarios this property will be filled
|
||||||
|
parent = models.ForeignKey('self', on_delete=models.CASCADE, default=None, null=True)
|
||||||
|
|
||||||
additional_information = models.JSONField(verbose_name='Additional Information')
|
additional_information = models.JSONField(verbose_name='Additional Information')
|
||||||
|
|
||||||
@ -1470,47 +1469,15 @@ class Scenario(EnviPathModel):
|
|||||||
def set_additional_information(self, data):
|
def set_additional_information(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
example = {
|
def get_additional_information(self):
|
||||||
"additionalInformationCollection": {
|
from envipy_additional_information import NAME_MAPPING
|
||||||
"additionalInformation": [
|
|
||||||
{
|
|
||||||
"addInfoName": "referringscenario",
|
|
||||||
"creationDate": "2017-12-15 11:46:07.993",
|
|
||||||
"data": "http://localhost:8080/package/5882df9c-dae1-4d80-a40e-db4724271456/scenario/11482bc1-8a0c-44a0-ae8b-5a02ae732559",
|
|
||||||
"id": "http://localhost:8080/package/5882df9c-dae1-4d80-a40e-db4724271456/infocollection/0f30d0ca-b2bd-4c85-a425-ed8b22d4fed6/referringscenario/41532eac-e04a-4474-937a-df1344c3dce7",
|
|
||||||
"identifier": "referringscenario",
|
|
||||||
"lastModified": "2017-12-15 11:46:07.993",
|
|
||||||
"name": "referringscenario"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"addInfoName": "halflife",
|
|
||||||
"creationDate": "2017-12-15 11:46:07.934",
|
|
||||||
"data": "First Order;;reported,no further information about the model;3690.0 - 3690.0;McCorquodale, G. & Wardrope, L. (2006)",
|
|
||||||
"id": "http://localhost:8080/package/5882df9c-dae1-4d80-a40e-db4724271456/infocollection/0f30d0ca-b2bd-4c85-a425-ed8b22d4fed6/halflife/8f44fdd9-f453-4ab1-8509-2ee5826faad7",
|
|
||||||
"identifier": "halflife",
|
|
||||||
"lastModified": "2020-05-05 17:26:14.753",
|
|
||||||
"name": "halflife"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"creationDate": "2017-12-15 11:46:07.608",
|
|
||||||
"id": "http://localhost:8080/package/5882df9c-dae1-4d80-a40e-db4724271456/infocollection/0f30d0ca-b2bd-4c85-a425-ed8b22d4fed6",
|
|
||||||
"identifier": "infocollection",
|
|
||||||
"lastModified": "2020-05-05 17:26:15.496",
|
|
||||||
"name": "no name"
|
|
||||||
},
|
|
||||||
"aliases": [],
|
|
||||||
"creationDate": "2017-12-15 11:46:08.221",
|
|
||||||
"date": "no date",
|
|
||||||
"description": "no description",
|
|
||||||
"id": "http://localhost:8080/package/5882df9c-dae1-4d80-a40e-db4724271456/scenario/e7089e49-e07d-4a2d-8045-e144b7eb5a5e",
|
|
||||||
"identifier": "scenario",
|
|
||||||
"lastModified": "2020-05-05 17:26:15.065",
|
|
||||||
"name": "McCorquodale, G. & Wardrope, L. (2006) - (00002) (Related Scenario) - (00000)",
|
|
||||||
"reviewStatus": "reviewed",
|
|
||||||
"scenarios": [],
|
|
||||||
"type": "Not specified"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for k, vals in self.additional_information.items():
|
||||||
|
if k == 'enzyme':
|
||||||
|
continue
|
||||||
|
|
||||||
|
for v in vals:
|
||||||
|
yield NAME_MAPPING[k](**json.loads(v))
|
||||||
|
|
||||||
class UserSettingPermission(Permission):
|
class UserSettingPermission(Permission):
|
||||||
uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', primary_key=True,
|
uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', primary_key=True,
|
||||||
|
|||||||
@ -1141,7 +1141,24 @@ def package_scenarios(request, package_uuid):
|
|||||||
|
|
||||||
# https://envipath.org/package/<id>/scenario/<id>
|
# https://envipath.org/package/<id>/scenario/<id>
|
||||||
def package_scenario(request, package_uuid, scenario_uuid):
|
def package_scenario(request, package_uuid, scenario_uuid):
|
||||||
pass
|
current_user = _anonymous_or_real(request)
|
||||||
|
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||||
|
current_scenario = Scenario.objects.get(package=current_package, uuid=scenario_uuid)
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
context = get_base_context(request)
|
||||||
|
context['title'] = f'enviPath - {current_package.name} - {current_scenario.name}'
|
||||||
|
|
||||||
|
context['meta']['current_package'] = current_package
|
||||||
|
context['object_type'] = 'scenario'
|
||||||
|
context['breadcrumbs'] = breadcrumbs(current_package, 'scenario', current_scenario)
|
||||||
|
|
||||||
|
context['scenario'] = current_scenario
|
||||||
|
|
||||||
|
return render(request, 'objects/scenario.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### END UNTESTED
|
### END UNTESTED
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ dependencies = [
|
|||||||
"django-ninja>=1.4.1",
|
"django-ninja>=1.4.1",
|
||||||
"django-polymorphic>=4.1.0",
|
"django-polymorphic>=4.1.0",
|
||||||
"enviformer",
|
"enviformer",
|
||||||
|
"envipy-additional-information",
|
||||||
"envipy-plugins",
|
"envipy-plugins",
|
||||||
"epam-indigo>=1.30.1",
|
"epam-indigo>=1.30.1",
|
||||||
"gunicorn>=23.0.0",
|
"gunicorn>=23.0.0",
|
||||||
@ -28,3 +29,4 @@ dependencies = [
|
|||||||
[tool.uv.sources]
|
[tool.uv.sources]
|
||||||
enviformer = { git = "ssh://git@git.envipath.com/enviPath/enviformer.git", rev = "v0.1.0" }
|
enviformer = { git = "ssh://git@git.envipath.com/enviPath/enviformer.git", rev = "v0.1.0" }
|
||||||
envipy-plugins = { git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git", rev = "v0.1.0" }
|
envipy-plugins = { git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git", rev = "v0.1.0" }
|
||||||
|
envipy-additional-information = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git" }
|
||||||
|
|||||||
0
templates/actions/objects/scenario.html
Normal file
0
templates/actions/objects/scenario.html
Normal file
12
templates/errors/user_account_inactive.html
Normal file
12
templates/errors/user_account_inactive.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% extends "framework.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="alert alert-error" role="alert">
|
||||||
|
<h4 class="alert-heading">Your account has not been activated yet!</h4>
|
||||||
|
<p>Your account has not been activated yet. If you have question <a href="mailto:admin@envipath.org">contact
|
||||||
|
us.</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
51
templates/objects/scenario.html
Normal file
51
templates/objects/scenario.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{% extends "framework.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% block action_modals %}
|
||||||
|
|
||||||
|
{% endblock action_modals %}
|
||||||
|
<div class="panel-group" id="scenario-detail">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
|
{{ scenario.name }}
|
||||||
|
<div id="actionsButton"
|
||||||
|
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
||||||
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
style="padding-right:1em"></span></a>
|
||||||
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
|
{% block actions %}
|
||||||
|
{% include "actions/objects/scenario.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="scenario-table" class="table table-bordered table-striped table-hover">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Property</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th>Unit</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for ai in scenario.get_additional_information %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ ai.property_name|safe }} </td>
|
||||||
|
<td> {{ ai.property_data|safe }} </td>
|
||||||
|
<td> {{ ai.property_unit|safe }} </td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
10
uv.lock
generated
10
uv.lock
generated
@ -403,6 +403,7 @@ dependencies = [
|
|||||||
{ name = "django-ninja" },
|
{ name = "django-ninja" },
|
||||||
{ name = "django-polymorphic" },
|
{ name = "django-polymorphic" },
|
||||||
{ name = "enviformer" },
|
{ name = "enviformer" },
|
||||||
|
{ name = "envipy-additional-information" },
|
||||||
{ name = "envipy-plugins" },
|
{ name = "envipy-plugins" },
|
||||||
{ name = "epam-indigo" },
|
{ name = "epam-indigo" },
|
||||||
{ name = "gunicorn" },
|
{ name = "gunicorn" },
|
||||||
@ -425,6 +426,7 @@ requires-dist = [
|
|||||||
{ name = "django-ninja", specifier = ">=1.4.1" },
|
{ name = "django-ninja", specifier = ">=1.4.1" },
|
||||||
{ name = "django-polymorphic", specifier = ">=4.1.0" },
|
{ name = "django-polymorphic", specifier = ">=4.1.0" },
|
||||||
{ name = "enviformer", git = "ssh://git@git.envipath.com/enviPath/enviformer.git?rev=v0.1.0" },
|
{ name = "enviformer", git = "ssh://git@git.envipath.com/enviPath/enviformer.git?rev=v0.1.0" },
|
||||||
|
{ name = "envipy-additional-information", git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git" },
|
||||||
{ name = "envipy-plugins", git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git?rev=v0.1.0" },
|
{ name = "envipy-plugins", git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git?rev=v0.1.0" },
|
||||||
{ name = "epam-indigo", specifier = ">=1.30.1" },
|
{ name = "epam-indigo", specifier = ">=1.30.1" },
|
||||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||||
@ -438,6 +440,14 @@ requires-dist = [
|
|||||||
{ name = "setuptools", specifier = ">=80.8.0" },
|
{ name = "setuptools", specifier = ">=80.8.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "envipy-additional-information"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git#4804b24b3479bed6108a49e4401bff8947c03cbd" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pydantic" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "envipy-plugins"
|
name = "envipy-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user