diff --git a/.gitignore b/.gitignore index 97daa84..4f59cf1 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ venv/ env/ ENV/ .env/ +.env # Testing .coverage diff --git a/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md b/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md index 6ce3679..90d8797 100644 --- a/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md +++ b/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md @@ -122,7 +122,7 @@ I’m finally focusing on what matters: **writing**.  -My new ugly website. Source: [https://frodigo.com/Garage](https://frodigo.com/Garage) +My new ugly website. Source: [https://frodigo.com](https://frodigo.com) So now I’m writing — and I’ve made a deal with myself: no replatforming for the next year. diff --git a/Blog/2025/Why teaching kids how to program is not easy.md b/Blog/2025/Why teaching kids how to program is not easy.md index 03088cd..eee63d8 100644 --- a/Blog/2025/Why teaching kids how to program is not easy.md +++ b/Blog/2025/Why teaching kids how to program is not easy.md @@ -116,7 +116,5 @@ Teaching programming to kids requires luck, considerable strength, and abundant And may your child become an even better programmer than you! -If you are interested in learning how to teach kids programming, please check out my learning roadmap: [https://frodigo.com/Garage/Programming+for+Kids](https://frodigo.com/Garage/Programming+for+Kids) - --- *Published 07/04/2025* #blog #ProgrammingFundamentals #Python #BestPractices #ConceptExplanation #CaseStudy #Beginner diff --git a/Now/History/1. 25-03-2025.md b/Now/History/1. 25-03-2025.md index 3c6c98b..e452cd9 100644 --- a/Now/History/1. 25-03-2025.md +++ b/Now/History/1. 25-03-2025.md @@ -10,7 +10,6 @@ My son has repeatedly asked me what I do in my work, so I showed him some code a Connected resources: -- [Programming for Kids](Programming%20for%20Kids.md) - [Why teaching kids how to program is not easy](Why%20teaching%20kids%20how%20to%20program%20is%20not%20easy.md) --- diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/.env.example b/Projects/Testtrack/M1 - Django Learning/mysite/.env.example new file mode 100644 index 0000000..42b9fcf --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/.env.example @@ -0,0 +1,3 @@ +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +DEFAULT_FROM_EMAIL= diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore b/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore @@ -0,0 +1 @@ +.env diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py index a6157cd..095f9e4 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Post +from .models import Post, Comment @admin.register(Post) @@ -12,3 +12,10 @@ class PostAdmin(admin.ModelAdmin): date_hierarchy = 'published_at' ordering = ('status', 'published_at') show_facets = admin.ShowFacets.ALWAYS + + +@admin.register(Comment) +class CommentAdmin(admin.ModelAdmin): + list_display = ('name', 'email', 'post', 'created_at', 'active') + list_filter = ('active', 'created_at', 'updated_at') + search_fields = ('name', 'email', 'body') diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py new file mode 100644 index 0000000..ba0971f --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py @@ -0,0 +1,23 @@ +import markdown +from django.contrib.syndication.views import Feed +from django.template.defaultfilters import truncatewords_html +from django.urls import reverse_lazy +from .models import Post + + +class LatestPostsFeed(Feed): + title = "My Blog" + link = reverse_lazy('blog:post_list') + description = "Updates on the latest blog posts." + + def items(self): + return Post.published.all()[:5] + + def item_title(self, item): + return item.title + + def item_description(self, item): + return truncatewords_html(markdown.markdown(item.body), 30) + + def item_pubdate(self, item): + return item.published_at diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py new file mode 100644 index 0000000..b98117e --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py @@ -0,0 +1,18 @@ +from django import forms +from .models import Comment + + +class EmailPostForm(forms.Form): + name = forms.CharField(max_length=25) + email = forms.EmailField() + to = forms.EmailField() + comments = forms.CharField( + required=False, + widget=forms.Textarea + ) + + +class CommentForm(forms.ModelForm): + class Meta: + model = Comment + fields = ('name', 'email', 'body') diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py new file mode 100644 index 0000000..2f3c474 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-10-06 05:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='post', + name='slug', + field=models.SlugField( + max_length=250, unique_for_date='published_at'), + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py new file mode 100644 index 0000000..ab10940 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2025-10-06 13:05 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0002_alter_post_slug'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ('email', models.EmailField(max_length=254)), + ('body', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('active', models.BooleanField(default=True)), + ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='comments', to='blog.post')), + ], + options={ + 'ordering': ('created_at',), + 'indexes': [models.Index(fields=['created_at'], name='blog_commen_created_4e025c_idx')], + }, + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py new file mode 100644 index 0000000..f212111 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.7 on 2025-10-07 11:52 + +import taggit.managers +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0003_comment'), + ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='tags', + field=taggit.managers.TaggableManager( + help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py index 9d8e590..441ae16 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.urls import reverse from django.utils import timezone from django.conf import settings +from taggit.managers import TaggableManager class PublishedManager(models.Manager): @@ -15,7 +17,10 @@ class Status(models.TextChoices): PUBLISHED = 'PB', 'Published' title = models.CharField(max_length=250) - slug = models.SlugField(max_length=250) + slug = models.SlugField( + max_length=250, + unique_for_date='published_at' + ) author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, @@ -30,6 +35,7 @@ class Status(models.TextChoices): objects = models.Manager() published = PublishedManager() + tags = TaggableManager() class Meta: ordering = ('-published_at',) @@ -39,3 +45,37 @@ class Meta: def __str__(self): return self.title + + def get_absolute_url(self): + return reverse( + 'blog:post_detail', + args=[ + self.published_at.year, + self.published_at.month, + self.published_at.day, + self.slug + ] + ) + + +class Comment(models.Model): + post = models.ForeignKey( + Post, + on_delete=models.CASCADE, + related_name='comments' + ) + name = models.CharField(max_length=80) + email = models.EmailField() + body = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + active = models.BooleanField(default=True) + + class Meta: + ordering = ('created_at',) + indexes = [ + models.Index(fields=['created_at']), + ] + + def __str__(self): + return f'Comment by {self.name} on {self.post}' diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py new file mode 100644 index 0000000..dbbd97a --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py @@ -0,0 +1,13 @@ +from django.contrib.sitemaps import Sitemap +from .models import Post + + +class PostSitemap(Sitemap): + changefreq = 'weekly' + priority = 0.9 + + def items(self): + return Post.published.all() + + def lastmod(self, obj): + return obj.updated_at diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html index 2e4bba1..ebc5f98 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html @@ -1,3 +1,4 @@ +{% load blog_tags %} {% load static %} @@ -9,11 +10,26 @@