Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/jsolly/blogthedata into l…
Browse files Browse the repository at this point in the history
…imit-snippet-characters
  • Loading branch information
freedompraise committed Nov 11, 2023
2 parents 254e373 + 3b6b69f commit 4e53a4b
Show file tree
Hide file tree
Showing 21 changed files with 264 additions and 97 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

[![awesome-django-blog-tests](https://github.com/jsolly/awesome-django-blog/actions/workflows/django-test-deploy-master.yaml/badge.svg)](https://github.com/jsolly/awesome-django-blog/actions/workflows/django-test-deploy-master.yaml)
[![Coverage Status](https://coveralls.io/repos/github/jsolly/awesome-django-blog/badge.svg?branch=master&service=github)](https://coveralls.io/github/jsolly/awesome-django-blog?branch=master)
![CodeStyle](https://img.shields.io/badge/code%20style-black-000000.svg)
![Linting](https://img.shields.io/badge/linting-ruff-orange)
[![Python Version](https://img.shields.io/badge/python-3.10-brightgreen.svg)](https://www.python.org/downloads/)
[![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org)
![CodeStyle](https://img.shields.io/badge/ruff-orange?logo=ruff&label=code-style)
![Linting](https://img.shields.io/badge/ruff-orange?logo=ruff&label=linting)
![PythonVersion](https://img.shields.io/badge/3.11-yellow?logo=Python&logoColor=yellow&label=Python)
[![License](https://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org)

Awesome-django-blog is a fully functional blogging platform built using the Django web framework. It includes features such as creating and managing blog posts, comments, and categories, as well as user authentication and authorization.

Expand Down Expand Up @@ -44,6 +44,12 @@ cd awesome-django-blog/app
python3 manage.py runserver
```

### Seed Posts (Optional)
This command creates sample posts.
```shell
python3 app/manage.py import_posts utilities/seed_posts/posts.json
```


<!-- ## Installation (Docker)
Expand Down Expand Up @@ -83,7 +89,7 @@ ruff --config ./config/pyproject.toml app
### Formating

```shell
black --config ./config/pyproject.toml app
ruff format app
```

Also see the [actions tab](https://github.com/jsolly/awesome-django-blog/actions)
Expand Down Expand Up @@ -137,7 +143,7 @@ $ pre-commit install
- 100% linted with [ruff](https://pypi.org/project/ruff/) and PEP8 compliant for beautiful Python code.
- Static scans with [CodeQL](https://codeql.github.com/) and pip
dependency checks with [Dependabot](https://github.com/dependabot) for automated security and updates.
- Formatted with [Black](https://pypi.org/project/black/) for beauty and readability
- Formatted with [Ruff](https://github.com/astral-sh/ruff) for beauty and speed.
- Strict Content Security Policy preventing inline styles and scripts for better security
- Subresource Integrity for better security
- [A+ Score on Mozilla Observatory](<[url](https://observatory.mozilla.org/analyze/blogthedata.com)>)
Expand Down
10 changes: 8 additions & 2 deletions app/app/settings/base_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,21 @@
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True


STATIC_URL = "/static/"
MEDIA_URL = "/media/"

# Extra places for collectstatic to find static files.
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
}
# DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATICFILES_DIRS = [str(BASE_DIR / "staticfiles")]

Expand Down
3 changes: 3 additions & 0 deletions app/blog/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

class BlogConfig(AppConfig):
name = "blog"

def ready(self):
import blog.signals
Empty file added app/blog/management/__init__.py
Empty file.
Empty file.
30 changes: 30 additions & 0 deletions app/blog/management/commands/import_posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.core.management.base import BaseCommand
import json
from blog.models import Post, Category


class Command(BaseCommand):
help = "Load a list of posts from a JSON file into the Post model"

def add_arguments(self, parser):
parser.add_argument("json_file", type=str, help="The JSON file to load")

def handle(self, *args, **kwargs):
json_file = kwargs["json_file"]
with open(json_file, "r") as f:
posts_json = json.load(f)

for post_data in posts_json:
category = Category.objects.get(slug=post_data["category_slug"])

post = Post(
title=post_data["title"],
content=post_data["content"],
author_id=post_data["user_id"],
category=category,
)
post.save()

self.stdout.write(
self.style.SUCCESS(f"Successfully imported posts from {json_file}")
)
50 changes: 50 additions & 0 deletions app/blog/migrations/0042_add_simularity_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.2.5 on 2023-11-08 23:37

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("blog", "0041_disallow_null_values_date_updated"),
]

operations = [
migrations.CreateModel(
name="Similarity",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("score", models.FloatField()),
(
"post1",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="similarities1",
to="blog.post",
),
),
(
"post2",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="similarities2",
to="blog.post",
),
),
],
),
migrations.AddConstraint(
model_name="similarity",
constraint=models.UniqueConstraint(
fields=("post1", "post2"), name="unique_pair"
),
),
]
18 changes: 18 additions & 0 deletions app/blog/migrations/0043_update_simularities_on_existing_posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.db import migrations
from blog.utils import compute_similarity


def update_similarity(apps, schema_editor):
Post = apps.get_model("blog", "Post")
for post in Post.objects.all():
compute_similarity(post.id)


class Migration(migrations.Migration):
dependencies = [
("blog", "0042_add_simularity_model"),
]

operations = [
migrations.RunPython(update_similarity),
]
27 changes: 27 additions & 0 deletions app/blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ class Post(models.Model):

objects = PostManager() # Make sure objects only include active (not draft) posts.

def get_related_posts(self) -> models.QuerySet:
"""
Get the top 3 related posts based on the similarities
"""

return Post.objects.filter(
id__in=self.similarities1.order_by("-score").values_list(
"post2", flat=True
)[:3]
)

def __str__(self):
return self.title + " | " + str(self.author)

Expand All @@ -93,6 +104,22 @@ def save(self, *args, **kwargs):
super().save(*args, **kwargs)


class Similarity(models.Model):
post1 = models.ForeignKey(
Post, related_name="similarities1", on_delete=models.CASCADE
)
post2 = models.ForeignKey(
Post, related_name="similarities2", on_delete=models.CASCADE
)
score = models.FloatField()

# Ensure that the same pair of posts can't be added twice
class Meta:
constraints = [
models.UniqueConstraint(fields=["post1", "post2"], name="unique_pair")
]


class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
author = models.ForeignKey(User, on_delete=models.CASCADE)
Expand Down
9 changes: 9 additions & 0 deletions app/blog/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from .utils import compute_similarity
from .models import Post


@receiver(post_save, sender=Post)
def trigger_similarity_computation(sender, instance, **kwargs):
compute_similarity(instance.id)
7 changes: 2 additions & 5 deletions app/blog/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,7 @@ def get_queryset(self):
return Post.objects.filter(slug=self.kwargs["slug"])

def get_context_data(self, **kwargs):
category = self.object.category
related_posts = Post.objects.filter(category=category).exclude(
slug=self.object.slug
)[:3]
related_posts = self.object.get_related_posts()

context = super().get_context_data(**kwargs)
context["title"] = self.object.title
Expand Down Expand Up @@ -477,7 +474,7 @@ def generate_gpt_input_value(request):
def get_safe_completion(prompt, max_tokens):
completion = (
openai.Completion.create(
model="text-davinci-003",
model="gpt-3.5-turbo-instruct",
prompt=prompt,
max_tokens=max_tokens,
temperature=0.5,
Expand Down
68 changes: 43 additions & 25 deletions app/staticfiles/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ body {
padding: 0;
margin: 0;
line-height: 1.6;
font-size: 1.3rem;
}

body,
input,
.post-card,
.list-group-item {
background-color: var(--primary-color);
Expand Down Expand Up @@ -713,27 +713,13 @@ label[for="id_draft"] {
Typeography
============= */

:is(
h1,
h2,
h3,
h4,
h5,
h6,
strong,
.list-group-item,
svg,
table,
p,
blockquote,
li,
legend,
small,
.word-count,
label,

) {
color: var(--secondary-color);
code {
background-color: #f4f4f4; /* Light grey background */
border: 1px solid #ddd; /* Grey border */
border-radius: 4px; /* Rounded corners */
padding: 2px 4px; /* Top and bottom padding of 2px, left and right padding of 4px */
font-family: monospace;
font-size: 0.95em;
}

.category-title {
Expand Down Expand Up @@ -782,6 +768,28 @@ a.hidden-link {
:is(h1, h2, h3) {
line-height: 1.1;
}
:is(
h1,
h2,
h3,
h4,
h5,
h6,
strong,
.list-group-item,
svg,
table,
p,
blockquote,
li,
legend,
small,
.word-count,
label,

) {
color: var(--secondary-color);
}

hr {
border-color: #e0e0e0;
Expand Down Expand Up @@ -1184,14 +1192,24 @@ img {

@media (prefers-color-scheme: dark) {
:root {
--primary-color: rgb(18, 18, 18);
--secondary-color: rgb(240, 240, 240);
--secondary-color-rgb: 240, 240, 240;
--primary-color: rgb(24 24 26);
--secondary-color: rgb(161, 161, 170);
--secondary-color-rgb: 161, 161, 170;
--post-card-hover-background-color: hsl(0, 0%, 100%, 0.05);
--background-hover-color: hsl(0, 0%, 100%, 0.05);
--link-color: #6c9ff2;
}

code {
background-color: #2d2d2d;
color: #c5c8c6;
border: 1px solid #444;
border-radius: 4px; /* Rounded corners */
padding: 2px 4px; /* Top and bottom padding of 2px, left and right padding of 4px */
font-family: monospace;
font-size: 0.95em;
}

.github-icon svg {
fill: black;
}
Expand Down
4 changes: 2 additions & 2 deletions app/staticfiles/django_ckeditor_5/prism.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4e53a4b

Please sign in to comment.