diff --git a/cloud/routes.py b/cloud/routes.py index e1ffa00..cb3131b 100644 --- a/cloud/routes.py +++ b/cloud/routes.py @@ -1,17 +1,22 @@ +import functools import itertools import json import logging +import typing -import pillar.api -from pillar.web.users import forms - +from flask_login import current_user, login_required +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.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 attach_project_pictures from pillar.web.settings import blueprint as blueprint_settings +from pillar.web.nodes.routes import url_for_node + blueprint = Blueprint('cloud', __name__) log = logging.getLogger(__name__) @@ -37,6 +42,7 @@ def homepage(): # Append picture Files to last_posts for post in latest_posts._items: post.picture = get_file(post.picture, api=api) + post.url = url_for_node(node=post) # Get latest assets added to any project latest_assets = Node.latest('assets', api=api) @@ -44,6 +50,7 @@ def homepage(): # Append picture Files to latest_assets for asset in latest_assets._items: asset.picture = get_file(asset.picture, api=api) + asset.url = url_for_node(node=asset) # Get latest comments to any node latest_comments = Node.latest('comments', api=api) @@ -53,18 +60,27 @@ def homepage(): # Parse results for replies 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): if comment.properties.is_reply: try: - comment.attached_to = Node.find(comment.parent.parent, - {'projection': { - '_id': 1, - 'name': 1, - }}, - api=api) + comment.attached_to = _find_parent(comment.parent.parent) except ResourceNotFound: # Remove this comment to_remove.append(idx) + else: + comment.attached_to.url = url_for_node(node=comment.attached_to) else: comment.attached_to = comment.parent @@ -82,6 +98,9 @@ def homepage(): latest_comments._items) activity_stream = sorted(activities, key=sort_key, reverse=True) + for node in activity_stream: + node.url = url_for_node(node=node) + return render_template( 'homepage.html', main_project=main_project, @@ -192,33 +211,49 @@ def workshops(): 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() - projects = Project.all({ - 'projection': {'nodes_featured': 1}, - 'where': {'is_private': False}, - 'max_results': '15' - }, api=api) + The dict is a node, with a 'project' key that contains a projected project. + """ - featured_nodes = (p.nodes_featured for p in projects._items if p.nodes_featured) - featured_nodes = [item for sublist in featured_nodes for item in sublist] - if len(featured_nodes) > 3: - featured_nodes = random.sample(featured_nodes, 3) + proj_coll = current_app.db('projects') + featured_nodes = proj_coll.aggregate([ + {'$match': {'is_private': False}}, + {'$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 = [] + 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_document = Node.find(node, { - 'projection': {'name': 1, 'project': 1, 'picture': 1, - 'properties.content_type': 1, 'properties.url': 1}, - 'embedded': {'project': 1} - }, api=api) - - node_document.picture = get_file(node_document.picture, api=api) - featured_node_documents.append(node_document) + node = Node(node_document) + node.picture = get_file(node.picture, api=api) + node.url = url_for_node(node=node) + featured_node_documents.append(node) return featured_node_documents diff --git a/src/templates/homepage.pug b/src/templates/homepage.pug index a0a79a9..2f01d0a 100644 --- a/src/templates/homepage.pug +++ b/src/templates/homepage.pug @@ -50,10 +50,10 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ | {% for n in activity_stream %} li.activity-stream__list-item( 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( class="{{ n.properties.content_type }}", - href="{{ url_for_node(node=n) }}") + href="{{ n.url }}") | {% if n.picture %} img(src="{{ n.picture.thumbnail('m', api=api) }}") | {% endif %} @@ -75,7 +75,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ .activity-stream__list-details - a.title(href="{{ url_for_node(node=n) }}") + a.title(href="{{ n.url }}") | {% if n.node_type == 'comment' %} | {{ n.properties.content | striptags | truncate(200) }} | {% else %} @@ -88,13 +88,13 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ ul.meta | {% if n.node_type == 'comment' or not n.picture %} 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 }} | {% endif %} | {% if n.attached_to %} 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 %} li.where-project 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 %} ul.meta.extra 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 }} | {% endif %} | {% endfor %} @@ -159,31 +159,31 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ | {% if n.picture and loop.first %} li.blog-stream__list-item.featured a.blog-stream__thumbnail( - href="{{ url_for_node(node=n) }}") + href="{{ n.url }}") img(src="{{ n.picture.thumbnail('l', api=api) }}") - a.title(href="{{ url_for_node(node=n) }}") + a.title(href="{{ n.url }}") | {{ n.name }} ul.meta li.when - a(href="{{ url_for_node(node=n) }}", + a(href="{{ n.url }}", title="Updated {{ n._updated | pretty_date }}") | {{ n._created | pretty_date }} li.where-project a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }} | {% else %} 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 %} img.image(src="{{ n.picture.thumbnail('s', api=api) }}") | {% else %} i.pi-newspaper | {% endif %} .blog-stream__list-details - a.title(href="{{ url_for_node(node=n) }}") {{ n.name }} + a.title(href="{{ n.url }}") {{ n.name }} ul.meta li.when - a(href="{{ url_for_node(node=n) }}", + a(href="{{ n.url }}", title="Updated {{ n._updated | pretty_date }}") | {{ n._created | pretty_date }} li.where-project @@ -214,7 +214,7 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ span free | {% endif %} a.random-asset__thumbnail( - href="{{ url_for_node(node=n) }}", + href="{{ n.url }}", class="{{ n.properties.content_type }}") | {% if n.picture %} img(src="{{ n.picture.thumbnail('l', api=api) }}") @@ -225,11 +225,11 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_ | {% endif %} - a.title(href="{{ url_for_node(node=n) }}") + a.title(href="{{ n.url }}") | {{ n.name }} ul.meta 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 %} li.where 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 | {% endif %} a.random-asset__list-thumbnail( - href="{{ url_for_node(node=n) }}", + href="{{ n.url }}", class="{{ n.properties.content_type }}") | {% if n.picture %} 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 %} .random-asset__list-details - a.title(href="{{ url_for_node(node=n) }}") {{ n.name }} + a.title(href="{{ n.url }}") {{ n.name }} ul.meta 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 %} li.where a(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}