Compare commits

..
Author SHA1 Message Date
Tim Lorsbach 2a8974b1b8 App Integration Secret Packages
API CI / api-tests (pull_request) Failing after 39s
CI / test (pull_request) Failing after 43s
2026-11-11 20:07:07 +01:00
Tim Lorsbach 20b3e73347 v1.21
API CI / api-tests (pull_request) Failing after 44s
CI / test (pull_request) Failing after 43s
2026-11-10 21:00:59 +01:00
Tim Lorsbach 8a1ed2fc6d Import Export cycle of probs 2026-11-10 19:57:57 +01:00
Tim Lorsbach a6b901aa47 merged pathways
API CI / api-tests (pull_request) Failing after 39s
CI / test (pull_request) Failing after 42s
2026-11-10 07:30:27 +01:00
Tim Lorsbach 423ddf16d1 Adjust data_pool resolution for app integrations
API CI / api-tests (pull_request) Failing after 34s
CI / test (pull_request) Failing after 36s
2026-11-09 23:16:01 +01:00
Tim Lorsbach a7b2e60660 fixed width for news
API CI / api-tests (pull_request) Failing after 40s
CI / test (pull_request) Failing after 36s
2026-11-09 22:52:15 +01:00
jebusandTim Lorsbach ae3a164e74 Feat/bb4g (#2)
CI / test (pull_request) Failing after 40s
API CI / api-tests (pull_request) Failing after 41s
Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: #2
2026-11-10 10:29:05 +13:00
Tim Lorsbach 2e2abf0287 ...
API CI / api-tests (pull_request) Failing after 32s
CI / test (pull_request) Failing after 33s
2026-10-21 11:59:11 +02:00
Tim Lorsbach 86c9ac887e Added news entries
API CI / api-tests (pull_request) Failing after 32s
CI / test (pull_request) Failing after 33s
2026-10-21 09:53:58 +02:00
20 changed files with 404 additions and 77 deletions
+6 -1
View File
@@ -3,7 +3,7 @@ from django.contrib import admin
# Register your models here.
from .models import (
PESCompound,
PESStructure
PESStructure, NewsEntry
)
@@ -15,5 +15,10 @@ class PESStructureAdmin(admin.ModelAdmin):
pass
class NewsEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(PESCompound, PESCompoundAdmin)
admin.site.register(PESStructure, PESStructureAdmin)
admin.site.register(NewsEntry, NewsEntryAdmin)
+25
View File
@@ -0,0 +1,25 @@
"""
Context processors for enviPy application.
Context processors automatically make variables available to all templates.
"""
from django.utils import timezone
from .models import NewsEntry
def news_entries_context(request):
"""
Provides package data for the search modal which is included globally
in framework_modern.html.
Returns:
dict: Context dictionary with reviewed and unreviewed packages
"""
qs = NewsEntry.objects.filter(publication_date__lte=timezone.now()).order_by('-publication_date')[:3]
return {
"news_entries": qs,
}
+29
View File
@@ -0,0 +1,29 @@
# Generated by Django 6.0.3 on 2026-10-16 07:50
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bayer', '0004_package_shareable'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='NewsEntry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
('url', models.TextField(null=True, unique=True, verbose_name='URL')),
('title', models.TextField(verbose_name='Title')),
('publication_date', models.DateTimeField(verbose_name='Date')),
('content', models.TextField(verbose_name='Content')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
]
@@ -0,0 +1,17 @@
# Generated by Django 6.0.3 on 2026-10-16 07:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('bayer', '0005_newsentry'),
]
operations = [
migrations.RemoveField(
model_name='newsentry',
name='url',
),
]
@@ -0,0 +1,19 @@
# Generated by Django 6.0.3 on 2026-11-09 21:43
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bayer', '0006_remove_newsentry_url'),
]
operations = [
migrations.AlterField(
model_name='newsentry',
name='publication_date',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date'),
),
]
+31 -10
View File
@@ -1,10 +1,12 @@
from typing import List
from uuid import uuid4
import urllib.parse
import nh3
from django.conf import settings as s
from django.db import models, transaction
from django.db.models import QuerySet
from django.urls import reverse
from django.utils import timezone
from epdb.models import (
EnviPathModel,
@@ -27,7 +29,7 @@ class Package(EnviPathModel):
class Classification(models.IntegerChoices):
INTERNAL = 0, "Internal"
RESTRICTED = 10 , "Restricted"
RESTRICTED = 10, "Restricted"
SECRET = 20, "Secret"
classification_level = models.IntegerField(
@@ -109,7 +111,7 @@ class PESCompound(Compound):
@staticmethod
@transaction.atomic
def create(
package: "Package", pes_data: dict, name: str = None, description: str = None, *args, **kwargs
package: "Package", pes_data: dict, name: str = None, description: str = None, *args, **kwargs
) -> "Compound":
pes_url = pes_data["pes_url"]
@@ -161,7 +163,6 @@ class PESCompound(Compound):
normalized_structure=True,
)
cs = PESStructure.create(
c,
pes_url,
@@ -218,13 +219,13 @@ class PESStructure(CompoundStructure):
@transaction.atomic
def add_structure(
self,
smiles: str,
name: str = None,
description: str = None,
default_structure: bool = False,
*args,
**kwargs,
self,
smiles: str,
name: str = None,
description: str = None,
default_structure: bool = False,
*args,
**kwargs,
) -> "CompoundStructure":
raise ValueError("Not supported!")
@@ -236,3 +237,23 @@ class PESStructure(CompoundStructure):
"image": f"{reverse('depict_pes')}?pesLink={urllib.parse.quote(self.pes_link)}",
"image_type": "png",
}
class NewsEntry(models.Model):
uuid = models.UUIDField(
null=False, blank=False, verbose_name="UUID of this object", unique=True, default=uuid4
)
title = models.TextField(verbose_name="Title")
publication_date = models.DateTimeField(verbose_name="Date", default=timezone.now)
content = models.TextField(verbose_name="Content")
author = models.ForeignKey("epdb.User", on_delete=models.SET_NULL, blank=True, null=True)
@property
def url(self):
return f"{s.SERVER_URL}/news/{self.uuid}"
def save(self, *args, **kwargs):
if not self.url:
self.url = self._url()
super().save(*args, **kwargs)
+47
View File
@@ -0,0 +1,47 @@
{% extends "framework_modern.html" %}
{% block content %}
<div class="mx-auto max-w-4xl px-4 py-8">
{# Header / Title row #}
<div class="mb-6 flex items-center justify-between">
<h1 class="text-2xl font-bold">{{ news_entry.title }}</h1>
</div>
{# Meta info card #}
<div class="card bg-base-100 shadow mb-6">
<div class="card-body">
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<span class="text-sm font-semibold text-base-content/60">Published</span>
<p class="mt-1">{{ news_entry.publication_date|date:"N j, Y, P" }}</p>
</div>
<div>
<span class="text-sm font-semibold text-base-content/60">Author</span>
<p class="mt-1">
{% if news_entry.author %}
{{ news_entry.author.get_full_name|default:news_entry.author.username }}
{% else %}
<span class="text-base-content/40 italic">Unknown</span>
{% endif %}
</p>
</div>
</div>
</div>
</div>
{# Content card #}
<div class="card bg-base-100 shadow">
<div class="card-body prose max-w-none">
{{ news_entry.content|safe }}
</div>
</div>
{# Back link #}
<div class="mt-6">
<a href="javascript:history.back()" class="btn btn-ghost btn-sm">
← Back
</a>
</div>
</div>
{% endblock content %}
+1
View File
@@ -16,4 +16,5 @@ urlpatterns = [
v.create_pes_node,
name="create pes node",
),
re_path(rf"^news/(?P<news_entry_uuid>{UUID})$", v.news_detail, name="news detail"),
]
+12 -3
View File
@@ -4,12 +4,12 @@ import logging
import requests
from django.conf import settings as s
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed
from django.shortcuts import redirect
from django.shortcuts import redirect, render
from bayer.models import PESCompound
from bayer.models import PESCompound, NewsEntry
from epdb.logic import PackageManager
from epdb.models import Pathway, Node, Group
from epdb.views import _anonymous_or_real, error
from epdb.views import _anonymous_or_real, get_base_context, error
from utilities.decorators import package_permission_required
Package = s.GET_PACKAGE_MODEL()
@@ -270,3 +270,12 @@ def visualize_pes(request):
if rep.get('type') == 'color':
image_data = base64.b64decode(rep.get('base64').replace("data:image/png;base64,", ""))
return HttpResponse(image_data, content_type="image/png")
def news_detail(request, news_entry_uuid):
current_user = _anonymous_or_real(request)
context = get_base_context(request, current_user)
context["entry"] = NewsEntry.objects.get(uuid=news_entry_uuid)
return render(request, "objects/news_entry.html", context)
+16
View File
@@ -29,6 +29,8 @@ class SamplingAlgorithm(enum.Enum):
class BB4GConfig(EnviPyModel):
sampling_algorithm: SamplingAlgorithm = SamplingAlgorithm.EXACT
cutoff: int = -5
model: str = "rat"
mode: str | None = None
class UI:
title = "BB4G Configuration"
@@ -44,6 +46,18 @@ class BB4GConfig(EnviPyModel):
order=2,
placeholder="If unset defaults to -5"
)
model = UIConfig(
widget=WidgetType.TEXT,
label="BB4G Model",
order=3,
placeholder="If unset defaults to rat"
)
mode = UIConfig(
widget=WidgetType.TEXT,
label="BB4G Mode",
order=4,
placeholder="If unset defaults to None"
)
# Once stable these will be exposed by enviPy-plugins lib
@@ -167,6 +181,8 @@ class BB4G(Classifier):
"smiles": smi,
"sampling_alg": self.config.sampling_algorithm.value,
"cutoff": self.config.cutoff,
"model": self.config.model,
"mode": self.config.mode,
}
retries = 0
+1
View File
@@ -114,6 +114,7 @@ TEMPLATES = [
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"epdb.context_processors.package_context",
"bayer.context_processors.news_entries_context"
],
},
},
+7
View File
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional
import jwt
import nh3
import requests
from django.db import transaction
from django.conf import settings as s
from django.contrib.auth import get_user_model
from django.core.cache import cache
@@ -2158,6 +2159,7 @@ class CreateEdge(Schema):
products: str | None = None # Node URIs comma sep
multistep: str | None = None
edgeReason: str | None = None
probability: float | None = None
@router.post(
@@ -2225,6 +2227,10 @@ def add_pathway_edge(request, package_uuid, pathway_uuid, e: Form[CreateEdge]):
multi_step=multi_step,
)
if e.probability:
new_e.kv["probability"] = e.probability
new_e.save()
# Update depths as sideeffect of above operation
pw.update_depths()
@@ -2495,6 +2501,7 @@ class RegisterApplicationInSchema(Schema):
@router.post("/integration", response={200: Any, 403: Error})
@transaction.atomic
def register_app(request, app: Form[RegisterApplicationInSchema]):
try:
user = request.user
+9 -2
View File
@@ -376,9 +376,12 @@ class PackageManager(object):
groups = GroupManager.get_groups(user)
# EDIT START
if package.classification_level == Package.Classification.SECRET:
if package.data_pool not in groups:
if user.principal_type == user.PrincipalType.APP:
if not user.app_details.secret_allowed or package.data_pool != user.app_details.data_pool:
return False
elif package.data_pool not in groups:
return False
# EDIT END
@@ -440,6 +443,10 @@ class PackageManager(object):
@staticmethod
def check_package_classification(user, pack: Package):
if pack.classification_level == Package.Classification.SECRET:
if user.principal_type == user.PrincipalType.APP:
if pack.data_pool == user.app_details.data_pool:
return pack
if pack.data_pool.user_member.filter(id=user.id).exists():
return pack
+102 -15
View File
@@ -2071,7 +2071,10 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
def empty_due_to_threshold(self):
return self.kv.get("empty_due_to_threshold", False)
def d3_json(self):
def merged_d3_json(self):
return self.d3_json(merged=True)
def d3_json(self, merged=False):
# Ideally it would be something like this but
# to reduce crossing in edges do a DFS
# nodes = [n.d3_json() for n in self.nodes]
@@ -2079,15 +2082,68 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
nodes = []
processed = set()
structure_to_combined_nodes = dict()
node_to_combined_node = dict()
queue = list()
for n in self.root_nodes:
queue.append(n)
# Add unconnected nodes
for n in self.nodes.order_by("url"):
if len(n.out_edges.all()) == 0:
if n not in queue:
queue.append(n)
class CombinedNode(object):
def __init__(self, representative: "Node"):
self.representative = representative
self.nodes = set([self.representative])
def ids(self):
return [n.id for n in self.nodes]
def d3_json(self):
d3_json = self.representative.d3_json()
compound = self.representative.default_node_label.compound
d3_json["name"] = compound.get_name()
d3_json["smiles"] = compound.default_structure.smiles
d3_json["node_label_id"] = compound.default_structure.url
d3_json["image"] = f"{compound.default_structure.url}?image=svg",
d3_json["image_svg"] = IndigoUtils.mol_to_svg(
compound.default_structure.molfile
if compound.default_structure.molfile is not None
and compound.default_structure.molfile.strip()
else compound.default_structure.smiles,
width=40,
height=40,
),
return d3_json
def __repr__(self):
return f"CombinedNode({self.nodes})"
if merged:
for n in self.nodes:
def_struct = n.default_node_label.compound.default_structure
if def_struct in structure_to_combined_nodes:
structure_to_combined_nodes[def_struct].nodes.add(n)
else:
cn = CombinedNode(n)
structure_to_combined_nodes[def_struct] = cn
# for n in self.nodes.order_by("url"):
# if len(n.out_edges.all()) == 0:
# if n not in queue:
# queue.append(n)
for cn in structure_to_combined_nodes.values():
for n in cn.nodes:
node_to_combined_node[n] = cn
queue = list(structure_to_combined_nodes.values())
else:
for n in self.nodes:
queue.append(n)
# Add unconnected nodes
for n in self.nodes.order_by("url"):
if len(n.out_edges.all()) == 0:
if n not in queue:
queue.append(n)
for i in queue:
processed.add(i)
@@ -2097,25 +2153,56 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
processed.add(current)
nodes.append(current.d3_json())
for e in self.edges.filter(start_nodes=current).order_by("url").distinct():
if merged:
q = Q(start_nodes__in=current.ids())
else:
q = Q(start_nodes=current)
for e in self.edges.filter(q).order_by("url").distinct():
for prod in e.end_nodes.all().order_by("url"):
if prod not in queue and prod not in processed:
queue.append(prod)
if merged:
def_struct = n.default_node_label.compound.default_structure
if def_struct in structure_to_combined_nodes:
structure_to_combined_nodes[def_struct].nodes.add(n)
else:
cn = CombinedNode(n)
structure_to_combined_nodes[def_struct] = cn
if cn not in queue and cn not in processed:
queue.append(cn)
else:
if prod not in queue and prod not in processed:
queue.append(prod)
# We shouldn't lose or make up nodes...
if len(nodes) != len(self.nodes):
logger.debug(
f"{self.get_name()}: Num Nodes {len(nodes)} vs. DB Nodes {len(self.nodes)}"
)
if not merged:
if len(nodes) != len(self.nodes):
logger.debug(
f"{self.get_name()}: Num Nodes {len(nodes)} vs. DB Nodes {len(self.nodes)}"
)
links = [e.d3_json() for e in self.edges]
# D3 links Nodes based on indices in nodes array
combined_node_depth_to_idx = dict()
node_url_to_idx = dict()
for i, n in enumerate(nodes):
n["id"] = i
node_url_to_idx[n["url"]] = i
# Add potential missing mappings
if merged:
cn_idx_mapping = dict()
for n in nodes:
for cn in processed:
for sub_n in cn.nodes:
if n["url"] == sub_n.url:
cn_idx_mapping[cn] = n["id"]
for n in processed:
for sub_n in n.nodes:
node_url_to_idx[sub_n.url] = cn_idx_mapping[n]
adjusted_links = []
for link in links:
# Check if we'll need pseudo nodes
+1
View File
@@ -2313,6 +2313,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
context["pathway"] = current_pathway
context["current_object"] = current_pathway
context["merged"] = request.GET.get("merged", False) == "true"
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
-1
View File
@@ -15,7 +15,6 @@ dependencies = [
"django-polymorphic>=4.1.0",
"enviformer",
"envipy-additional-information",
"envipy-ambit>=0.1.0",
"envipy-plugins",
"epam-indigo>=1.30.1",
"gunicorn>=23.0.0",
+33 -10
View File
@@ -260,17 +260,43 @@
</div>
<!-- Community News Section -->
<section class="bg-base-200 z-10 mx-8 py-16">
<section class="bg-base-200 z-10 mx-8 py-6">
<div class="mx-auto max-w-7xl px-4">
<h2 class="h2 mb-8 text-left font-bold">Community Updates</h2>
<h2 class="h2 mb-8 text-left font-bold">News</h2>
<div id="community-news-container" class="flex justify-center gap-4">
<!-- News cards will be populated here -->
<div id="loading" class="flex w-full justify-center">
<span class="loading loading-spinner loading-lg"></span>
</div>
<div id="services-container" class="flex justify-center gap-4">
{% for ne in news_entries %}
<div class="card-body flex flex-col h-full justify-between p-5 w-96"> <!-- Fixed width applied via w-96 -->
<div>
<div class="flex items-center gap-2 mb-2">
<div class="bg-primary/10 text-primary rounded-lg p-1.5">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg>
</div>
<h3 class="card-title text-base font-semibold tracking-tight">
{{ ne.title }} - {{ ne.publication_date|date }}
</h3>
</div>
<p class="text-sm text-base-content/60 leading-snug line-clamp-4">
{{ ne.content }}
</p>
</div>
<a href="{{ ne.url }}" class="btn btn-primary btn-sm w-full group-hover:btn-active mt-1">
Read Full
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
</a>
</div>
{% endfor %}
</div>
<div class="mt-6 text-right">
<a
href="https://community.envipath.org/c/announcements/10"
@@ -280,9 +306,6 @@
Read More Announcements
</a>
</div>
<!-- Discourse API integration -->
<script src="{% static 'js/discourse-api.js' %}"></script>
</div>
</section>
+46 -1
View File
@@ -227,6 +227,29 @@
Reaction Names
</a>
</li>
<li>
<a href="{% if merged %}{{ current_object.url }}{% else %}{{ current_object.url }}?merged=true{% endif %}" id="merged-toggle-button" class="cursor-pointer">
<svg
id="merged-icon"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-eye"
>
<path
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
/>
<circle cx="12" cy="12" r="3" />
</svg>
Merged View
</a>
</li>
{% if pathway.setting.model.app_domain %}
<li>
<a id="app-domain-toggle-button" class="cursor-pointer">
@@ -544,8 +567,11 @@
</div>
{% endif %}
</div>
{% if merged %}
{{ pathway.merged_d3_json|json_script:"pathway" }}
{% else %}
{{ pathway.d3_json|json_script:"pathway" }}
{% endif %}
<script>
// Global switch for compound names view
var compoundNamesViewEnabled = false;
@@ -557,6 +583,8 @@
var timeseriesViewEnabled = false;
// Predicted Property View
var predictedPropertyViewEnabled = false;
// Merged View
var mergedViewEnabled = new URLSearchParams(window.location.search).get("merged") !== null;
function goFullscreen(id) {
var element = document.getElementById(id);
@@ -702,6 +730,23 @@
});
}
const mergedViewBtn = document.getElementById(
"merged-toggle-button",
);
if (mergedViewBtn) {
const icon = document.getElementById("merged-icon");
if (mergedViewEnabled) {
// Change to eye-off icon
icon.innerHTML =
'<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" x2="22" y1="2" y2="22"/>';
} else {
// Change back to eye icon
icon.innerHTML =
'<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>';
}
};
// Timeseries toggle
const timeseriesBtn = document.getElementById("timeseries-toggle-button");
if (timeseriesBtn) {
+2
View File
@@ -214,6 +214,7 @@ class EdgeExportSchema(RefEdgeExportSchema):
start_nodes: List[RefNodeExportSchema]
end_nodes: List[RefNodeExportSchema]
scenarios: List[RefScenarioExportSchema]
kv: Optional[dict]
class PathwayExportSchema(RefPathwayExportSchema):
@@ -623,6 +624,7 @@ class PackageImporter:
new_e.description = e.description
new_e.aliases = e.aliases
new_e.edge_label = self._cache[e.edge_label.uuid]
new_e.kv = e.kv
new_e.save()
for sn in e.start_nodes:
Generated
-34
View File
@@ -802,7 +802,6 @@ dependencies = [
{ name = "django-polymorphic" },
{ name = "enviformer" },
{ name = "envipy-additional-information" },
{ name = "envipy-ambit" },
{ name = "envipy-plugins" },
{ name = "epam-indigo" },
{ name = "gunicorn" },
@@ -859,7 +858,6 @@ requires-dist = [
{ name = "emcee", marker = "extra == 'pepper-plugin'", specifier = ">=3.1.6" },
{ name = "enviformer", git = "ssh://git@git.envipath.com/enviPath/enviformer.git?rev=main" },
{ name = "envipy-additional-information", git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git?branch=develop" },
{ name = "envipy-ambit", git = "ssh://git@git.envipath.com/enviPath/enviPy-ambit.git" },
{ 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 = "gunicorn", specifier = ">=23.0.0" },
@@ -899,14 +897,6 @@ dependencies = [
{ name = "pydantic" },
]
[[package]]
name = "envipy-ambit"
version = "0.1.0"
source = { git = "ssh://git@git.envipath.com/enviPath/enviPy-ambit.git#0c424ba153105a54f758569a784d5871989338d8" }
dependencies = [
{ name = "jpype1" },
]
[[package]]
name = "envipy-plugins"
version = "0.1.0"
@@ -1190,30 +1180,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396, upload-time = "2025-08-27T12:15:45.188Z" },
]
[[package]]
name = "jpype1"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/49/6090a131d84b22c6aae13b1853092028b060fd17da1af87c0e42ad69d50f/jpype1-1.6.0.tar.gz", hash = "sha256:2d46b2a14f8f0e6f17d8aa22b4fc3a64b2790851ebf1409ad79a37c698fd6e9a", size = 1057888, upload-time = "2025-07-07T14:04:11.244Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/88/2156a27251492169f947850293d9870d49d784f1355fad7e920095636721/jpype1-1.6.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5454322887505097c46f41521becf31cda2303f54e35cb42e20f1a180055a558", size = 582187, upload-time = "2025-07-07T13:50:39.034Z" },
{ url = "https://files.pythonhosted.org/packages/6b/5d/51bde8695cc3eabcc465779f6dc23b149194bbcdcb8d221a9572422053f4/jpype1-1.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf646459023a0dd71b3cc3aeeb977225a93f94c97839bb4e6146977b166c7caf", size = 468341, upload-time = "2025-07-07T13:50:40.739Z" },
{ url = "https://files.pythonhosted.org/packages/7a/a1/50cdc2bf9e51c0828faabec6fad50d61aada5a57d0bc435f4db0c16eb2ca/jpype1-1.6.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:7a98b34bc68a117881382dffa9b02d5e1d0d94325f85d7f564c33cc9e9d2916b", size = 511724, upload-time = "2025-07-07T13:50:42.479Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a4/6038228e44cbcdb8f96ff2518cd6c577730f31fb62e9f46de7efdfabc2de/jpype1-1.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b56d7b662ce353c7f876e9ff3d4e775918348340795801e1f00c3b6241006264", size = 495897, upload-time = "2025-07-07T13:50:44.018Z" },
{ url = "https://files.pythonhosted.org/packages/8c/e4/af69d43779da0e49d37d7fd8a4a01bede03f6a3bab450eab1c10cf48e20e/jpype1-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf41134bc59c28de2721614ed00a34f76f69f2b525583a5c42fee5c7bae05d40", size = 355702, upload-time = "2025-07-07T13:50:45.368Z" },
{ url = "https://files.pythonhosted.org/packages/3f/1b/7167cfb702cfbcb17b0973150608f9cb92c3f409ebaaff655f7389617766/jpype1-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ce1da9ff01010a4529de194aecc053bfc8ab25440ea6b63579ef8c8d381da21d", size = 582384, upload-time = "2025-07-07T13:50:47.418Z" },
{ url = "https://files.pythonhosted.org/packages/c0/48/8ae425d8842bc76eb51052bbea5941dac230c8f6b58614ef2b7cb2da1a74/jpype1-1.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4a387430cd4f3f68761013dca7f71b79d8d64b47dfd56eedcbd44c894d98d7f2", size = 468237, upload-time = "2025-07-07T13:50:48.925Z" },
{ url = "https://files.pythonhosted.org/packages/ef/2f/dbdc15aa99404dda0315a9883add7422c9ac7acce28ce24b66c89052c2fb/jpype1-1.6.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:6c9d99f41a5d05a8a2b6aeda796f646904c1c2b68aed0cc9a3bbc165483d9e1e", size = 511905, upload-time = "2025-07-07T13:50:50.87Z" },
{ url = "https://files.pythonhosted.org/packages/00/0d/50fcdd6d3bb42a87cbda8b04fc72283a9c6d955ada0d011190ce31e0e027/jpype1-1.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:55ba7257988a69bee84f7cd1444131725d5447999149fdafdba25bf46e4b1a3f", size = 496001, upload-time = "2025-07-07T13:50:52.711Z" },
{ url = "https://files.pythonhosted.org/packages/4f/b8/25214a9ebecbd31447b685500dca5fe2ac070df38358d6823f591a8ce52e/jpype1-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:ef8dd72442d91cde7e5eefef24ab71323cdd4137283a59f79d265a6620f7d0ab", size = 355727, upload-time = "2025-07-07T13:51:00.402Z" },
{ url = "https://files.pythonhosted.org/packages/29/a0/b2347572998020de8ce3ee0a285a1b84b052b522e8d452ee9ad330570918/jpype1-1.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc0c1c8b989b9857f87648980a746084b323858b0450225eab3642bb674645d6", size = 471150, upload-time = "2025-07-07T13:50:54.254Z" },
{ url = "https://files.pythonhosted.org/packages/7c/62/5e0f64a185f37303d0dd07e7b7a927cd5237fdc3c8176add949ed8cafaa2/jpype1-1.6.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cbbba16381ccd23530ad4ef09f7b2b1b12bd7fd0f6ca9fe4c7b4e3da8fe4cf63", size = 514015, upload-time = "2025-07-07T13:50:56.299Z" },
{ url = "https://files.pythonhosted.org/packages/72/68/84050ed2679e2d2ee2030a60d3e5a59a29d0533cd03fd8d9891e36592b0f/jpype1-1.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1a3814e4f65d67e36bdb03b8851d5ece8d7a408aa3a24251ea0609bb8fba77dd", size = 497229, upload-time = "2025-07-07T13:50:57.842Z" },
]
[[package]]
name = "jsonref"
version = "1.1.0"