diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..df0053630
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*__pycache__*
+.env
diff --git a/db.sqlite3 b/db.sqlite3
index 1cd07fd88..c919a2d48 100644
Binary files a/db.sqlite3 and b/db.sqlite3 differ
diff --git a/home/admin.py b/home/admin.py
index 5a4e10383..e7b6e6a33 100644
--- a/home/admin.py
+++ b/home/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
from django import forms
-from home.models import Blog
+from home.models import Blog, Project, Skill, About, Category
# Register your models here.
class BlogAdminForm(forms.ModelForm):
@@ -13,4 +13,8 @@ class Meta:
class BlogAdmin(admin.ModelAdmin):
form = BlogAdminForm
-admin.site.register(Blog, BlogAdmin)
\ No newline at end of file
+admin.site.register(Blog, BlogAdmin)
+admin.site.register(Project)
+admin.site.register(Skill)
+admin.site.register(About)
+admin.site.register(Category)
diff --git a/home/migrations/0001_initial.py b/home/migrations/0001_initial.py
deleted file mode 100644
index f8ea8f939..000000000
--- a/home/migrations/0001_initial.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 4.1.4 on 2023-01-22 06:13
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='Blog',
- fields=[
- ('sno', models.AutoField(primary_key=True, serialize=False)),
- ('title', models.CharField(max_length=200)),
- ('meta', models.CharField(max_length=300)),
- ('content', models.TextField()),
- ('thumbnail_img', models.ImageField(blank=True, null=True, upload_to='images/')),
- ('slug', models.CharField(max_length=100)),
- ('time', models.DateField(auto_now_add=True)),
- ],
- ),
- ]
diff --git a/home/migrations/0002_blog_category.py b/home/migrations/0002_blog_category.py
deleted file mode 100644
index 241a11826..000000000
--- a/home/migrations/0002_blog_category.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 4.1.4 on 2023-01-25 12:30
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('home', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='blog',
- name='category',
- field=models.CharField(default='uncategorized', max_length=255),
- ),
- ]
diff --git a/home/migrations/0003_blog_remark.py b/home/migrations/0003_blog_remark.py
deleted file mode 100644
index 949e111aa..000000000
--- a/home/migrations/0003_blog_remark.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 4.1.4 on 2023-01-26 07:00
-
-import ckeditor.fields
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('home', '0002_blog_category'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='blog',
- name='remark',
- field=ckeditor.fields.RichTextField(null=True, unique=True),
- ),
- ]
diff --git a/home/migrations/0004_remove_blog_remark.py b/home/migrations/0004_remove_blog_remark.py
deleted file mode 100644
index f45a166de..000000000
--- a/home/migrations/0004_remove_blog_remark.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.1.4 on 2023-01-26 07:12
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('home', '0003_blog_remark'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='blog',
- name='remark',
- ),
- ]
diff --git a/home/migrations/0005_blog_thumbnail_url.py b/home/migrations/0005_blog_thumbnail_url.py
deleted file mode 100644
index 56bcf0a19..000000000
--- a/home/migrations/0005_blog_thumbnail_url.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 4.1.4 on 2023-01-31 05:32
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('home', '0004_remove_blog_remark'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='blog',
- name='thumbnail_url',
- field=models.URLField(blank=True, null=True),
- ),
- ]
diff --git a/home/models.py b/home/models.py
index aa1847cca..a0438f720 100644
--- a/home/models.py
+++ b/home/models.py
@@ -1,6 +1,59 @@
+from typing import Iterable
from django.db import models
+from django.utils import timezone
+import random
+
+ALPHA = "abcdefghijklmnopqrstuvwxyz "
# Create your models here.
+class Project(models.Model):
+ title = models.CharField(max_length=200)
+ description = models.CharField(max_length=300)
+ features = models.TextField(null=True, blank=True)
+ thumbnail_url = models.URLField(blank=True, null=True)
+ technologies = models.CharField(max_length=255)
+ demo_url = models.URLField(blank=True, null=True)
+ github_url = models.URLField(blank=True, null=True)
+ date = models.DateTimeField(auto_now_add=True)
+
+ def __str__(self):
+ return self.title
+
+class Skill(models.Model):
+ name = models.CharField(max_length=200)
+ rate = models.IntegerField(verbose_name="Rate (out of 100)", default=0, null=False, blank=0)
+ tag = models.CharField(max_length=200, editable=False, default="", null=False)
+
+ def clamp_rate(self):
+ self.rate = min(100, max(0, self.rate))
+
+ def save(self, *args) -> None:
+ self.tag = "".join([char.lower() for char in self.tag if char.lower() in ALPHA])
+
+ if self.tag == "":
+ for _ in range(15):
+ self.tag += random.choice(ALPHA)
+
+ self.tag = self.tag.replace(" ", "_")
+
+ self.clamp_rate()
+ return super().save(*args)
+
+ def __str__(self):
+ return self.name
+
+class About(models.Model):
+ text = models.TextField(blank=True, null=True)
+
+ def __str__(self):
+ return self.text.split("\n")[0][:20] # first line until 2Oth char
+
+class Category(models.Model):
+ name = models.CharField(max_length=200)
+
+ def __str__(self) -> str:
+ return self.name
+
class Blog(models.Model):
sno = models.AutoField(primary_key=True)
title = models.CharField(max_length=200)
@@ -8,9 +61,33 @@ class Blog(models.Model):
content = models.TextField()
thumbnail_img = models.ImageField(null=True, blank=True, upload_to="images/")
thumbnail_url = models.URLField(blank=True, null=True)
- category = models.CharField(max_length=255, default="uncategorized")
- slug = models.CharField(max_length=100)
+ categories = models.ManyToManyField("Category", blank=False) # TODO: Auto set to uncategorized
+ slug = models.CharField(max_length=100, unique=True)
time = models.DateField(auto_now_add=True)
def __str__(self):
- return self.title
\ No newline at end of file
+ categories = list(self.categories.all())
+ print(self.title, categories)
+ categories = [c.name for c in categories]
+ return f"{self.title} || {'-'.join(categories)}"
+
+""" def save(self, *args, **kwargs):
+ # Appel à super() pour sauvegarder l'objet dans la base de données
+ super().save(*args, **kwargs)
+
+ categories = list(self.categories.all())
+ if not categories:
+ # get the default category
+ default = Category.objects.filter(name="Uncategorized").first()
+
+ # Vérifiez si l'objet a un ID (c'est-à-dire s'il a été enregistré dans la base de données)
+ if self.pk:
+ # Ajoutez la catégorie par défaut
+ self.categories.add(default)
+ self.save(update_fields=['categories'])
+
+ print(f"Added category {default.name} to the post {self.title}")
+ print([c for c in self.categories.all()])
+ else:
+ print("Object not saved yet, cannot add default category.")"""
+
diff --git a/home/urls.py b/home/urls.py
index ffdd8081a..b8ea3f6d4 100644
--- a/home/urls.py
+++ b/home/urls.py
@@ -5,7 +5,7 @@
urlpatterns = [
path('', views.index, name='home'),
path('about', views.about, name='about'),
- path('contact', views.contact, name='contact'),
+ #path('contact', views.contact, name='contact'),
path('blog', views.blog, name='blog'),
path('projects', views.projects, name='projects'),
path('blogpost/ About Me About Me
- 👋 Hi, Skills My Skills {{ skill.name }} HTML CSS
")
+
+ NB_COLUMNS = 2
+ columns = [[] for _ in range(NB_COLUMNS)]
+
+ for i in range(len(skills)):
+ columns[i%NB_COLUMNS].append(skills[i])
+
+ return render(request, 'about.html', context={"about": about, "skills": columns, "categories": get_search_categories()})
def thanks(request):
- return render(request, 'thanks.html')
+ return render(request, 'thanks.html', context={ "categories": get_search_categories()})
def contact (request):
if request.method == 'POST':
@@ -52,32 +93,62 @@ def contact (request):
# return HttpResponseRedirect('/thanks')
else:
messages.error(request, 'Email or Phone is Invalid!')
- return render(request, 'contact.html', {})
+ return render(request, 'contact.html', context={"categories": get_search_categories()})
+
+def projects(request):
+ limit = 5
+ if request.method == 'GET':
+ if 'all' in request.GET:
+ if request.GET['all'] == '1':
+ limit = None
+
+ projects_items = Project.objects.all().order_by('-date')
+
+ total_projects = len(projects_items)
+
+ # Limit the number of projects to display
+ if limit:
+ projects_items = projects_items[:limit]
-def projects (request):
- return render(request, 'projects.html')
+ all_there = len(projects_items) == total_projects
+
+ # Convert technologies to HTML tags
+ for project in projects_items:
+ techs = project.technologies.split(',')
+ project.technologies = [get_tech_tags(tech) for tech in techs]
+
+ # split the featured projects from lines to list
+ for project in projects_items:
+ project.features = project.features.split('\n')
+
+ return render(request, 'projects.html', {'projects': projects_items, "all": all_there, "categories": get_search_categories()})
def blog(request):
blogs = Blog.objects.all().order_by('-time')
- paginator = Paginator(blogs, 3)
+ paginator = Paginator(blogs, 5)
page = request.GET.get('page')
blogs = paginator.get_page(page)
- context = {'blogs': blogs}
+ context = {'blogs': blogs, "categories": get_search_categories()}
return render(request, 'blog.html', context)
def category(request, category):
- category_posts = Blog.objects.filter(category=category).order_by('-time')
+ category = Category.objects.filter(name=category)
+ posts = Blog.objects.all()
+ category_posts = []
+ for post in posts:
+ if category in post.categories.all():
+ category_posts.append(post)
if not category_posts:
message = f"No posts found in category: '{category}'"
return render(request, "category.html", {"message": message})
paginator = Paginator(category_posts, 3)
page = request.GET.get('page')
category_posts = paginator.get_page(page)
- return render(request, "category.html", {"category": category, 'category_posts': category_posts})
+ return render(request, "category.html", {"category": category, 'category_posts': category_posts, "categories": get_search_categories()})
def categories(request):
- all_categories = Blog.objects.values('category').distinct().order_by('category')
- return render(request, "categories.html", {'all_categories': all_categories})
+ all_categories = Category.objects.values('name')
+ return render(request, "categories.html", {'all_categories': all_categories, "categories": get_search_categories()})
def search(request):
query = request.GET.get('q')
@@ -92,18 +163,19 @@ def search(request):
message = "Sorry, no results found for your search query."
else:
message = ""
- return render(request, 'search.html', {'results': results, 'query': query, 'message': message})
+ return render(request, 'search.html', {'results': results, 'query': query, 'message': message, "categories": get_search_categories()})
def blogpost (request, slug):
try:
blog = Blog.objects.get(slug=slug)
- context = {'blog': blog}
+ context = {'blog': blog, "categories": get_search_categories()}
return render(request, 'blogpost.html', context)
except Blog.DoesNotExist:
- context = {'message': 'Blog post not found'}
+ context = {'message': 'Blog post not found', "categories": get_search_categories()}
return render(request, '404.html', context, status=404)
+
# def blogpost (request, slug):
# blog = Blog.objects.filter(slug=slug).first()
# context = {'blog': blog}
diff --git a/media/images/modem.png b/media/images/modem.png
new file mode 100644
index 000000000..1fe88cc5f
Binary files /dev/null and b/media/images/modem.png differ
diff --git a/media/images/reading_translucent.png b/media/images/reading_translucent.png
new file mode 100644
index 000000000..0ad1fa3ef
Binary files /dev/null and b/media/images/reading_translucent.png differ
diff --git a/requirements.txt b/requirements.txt
index 07be74bb9..893f4cb02 100644
Binary files a/requirements.txt and b/requirements.txt differ
diff --git a/start.sh b/start.sh
new file mode 100644
index 000000000..53e29b35a
--- /dev/null
+++ b/start.sh
@@ -0,0 +1,4 @@
+source .env/bin/activate
+python manage.py makemigrations
+python manage.py migrate
+python manage.py runserver
diff --git a/static/images/favicon.png b/static/images/favicon.png
new file mode 100644
index 000000000..0560e8ce2
Binary files /dev/null and b/static/images/favicon.png differ
diff --git a/templates/about.html b/templates/about.html
index ac7108d2f..d1565a0c2 100644
--- a/templates/about.html
+++ b/templates/about.html
@@ -9,28 +9,30 @@
-
- My name is Ashish and I am a highly motivated and experienced developer with a passion for creating innovative solutions for the internet.
-
- After completing my schooling in 2021, I have chosen to further my education in Computer Science while concurrently taking on freelance projects and blogging to expand my skillset. I possess a diverse range of technical knowledge and experience, including proficiency in programming languages such as Python, HTML, CSS, Javascript and SQL, as well as experience with frameworks such as Tailwind CSS and Django.
-
- I have applied these skills to develop a number of successful real-world applications such as a password manager, antivirus application, and full-stack blog website. Here are my projects in action.
-
- I am continuously seeking new opportunities to expand my knowledge and improve my abilities as a developer, in order to assist clients in achieving their project goals.
- {% comment %} I'm Ashish. I am passionate about developing things for the internet. This passion led me to learn various technologies such as Python, HTML, CSS, Javascript and so on.
-
- After completing my schooling in 2021, I looked forward to pursue further studies in Computer Science. Part time I started Freelancing, Blogging and learning various programming languages. I learned HTML, CSS, Javascript, Python, SQL and various CSS Frameworks such as Tailwind CSS and Python Libraries such as Django.
-
- Using these skills I developed various real world applications such as Passsave - Password Manager using Python, PyShiels - Antivirus Application using Python,Full Stack Blogging Website and more. Checkout more projects on Projects page.
-
- Currently I am extending my learnings to be a skillled developer who can assist you in your projects. {% endcomment %}
+ {{ about.text | safe }}
Copyright © Petchou All Rights Reserved
+Forked from the amazing work of Ashish.