129 lines
5.0 KiB
Python
129 lines
5.0 KiB
Python
import hashlib
|
|
from django.contrib import messages
|
|
from django.db.models import Q
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.utils import timezone
|
|
|
|
from .forms import ArticleFilterForm, OriginalArticleForm
|
|
from .models import Article, NewsSource, Topic
|
|
from .rss import sync_active_sources
|
|
|
|
|
|
def _published_articles():
|
|
return Article.objects.filter(is_published=True).select_related('topic', 'source')
|
|
|
|
|
|
def _build_filter_form(data=None):
|
|
return ArticleFilterForm(data or None)
|
|
|
|
|
|
def home(request):
|
|
sync_active_sources(limit=6)
|
|
filter_form = _build_filter_form(request.GET or None)
|
|
stories = _published_articles()
|
|
if filter_form.is_valid():
|
|
query = filter_form.cleaned_data.get('q')
|
|
topic_slug = filter_form.cleaned_data.get('topic')
|
|
if query:
|
|
stories = stories.filter(
|
|
Q(title__icontains=query)
|
|
| Q(excerpt__icontains=query)
|
|
| Q(content__icontains=query)
|
|
| Q(author_name__icontains=query)
|
|
)
|
|
if topic_slug:
|
|
stories = stories.filter(topic__slug=topic_slug)
|
|
|
|
featured_story = stories.filter(is_featured=True).first() or stories.first()
|
|
spotlight_stories = stories.exclude(pk=getattr(featured_story, 'pk', None))[:6]
|
|
original_story = _published_articles().filter(article_kind=Article.ArticleKind.ORIGINAL).first()
|
|
|
|
context = {
|
|
'page_title': 'Signal / Startup & Tech Newsroom',
|
|
'meta_description': 'A polished startup and tech news magazine with RSS aggregation, editorial curation, and a fast CMS workflow.',
|
|
'filter_form': filter_form,
|
|
'featured_story': featured_story,
|
|
'spotlight_stories': spotlight_stories,
|
|
'topics': Topic.objects.order_by('name'),
|
|
'sources': NewsSource.objects.filter(is_active=True).order_by('name')[:6],
|
|
'story_count': stories.count(),
|
|
'original_story': original_story,
|
|
'last_updated': _published_articles().first(),
|
|
}
|
|
return render(request, 'core/index.html', context)
|
|
|
|
|
|
def article_list(request):
|
|
sync_active_sources(limit=4)
|
|
filter_form = _build_filter_form(request.GET or None)
|
|
stories = _published_articles()
|
|
selected_topic = None
|
|
if filter_form.is_valid():
|
|
query = filter_form.cleaned_data.get('q')
|
|
topic_slug = filter_form.cleaned_data.get('topic')
|
|
if query:
|
|
stories = stories.filter(
|
|
Q(title__icontains=query)
|
|
| Q(excerpt__icontains=query)
|
|
| Q(content__icontains=query)
|
|
| Q(author_name__icontains=query)
|
|
)
|
|
if topic_slug:
|
|
stories = stories.filter(topic__slug=topic_slug)
|
|
selected_topic = Topic.objects.filter(slug=topic_slug).first()
|
|
|
|
context = {
|
|
'page_title': 'All Stories / Signal',
|
|
'meta_description': 'Browse curated startup and tech stories by topic, recency, and editorial origin.',
|
|
'filter_form': filter_form,
|
|
'stories': stories[:24],
|
|
'selected_topic': selected_topic,
|
|
'topics': Topic.objects.order_by('name'),
|
|
}
|
|
return render(request, 'core/article_list.html', context)
|
|
|
|
|
|
def article_detail(request, slug):
|
|
article = get_object_or_404(_published_articles(), slug=slug)
|
|
related_articles = _published_articles().filter(topic=article.topic).exclude(pk=article.pk)[:3]
|
|
context = {
|
|
'page_title': f'{article.title} / Signal',
|
|
'meta_description': article.excerpt or 'Read the latest curated startup and tech story on Signal.',
|
|
'article': article,
|
|
'related_articles': related_articles,
|
|
}
|
|
return render(request, 'core/article_detail.html', context)
|
|
|
|
|
|
def article_submit(request):
|
|
if request.method == 'POST':
|
|
form = OriginalArticleForm(request.POST)
|
|
if form.is_valid():
|
|
article = form.save(commit=False)
|
|
article.article_kind = Article.ArticleKind.ORIGINAL
|
|
article.is_published = True
|
|
article.published_at = timezone.now()
|
|
article.dedupe_key = hashlib.sha256(f"original|{timezone.now().isoformat()}|{article.title}".encode('utf-8')).hexdigest()
|
|
article.save()
|
|
messages.success(request, 'Your editorial story is live in the newsroom.')
|
|
return redirect('article_submit_success', slug=article.slug)
|
|
else:
|
|
form = OriginalArticleForm()
|
|
|
|
context = {
|
|
'page_title': 'Publish a Story / Signal CMS',
|
|
'meta_description': 'Draft an original startup or tech story and publish it into the Signal magazine flow.',
|
|
'form': form,
|
|
}
|
|
return render(request, 'core/article_form.html', context)
|
|
|
|
|
|
def article_submit_success(request, slug):
|
|
article = get_object_or_404(Article, slug=slug, article_kind=Article.ArticleKind.ORIGINAL)
|
|
context = {
|
|
'page_title': 'Story Published / Signal CMS',
|
|
'meta_description': 'Your original story has been published and added to the curated tech newsroom.',
|
|
'article': article,
|
|
}
|
|
return render(request, 'core/article_submit_success.html', context)
|