mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
flag low effort commenters
This commit is contained in:
parent
fa953ba976
commit
709980c6f3
13 changed files with 90 additions and 6 deletions
|
@ -1426,6 +1426,8 @@ def create_post_reply(activity_log: ActivityPubLog, community: Community, in_rep
|
|||
post_reply.ranking = confidence(post_reply.up_votes, post_reply.down_votes)
|
||||
db.session.commit()
|
||||
|
||||
user.recalculate_avg_comment_length()
|
||||
|
||||
# send notification to the post/comment being replied to
|
||||
if parent_comment_id:
|
||||
notify_about_post_reply(parent_comment, post_reply)
|
||||
|
|
|
@ -3,9 +3,10 @@ from time import time
|
|||
from typing import List, Union
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from flask import current_app, escape, url_for, render_template_string
|
||||
from flask_login import UserMixin, current_user
|
||||
from sqlalchemy import or_, text
|
||||
from sqlalchemy import or_, text, desc
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from flask_babel import _, lazy_gettext as _l
|
||||
from sqlalchemy.orm import backref
|
||||
|
@ -615,6 +616,8 @@ class User(UserMixin, db.Model):
|
|||
markdown_editor = db.Column(db.Boolean, default=False)
|
||||
interface_language = db.Column(db.String(10)) # a locale that the translation system understands e.g. 'en' or 'en-us'. If empty, use browser default
|
||||
language_id = db.Column(db.Integer, db.ForeignKey('language.id')) # the default choice in the language dropdown when composing posts & comments
|
||||
average_comment_length = db.Column(db.Integer)
|
||||
comment_length_warning = db.Column(db.Integer, default=15)
|
||||
|
||||
avatar = db.relationship('File', lazy='joined', foreign_keys=[avatar_id], single_parent=True, cascade="all, delete-orphan")
|
||||
cover = db.relationship('File', lazy='joined', foreign_keys=[cover_id], single_parent=True, cascade="all, delete-orphan")
|
||||
|
@ -808,6 +811,19 @@ class User(UserMixin, db.Model):
|
|||
else:
|
||||
self.attitude = (total_upvotes - total_downvotes) / (total_upvotes + total_downvotes)
|
||||
|
||||
def recalculate_avg_comment_length(self):
|
||||
replies = db.session.execute(text('SELECT body, body_html FROM "post_reply" WHERE user_id = :user_id ORDER BY created_at DESC LIMIT 30'),
|
||||
{'user_id': self.id}).all()
|
||||
lengths = []
|
||||
for reply in replies:
|
||||
if reply.body.strip() != '':
|
||||
lengths.append(len(reply.body.strip()))
|
||||
else:
|
||||
soup = BeautifulSoup(reply.body_html, 'html.parser')
|
||||
lengths.append(len(soup.get_text()))
|
||||
if len(lengths) > 5:
|
||||
self.average_comment_length = math.ceil(sum(lengths) / len(lengths))
|
||||
|
||||
def subscribed(self, community_id: int) -> int:
|
||||
if community_id is None:
|
||||
return False
|
||||
|
|
|
@ -111,6 +111,8 @@ def show_post(post_id: int):
|
|||
db.session.add(reply)
|
||||
db.session.commit()
|
||||
|
||||
current_user.recalculate_avg_comment_length()
|
||||
|
||||
notify_about_post_reply(None, reply)
|
||||
|
||||
# Subscribe to own comment
|
||||
|
@ -671,6 +673,8 @@ def add_reply(post_id: int, comment_id: int):
|
|||
db.session.add(reply)
|
||||
db.session.commit()
|
||||
|
||||
current_user.recalculate_avg_comment_length()
|
||||
|
||||
# Notify subscribers
|
||||
notify_about_post_reply(in_reply_to, reply)
|
||||
|
||||
|
|
|
@ -15,3 +15,7 @@ $super-dark-grey: #424549;
|
|||
.orangered {
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: purple;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@ nav, etc which are used site-wide */
|
|||
color: orangered;
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
color: orangered;
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,9 @@
|
|||
</div>
|
||||
{% if post.type == POST_TYPE_POLL %}
|
||||
<div class="post_poll">
|
||||
{% if poll_results %}
|
||||
{% if poll_results and poll_total_votes == 0 %}
|
||||
<p>{{ _('The poll has finished, yet no votes were cast.') }}</p>
|
||||
{% elif poll_results and poll_total_votes %}
|
||||
<ul>
|
||||
{% for choice in poll_choices %}
|
||||
<li>
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
{% elif comment['comment'].author.reputation < 0 %}
|
||||
<span class="fe fe-warning orangered" title="Low reputation."> </span>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated and current_user.comment_length_warning and comment['comment'].author.average_comment_length and comment['comment'].author.average_comment_length <= current_user.comment_length_warning %}
|
||||
<span class="fe fe-warning purple" title="{{ _('Usually makes short and low effort comments.') }}"> </span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if comment['comment'].author.id == post.author.id %}<span title="Submitter of original post" aria-label="{{ _('Post creator') }}" class="small">[OP]</span>{% endif %}
|
||||
<span class="text-muted small" aria_label="{{ _('When: ') }}">{{ moment(comment['comment'].posted_at).fromNow(refresh=True) }}{% if comment['comment'].edited_at %}, edited {{ moment(comment['comment'].edited_at).fromNow(refresh=True) }} {% endif %}</span>
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
{{ render_field(form.markdown_editor) }}
|
||||
{{ render_field(form.default_sort) }}
|
||||
{{ render_field(form.theme) }}
|
||||
{{ render_field(form.comment_length_warning) }}
|
||||
<small class="field_hint">{{ _('Accounts which usually post comments shorter than this will have a warning. Set to 0 to disable warning.') }}</small>
|
||||
<h5>Import</h5>
|
||||
{{ render_field(form.import_file) }}
|
||||
{{ render_field(form.submit) }}
|
||||
|
|
|
@ -2,7 +2,7 @@ from flask import session
|
|||
from flask_login import current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SubmitField, PasswordField, BooleanField, EmailField, TextAreaField, FileField, \
|
||||
RadioField, DateField, SelectField
|
||||
RadioField, DateField, SelectField, IntegerField
|
||||
from wtforms.fields.choices import SelectMultipleField
|
||||
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, Length, Optional
|
||||
from flask_babel import _, lazy_gettext as _l
|
||||
|
@ -50,6 +50,7 @@ class SettingsForm(FlaskForm):
|
|||
]
|
||||
default_sort = SelectField(_l('By default, sort posts by'), choices=sorts, validators=[DataRequired()], coerce=str, render_kw={'class': 'form-select'})
|
||||
theme = SelectField(_l('Theme'), coerce=str, render_kw={'class': 'form-select'})
|
||||
comment_length_warning = IntegerField(_('Useless comment cutoff length'))
|
||||
submit = SubmitField(_l('Save settings'))
|
||||
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ def change_settings():
|
|||
current_user.email_unread = form.email_unread.data
|
||||
current_user.markdown_editor = form.markdown_editor.data
|
||||
current_user.interface_language = form.interface_language.data
|
||||
current_user.comment_length_warning = form.comment_length_warning.data
|
||||
session['ui_language'] = form.interface_language.data
|
||||
import_file = request.files['import_file']
|
||||
if propagate_indexable:
|
||||
|
@ -227,6 +228,7 @@ def change_settings():
|
|||
form.theme.data = current_user.theme
|
||||
form.markdown_editor.data = current_user.markdown_editor
|
||||
form.interface_language.data = current_user.interface_language
|
||||
form.comment_length_warning.data = current_user.comment_length_warning
|
||||
|
||||
return render_template('user/edit_settings.html', title=_('Edit profile'), form=form, user=current_user,
|
||||
moderating_communities=moderating_communities(current_user.get_id()),
|
||||
|
|
10
app/utils.py
10
app/utils.py
|
@ -258,6 +258,13 @@ def markdown_to_text(markdown_text) -> str:
|
|||
return markdown_text.replace("# ", '')
|
||||
|
||||
|
||||
def html_to_text(html) -> str:
|
||||
if html is None or html == '':
|
||||
return ''
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
return soup.get_text()
|
||||
|
||||
|
||||
def microblog_content_to_title(html: str) -> str:
|
||||
title = ''
|
||||
if '<p>' in html:
|
||||
|
@ -267,8 +274,7 @@ def microblog_content_to_title(html: str) -> str:
|
|||
if title and title.strip() != '' and len(title.strip()) >= 5:
|
||||
break
|
||||
else:
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
title = soup.get_text()
|
||||
title = html_to_text(html)
|
||||
|
||||
period_index = title.find('.')
|
||||
question_index = title.find('?')
|
||||
|
|
34
migrations/versions/e76f2067e689_comment_length.py
Normal file
34
migrations/versions/e76f2067e689_comment_length.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""comment length
|
||||
|
||||
Revision ID: e76f2067e689
|
||||
Revises: 05e3a7023a1e
|
||||
Create Date: 2024-05-21 21:58:22.133194
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e76f2067e689'
|
||||
down_revision = '05e3a7023a1e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('average_comment_length', sa.Integer(), nullable=True))
|
||||
batch_op.add_column(sa.Column('comment_length_warning', sa.Integer(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||
batch_op.drop_column('comment_length_warning')
|
||||
batch_op.drop_column('average_comment_length')
|
||||
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in a new issue