Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/vitorfs/bootcamp into de…
Browse files Browse the repository at this point in the history
…velop
  • Loading branch information
vamoss committed Apr 13, 2020
2 parents 70f4679 + f12fcfe commit 21c4c04
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 47 deletions.
79 changes: 78 additions & 1 deletion bootcamp/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import re
from urllib.parse import urljoin

from django.core.exceptions import PermissionDenied
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.http import HttpResponseBadRequest
from django.views.generic import View
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator

import bs4
import requests


def paginate_data(qs, page_size, page, paginated_type, **kwargs):
Expand Down Expand Up @@ -79,3 +85,74 @@ def update_votes(obj, user, value):
user=user, defaults={"value": value},
)
obj.count_votes()


def fetch_metadata(text):
"""Method to consolidate workflow to recover the metadata of a page of the first URL found a in
a given text block.
:requieres:
:param text: Block of text of any lenght
"""
urls = get_urls(text)
try:
return get_metadata(urls[0])

except IndexError:
return None


def get_urls(text):
"""Method to look for all URLs in a given text, extract them and return them as a tuple of urls.
:requires:
:param text: A valid block of text of any lenght.
:returns:
A tuple of valid URLs extracted from the text.
"""
regex = r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
return re.findall(regex, text)


def get_metadata(url):
"""This function looks for the page of a given URL, extracts the page content and parses the content
with bs4. searching for the page meta tags giving priority to the Open Graph Protocol
https://ogp.me/, then it returns the metadata in case there is any, or tries to build one.
:requires:
:param url: Any valid URL to search for.
:returns:
A dictionary with metadata from a given webpage.
"""
response = requests.get(url)
soup = bs4.BeautifulSoup(response.content)
ogs = soup.html.head.find_all(property=re.compile(r"^og"))
data = {og.get("property")[3:]: og.get("content") for og in ogs}
if not data.get("url"):
data["url"] = url

if not data.get("title"):
data["title"] = soup.html.title.text

if not data.get("image"):
images = soup.find_all("img")
if len(images) > 0:
data["image"] = urljoin(url, images[0].get("src"))

if not data.get("description"):
data["description"] = ""
for text in soup.body.find_all(string=True):
if (
text.parent.name != "script"
and text.parent.name != "style"
and not isinstance(text, bs4.Comment)
):
data["description"] += text

data["description"] = re.sub("\n|\r|\t", " ", data["description"])
data["description"] = re.sub(" +", " ", data["description"])
data["description"] = data["description"].strip()[:255]

return data
38 changes: 38 additions & 0 deletions bootcamp/news/migrations/0002_auto_20200405_1227.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 3.0.3 on 2020-04-05 12:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("news", "0001_initial"),
]

operations = [
migrations.AddField(
model_name="news",
name="meta_description",
field=models.TextField(max_length=255, null=True),
),
migrations.AddField(
model_name="news",
name="meta_image",
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name="news",
name="meta_title",
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name="news",
name="meta_type",
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name="news",
name="meta_url",
field=models.CharField(max_length=2048, null=True),
),
]
14 changes: 14 additions & 0 deletions bootcamp/news/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from channels.layers import get_channel_layer

from bootcamp.notifications.models import Notification, notification_handler
from bootcamp.helpers import fetch_metadata


class News(models.Model):
Expand All @@ -32,6 +33,11 @@ class News(models.Model):
settings.AUTH_USER_MODEL, blank=True, related_name="liked_news"
)
reply = models.BooleanField(verbose_name=_("Is a reply?"), default=False)
meta_url = models.CharField(max_length=2048, null=True)
meta_type = models.CharField(max_length=255, null=True)
meta_title = models.CharField(max_length=255, null=True)
meta_description = models.TextField(max_length=255, null=True)
meta_image = models.CharField(max_length=255, null=True)

class Meta:
verbose_name = _("News")
Expand All @@ -42,6 +48,14 @@ def __str__(self):
return str(self.content)

def save(self, *args, **kwargs):
# extract metada from content url
data = fetch_metadata(self.content)
self.meta_url = data.get("url")
self.meta_type = data.get("type", "website")
self.meta_title = data.get("title")
self.meta_description = data.get("description")
self.meta_image = data.get("image")

super().save(*args, **kwargs)
if not self.reply:
channel_layer = get_channel_layer()
Expand Down
Empty file.
12 changes: 12 additions & 0 deletions bootcamp/news/templatetags/urlize_target_blank.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
from django.utils.html import urlize as urlize_impl

register = template.Library()


@register.filter(is_safe=True, needs_autoescape=True)
@stringfilter
def urlize_target_blank(value, autoescape=None):
return value.replace("<a", '<a target="_blank"')
2 changes: 1 addition & 1 deletion bootcamp/news/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def get_thread(request):
news = News.objects.get(pk=news_id)
news_html = render_to_string("news/news_single.html", {"news": news})
thread_html = render_to_string(
"news/news_thread.html", {"thread": news.get_thread()}
"news/news_thread.html", {"thread": news.get_thread(), "request": request}
)
return JsonResponse({"uuid": news_id, "news": news_html, "thread": thread_html})

Expand Down
52 changes: 41 additions & 11 deletions bootcamp/static/css/news.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.stream {
margin: 2em 0 0 0;
padding: 0;
}

.stream li {
.infinite-container li {
list-style: none;
border-style: solid none solid none;

}

.stream {
margin: 2em 0 0 0;
padding: 0;
}

.stream li:last-child {
border-bottom: none;
}
Expand All @@ -35,11 +35,6 @@
border-radius: 50%;
}

.interaction {
padding-left: 1.5em;
margin-bottom: 1em
}

.interaction a {
margin-right: 1.3em;
font-size: 1.1em;
Expand Down Expand Up @@ -159,3 +154,38 @@
.remove-news:hover {
color: #333333;
}

.meta.card {
background-color: #f8f8f8;
margin-bottom: 10px;
color: #6c757d;
}

.meta.card:hover {
background-color: #fff;
}

.meta .card-body {
padding: 0.8rem;
}

.meta .card-title, .meta .card-text, .meta .card-btn{
font-size: 0.8em;
line-height: 1.4em;
margin: 0;
}

.meta .card-img-top{
height: 100px;
background: center no-repeat #ccc;
background-size: cover;
}

.meta .card-text{
margin-bottom: 5px;
}

.meta:hover .btn{
background-color: #888;
color: #FFF;
}
9 changes: 7 additions & 2 deletions bootcamp/templates/articles/article_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ <h5 class="card-header">{% trans 'Leave a Comment' %}:</h5>
{% get_comment_form for article as form %}
<form action="{% comment_form_target %}" method="POST">
{% csrf_token %}
{{ form|crispy }}
{{ form.comment|as_crispy_field }}
{{ form.honeyspot }}
{{ form.content_type }}
{{ form.object_pk }}
{{ form.timestamp }}
{{ form.security_hash }}
<input type="hidden" name="next" value="{% url 'articles:article' article.slug %}" />
<input type="submit" value="{% trans 'Add comment' %}" id="id_submit" />
</form>
Expand All @@ -67,7 +72,7 @@ <h5 class="card-header">{% trans 'Leave a Comment' %}:</h5>
{% endthumbnail %}
<div class="media-body">
<h5 class="mt-0">{{ comment.user.get_profile_name|title }}</h5>
{{ comment }}
{{ comment.comment|linebreaks }}
</div>
</div>
{% endfor %}
Expand Down
51 changes: 34 additions & 17 deletions bootcamp/templates/news/news_single.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% load i18n %}
{% load humanize static %}
{% load thumbnail %}
{% load urlize_target_blank %}

<li class="infinite-item card" news-id="{{ news.uuid_id }}">
<div class="card-body">
Expand All @@ -22,24 +23,40 @@
<a href="{% url 'users:detail' news.user.username %}">{{ news.user.get_profile_name|title }}</a>
</strong>
</p>
<p>{{ news }}</p>
{% if news.image %}
<img class="card-img-top" src="{{ news.image }}" alt="Card image cap">
<p>{{ news|urlize|urlize_target_blank }}</p>

{% if news.meta_url %}
<a href="{{ news.meta_url }}" target="_blank" class="card meta">
{% if news.meta_image %}
<div class="card-img-top" style="background-image: url({{ news.meta_image }})"></div>
{% endif %}
<div class="card-body">
{% if news.meta_title %}
<h5 class="card-title">{{ news.meta_title }}</h5>
{% endif %}
{% if news.meta_description %}
<p class="card-text">{{ news.meta_description }}</p>
{% endif %}
{% if news.meta_url %}
<p class="card-btn">{{ news.meta_url }}</p>
{% endif %}
</div>
</a>
{% endif %}
<div class="interaction" id="interaction">
<a href="#" class="like" title="{% for i in news.get_likers %}{{ i }}&#10;{% endfor %}">
{% if request.user in news.get_likers %}
<i class="heart fa fa-heart" aria-hidden="true"></i>
{% else %}
<i class="heart fa fa-heart-o" aria-hidden="true"></i>
{% endif %}
<span class="like-count">{{ news.count_likers }}</span>
</a>
<a href="#" class="comment"><i class="fa fa-comment-o" aria-hidden="true"></i>
<span class="comment-count">{{ news.count_thread }}</span>
</a>
<span class="timestamp">{{ news.timestamp|naturaltime }}</span>
</div>
</div>
</div>
<div class="interaction" id="interaction">
<a href="#" class="like" title="{% for i in news.get_likers %}{{ i }}&#10;{% endfor %}">
{% if request.user in news.get_likers %}
<i class="heart fa fa-heart" aria-hidden="true"></i>
{% else %}
<i class="heart fa fa-heart-o" aria-hidden="true"></i>
{% endif %}
<span class="like-count">{{ news.count_likers }}</span>
</a>
<a href="#" class="comment"><i class="fa fa-comment-o" aria-hidden="true"></i>
<span class="comment-count">{{ news.count_thread }}</span>
</a>
<span class="timestamp">{{ news.timestamp|naturaltime }}</span>
</div>
</li>
Loading

0 comments on commit 21c4c04

Please sign in to comment.