Compare commits

...
5 Commits
Author SHA1 Message Date
Tim Lorsbach d1dd6127f7 Enable init call for transition 2026-11-09 22:25:20 +01:00
Tim Lorsbach 493990566a adjusted bb4g for dev 2026-10-21 12:00:02 +02:00
Tim Lorsbach 7a6d8d7aec ... 2026-10-21 11:59:53 +02: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
12 changed files with 210 additions and 24 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',
),
]
+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"
],
},
},
+2
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
@@ -2495,6 +2496,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
+23 -10
View File
@@ -260,15 +260,31 @@
</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">
<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>
</div>
{% endfor %}
</div>
<div class="mt-6 text-right">
@@ -280,9 +296,6 @@
Read More Announcements
</a>
</div>
<!-- Discourse API integration -->
<script src="{% static 'js/discourse-api.js' %}"></script>
</div>
</section>