CCCPaste
CUSTOM MANAGER

class AuthorManager(models.Manager):
    def get_authors_by_article_count(self):
        return self.annotate(
            num_articles=models.Count('articles')
        ).order_by(
            '-num_articles', 'email'
        )

QUERIES 1.1 Q

from main_app.models import Author, Article, Review
from django.db.models import Q


def get_authors(search_name=None, search_email=None):
    if search_name is None and search_email is None:
        return ""

    query_name = Q(full_name__icontains=search_name)
    query_email = Q(email__icontains=search_email)

    if search_name and search_email:
        query = query_name & query_email
    elif search_name:
        query = query_name
    else:
        query = query_email

    authors = Author.objects.filter(query).order_by('-full_name')

    if not authors:
        return ""

    result = []

    for author in authors:
        result.append(f"Author: {author.full_name}, email: {author.email}, status: {'Banned' if author.is_banned else 'Not Banned'}")

    return "\n".join(result)

QUERIES 1.2 AND 1.3

def get_top_publisher():
    top_publisher = Author.objects.annotate(
        num_articles=Count("articles")
    ).order_by(
        '-num_articles', 'email'
    ).first()

    if top_publisher is None or top_publisher.num_articles == 0:
        return ""

    return f"Top Author: {top_publisher.full_name} with {top_publisher.num_articles} published articles."


def get_top_reviewer():
    top_reviewer = Author.objects.annotate(
        num_reviews=Count('reviews')
    ).order_by(
        '-num_reviews', 'email'
    ).first()

    if top_reviewer is None or top_reviewer.num_reviews == 0:
        return ''

    return f"Top Reviewer: {top_reviewer.full_name} with {top_reviewer.num_reviews} published reviews."

QUERIES 2.1

def get_latest_article():
    latest_article = Article.objects.prefetch_related('authors', 'reviews').order_by('-published_on').first()

    if latest_article is None:
        return ''

    author_names = ', '.join(author.full_name for author in latest_article.authors.all().order_by('full_name'))
    num_reviews = latest_article.reviews.count()
    avg_rating = sum([r.rating for r in latest_article.reviews.all()]) / num_reviews if num_reviews else 0.0

    return (f"The latest article is: {latest_article.title}."
            f" Authors: {author_names}. Reviewed: {num_reviews} times."
            f" Average Rating: {avg_rating:.2f}.")

QUERIES 2.2

def get_top_rated_article():
    top_article = Article.objects.annotate(
        average_rating=Avg('reviews__rating') # СРЕДИЯ РЕЙТИНГ НА ВСЯКА ЕДНА СТАРИЯ
    ).order_by(
        '-average_rating', 'title' # СОРТИРАМЕ В НИЗХОДЯЩ РЕД ПО СРЕДЕН РЕЙТИНГ И ЗАГЛАВИЕ НА СТАТИЯТА
    ).first() # ВЗИМАМЕ ПЪРВАТА СТАТИЯ, Т.Е ТАЗИ С НАЙ-ВИСОК СРЕДЕН РЕЙТИНГ

    num_reviews = top_article.reviews.count()

    if top_article is None or num_reviews == 0:
        return ''
    avg_rating = top_article.average_rating

    return (f"The top-rated article is: {top_article.title},"
            f" with an average rating of {avg_rating:.2f},"
            f" reviewed {num_reviews} times.")

QUERIES 2.3

def ban_author(email=None):
    author_banned = Author.objects.prefetch_related('reviews').filter(email__exact=email).first() # използваме
    # prefetch_related защото по надолу ни трябва reviews

    if author_banned is None or email is None:
        return "No authors banned."

    num_reviews = author_banned.reviews.count()

    author_banned.is_banned = True
    author_banned.save()
    author_banned.reviews.all().delete()

    return f"Author: {author_banned.full_name} is banned! {num_reviews} reviews deleted."