diff --git a/pillar/api/nodes/comments.py b/pillar/api/nodes/comments.py index 7e324029..25b9978a 100644 --- a/pillar/api/nodes/comments.py +++ b/pillar/api/nodes/comments.py @@ -79,7 +79,9 @@ class CommentTreeBuilder: self.nbr_of_Comments: int = 0 def build(self) -> CommentTreeDO: - enriched_comments = self.child_comments(self.node_id) + enriched_comments = self.child_comments(self.node_id, + sort={'properties.rating_positive': pymongo.DESCENDING, + '_created': pymongo.DESCENDING}) project_id = self.get_project_id() return CommentTreeDO( node_id=self.node_id, @@ -88,14 +90,15 @@ class CommentTreeBuilder: comments=enriched_comments ) - def child_comments(self, node_id: bson.ObjectId) -> typing.List[CommentDO]: - raw_comments = self.mongodb_comments(node_id) + def child_comments(self, node_id: bson.ObjectId, sort: dict) -> typing.List[CommentDO]: + raw_comments = self.mongodb_comments(node_id, sort) return [self.enrich(comment) for comment in raw_comments] def enrich(self, mongo_comment: dict) -> CommentDO: self.nbr_of_Comments += 1 comment = to_comment_data_object(mongo_comment) - comment.replies = self.child_comments(mongo_comment['_id']) + comment.replies = self.child_comments(mongo_comment['_id'], + sort={'_created': pymongo.ASCENDING}) return comment def get_project_id(self): @@ -104,7 +107,7 @@ class CommentTreeBuilder: return result['project'] @classmethod - def mongodb_comments(cls, node_id: bson.ObjectId) -> typing.Iterator: + def mongodb_comments(cls, node_id: bson.ObjectId, sort: dict) -> typing.Iterator: nodes_coll = current_app.db('nodes') return nodes_coll.aggregate([ {'$match': {'node_type': 'comment', @@ -116,8 +119,7 @@ class CommentTreeBuilder: "foreignField": "_id", "as": "user"}}, {'$unwind': {'path': "$user"}}, - {'$sort': {'properties.rating_positive': pymongo.DESCENDING, - '_created': pymongo.DESCENDING}}, + {'$sort': sort}, ]) diff --git a/pillar/api/utils/__init__.py b/pillar/api/utils/__init__.py index c244ba63..47225f75 100644 --- a/pillar/api/utils/__init__.py +++ b/pillar/api/utils/__init__.py @@ -63,7 +63,7 @@ def remove_private_keys(document): return doc_copy -def pretty_duration(seconds): +def pretty_duration(seconds: typing.Union[None, int, float]): if seconds is None: return '' seconds = round(seconds) @@ -75,6 +75,27 @@ def pretty_duration(seconds): return f'{minutes:02}:{seconds:02}' +def pretty_duration_fractional(seconds: typing.Union[None, int, float]): + if seconds is None: + return '' + + # Remove fraction of seconds from the seconds so that the rest is done as integers. + seconds, fracs = divmod(seconds, 1) + hours, seconds = divmod(int(seconds), 3600) + minutes, seconds = divmod(seconds, 60) + msec = int(round(fracs * 1000)) + + if msec == 0: + msec_str = '' + else: + msec_str = f'.{msec:03}' + + if hours > 0: + return f'{hours:02}:{minutes:02}:{seconds:02}{msec_str}' + else: + return f'{minutes:02}:{seconds:02}{msec_str}' + + class PillarJSONEncoder(json.JSONEncoder): """JSON encoder with support for Pillar resources.""" diff --git a/pillar/web/jinja.py b/pillar/web/jinja.py index 4677d737..57327cac 100644 --- a/pillar/web/jinja.py +++ b/pillar/web/jinja.py @@ -35,6 +35,10 @@ def format_pretty_duration(s): return pretty_duration(s) +def format_pretty_duration_fractional(s): + return pillar.api.utils.pretty_duration_fractional(s) + + def format_undertitle(s): """Underscore-replacing title filter. @@ -232,6 +236,7 @@ def setup_jinja_env(jinja_env, app_config: dict): jinja_env.filters['pretty_date'] = format_pretty_date jinja_env.filters['pretty_date_time'] = format_pretty_date_time jinja_env.filters['pretty_duration'] = format_pretty_duration + jinja_env.filters['pretty_duration_fractional'] = format_pretty_duration_fractional jinja_env.filters['undertitle'] = format_undertitle jinja_env.filters['hide_none'] = do_hide_none jinja_env.filters['pluralize'] = do_pluralize diff --git a/requirements.txt b/requirements.txt index 0d7b0104..195ba6ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,9 +42,9 @@ asn1crypto==0.24.0 Babel==2.6.0 billiard==3.5.0.4 Cerberus==1.2 -cffi==1.10.0 +cffi==1.12.2 click==6.7 -cryptography==2.0.3 +cryptography==2.6.1 Events==0.3 future==0.16.0 googleapis-common-protos==1.5.3 @@ -61,14 +61,14 @@ protobuf==3.6.0 protorpc==0.12.0 pyasn1==0.4.4 pyasn1-modules==0.2.2 -pycparser==2.17 +pycparser==2.19 pymongo==3.7.0 pyOpenSSL==16.2.0 pytz==2018.5 requests-oauthlib==1.0.0 rsa==3.4.2 simplejson==3.16.0 -six==1.10.0 +six==1.12.0 urllib3==1.22 vine==1.1.4 webencodings==0.5.1 diff --git a/src/styles/_comments.sass b/src/styles/_comments.sass index 101ca042..edf0e3e8 100644 --- a/src/styles/_comments.sass +++ b/src/styles/_comments.sass @@ -38,7 +38,7 @@ $comments-width-max: 710px display: inline-block float: left font-weight: bold - margin-right: 8px + margin-right: 10px white-space: nowrap &.op diff --git a/src/styles/_utils.sass b/src/styles/_utils.sass index 9f29785b..15cd33ce 100644 --- a/src/styles/_utils.sass +++ b/src/styles/_utils.sass @@ -394,14 +394,17 @@ word-wrap: break-word blockquote + +clearfix background-color: lighten($color-background-light, 5%) box-shadow: inset 5px 0 0 $color-background - display: block + display: inline-block + width: 100% font-size: 1em margin: + bottom: 10px left: 0 right: 20px - bottom: 30px + top: 10px padding: 5px 5px 5px 20px text-shadow: 1px 1px 0 rgba(white, .2) @@ -446,11 +449,10 @@ +list-bullets ul - margin-bottom: 25px + +clearfix + margin-bottom: 15px li - margin-bottom: 7px - img display: block padding: @@ -462,8 +464,8 @@ padding-left: 20px code, kbd, pre, samp - background-color: rgba($color-primary, .05) - color: darken($color-primary, 15%) + background-color: darken(rgba($color-primary, .1), 30%) + color: $color-primary font-size: inherit white-space: pre-line @@ -472,7 +474,7 @@ kbd border: - color: rgba($color-primary, .33) + color: darken(rgba($color-primary, .33), 50%) radius: 3px style: solid width: 2px @@ -481,9 +483,13 @@ font: size: .9em weight: bold - margin: 2px + margin: + bottom: initial + left: 3px + right: 3px + top: initial min-width: 15px - padding: 0 3px + padding: 0 5px text: align: center transform: uppercase diff --git a/tests/test_web/test_jinja.py b/tests/test_web/test_jinja.py index 7b2b7944..4dc7e56b 100644 --- a/tests/test_web/test_jinja.py +++ b/tests/test_web/test_jinja.py @@ -41,3 +41,12 @@ class MarkdownTest(unittest.TestCase): '

Title

\n

Before

\n' '
test
a
b
c
d
\n', jinja.do_markdowned({'eek': '# Title\n\nBefore\n{test a="b" c="d"}'}, 'eek')) + + def test_pretty_duration_fractional(self): + from pillar.web import jinja + + self.assertEqual('03:04.568', jinja.format_pretty_duration_fractional(184.5678911111)) + self.assertEqual('02:03:04.568', jinja.format_pretty_duration_fractional(7384.5678911111)) + + self.assertEqual('03:04', jinja.format_pretty_duration_fractional(184.00049)) + self.assertEqual('02:03:04', jinja.format_pretty_duration_fractional(7384.00049))