Skip to content

Commit

Permalink
The initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sshewalealgo committed Jul 20, 2024
1 parent 44e93cc commit 18c0cc1
Show file tree
Hide file tree
Showing 98 changed files with 8,233 additions and 0 deletions.
60 changes: 60 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Use an official Python runtime based on Debian 10 "buster" as a parent image.
FROM python:3.8.1-slim-buster

# Add user that will be used in the container.
RUN useradd wagtail

# Port used by this container to serve HTTP.
EXPOSE 8000

# Set environment variables.
# 1. Force Python stdout and stderr streams to be unbuffered.
# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE"
# command.
ENV PYTHONUNBUFFERED=1 \
PORT=8000

# Install system packages required by Wagtail and Django.
RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
build-essential \
libpq-dev \
libmariadbclient-dev \
libjpeg62-turbo-dev \
zlib1g-dev \
libwebp-dev \
&& rm -rf /var/lib/apt/lists/*

# Install the application server.
RUN pip install "gunicorn==20.0.4"

# Install the project requirements.
COPY requirements.txt /
RUN pip install -r /requirements.txt

# Use /app folder as a directory where the source code is stored.
WORKDIR /app

# Set this directory to be owned by the "wagtail" user. This Wagtail project
# uses SQLite, the folder needs to be owned by the user that
# will be writing to the database file.
RUN chown wagtail:wagtail /app

# Copy the source code of the project into the container.
COPY --chown=wagtail:wagtail . .

# Use user "wagtail" to run the build commands below and the server itself.
USER wagtail

# Collect static files.
RUN python manage.py collectstatic --noinput --clear

# Runtime command that executes when "docker run" is called, it does the
# following:
# 1. Migrate the database.
# 2. Start the application server.
# WARNING:
# Migrating database at the same time as starting the server IS NOT THE BEST
# PRACTICE. The database should be migrated manually or using the release
# phase facilities of your hosting platform. This is used only so the
# Wagtail instance can be started with a simple "docker run" command.
CMD set -xe; python manage.py migrate --noinput; gunicorn unblocklife.wsgi:application
18 changes: 18 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
django = "*"
tzdata = "*" # Fixes "zoneinfo._common.ZoneInfoNotFoundError" on docker server
wagtail = "*"
whitenoise = "*"

[dev-packages]
black = "*"
flake8 = "*"
isort = "*"

[requires]
python_version = "3.10"
Empty file added courses/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions courses/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions courses/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class CoursesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'courses'
166 changes: 166 additions & 0 deletions courses/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from django.db import models
from wagtail.models import Page
from django.shortcuts import redirect, render
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.search import index
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail.images.blocks import ImageChooserBlock
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import Tag, TaggedItemBase
from home.blocks import BaseStreamBlock, TrainerBlock
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.admin.filters import WagtailFilterSet


class CoursesIndexPage(RoutablePageMixin, Page):
max_count=1
summary = models.CharField(blank=True, max_length=255)
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text="Landscape mode only; horizontal width between 1000px and 3000px.",
)

template = "courses/courses_index_page.html"

content_panels = Page.content_panels + [
FieldPanel('summary'),
FieldPanel("image")
]

def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context['course_entries'] = CoursesPage.objects.child_of(self).live().order_by('-date')
return context

subpage_types = ["CoursesPage"]

def children(self):
return self.get_children().specific().live()

@route(r"^tags/$", name="tag_archive")
@route(r"^tags/([\w-]+)/$", name="tag_archive")
def tag_archive(self, request, tag=None):
try:
tag = Tag.objects.get(slug=tag)
except Tag.DoesNotExist:
if tag:
msg = 'There are no Course Pagess tagged with "{}"'.format(tag)
messages.add_message(request, messages.INFO, msg)
return redirect(self.url)
course_entries = self.get_posts(tag=tag)
context = {"self": self, "tag": tag, "course_entries": course_entries}
return render(request, "courses/courses_index_page.html", context)

def serve_preview(self, request, mode_name):
return self.serve(request)

# Returns the child CoursePage objects for this CoursePageIndex.
# If a tag is used then it will filter the posts by tag.
def get_posts(self, tag=None):
posts = CoursesPage.objects.live().descendant_of(self)
if tag:
posts = posts.filter(tags=tag)
return posts
# Returns the list of Tags for all child posts of this CoursePage.
def get_child_tags(self):
tags = []
for post in self.get_posts():
# Not tags.append() because we don't want a list of lists
tags += post.get_tags
tags = sorted(set(tags))
return tags



class CoursesPageTag(TaggedItemBase):
"""
This model allows us to create a many-to-many relationship between
the CoursePage object and tags. There's a longer guide on using it at
https://docs.wagtail.org/en/stable/reference/pages/model_recipes.html#tagging
"""

content_object = ParentalKey(
"CoursesPage", related_name="tagged_items", on_delete=models.CASCADE
)






class CoursesPage(Page):
author = models.CharField(max_length=255)
date = models.DateField("Post date")
summary = models.CharField(max_length=1000)
start_date = models.DateField(null=True, help_text="Schedule Start Date")
end_date = models.DateField( null=True, help_text="Schedule End Date")
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True)
trainers = StreamField( TrainerBlock(), verbose_name="Trainers", blank=True, use_json_field=True)

tags = ClusterTaggableManager(through=CoursesPageTag, blank=True)

search_fields = Page.search_fields + [
index.SearchField('summary'),
index.SearchField('author'),
index.SearchField('body'),
index.SearchField('trainers'),
]

content_panels = Page.content_panels + [
FieldPanel('trainers'),
FieldPanel('date'),
FieldPanel('summary'),
FieldPanel('start_date'),
FieldPanel('end_date'),
FieldPanel('body'),
FieldPanel('author'),
FieldPanel("tags"),
]
panels = [
FieldPanel('start_date'),
]


promote_panels = [
MultiFieldPanel(Page.promote_panels, "Common page configuration"),
FieldPanel('feed_image'),
]

@property
def get_tags(self):
"""
Similar to the authors function above we're returning all the tags that
are related to the course page into a list we can access on the template.
We're additionally adding a URL to access CoursePage objects with that tag
"""
tags = self.tags.all()
base_url = self.get_parent().url
for tag in tags:
tag.url = f"{base_url}tags/{tag.slug}/"
return tags

# Specifies parent to CoursePage as being CourseIndexPages
parent_page_types = ["CoursesIndexPage"]

# Specifies what content types can exist as children of CoursePage.
# Empty list means that no child content types are allowed.
subpage_types = []

class CoursesPageFillterSet(WagtailFilterSet):
class Meta:
model = CoursesPage
fields = ["start_date"]
54 changes: 54 additions & 0 deletions courses/templates/courses/courses_index_page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{% extends "base.html" %}

{% load wagtailcore_tags wagtailimages_tags %}

{% block body_class %}template-blogindexpage{% endblock %}

{% block content %}

<div class="h-28"></div>

<section id="blog" class="text-gray-700 body-font border-t border-gray-200">
<div class="container px-5 py-24 mx-auto">
<div class="flex flex-wrap w-full mb-20 flex-col items-center text-center">
<h1 class="sm:text-3xl text-2xl font-medium title-font mb-2 text-gray-900"> {{ page.title }} </h1>
<p class="lg:w-1/2 w-full leading-relaxed text-base"> {{ page.summary }} </p>

</div>
<div class="flex flex-wrap -m-4">
{% for post in course_entries %}

<div class="xl:w-1/3 md:w-1/2 p-4">
<div class="border border-gray-300 p-6 rounded-lg">

{% image post.feed_image fill-840x240 as img %}


<div
class="w-full inline-flex items-center justify-center rounded-full bg-green-100 text-green-500 mb-4">
<img class="object-cover object-center h-full w-full" src="{{img.url}}">
</div>
<div>
<h2 class="text-2xl text-gray-900 font-medium title-font mb-2"> {{ post.title }} </h2>
<p></p>
<p class="leading-relaxed text-lg"> {{ post.specific.summary }}</p>
<a href="{% pageurl post %}" class="mt-3 text-green-500 inline-flex items-center text-xl">Read More
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" class="w-4 h-4 ml-2" viewBox="0 0 24 24">
<path d="M5 12h14M12 5l7 7-7 7"></path>
</svg>
</a>
</div>
</div>
</div>
{% endfor %}

</div>

</div>
</section>


{% endblock %}


75 changes: 75 additions & 0 deletions courses/templates/courses/courses_page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-blogpage{% endblock %}

{% block content %}
{% load wagtailcore_tags wagtailimages_tags %}
<div class="h-10"></div>
<div class="flex justify-center items-center">
{% image page.feed_image fill-840x240 as img %}
<img src='{{ img.url }}' alt='#' class="p-4 rounded rounded-lg" />

</div>

<div class="w-full flex justify-center items-center px-2 sm:px-10 md:px-24">
<div class="pt-24">
<h1 class="mb-4 text-2xl md:text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl px-4 ">{{ page.title }} </h1>
<div class=" grid grid-cols-1 gap-4">
<p class="text-sm dark:text-gray-600 px-4 mb-4 md:mb-0">by
<a rel="noopener noreferrer" href="#" target="_blank" class="underline dark:text-violet-600">
<span itemprop="name">{{ page.author }}</span>
</a>on
<time datetime="2021-02-12 15:34:18-0200"> {{ page.date }} </time>
</p>
<div class=" border-2 border-dotted rounded rounded-lg shadow shadow-lg md:mt-0 w-full md:w-3/4">
<p class="font-bold border-b-2 border-gray-400 text-2xl"> Schedule </p>
<p> Start Date: <time datetime="2021-02-12 15:34:18-0200"> {{ page.start_date }} </time> </p>
<p> End Date: <time datetime="2021-02-12 15:34:18-0200"> {{ page.end_date }} </time> </p>
</div>
</div>

<div class="pt-4">
{% for trainer in page.trainers %}
<div class="px-4 md:px-0"> {{ trainer }} </div>
{% endfor %}

</div>

<div class="h-32"></div>


{% for block in page.body %}
<div class="md:px-0"> {{ block }} </div>
{% endfor %}

<hr>

{% if page.get_tags %}
<h3 class="my-4 font-bold">Find more blog posts with similar tags</h3>
<div class="">
{% for tag in page.get_tags %}
<span class="border-2 rounded rounded-3xl p-4 inline-flex items-baseline"> <svg xmlns="http://www.w3.org/2000/svg" class="pt-2 pr-2" width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor"><path d="M10.9042 2.10025L20.8037 3.51446L22.2179 13.414L13.0255 22.6063C12.635 22.9969 12.0019 22.9969 11.6113 22.6063L1.71184 12.7069C1.32131 12.3163 1.32131 11.6832 1.71184 11.2926L10.9042 2.10025ZM11.6113 4.22157L3.83316 11.9997L12.3184 20.485L20.0966 12.7069L19.036 5.28223L11.6113 4.22157ZM13.7327 10.5855C12.9516 9.80448 12.9516 8.53815 13.7327 7.7571C14.5137 6.97606 15.78 6.97606 16.5611 7.7571C17.3421 8.53815 17.3421 9.80448 16.5611 10.5855C15.78 11.3666 14.5137 11.3666 13.7327 10.5855Z"></path></svg>
<a href="{{ tag.url }}" class="blog-tags__pill">{{ tag }}</a> </span>
{% endfor %}
</div>
{% endif %}



<div class="w-full relative pt-10">
<div class="absolute right-4 bg-gray-400 hover:bg-gray-800 hover:text-white p-4 rounded rounded-lg">
<a rel="noopener noreferrer" href="{{ page.get_parent.url }}" class="flex items-center justify-between w-full">
<span class="text-xs font-bold tracking-wider uppercase">Return to Courses</span>
<svg viewBox="0 0 24 24" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round" class="w-4 stroke-current dark:text-violet-600">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>


<hr>
<div class="h-10"></div>

{% endblock content %}

3 changes: 3 additions & 0 deletions courses/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
Loading

0 comments on commit 18c0cc1

Please sign in to comment.