Add content,comment opinions to comment server #48

Closed
tiger5226 wants to merge 1 commit from opinions into master
8 changed files with 187 additions and 21 deletions

View file

@ -28,4 +28,7 @@ logging:
datefmt: "%Y-%m-%d %H:%M:%S" datefmt: "%Y-%m-%d %H:%M:%S"
host: localhost host: localhost
port: 5921 port: 5921
lbrynet: http://localhost:5279 lbrynet: http://localhost:5279
notifications:
url: https://api.lbry.com/event/comment
auth_token: mytoken

View file

@ -48,3 +48,33 @@ ALTER TABLE COMMENT
CREATE INDEX `claim_comment_index` ON `COMMENT` (`lbryclaimid`, `commentid`); CREATE INDEX `claim_comment_index` ON `COMMENT` (`lbryclaimid`, `commentid`);
CREATE INDEX `channel_comment_index` ON `COMMENT` (`channelid`, `commentid`); CREATE INDEX `channel_comment_index` ON `COMMENT` (`channelid`, `commentid`);
CREATE TABLE `COMMENTOPINION` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`commentid` char(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`channelid` char(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`signature` char(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`signingts` varchar(22) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`timestamp` int NOT NULL,
`rating` tinyint DEFAULT '1',
PRIMARY KEY (`id`),
KEY `comment_channel_fk` (`channelid`),
CONSTRAINT `comment_fk` FOREIGN KEY (`commentid`) REFERENCES `COMMENT` (`commentid`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `comment_channel_fk` FOREIGN KEY (`channelid`) REFERENCES `CHANNEL` (`claimid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
CREATE TABLE `CONTENTOPINION` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`claimid` char(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`channelid` char(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`signature` char(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`signingts` varchar(22) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`timestamp` int NOT NULL,
`rating` tinyint DEFAULT '1',
PRIMARY KEY (`id`),
KEY `comment_channel_fk` (`channelid`),
KEY `lbryclaimid` (`lbryclaimid`),
CONSTRAINT `comment_channel_fk` FOREIGN KEY (`channelid`) REFERENCES `CHANNEL` (`claimid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

View file

@ -51,6 +51,50 @@ class Comment(Model):
) )
class CommentOpinion(Model):
id = BigAutoField(column_name='id', primary_key=True)
comment = ForeignKeyField(
backref='opinions',
column_name='commentid',
field='comment_id',
model=Comment,
null=True
)
channel = ForeignKeyField(
backref='opinions',
column_name='channelid',
field='claim_id',
model=Channel,
null=True
)
signature = FixedCharField(max_length=128, column_name='signature', null=True, unique=True)
signing_ts = TextField(column_name='signingts', null=True)
timestamp = IntegerField(column_name='timestamp')
rating = SmallIntegerField(column_name='rating', null=False)
class Meta:
table_name = 'COMMENTOPINION'
class ContentOpinion(Model):
id = BigAutoField(column_name='id', primary_key=True)
channel = ForeignKeyField(
backref='opinions',
column_name='channelid',
field='claim_id',
model=Channel,
null=True
)
claim_id = FixedCharField(max_length=40, column_name='lbryclaimid')
signature = FixedCharField(max_length=128, column_name='signature', null=True, unique=True)
signing_ts = TextField(column_name='signingts', null=True)
timestamp = IntegerField(column_name='timestamp')
rating = SmallIntegerField(column_name='rating', null=False, default=0)
class Meta:
table_name = 'CONTENTOPINION'
FIELDS = { FIELDS = {
'comment': Comment.comment, 'comment': Comment.comment,
'comment_id': Comment.comment_id, 'comment_id': Comment.comment_id,
@ -66,17 +110,21 @@ FIELDS = {
} }
def comment_list(claim_id: str = None, parent_id: str = None, def comment_list(claim_id: str = None,
top_level: bool = False, exclude_mode: str = None, parent_id: str = None,
page: int = 1, page_size: int = 50, expressions=None, top_level: bool = False,
select_fields: list = None, exclude_fields: list = None) -> dict: exclude_mode: str = None,
page: int = 1,
page_size: int = 50, expressions=None,
select_fields: list = None,
exclude_fields: list = None) -> dict:
fields = FIELDS.keys() fields = FIELDS.keys()
if exclude_fields: if exclude_fields:
fields -= set(exclude_fields) fields -= set(exclude_fields)
if select_fields: if select_fields:
fields &= set(select_fields) fields &= set(select_fields)
attributes = [FIELDS[field] for field in fields] attributes = [FIELDS[field] for field in fields]
query = Comment.select(*attributes) query = Comment.select(fn.SUM(CommentOpinion.rating).alias('rating'), *attributes)
# todo: allow this process to be more automated, so it can just be an expression # todo: allow this process to be more automated, so it can just be an expression
if claim_id: if claim_id:
@ -97,6 +145,8 @@ def comment_list(claim_id: str = None, parent_id: str = None,
total = query.count() total = query.count()
query = (query query = (query
.join(Channel, JOIN.LEFT_OUTER) .join(Channel, JOIN.LEFT_OUTER)
.join(CommentOpinion, JOIN.LEFT_OUTER)
.group_by(Comment.comment_id)
.order_by(Comment.timestamp.desc()) .order_by(Comment.timestamp.desc())
.paginate(page, page_size)) .paginate(page, page_size))
items = [clean(item) for item in query.dicts()] items = [clean(item) for item in query.dicts()]
@ -203,6 +253,26 @@ def set_hidden_flag(comment_ids: typing.List[str], hidden=True) -> bool:
return update.execute() > 0 return update.execute() > 0
def create_comment_opinion(comment_id: str = None,
channel_id: str = None,
channel_name: str = None,
signature: str = None,
signing_ts: str = None,
rating: int = None) -> dict:
channel, _ = Channel.get_or_create(name=channel_name, claim_id=channel_id)
timestamp = int(time.time())
new_comment_opinion = CommentOpinion.create(
commentid=comment_id,
channel=channel,
signature=signature,
signing_ts=signing_ts,
timestamp=timestamp,
rating=rating,
)
return new_comment_opinion
if __name__ == '__main__': if __name__ == '__main__':
logger = logging.getLogger('peewee') logger = logging.getLogger('peewee')
logger.addHandler(logging.StreamHandler()) logger.addHandler(logging.StreamHandler())

View file

@ -10,9 +10,9 @@ from aiohttp import web
from peewee import * from peewee import *
from src.server.handles import api_endpoint, get_api_endpoint from src.server.handles import api_endpoint, get_api_endpoint
from src.database.models import Comment, Channel from src.database.models import Comment, Channel, CommentOpinion, ContentOpinion
MODELS = [Comment, Channel] MODELS = [Comment, Channel, CommentOpinion, ContentOpinion]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -11,7 +11,7 @@ from src.server.external import send_notification
from src.server.validation import validate_signature_from_claim from src.server.validation import validate_signature_from_claim
from src.misc import clean_input_params, get_claim_from_id from src.misc import clean_input_params, get_claim_from_id
from src.server.errors import make_error, report_error from src.server.errors import make_error, report_error
from src.database.models import Comment, Channel from src.database.models import Comment, Channel, create_comment_opinion
from src.database.models import get_comment from src.database.models import get_comment
from src.database.models import comment_list from src.database.models import comment_list
from src.database.models import create_comment from src.database.models import create_comment
@ -19,7 +19,6 @@ from src.database.models import edit_comment
from src.database.models import delete_comment from src.database.models import delete_comment
from src.database.models import set_hidden_flag from src.database.models import set_hidden_flag
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -151,10 +150,10 @@ async def handle_hide_comments(app: web.Application, pieces: list, hide: bool =
channel = claims[claim_id]['signing_channel'] channel = claims[claim_id]['signing_channel']
piece = pieces_by_id[comment_id] piece = pieces_by_id[comment_id]
is_valid_signature = validate_signature_from_claim( is_valid_signature = validate_signature_from_claim(
claim=channel, claim=channel,
signature=piece['signature'], signature=piece['signature'],
signing_ts=piece['signing_ts'], signing_ts=piece['signing_ts'],
data=piece['comment_id'] data=piece['comment_id']
) )
if not is_valid_signature: if not is_valid_signature:
raise ValueError(f'could not validate signature on comment_id: {comment_id}') raise ValueError(f'could not validate signature on comment_id: {comment_id}')
@ -192,8 +191,8 @@ async def handle_edit_comment(app, comment: str = None, comment_id: str = None,
# TODO: retrieve stake amounts for each channel & store in db # TODO: retrieve stake amounts for each channel & store in db
async def handle_create_comment(app, comment: str = None, claim_id: str = None, async def handle_create_comment(app, comment: str = None, claim_id: str = None,
parent_id: str = None, channel_id: str = None, channel_name: str = None, parent_id: str = None, channel_id: str = None, channel_name: str = None,
signature: str = None, signing_ts: str = None) -> dict: signature: str = None, signing_ts: str = None) -> dict:
with app['db'].atomic(): with app['db'].atomic():
comment = create_comment( comment = create_comment(
comment=comment, comment=comment,
@ -208,18 +207,37 @@ async def handle_create_comment(app, comment: str = None, claim_id: str = None,
return comment return comment
async def handle_create_comment_opinion(app,
comment_id: str = None,
channel_id: str = None,
channel_name: str = None,
signature: str = None,
signing_ts: str = None,
rating: int = None) -> dict:
with app['db'].atomic():
return create_comment_opinion(
comment_id=comment_id,
channel_id=channel_id,
channel_name=channel_name,
signature=signature,
signing_ts=signing_ts,
rating=rating,
)
METHODS = { METHODS = {
'ping': ping, 'ping': ping,
'get_claim_comments': handle_get_claim_comments, # this gets used 'get_claim_comments': handle_get_claim_comments, # this gets used
'get_claim_hidden_comments': handle_get_claim_hidden_comments, # this gets used 'get_claim_hidden_comments': handle_get_claim_hidden_comments, # this gets used
'get_comment_ids': handle_get_comment_ids, 'get_comment_ids': handle_get_comment_ids,
'get_comments_by_id': handle_get_comments_by_id, # this gets used 'get_comments_by_id': handle_get_comments_by_id, # this gets used
'get_channel_from_comment_id': handle_get_channel_from_comment_id, # this gets used 'get_channel_from_comment_id': handle_get_channel_from_comment_id, # this gets used
'create_comment': handle_create_comment, # this gets used 'create_comment': handle_create_comment, # this gets used
'create_comment_opinion': handle_create_comment_opinion, # this gets used
'delete_comment': handle_abandon_comment, 'delete_comment': handle_abandon_comment,
'abandon_comment': handle_abandon_comment, # this gets used 'abandon_comment': handle_abandon_comment, # this gets used
'hide_comments': handle_hide_comments, # this gets used 'hide_comments': handle_hide_comments, # this gets used
'edit_comment': handle_edit_comment # this gets used 'edit_comment': handle_edit_comment # this gets used
} }

View file

@ -0,0 +1,21 @@
# For a quick start check out our HTTP Requests collection (Tools|HTTP Client|Open HTTP Requests Collection).
#
# Following HTTP Request Live Templates are available:
# * 'gtrp' and 'gtr' create a GET request with or without query parameters;
# * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body;
# * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data);
POST http://localhost:5921/api
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": null,
"method": "get_claim_comments",
"params": {
"claim_id": "6d266af6c25c80fa2ac6cc7662921ad2e90a07e7"
}
}
###

View file

@ -0,0 +1,24 @@
# For a quick start check out our HTTP Requests collection (Tools|HTTP Client|Open HTTP Requests Collection).
#
# Following HTTP Request Live Templates are available:
# * 'gtrp' and 'gtr' create a GET request with or without query parameters;
# * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body;
# * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data);
POST http://localhost:5921/api
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": null,
"method": "create_comment_opinion",
"params": {
"comment_id": "34c7b8266e12eb4887da31ef5401c36484e5c450e979736c1caa19a0d2fd1470",
"channel_name": "@bbbbb",
"channel_id": "9cb713f01bf247a0e03170b5ed00d5161340c486",
"signing_ts": "1234567",
"rating": 1
}
}
###

View file

@ -4,7 +4,7 @@
# * 'gtrp' and 'gtr' create a GET request with or without query parameters; # * 'gtrp' and 'gtr' create a GET request with or without query parameters;
# * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body; # * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body;
# * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data); # * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data);
POST http://localhost:5922/api POST http://localhost:5921/api
Content-Type: application/json Content-Type: application/json
{ {