diff --git a/tildes/alembic/env.py b/tildes/alembic/env.py index 7d56167..3eb9fe2 100644 --- a/tildes/alembic/env.py +++ b/tildes/alembic/env.py @@ -12,7 +12,12 @@ config = context.config fileConfig(config.config_file_name) # import all DatabaseModel subclasses here for autogenerate support -from tildes.models.comment import Comment, CommentNotification, CommentTag, CommentVote +from tildes.models.comment import ( + Comment, + CommentLabel, + CommentNotification, + CommentVote, +) from tildes.models.group import Group, GroupSubscription from tildes.models.log import Log from tildes.models.message import MessageConversation, MessageReply diff --git a/tildes/alembic/versions/5cd2db18b722_rename_comment_tags_to_labels.py b/tildes/alembic/versions/5cd2db18b722_rename_comment_tags_to_labels.py new file mode 100644 index 0000000..330df08 --- /dev/null +++ b/tildes/alembic/versions/5cd2db18b722_rename_comment_tags_to_labels.py @@ -0,0 +1,38 @@ +"""Rename comment tags to labels + +Revision ID: 5cd2db18b722 +Revises: afa3128a9b54 +Create Date: 2018-09-25 01:05:55.606680 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "5cd2db18b722" +down_revision = "afa3128a9b54" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute("ALTER TYPE commenttagoption RENAME TO commentlabeloption") + + op.rename_table("comment_tags", "comment_labels") + op.alter_column("comment_labels", "tag", new_column_name="label") + + op.alter_column( + "users", "comment_tag_weight", new_column_name="comment_label_weight" + ) + + +def downgrade(): + op.alter_column( + "users", "comment_label_weight", new_column_name="comment_tag_weight" + ) + + op.alter_column("comment_labels", "label", new_column_name="tag") + op.rename_table("comment_labels", "comment_tags") + + op.execute("ALTER TYPE commentlabeloption RENAME TO commenttagoption") diff --git a/tildes/development.ini b/tildes/development.ini index 20bbe49..03cae9b 100644 --- a/tildes/development.ini +++ b/tildes/development.ini @@ -36,7 +36,7 @@ redis.sessions.timeout = 600 sqlalchemy.url = postgresql+psycopg2://tildes:@:6432/tildes -tildes.default_user_comment_tag_weight = 1.0 +tildes.default_user_comment_label_weight = 1.0 webassets.auto_build = false webassets.base_dir = %(here)s/static diff --git a/tildes/production.ini.example b/tildes/production.ini.example index f6280c3..37ab06c 100644 --- a/tildes/production.ini.example +++ b/tildes/production.ini.example @@ -20,7 +20,7 @@ redis.sessions.timeout = 3600 sqlalchemy.url = postgresql+psycopg2://tildes:@:6432/tildes -tildes.default_user_comment_tag_weight = 1.0 +tildes.default_user_comment_label_weight = 1.0 tildes.welcome_message_sender = Deimos webassets.auto_build = false diff --git a/tildes/scripts/clean_private_data.py b/tildes/scripts/clean_private_data.py index f7ec808..ee3dd1a 100644 --- a/tildes/scripts/clean_private_data.py +++ b/tildes/scripts/clean_private_data.py @@ -5,7 +5,7 @@ Other things that should probably be added here eventually: - Delete individual votes on comments/topics after voting has been closed - - Delete which users tagged comments after tagging has been closed + - Delete which users labeled comments after labeling has been closed - Delete old used invite codes (30 days after used?) """ diff --git a/tildes/scss/_themes.scss b/tildes/scss/_themes.scss index 7040a09..828ed3b 100644 --- a/tildes/scss/_themes.scss +++ b/tildes/scss/_themes.scss @@ -117,11 +117,11 @@ color: $text-secondary-color; } - .label-comment-tag-exemplary { @include specialtag($comment-tag-exemplary-color, $is-light); } - .label-comment-tag-joke { @include specialtag($comment-tag-joke-color, $is-light); } - .label-comment-tag-noise { @include specialtag($comment-tag-noise-color, $is-light); } - .label-comment-tag-offtopic { @include specialtag($comment-tag-offtopic-color, $is-light); } - .label-comment-tag-malice { @include specialtag($comment-tag-malice-color, $is-light); } + .label-comment-exemplary { @include specialtag($comment-label-exemplary-color, $is-light); } + .label-comment-joke { @include specialtag($comment-label-joke-color, $is-light); } + .label-comment-noise { @include specialtag($comment-label-noise-color, $is-light); } + .label-comment-offtopic { @include specialtag($comment-label-offtopic-color, $is-light); } + .label-comment-malice { @include specialtag($comment-label-malice-color, $is-light); } %collapsed-theme { header { diff --git a/tildes/scss/_variables.scss b/tildes/scss/_variables.scss index 59f6176..13b205a 100644 --- a/tildes/scss/_variables.scss +++ b/tildes/scss/_variables.scss @@ -35,12 +35,12 @@ $fg-lightest: $base1; $topic-tag-nsfw-color: $red; $topic-tag-spoiler-color: $yellow; -// Colors for comment tags -$comment-tag-exemplary-color: $green; -$comment-tag-joke-color: $cyan; -$comment-tag-noise-color: $yellow; -$comment-tag-offtopic-color: $blue; -$comment-tag-malice-color: $red; +// Colors for comment labels +$comment-label-exemplary-color: $green; +$comment-label-joke-color: $cyan; +$comment-label-noise-color: $yellow; +$comment-label-offtopic-color: $blue; +$comment-label-malice-color: $red; $sidebar-width: 300px; diff --git a/tildes/scss/modules/_btn.scss b/tildes/scss/modules/_btn.scss index 9d87a0e..9acb696 100644 --- a/tildes/scss/modules/_btn.scss +++ b/tildes/scss/modules/_btn.scss @@ -66,7 +66,7 @@ } } -.btn-comment-tag { +.btn-comment-label { display: inline-flex; align-items: center; margin: 0.4rem; @@ -87,7 +87,7 @@ } } -@mixin tagbutton($color) { +@mixin labelbutton($color) { color: $color; border-color: $color; @@ -101,22 +101,22 @@ } } -.btn-comment-tag-exemplary { - @include tagbutton($comment-tag-exemplary-color); +.btn-comment-label-exemplary { + @include labelbutton($comment-label-exemplary-color); } -.btn-comment-tag-joke { - @include tagbutton($comment-tag-joke-color); +.btn-comment-label-joke { + @include labelbutton($comment-label-joke-color); } -.btn-comment-tag-noise { - @include tagbutton($comment-tag-noise-color); +.btn-comment-label-noise { + @include labelbutton($comment-label-noise-color); } -.btn-comment-tag-offtopic { - @include tagbutton($comment-tag-offtopic-color); +.btn-comment-label-offtopic { + @include labelbutton($comment-label-offtopic-color); } -.btn-comment-tag-malice { - @include tagbutton($comment-tag-malice-color); +.btn-comment-label-malice { + @include labelbutton($comment-label-malice-color); } diff --git a/tildes/scss/modules/_comment.scss b/tildes/scss/modules/_comment.scss index 621ee6b..d65cbe6 100644 --- a/tildes/scss/modules/_comment.scss +++ b/tildes/scss/modules/_comment.scss @@ -87,7 +87,7 @@ } } -.comment-tags { +.comment-labels { margin: 0 0 0 0.4rem; list-style-type: none; @@ -97,7 +97,7 @@ } } -.comment-tag-buttons { +.comment-label-buttons { display: flex; margin: 0; padding: 0 1rem; @@ -109,7 +109,7 @@ } } -.comment-tag-count { +.comment-label-count { font-size: 0.6rem; } @@ -144,11 +144,11 @@ %collapsed { .comment-edited-time, + .comment-label-buttons, + .comment-labels, .comment-nav-link, .comment-posted-time, .comment-replies, - .comment-tag-buttons, - .comment-tags, .comment-text, .comment-votes, .post-buttons { @@ -211,6 +211,6 @@ .is-comment-exemplary { & > .comment-itself { margin-left: -2px; - border-left: 3px solid $comment-tag-exemplary-color !important; + border-left: 3px solid $comment-label-exemplary-color !important; } } diff --git a/tildes/scss/modules/_label.scss b/tildes/scss/modules/_label.scss index 5eb6414..aec9730 100644 --- a/tildes/scss/modules/_label.scss +++ b/tildes/scss/modules/_label.scss @@ -5,7 +5,7 @@ font-size: 0.6rem; } -.label-comment-tag { +.label-comment { font-size: 0.5rem; font-weight: bold; color: #fff; diff --git a/tildes/static/js/behaviors/comment-label-button.js b/tildes/static/js/behaviors/comment-label-button.js new file mode 100644 index 0000000..5cfd683 --- /dev/null +++ b/tildes/static/js/behaviors/comment-label-button.js @@ -0,0 +1,100 @@ +// Copyright (c) 2018 Tildes contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +$.onmount('[data-js-comment-label-button]', function() { + $(this).click(function(event) { + event.preventDefault(); + + var $comment = $(this).parents('.comment').first(); + var userLabels = $comment.attr('data-comment-user-labels'); + + // check if the label button div already exists and just remove it if so + $labelButtons = $comment.find('.comment-itself:first').find('.comment-label-buttons'); + if ($labelButtons.length > 0) { + $labelButtons.remove(); + return; + } + + var commentID = $comment.attr('data-comment-id36'); + var labelURL = '/api/web/comments/' + commentID + '/labels/'; + + var labeltemplate = document.querySelector('#comment-label-options'); + var clone = document.importNode(labeltemplate.content, true); + var options = clone.querySelectorAll('a'); + + for (i = 0; i < options.length; i++) { + var label = options[i]; + var labelName = label.textContent; + + var labelOptionActive = false; + if (userLabels.indexOf(labelName) !== -1) { + labelOptionActive = true; + } + + var labelPrompt = label.getAttribute("data-js-reason-prompt"); + + if (labelOptionActive) { + label.className += " btn btn-used"; + label.setAttribute('data-ic-delete-from', labelURL + labelName); + + // if the label requires a prompt, confirm that they want to remove it + // (since we don't want to accidentally lose the reason they typed in) + if (labelPrompt) { + label.setAttribute("data-ic-confirm", "Remove your "+labelName+" label?"); + } + + $(label).on('after.success.ic', function(evt) { + Tildes.removeUserLabel(commentID, evt.target.textContent); + }); + } else { + label.setAttribute('data-ic-put-to', labelURL + labelName); + + if (labelPrompt) { + label.setAttribute("data-ic-prompt", labelPrompt); + label.setAttribute("data-ic-prompt-name", "reason"); + } + + $(label).on('after.success.ic', function(evt) { + Tildes.addUserLabel(commentID, evt.target.textContent); + }); + } + + label.setAttribute('data-ic-target', '#comment-' + commentID + ' .comment-itself:first'); + } + + // update Intercooler so it knows about these new elements + Intercooler.processNodes(clone); + + $comment.find(".post-buttons").first().after(clone); + }); +}); + +Tildes.removeUserLabel = function(commentID, labelName) { + $comment = $("#comment-" + commentID); + var userLabels = $comment.attr('data-comment-user-labels').split(" "); + + // if the label isn't there, don't need to do anything + labelIndex = userLabels.indexOf(labelName); + if (labelIndex === -1) { + return; + } + + // remove the label from the list and update the data attr + userLabels.splice(labelIndex, 1); + $comment.attr('data-comment-user-labels', userLabels.join(" ")); +} + +Tildes.addUserLabel = function(commentID, labelName) { + $comment = $("#comment-" + commentID); + var userLabels = $comment.attr('data-comment-user-labels').split(" "); + + // don't add the label again if it's already there + labelIndex = userLabels.indexOf(labelName); + if (labelIndex !== -1) { + return; + } + + // add the label to the list and update the data attr + userLabels.push(labelName); + $comment.attr('data-comment-user-labels', userLabels.join(" ")); +} diff --git a/tildes/static/js/behaviors/comment-tag-button.js b/tildes/static/js/behaviors/comment-tag-button.js deleted file mode 100644 index d18f1e0..0000000 --- a/tildes/static/js/behaviors/comment-tag-button.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2018 Tildes contributors -// SPDX-License-Identifier: AGPL-3.0-or-later - -$.onmount('[data-js-comment-tag-button]', function() { - $(this).click(function(event) { - event.preventDefault(); - - var $comment = $(this).parents('.comment').first(); - var user_tags = $comment.attr('data-comment-user-tags'); - - // check if the tagging button div already exists and just remove it if so - $tagButtons = $comment.find('.comment-itself:first').find('.comment-tag-buttons'); - if ($tagButtons.length > 0) { - $tagButtons.remove(); - return; - } - - var commentID = $comment.attr('data-comment-id36'); - var tagURL = '/api/web/comments/' + commentID + '/tags/'; - - var tagtemplate = document.querySelector('#comment-tag-options'); - var clone = document.importNode(tagtemplate.content, true); - var options = clone.querySelectorAll('a'); - - for (i = 0; i < options.length; i++) { - var tag = options[i]; - var tagName = tag.textContent; - - var tagOptionActive = false; - if (user_tags.indexOf(tagName) !== -1) { - tagOptionActive = true; - } - - var tagPrompt = tag.getAttribute("data-js-reason-prompt"); - - if (tagOptionActive) { - tag.className += " btn btn-used"; - tag.setAttribute('data-ic-delete-from', tagURL + tagName); - - // if the tag requires a prompt, confirm that they want to remove it - // (since we don't want to accidentally lose the reason they typed in) - if (tagPrompt) { - tag.setAttribute("data-ic-confirm", "Remove your "+tagName+" tag?"); - } - - $(tag).on('after.success.ic', function(evt) { - Tildes.removeUserTag(commentID, evt.target.textContent); - }); - } else { - tag.setAttribute('data-ic-put-to', tagURL + tagName); - - if (tagPrompt) { - tag.setAttribute("data-ic-prompt", tagPrompt); - tag.setAttribute("data-ic-prompt-name", "reason"); - } - - $(tag).on('after.success.ic', function(evt) { - Tildes.addUserTag(commentID, evt.target.textContent); - }); - } - - tag.setAttribute('data-ic-target', '#comment-' + commentID + ' .comment-itself:first'); - } - - // update Intercooler so it knows about these new elements - Intercooler.processNodes(clone); - - $comment.find(".post-buttons").first().after(clone); - }); -}); - -Tildes.removeUserTag = function(commentID, tagName) { - $comment = $("#comment-" + commentID); - var user_tags = $comment.attr('data-comment-user-tags').split(" "); - - // if the tag isn't there, don't need to do anything - tagIndex = user_tags.indexOf(tagName); - if (tagIndex === -1) { - return; - } - - // remove the tag from the list and update the data attr - user_tags.splice(tagIndex, 1); - $comment.attr('data-comment-user-tags', user_tags.join(" ")); -} - -Tildes.addUserTag = function(commentID, tagName) { - $comment = $("#comment-" + commentID); - var user_tags = $comment.attr('data-comment-user-tags').split(" "); - - // don't add the tag again if it's already there - tagIndex = user_tags.indexOf(tagName); - if (tagIndex !== -1) { - return; - } - - // add the tag to the list and update the data attr - user_tags.push(tagName); - $comment.attr('data-comment-user-tags', user_tags.join(" ")); -} diff --git a/tildes/tildes/enums.py b/tildes/tildes/enums.py index 2349d7d..1633486 100644 --- a/tildes/tildes/enums.py +++ b/tildes/tildes/enums.py @@ -37,8 +37,8 @@ class CommentSortOption(enum.Enum): return "most {}".format(self.name.lower()) -class CommentTagOption(enum.Enum): - """Enum for the (site-wide) comment tag options.""" +class CommentLabelOption(enum.Enum): + """Enum for the (site-wide) comment label options.""" EXEMPLARY = enum.auto() JOKE = enum.auto() @@ -48,7 +48,7 @@ class CommentTagOption(enum.Enum): @property def reason_prompt(self) -> Optional[str]: - """Return the reason prompt for this tag, if any.""" + """Return the reason prompt for this label, if any.""" if self.name == "EXEMPLARY": return ( "What makes this comment exemplary? " diff --git a/tildes/tildes/models/comment/__init__.py b/tildes/tildes/models/comment/__init__.py index a0eee65..fa574c5 100644 --- a/tildes/tildes/models/comment/__init__.py +++ b/tildes/tildes/models/comment/__init__.py @@ -4,6 +4,6 @@ from .comment import Comment, EDIT_GRACE_PERIOD from .comment_notification import CommentNotification from .comment_notification_query import CommentNotificationQuery from .comment_query import CommentQuery -from .comment_tag import CommentTag +from .comment_label import CommentLabel from .comment_tree import CommentTree from .comment_vote import CommentVote diff --git a/tildes/tildes/models/comment/comment.py b/tildes/tildes/models/comment/comment.py index 14edb16..0685408 100644 --- a/tildes/tildes/models/comment/comment.py +++ b/tildes/tildes/models/comment/comment.py @@ -157,7 +157,7 @@ class Comment(DatabaseModel): acl.append((Allow, Everyone, "view")) # view exemplary reasons: - # - only author gets shown the reasons (admins can see as well with all tags) + # - only author gets shown the reasons (admins can see as well with all labels) acl.append((Allow, self.user_id, "view_exemplary_reasons")) # vote: @@ -169,14 +169,14 @@ class Comment(DatabaseModel): acl.append((Deny, self.user_id, "vote")) acl.append((Allow, Authenticated, "vote")) - # tag: - # - removed comments can't be tagged by anyone - # - otherwise, people with the "comment.tag" permission other than the author + # label: + # - removed comments can't be labeled by anyone + # - otherwise, people with the "comment.label" permission other than the author if self.is_removed: - acl.append((Deny, Everyone, "tag")) + acl.append((Deny, Everyone, "label")) - acl.append((Deny, self.user_id, "tag")) - acl.append((Allow, "comment.tag", "tag")) + acl.append((Deny, self.user_id, "label")) + acl.append((Allow, "comment.label", "label")) # reply: # - removed comments can't be replied to by anyone @@ -205,7 +205,7 @@ class Comment(DatabaseModel): # tools that require specifically granted permissions acl.append((Allow, "admin", "remove")) - acl.append((Allow, "admin", "view_tags")) + acl.append((Allow, "admin", "view_labels")) acl.append(DENY_ALL) @@ -238,34 +238,34 @@ class Comment(DatabaseModel): return f"{self.topic.permalink}#comment-{self.parent_comment_id36}" @property - def tag_counts(self) -> Counter: - """Counter for number of times each tag is on this comment.""" - return Counter([tag.name for tag in self.tags]) + def label_counts(self) -> Counter: + """Counter for number of times each label is on this comment.""" + return Counter([label.name for label in self.labels]) @property - def tag_weights(self) -> Counter: - """Counter with cumulative weights of each tag on this comment.""" + def label_weights(self) -> Counter: + """Counter with cumulative weights of each label on this comment.""" weights: Counter = Counter() - for tag in self.tags: - weights[tag.name] += tag.weight + for label in self.labels: + weights[label.name] += label.weight return weights - def tags_by_user(self, user: User) -> Sequence[str]: - """Return list of tag names that a user has applied to this comment.""" - return [tag.name for tag in self.tags if tag.user_id == user.user_id] + def labels_by_user(self, user: User) -> Sequence[str]: + """Return list of label names that a user has applied to this comment.""" + return [label.name for label in self.labels if label.user_id == user.user_id] - def is_tag_active(self, tag_name: str) -> bool: - """Return whether a tag has been applied enough to be considered "active".""" - tag_weight = self.tag_weights[tag_name] + def is_label_active(self, label_name: str) -> bool: + """Return whether a label has been applied enough to be considered "active".""" + label_weight = self.label_weights[label_name] - # all tags must have at least 1.0 weight - if tag_weight < 1.0: + # all labels must have at least 1.0 weight + if label_weight < 1.0: return False # for "noise", weight must be more than 1/5 of the vote count (5 votes - # effectively override 1.0 of tag weight) - if tag_name == "noise" and self.num_votes >= tag_weight * 5: + # effectively override 1.0 of label weight) + if label_name == "noise" and self.num_votes >= label_weight * 5: return False return True diff --git a/tildes/tildes/models/comment/comment_tag.py b/tildes/tildes/models/comment/comment_label.py similarity index 69% rename from tildes/tildes/models/comment/comment_tag.py rename to tildes/tildes/models/comment/comment_label.py index a4e8413..451419e 100644 --- a/tildes/tildes/models/comment/comment_tag.py +++ b/tildes/tildes/models/comment/comment_label.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Tildes contributors # SPDX-License-Identifier: AGPL-3.0-or-later -"""Contains the CommentTag class.""" +"""Contains the CommentLabel class.""" from datetime import datetime from typing import Optional @@ -11,22 +11,22 @@ from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import backref, relationship from sqlalchemy.sql.expression import text -from tildes.enums import CommentTagOption +from tildes.enums import CommentLabelOption from tildes.models import DatabaseModel from tildes.models.user import User from .comment import Comment -class CommentTag(DatabaseModel): - """Model for the tags attached to comments by users.""" +class CommentLabel(DatabaseModel): + """Model for the labels attached to comments by users.""" - __tablename__ = "comment_tags" + __tablename__ = "comment_labels" comment_id: int = Column( Integer, ForeignKey("comments.comment_id"), nullable=False, primary_key=True ) - tag: CommentTagOption = Column( - ENUM(CommentTagOption), nullable=False, primary_key=True + label: CommentLabelOption = Column( + ENUM(CommentLabelOption), nullable=False, primary_key=True ) user_id: int = Column( Integer, ForeignKey("users.user_id"), nullable=False, primary_key=True @@ -37,26 +37,26 @@ class CommentTag(DatabaseModel): weight: float = Column(REAL, nullable=False, server_default=text("1.0")) reason: Optional[str] = Column(Text) - comment: Comment = relationship(Comment, backref=backref("tags", lazy=False)) + comment: Comment = relationship(Comment, backref=backref("labels", lazy=False)) user: User = relationship(User, lazy=False, innerjoin=True) def __init__( self, comment: Comment, user: User, - tag: CommentTagOption, + label: CommentLabelOption, weight: float, reason: Optional[str] = None, ) -> None: - """Add a new tag to a comment.""" + """Add a new label to a comment.""" # pylint: disable=too-many-arguments self.comment_id = comment.comment_id self.user_id = user.user_id - self.tag = tag + self.label = label self.weight = weight self.reason = reason @property def name(self) -> str: - """Return the name of the tag (to avoid needing to do .tag.name).""" - return self.tag.name.lower() + """Return the name of the label (to avoid needing to do .label.name).""" + return self.label.name.lower() diff --git a/tildes/tildes/models/comment/comment_tree.py b/tildes/tildes/models/comment/comment_tree.py index 2d71dcb..89fb711 100644 --- a/tildes/tildes/models/comment/comment_tree.py +++ b/tildes/tildes/models/comment/comment_tree.py @@ -172,14 +172,14 @@ class CommentTree: order=self.sort.name, ) - def collapse_from_tags(self) -> None: - """Collapse comments based on how they've been tagged.""" + def collapse_from_labels(self) -> None: + """Collapse comments based on how they've been labeled.""" for comment in self.comments: # never affect the viewer's own comments if comment.user == self.viewer: continue - if comment.is_tag_active("noise"): + if comment.is_label_active("noise"): comment.collapsed_state = "full" def uncollapse_new_comments(self, threshold: datetime) -> None: @@ -276,25 +276,25 @@ class CommentInTree(ObjectProxy): Returns a tuple, which allows sorting the comments into "tiers" and then still supporting further sorting inside those tiers when it's useful. For example, - comments tagged as offtopic can be sorted below all non-offtopic comments, but + comments labeled as offtopic can be sorted below all non-offtopic comments, but then still sorted by votes relative to other offtopic comments. """ if self.is_removed: return (-100,) - if self.is_tag_active("noise"): + if self.is_label_active("noise"): return (-2, self.num_votes) - if self.is_tag_active("offtopic"): + if self.is_label_active("offtopic"): return (-1, self.num_votes) - if self.is_tag_active("joke"): + if self.is_label_active("joke"): return (self.num_votes // 2,) - # Exemplary comments add 1.0 to the the total weight of the exemplary tags, and - # multiply the vote count by that. At minimum (weight 1.0), votes are doubled. - if self.is_tag_active("exemplary"): - multiplier = self.tag_weights["exemplary"] + 1.0 + # Exemplary comments add 1.0 to the the total weight of the exemplary labels, + # and multiply the vote count by that. Minimum (weight 1.0), votes are doubled. + if self.is_label_active("exemplary"): + multiplier = self.label_weights["exemplary"] + 1.0 return (round(multiplier * self.num_votes),) return (self.num_votes,) diff --git a/tildes/tildes/models/user/user.py b/tildes/tildes/models/user/user.py index 007dca8..83cf8e4 100644 --- a/tildes/tildes/models/user/user.py +++ b/tildes/tildes/models/user/user.py @@ -101,7 +101,7 @@ class User(DatabaseModel): _filtered_topic_tags: List[Ltree] = Column( "filtered_topic_tags", ArrayOfLtree, nullable=False, server_default="{}" ) - comment_tag_weight: Optional[float] = Column(REAL) + comment_label_weight: Optional[float] = Column(REAL) @hybrid_property def filtered_topic_tags(self) -> List[str]: @@ -228,9 +228,9 @@ class User(DatabaseModel): else: raise ValueError("Unknown permissions format") - # give the user the "comment.tag" permission if they're over a week old + # give the user the "comment.label" permission if they're over a week old if utc_now() - self.created_time > timedelta(days=7): - principals.append("comment.tag") + principals.append("comment.label") return principals diff --git a/tildes/tildes/routes.py b/tildes/tildes/routes.py index b2bfe60..ae051ec 100644 --- a/tildes/tildes/routes.py +++ b/tildes/tildes/routes.py @@ -134,7 +134,9 @@ def add_intercooler_routes(config: Configurator) -> None: "comment_vote", "/comments/{comment_id36}/vote", factory=comment_by_id36 ) add_ic_route( - "comment_tag", "/comments/{comment_id36}/tags/{name}", factory=comment_by_id36 + "comment_label", + "/comments/{comment_id36}/labels/{name}", + factory=comment_by_id36, ) add_ic_route( "comment_mark_read", diff --git a/tildes/tildes/schemas/comment.py b/tildes/tildes/schemas/comment.py index f215d80..d181aec 100644 --- a/tildes/tildes/schemas/comment.py +++ b/tildes/tildes/schemas/comment.py @@ -5,7 +5,7 @@ from marshmallow import Schema -from tildes.enums import CommentTagOption +from tildes.enums import CommentLabelOption from tildes.schemas.fields import Enum, ID36, Markdown, SimpleString @@ -21,10 +21,10 @@ class CommentSchema(Schema): strict = True -class CommentTagSchema(Schema): - """Marshmallow schema for comment tags.""" +class CommentLabelSchema(Schema): + """Marshmallow schema for comment labels.""" - name = Enum(CommentTagOption) + name = Enum(CommentLabelOption) reason = SimpleString(missing=None) class Meta: diff --git a/tildes/tildes/templates/macros/comments.jinja2 b/tildes/tildes/templates/macros/comments.jinja2 index 0848c76..050b1da 100644 --- a/tildes/tildes/templates/macros/comments.jinja2 +++ b/tildes/tildes/templates/macros/comments.jinja2 @@ -19,8 +19,8 @@ data-comment-depth="{{ loop.depth0 }}" {% endif %} - {% if request.has_permission("tag", comment) %} - data-comment-user-tags="{{ comment.tags_by_user(request.user)|join(' ') }}" + {% if request.has_permission("label", comment) %} + data-comment-user-labels="{{ comment.labels_by_user(request.user)|join(' ') }}" {% endif %} > {{ render_comment_contents(comment, is_individual_comment) }} @@ -97,28 +97,28 @@ {% endif %} {% if (request.has_permission("view_exemplary_reasons", comment) - and comment.is_tag_active("exemplary")) %} + and comment.is_label_active("exemplary")) %}
- Exemplary - x{{ comment.tag_counts["exemplary"] }} + Exemplary + x{{ comment.label_counts["exemplary"] }}
    - {% for tag in comment.tags if tag.name == "exemplary" %} -
  • "{{ tag.reason }}"
  • + {% for label in comment.labels if label.name == "exemplary" %} +
  • "{{ label.reason }}"
  • {% endfor %}
{% endif %} - {% if comment.tag_counts and request.has_permission("view_tags", comment) %} -
    - {% for tag_name, weight in comment.tag_weights.most_common() %} + {% if comment.label_counts and request.has_permission("view_labels", comment) %} +
      + {% for label_name, weight in comment.label_weights.most_common() %}
    • - {{ tag_name }} - + {{ label_name }} + {{ weight }}: - {% for tag in comment.tags if tag.name == tag_name %} - {{ username_linked(tag.user.username) }} ({{ tag.weight }}{{ ' "%s"' % tag.reason if tag.reason else '' }}) + {% for label in comment.labels if label.name == label_name %} + {{ username_linked(label.user.username) }} ({{ label.weight }}{{ ' "%s"' % label.reason if label.reason else '' }}) {% endfor %}
    • @@ -165,8 +165,8 @@ {% endif %} - {% if request.has_permission('tag', comment) %} -
    • Tag
    • + {% if request.has_permission('label', comment) %} +
    • Label
    • {% endif %} {% if request.has_permission('edit', comment) %} @@ -237,7 +237,7 @@ {% do classes.append('is-comment-by-op') %} {% endif %} - {% if request.has_permission('view', comment) and comment.is_tag_active("exemplary") %} + {% if request.has_permission('view', comment) and comment.is_label_active("exemplary") %} {% do classes.append("is-comment-exemplary") %} {% endif %} @@ -251,16 +251,16 @@ {{ classes|join(' ') }} {% endmacro %} -{% macro comment_tag_options_template(options) %} -