Add content,comment opinions to comment server #48
8 changed files with 187 additions and 21 deletions
|
@ -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
|
|
@ -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
|
|
@ -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())
|
||||||
|
|
|
@ -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__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
21
test/http_requests/comment-request_local.http
Normal file
21
test/http_requests/comment-request_local.http
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
24
test/http_requests/create-comment-opinion-local.http
Normal file
24
test/http_requests/create-comment-opinion-local.http
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue