diff --git a/bayer/admin.py b/bayer/admin.py index f0b257bb..9279fe7a 100644 --- a/bayer/admin.py +++ b/bayer/admin.py @@ -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) diff --git a/bayer/context_processors.py b/bayer/context_processors.py new file mode 100644 index 00000000..530751b4 --- /dev/null +++ b/bayer/context_processors.py @@ -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, + } diff --git a/bayer/migrations/0005_newsentry.py b/bayer/migrations/0005_newsentry.py new file mode 100644 index 00000000..f5e4f777 --- /dev/null +++ b/bayer/migrations/0005_newsentry.py @@ -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)), + ], + ), + ] diff --git a/bayer/migrations/0006_remove_newsentry_url.py b/bayer/migrations/0006_remove_newsentry_url.py new file mode 100644 index 00000000..209b2b96 --- /dev/null +++ b/bayer/migrations/0006_remove_newsentry_url.py @@ -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', + ), + ] diff --git a/bayer/models.py b/bayer/models.py index 0e0d5b48..4d3b8c09 100644 --- a/bayer/models.py +++ b/bayer/models.py @@ -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) diff --git a/bayer/templates/objects/news_entry.html b/bayer/templates/objects/news_entry.html new file mode 100644 index 00000000..f838aff5 --- /dev/null +++ b/bayer/templates/objects/news_entry.html @@ -0,0 +1,47 @@ + +{% extends "framework_modern.html" %} + +{% block content %} +
{{ news_entry.publication_date|date:"N j, Y, P" }}
++ {% if news_entry.author %} + {{ news_entry.author.get_full_name|default:news_entry.author.username }} + {% else %} + Unknown + {% endif %} +
++ {{ ne.content }} +
+