Performance improvements for the homepage (activity, blog, random featured)
This commit is contained in:
@@ -1,17 +1,22 @@
|
|||||||
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import typing
|
||||||
|
|
||||||
import pillar.api
|
from flask_login import current_user, login_required
|
||||||
from pillar.web.users import forms
|
from flask import Blueprint, render_template, redirect, session, url_for, abort, flash
|
||||||
|
|
||||||
from pillarsdk import Node, Project, User, exceptions as sdk_exceptions, Group
|
from pillarsdk import Node, Project, User, exceptions as sdk_exceptions, Group
|
||||||
from pillarsdk.exceptions import ResourceNotFound
|
from pillarsdk.exceptions import ResourceNotFound
|
||||||
from flask_login import current_user, login_required
|
|
||||||
from flask import Blueprint, current_app, render_template, redirect, session, url_for, abort, flash
|
from pillar import current_app
|
||||||
|
import pillar.api
|
||||||
|
from pillar.web.users import forms
|
||||||
from pillar.web.utils import system_util, get_file, current_user_is_authenticated
|
from pillar.web.utils import system_util, get_file, current_user_is_authenticated
|
||||||
from pillar.web.utils import attach_project_pictures
|
from pillar.web.utils import attach_project_pictures
|
||||||
from pillar.web.settings import blueprint as blueprint_settings
|
from pillar.web.settings import blueprint as blueprint_settings
|
||||||
|
from pillar.web.nodes.routes import url_for_node
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint('cloud', __name__)
|
blueprint = Blueprint('cloud', __name__)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -37,6 +42,7 @@ def homepage():
|
|||||||
# Append picture Files to last_posts
|
# Append picture Files to last_posts
|
||||||
for post in latest_posts._items:
|
for post in latest_posts._items:
|
||||||
post.picture = get_file(post.picture, api=api)
|
post.picture = get_file(post.picture, api=api)
|
||||||
|
post.url = url_for_node(node=post)
|
||||||
|
|
||||||
# Get latest assets added to any project
|
# Get latest assets added to any project
|
||||||
latest_assets = Node.latest('assets', api=api)
|
latest_assets = Node.latest('assets', api=api)
|
||||||
@@ -44,6 +50,7 @@ def homepage():
|
|||||||
# Append picture Files to latest_assets
|
# Append picture Files to latest_assets
|
||||||
for asset in latest_assets._items:
|
for asset in latest_assets._items:
|
||||||
asset.picture = get_file(asset.picture, api=api)
|
asset.picture = get_file(asset.picture, api=api)
|
||||||
|
asset.url = url_for_node(node=asset)
|
||||||
|
|
||||||
# Get latest comments to any node
|
# Get latest comments to any node
|
||||||
latest_comments = Node.latest('comments', api=api)
|
latest_comments = Node.latest('comments', api=api)
|
||||||
@@ -53,18 +60,27 @@ def homepage():
|
|||||||
|
|
||||||
# Parse results for replies
|
# Parse results for replies
|
||||||
to_remove = []
|
to_remove = []
|
||||||
|
|
||||||
|
@functools.lru_cache()
|
||||||
|
def _find_parent(parent_node_id) -> Node:
|
||||||
|
return Node.find(parent_node_id,
|
||||||
|
{'projection': {
|
||||||
|
'_id': 1,
|
||||||
|
'name': 1,
|
||||||
|
'node_type': 1,
|
||||||
|
'project': 1,
|
||||||
|
}},
|
||||||
|
api=api)
|
||||||
|
|
||||||
for idx, comment in enumerate(latest_comments._items):
|
for idx, comment in enumerate(latest_comments._items):
|
||||||
if comment.properties.is_reply:
|
if comment.properties.is_reply:
|
||||||
try:
|
try:
|
||||||
comment.attached_to = Node.find(comment.parent.parent,
|
comment.attached_to = _find_parent(comment.parent.parent)
|
||||||
{'projection': {
|
|
||||||
'_id': 1,
|
|
||||||
'name': 1,
|
|
||||||
}},
|
|
||||||
api=api)
|
|
||||||
except ResourceNotFound:
|
except ResourceNotFound:
|
||||||
# Remove this comment
|
# Remove this comment
|
||||||
to_remove.append(idx)
|
to_remove.append(idx)
|
||||||
|
else:
|
||||||
|
comment.attached_to.url = url_for_node(node=comment.attached_to)
|
||||||
else:
|
else:
|
||||||
comment.attached_to = comment.parent
|
comment.attached_to = comment.parent
|
||||||
|
|
||||||
@@ -82,6 +98,9 @@ def homepage():
|
|||||||
latest_comments._items)
|
latest_comments._items)
|
||||||
activity_stream = sorted(activities, key=sort_key, reverse=True)
|
activity_stream = sorted(activities, key=sort_key, reverse=True)
|
||||||
|
|
||||||
|
for node in activity_stream:
|
||||||
|
node.url = url_for_node(node=node)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'homepage.html',
|
'homepage.html',
|
||||||
main_project=main_project,
|
main_project=main_project,
|
||||||
@@ -192,33 +211,49 @@ def workshops():
|
|||||||
return render_page()
|
return render_page()
|
||||||
|
|
||||||
|
|
||||||
def get_random_featured_nodes():
|
def get_random_featured_nodes() -> typing.List[dict]:
|
||||||
|
"""Returns a list of project/node combinations for featured nodes.
|
||||||
|
|
||||||
import random
|
A random subset of 3 featured nodes from all public projects is returned.
|
||||||
|
Assumes that the user actually has access to the public projects' nodes.
|
||||||
|
|
||||||
api = system_util.pillar_api()
|
The dict is a node, with a 'project' key that contains a projected project.
|
||||||
projects = Project.all({
|
"""
|
||||||
'projection': {'nodes_featured': 1},
|
|
||||||
'where': {'is_private': False},
|
|
||||||
'max_results': '15'
|
|
||||||
}, api=api)
|
|
||||||
|
|
||||||
featured_nodes = (p.nodes_featured for p in projects._items if p.nodes_featured)
|
proj_coll = current_app.db('projects')
|
||||||
featured_nodes = [item for sublist in featured_nodes for item in sublist]
|
featured_nodes = proj_coll.aggregate([
|
||||||
if len(featured_nodes) > 3:
|
{'$match': {'is_private': False}},
|
||||||
featured_nodes = random.sample(featured_nodes, 3)
|
{'$project': {'nodes_featured': True,
|
||||||
|
'url': True,
|
||||||
|
'name': True}},
|
||||||
|
{'$unwind': {'path': '$nodes_featured'}},
|
||||||
|
{'$sample': {'size': 3}},
|
||||||
|
{'$lookup': {'from': 'nodes',
|
||||||
|
'localField': 'nodes_featured',
|
||||||
|
'foreignField': '_id',
|
||||||
|
'as': 'node'}},
|
||||||
|
{'$unwind': {'path': '$node'}},
|
||||||
|
{'$project': {'url': True,
|
||||||
|
'name': True,
|
||||||
|
'node._id': True,
|
||||||
|
'node.name': True,
|
||||||
|
'node.permissions': True,
|
||||||
|
'node.picture': True,
|
||||||
|
'node.properties.content_type': True,
|
||||||
|
'node.properties.url': True}},
|
||||||
|
])
|
||||||
|
|
||||||
featured_node_documents = []
|
featured_node_documents = []
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
for node_info in featured_nodes:
|
||||||
|
# Turn the project-with-node doc into a node-with-project doc.
|
||||||
|
node_document = node_info.pop('node')
|
||||||
|
node_document['project'] = node_info
|
||||||
|
|
||||||
for node in featured_nodes:
|
node = Node(node_document)
|
||||||
node_document = Node.find(node, {
|
node.picture = get_file(node.picture, api=api)
|
||||||
'projection': {'name': 1, 'project': 1, 'picture': 1,
|
node.url = url_for_node(node=node)
|
||||||
'properties.content_type': 1, 'properties.url': 1},
|
featured_node_documents.append(node)
|
||||||
'embedded': {'project': 1}
|
|
||||||
}, api=api)
|
|
||||||
|
|
||||||
node_document.picture = get_file(node_document.picture, api=api)
|
|
||||||
featured_node_documents.append(node_document)
|
|
||||||
|
|
||||||
return featured_node_documents
|
return featured_node_documents
|
||||||
|
|
||||||
|
@@ -50,10 +50,10 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
| {% for n in activity_stream %}
|
| {% for n in activity_stream %}
|
||||||
li.activity-stream__list-item(
|
li.activity-stream__list-item(
|
||||||
class="{{ n.node_type }} {{ n.properties.content_type }} {% if n.picture %}with-picture{% endif %}",
|
class="{{ n.node_type }} {{ n.properties.content_type }} {% if n.picture %}with-picture{% endif %}",
|
||||||
data-url="{{ url_for_node(node=n) }}")
|
data-url="{{ n.url }}")
|
||||||
a.activity-stream__list-thumbnail(
|
a.activity-stream__list-thumbnail(
|
||||||
class="{{ n.properties.content_type }}",
|
class="{{ n.properties.content_type }}",
|
||||||
href="{{ url_for_node(node=n) }}")
|
href="{{ n.url }}")
|
||||||
| {% if n.picture %}
|
| {% if n.picture %}
|
||||||
img(src="{{ n.picture.thumbnail('m', api=api) }}")
|
img(src="{{ n.picture.thumbnail('m', api=api) }}")
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
@@ -75,7 +75,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
|
|
||||||
|
|
||||||
.activity-stream__list-details
|
.activity-stream__list-details
|
||||||
a.title(href="{{ url_for_node(node=n) }}")
|
a.title(href="{{ n.url }}")
|
||||||
| {% if n.node_type == 'comment' %}
|
| {% if n.node_type == 'comment' %}
|
||||||
| {{ n.properties.content | striptags | truncate(200) }}
|
| {{ n.properties.content | striptags | truncate(200) }}
|
||||||
| {% else %}
|
| {% else %}
|
||||||
@@ -88,13 +88,13 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
ul.meta
|
ul.meta
|
||||||
| {% if n.node_type == 'comment' or not n.picture %}
|
| {% if n.node_type == 'comment' or not n.picture %}
|
||||||
li.when
|
li.when
|
||||||
a(href="{{ url_for_node(node=n) }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
a(href="{{ n.url }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
||||||
li.who {{ n.user.full_name }}
|
li.who {{ n.user.full_name }}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% if n.attached_to %}
|
| {% if n.attached_to %}
|
||||||
li.where-parent
|
li.where-parent
|
||||||
a(href="{{ url_for_node(node_id=n.attached_to._id) }}") {{ n.attached_to.name }}
|
a(href="{{ n.attached_to.url }}") {{ n.attached_to.name }}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
li.where-project
|
li.where-project
|
||||||
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
||||||
@@ -108,7 +108,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
| {% if n.picture %}
|
| {% if n.picture %}
|
||||||
ul.meta.extra
|
ul.meta.extra
|
||||||
li.when
|
li.when
|
||||||
a(href="{{ url_for_node(node=n) }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
a(href="{{ n.url }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
||||||
li.who {{ n.user.full_name }}
|
li.who {{ n.user.full_name }}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endfor %}
|
| {% endfor %}
|
||||||
@@ -159,31 +159,31 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
| {% if n.picture and loop.first %}
|
| {% if n.picture and loop.first %}
|
||||||
li.blog-stream__list-item.featured
|
li.blog-stream__list-item.featured
|
||||||
a.blog-stream__thumbnail(
|
a.blog-stream__thumbnail(
|
||||||
href="{{ url_for_node(node=n) }}")
|
href="{{ n.url }}")
|
||||||
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
||||||
a.title(href="{{ url_for_node(node=n) }}")
|
a.title(href="{{ n.url }}")
|
||||||
| {{ n.name }}
|
| {{ n.name }}
|
||||||
|
|
||||||
ul.meta
|
ul.meta
|
||||||
li.when
|
li.when
|
||||||
a(href="{{ url_for_node(node=n) }}",
|
a(href="{{ n.url }}",
|
||||||
title="Updated {{ n._updated | pretty_date }}")
|
title="Updated {{ n._updated | pretty_date }}")
|
||||||
| {{ n._created | pretty_date }}
|
| {{ n._created | pretty_date }}
|
||||||
li.where-project
|
li.where-project
|
||||||
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
||||||
| {% else %}
|
| {% else %}
|
||||||
li.blog-stream__list-item
|
li.blog-stream__list-item
|
||||||
a.blog-stream__list-thumbnail(href="{{ url_for_node(node=n) }}")
|
a.blog-stream__list-thumbnail(href="{{ n.url }}")
|
||||||
| {% if n.picture %}
|
| {% if n.picture %}
|
||||||
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
|
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
|
||||||
| {% else %}
|
| {% else %}
|
||||||
i.pi-newspaper
|
i.pi-newspaper
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
.blog-stream__list-details
|
.blog-stream__list-details
|
||||||
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
|
a.title(href="{{ n.url }}") {{ n.name }}
|
||||||
ul.meta
|
ul.meta
|
||||||
li.when
|
li.when
|
||||||
a(href="{{ url_for_node(node=n) }}",
|
a(href="{{ n.url }}",
|
||||||
title="Updated {{ n._updated | pretty_date }}")
|
title="Updated {{ n._updated | pretty_date }}")
|
||||||
| {{ n._created | pretty_date }}
|
| {{ n._created | pretty_date }}
|
||||||
li.where-project
|
li.where-project
|
||||||
@@ -214,7 +214,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
span free
|
span free
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
a.random-asset__thumbnail(
|
a.random-asset__thumbnail(
|
||||||
href="{{ url_for_node(node=n) }}",
|
href="{{ n.url }}",
|
||||||
class="{{ n.properties.content_type }}")
|
class="{{ n.properties.content_type }}")
|
||||||
| {% if n.picture %}
|
| {% if n.picture %}
|
||||||
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
||||||
@@ -225,11 +225,11 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
a.title(href="{{ url_for_node(node=n) }}")
|
a.title(href="{{ n.url }}")
|
||||||
| {{ n.name }}
|
| {{ n.name }}
|
||||||
ul.meta
|
ul.meta
|
||||||
li.what
|
li.what
|
||||||
a(href="{{ url_for_node(node=n) }}")
|
a(href="{{ n.url }}")
|
||||||
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
|
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
|
||||||
li.where
|
li.where
|
||||||
a(href="{{ url_for('projects.view', project_url=n.project.url) }}")
|
a(href="{{ url_for('projects.view', project_url=n.project.url) }}")
|
||||||
@@ -242,7 +242,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
span free
|
span free
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
a.random-asset__list-thumbnail(
|
a.random-asset__list-thumbnail(
|
||||||
href="{{ url_for_node(node=n) }}",
|
href="{{ n.url }}",
|
||||||
class="{{ n.properties.content_type }}")
|
class="{{ n.properties.content_type }}")
|
||||||
| {% if n.picture %}
|
| {% if n.picture %}
|
||||||
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
|
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
|
||||||
@@ -258,10 +258,10 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
.random-asset__list-details
|
.random-asset__list-details
|
||||||
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
|
a.title(href="{{ n.url }}") {{ n.name }}
|
||||||
ul.meta
|
ul.meta
|
||||||
li.what
|
li.what
|
||||||
a(href="{{ url_for_node(node=n) }}")
|
a(href="{{ n.url }}")
|
||||||
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
|
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
|
||||||
li.where
|
li.where
|
||||||
a(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
a(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
||||||
|
Reference in New Issue
Block a user