feat: Reports (#20)

* feat: added reports

* reports working

* changelog
pull/21/head
Divided by Zer0 2023-09-12 01:19:54 +02:00 committed by GitHub
parent 3eb37fa034
commit 2861f721af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 180 additions and 2 deletions

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
# 0.8.1 # 0.10.0
Added `/api/v1/reports` endpoint which can show and filter recent actions on the fediseer
# 0.9.1
Added sysadmins and moderators count. This allows instance admins to self-report how many people they have in sysadmins positions and how many in moderator positions. This might be useful to find which instances might be lacking in support for their user-base. Added sysadmins and moderators count. This allows instance admins to self-report how many people they have in sysadmins positions and how many in moderator positions. This might be useful to find which instances might be lacking in support for their user-base.

View File

@ -1,4 +1,5 @@
from flask_restx import fields from flask_restx import fields
from fediseer import enums
class Models: class Models:
def __init__(self,api): def __init__(self,api):
@ -65,3 +66,10 @@ class Models:
'sysadmins': fields.Integer(required=False, default=None, min=0, max=100, description="Report how many system administrators this instance currently has."), 'sysadmins': fields.Integer(required=False, default=None, min=0, max=100, description="Report how many system administrators this instance currently has."),
'moderators': fields.Integer(required=False, default=None, min=0, max=1000, description="Report how many instance moderators this instance currently has."), 'moderators': fields.Integer(required=False, default=None, min=0, max=1000, description="Report how many instance moderators this instance currently has."),
}) })
self.response_model_reports = api.model('ActivityReport', {
'source_domain': fields.String(description="The instance domain which initiated this activity", example="lemmy.dbzer0.com"),
'target_domain': fields.String(description="The instance domain which was the target of this activity", example="lemmy.dbzer0.com"),
'report_type': fields.String(description="The type of report activity", enum=[e.name for e in enums.ReportType]),
'report_activity': fields.String(description="The activity reported", enum=[e.name for e in enums.ReportActivity]),
'created': fields.DateTime(description="The date this record was added"),
})

View File

@ -6,6 +6,7 @@ import fediseer.apis.v1.guarantees as guarantees
import fediseer.apis.v1.activitypub as activitypub import fediseer.apis.v1.activitypub as activitypub
import fediseer.apis.v1.badges as badges import fediseer.apis.v1.badges as badges
import fediseer.apis.v1.find as find import fediseer.apis.v1.find as find
import fediseer.apis.v1.report as report
from fediseer.apis.v1.base import api from fediseer.apis.v1.base import api
api.add_resource(base.Suspicions, "/instances") api.add_resource(base.Suspicions, "/instances")
@ -22,3 +23,4 @@ api.add_resource(guarantees.Guarantors, "/guarantors/<string:domain>")
api.add_resource(guarantees.Guarantees, "/guarantees/<string:domain>") api.add_resource(guarantees.Guarantees, "/guarantees/<string:domain>")
api.add_resource(badges.GuaranteeBadge, "/badges/guarantees/<string:domain>.svg") api.add_resource(badges.GuaranteeBadge, "/badges/guarantees/<string:domain>.svg")
api.add_resource(badges.EndorsementBadge, "/badges/endorsements/<string:domain>.svg") api.add_resource(badges.EndorsementBadge, "/badges/endorsements/<string:domain>.svg")
api.add_resource(report.Report, "/reports/<int:page>")

View File

@ -1,6 +1,8 @@
from fediseer.apis.v1.base import * from fediseer.apis.v1.base import *
from fediseer.classes.instance import Censure,Endorsement from fediseer.classes.instance import Censure,Endorsement
from fediseer.utils import sanitize_string from fediseer.utils import sanitize_string
from fediseer.classes.reports import Report
from fediseer import enums
class CensuresGiven(Resource): class CensuresGiven(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
@ -135,6 +137,13 @@ class Censures(Resource):
reason=reason, reason=reason,
) )
db.session.add(new_censure) db.session.add(new_censure)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.ADDED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
logger.info(f"{instance.domain} Censured {domain}") logger.info(f"{instance.domain} Censured {domain}")
return {"message":'Changed'}, 200 return {"message":'Changed'}, 200
@ -174,6 +183,13 @@ class Censures(Resource):
if censure.reason == reason: if censure.reason == reason:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
censure.reason = reason censure.reason = reason
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.MODIFIED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
logger.info(f"{instance.domain} Modfied censure for {domain}") logger.info(f"{instance.domain} Modfied censure for {domain}")
return {"message":'Changed'}, 200 return {"message":'Changed'}, 200
@ -204,6 +220,13 @@ class Censures(Resource):
if not censure: if not censure:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
db.session.delete(censure) db.session.delete(censure)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.DELETED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
logger.info(f"{instance.domain} Withdrew censure from {domain}") logger.info(f"{instance.domain} Withdrew censure from {domain}")
return {"message":'Changed'}, 200 return {"message":'Changed'}, 200

View File

@ -1,5 +1,7 @@
from fediseer.apis.v1.base import * from fediseer.apis.v1.base import *
from fediseer.classes.instance import Endorsement,Censure from fediseer.classes.instance import Endorsement,Censure
from fediseer.classes.reports import Report
from fediseer import enums
class Approvals(Resource): class Approvals(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
@ -100,6 +102,13 @@ class Endorsements(Resource):
endorsed_id=target_instance.id, endorsed_id=target_instance.id,
) )
db.session.add(new_endorsement) db.session.add(new_endorsement)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.ENDORSEMENT,
report_activity=enums.ReportActivity.ADDED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
if not database.has_recent_endorsement(target_instance.id): if not database.has_recent_endorsement(target_instance.id):
activitypub_pm.pm_admins( activitypub_pm.pm_admins(
@ -137,6 +146,13 @@ class Endorsements(Resource):
if not endorsement: if not endorsement:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
db.session.delete(endorsement) db.session.delete(endorsement)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.ENDORSEMENT,
report_activity=enums.ReportActivity.DELETED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
activitypub_pm.pm_admins( activitypub_pm.pm_admins(
message=f"Oh no. {instance.domain} has just withdrawn the endorsement of your instance", message=f"Oh no. {instance.domain} has just withdrawn the endorsement of your instance",

View File

@ -1,5 +1,7 @@
from fediseer.apis.v1.base import * from fediseer.apis.v1.base import *
from fediseer.classes.instance import Guarantee, Endorsement, RejectionRecord from fediseer.classes.instance import Guarantee, Endorsement, RejectionRecord
from fediseer.classes.reports import Report
from fediseer import enums
class Guarantors(Resource): class Guarantors(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
@ -103,6 +105,13 @@ class Guarantees(Resource):
# endorsed_id=target_instance.id, # endorsed_id=target_instance.id,
# ) # )
# db.session.add(new_endorsement) # db.session.add(new_endorsement)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.GUARANTEE,
report_activity=enums.ReportActivity.ADDED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
activitypub_pm.pm_admins( activitypub_pm.pm_admins(
message=f"Congratulations! Your instance has just been [guaranteed](https://fediseer.com/faq#what-is-a-guarantee) by {instance.domain}. \n\nThis is an automated PM by the [Fediseer](https://fediseer.com) service. Replies will not be read.\nPlease contact @db0@lemmy.dbzer0.com for further inquiries.", message=f"Congratulations! Your instance has just been [guaranteed](https://fediseer.com/faq#what-is-a-guarantee) by {instance.domain}. \n\nThis is an automated PM by the [Fediseer](https://fediseer.com) service. Replies will not be read.\nPlease contact @db0@lemmy.dbzer0.com for further inquiries.",
@ -167,6 +176,13 @@ class Guarantees(Resource):
rejector_id=instance.id, rejector_id=instance.id,
) )
db.session.add(rejection) db.session.add(rejection)
new_report = Report(
source_domain=instance.domain,
target_domain=target_instance.domain,
report_type=enums.ReportType.GUARANTEE,
report_activity=enums.ReportActivity.DELETED,
)
db.session.add(new_report)
db.session.commit() db.session.commit()
try: try:
activitypub_pm.pm_admins( activitypub_pm.pm_admins(

View File

@ -0,0 +1,56 @@
from fediseer.apis.v1.base import *
from fediseer import enums
class Report(Resource):
get_parser = reqparse.RequestParser()
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("source_domains_csv", required=False, default=None, type=str, help="A csv of source domains for which to filter", location="args")
get_parser.add_argument("target_domains_csv", required=False, default=None, type=str, help="A csv of target domains for which to filter", location="args")
get_parser.add_argument("report_type", required=False, default=None, type=str, help=f"The activity of report to filer {[e.name for e in enums.ReportType]}", location="args")
get_parser.add_argument("report_activity", required=False, default=None, type=str, help=f"The activity of report to filer {[e.name for e in enums.ReportActivity]}", location="args")
@api.expect(get_parser)
@api.marshal_with(models.response_model_reports, code=200, description='Report', as_list=True)
@api.response(400, 'Validation Error', models.response_model_error)
def get(self,page=1):
'''Retrieve instance information via API Key at 10 results per page
'''
self.args = self.get_parser.parse_args()
source_domains = None
if self.args.source_domains_csv:
source_domains = self.args.source_domains_csv.split(',')
target_domains = None
if self.args.target_domains_csv:
target_domains = self.args.target_domains_csv.split(',')
report_type = None
if self.args.report_type:
try:
report_type = enums.ReportType[self.args.report_type]
except KeyError as err:
raise e.BadRequest(f"'{self.args.report_type}' is not a valid ReportType")
report_activity = None
if self.args.report_activity:
try:
report_activity = enums.ReportActivity[self.args.report_activity]
except KeyError as err:
raise e.BadRequest(f"'{self.args.report_activity}' is not a valid ReportActivity")
reports = database.get_reports(
source_instances = source_domains,
target_instances = target_domains,
report_type=report_type,
report_activity=report_activity,
page=page,
)
report_response = []
for r in reports:
report_response.append(
{
'source_domain': r.source_domain,
'target_domain': r.target_domain,
'report_type': r.report_type.name,
'report_activity': r.report_activity.name,
'created': r.created,
}
)
return report_response,200

View File

@ -9,6 +9,7 @@ from fediseer.utils import hash_api_key
from fediseer.classes.instance import Instance, Guarantee from fediseer.classes.instance import Instance, Guarantee
from fediseer.classes.user import User, Claim from fediseer.classes.user import User, Claim
import fediseer.classes.user import fediseer.classes.user
import fediseer.classes.reports
with OVERSEER.app_context(): with OVERSEER.app_context():

View File

@ -0,0 +1,16 @@
from datetime import datetime
from sqlalchemy import Enum
from fediseer.flask import db, SQLITE_MODE
from fediseer import enums
class Report(db.Model):
__tablename__ = "reports"
id = db.Column(db.Integer, primary_key=True)
# We don't do relations as we don't care if the columns are linked
source_domain = db.Column(db.String(255), unique=False, nullable=False, index=True)
target_domain = db.Column(db.String(255), unique=False, nullable=False, index=True)
report_type = db.Column(Enum(enums.ReportType), nullable=False, index=True)
report_activity = db.Column(Enum(enums.ReportActivity), nullable=False, index=True)
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)

View File

@ -1,4 +1,4 @@
FEDISEER_VERSION = "0.9.1" FEDISEER_VERSION = "0.10.0"
SUPPORTED_SOFTWARE = [ SUPPORTED_SOFTWARE = [
"lemmy", "lemmy",
"mastodon", "mastodon",

View File

@ -10,6 +10,8 @@ from fediseer.utils import hash_api_key
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from fediseer.classes.instance import Instance, Endorsement, Guarantee, RejectionRecord, Censure from fediseer.classes.instance import Instance, Endorsement, Guarantee, RejectionRecord, Censure
from fediseer.classes.user import Claim, User from fediseer.classes.user import Claim, User
from fediseer.classes.reports import Report
from fediseer import enums
def get_all_instances(min_endorsements = 0, min_guarantors = 1): def get_all_instances(min_endorsements = 0, min_guarantors = 1):
query = db.session.query( query = db.session.query(
@ -300,3 +302,25 @@ def get_instances_by_ids(instance_ids):
Instance.id.in_(instance_ids) Instance.id.in_(instance_ids)
) )
return query return query
def get_reports(
source_instances: list = None,
target_instances: list = None,
report_type: enums.ReportType = None,
report_activity: enums.ReportActivity = None,
page: int = 1,
):
query = Report.query
if source_instances is not None and len(source_instances) > 0:
query = query.filter(Report.source_domain.in_(source_instances)
)
if target_instances is not None and len(target_instances) > 0:
query = query.filter(Report.target_domain.in_(target_instances)
)
if report_type is not None:
query = query.filter(Report.report_type == report_type.name
)
if report_activity is not None:
query = query.filter(Report.report_activity == report_activity.name
)
return query.order_by(Report.created.desc()).offset(10 * (page - 1)).limit(10).all()

12
fediseer/enums.py 100644
View File

@ -0,0 +1,12 @@
import enum
class ReportType(enum.Enum):
GUARANTEE = 0
ENDORSEMENT = 1
CENSURE = 2
RESTRICTION = 3
class ReportActivity(enum.Enum):
ADDED = 0
DELETED = 1
MODIFIED = 2