Skip to content

Commit

Permalink
Added edit metadata GUI elements
Browse files Browse the repository at this point in the history
  • Loading branch information
devos50 committed Oct 21, 2022
1 parent c4d7d75 commit 69f04fb
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/tribler/core/components/knowledge/db/knowledge_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
from dataclasses import dataclass
from enum import IntEnum
from typing import Callable, Iterable, List, Optional, Set
from typing import Callable, Iterable, List, Optional, Set, Dict

from pony import orm
from pony.orm.core import Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def rqc(self) -> RemoteQueryCommunity:

@patch.object(RemoteQueryCommunity, 'knowledge_db', new=PropertyMock(return_value=None), create=True)
async def test_search_for_tags_no_db(self):
# test that in case of missed `tags_db`, function `search_for_tags` returns None
# test that in case of missed `knowledge_db`, function `search_for_tags` returns None
assert self.rqc.search_for_tags(tags=['tag']) is None

@patch.object(KnowledgeDatabase, 'get_subjects_intersection')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async def get_channel_contents(self, request):
contents_list.append(entry.to_simple_dict())
total = self.mds.get_total_count(**sanitized) if include_total else None
self.add_download_progress_to_metadata_list(contents_list)
self.add_tags_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"])
self.add_statements_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"])
response_dict = {
"results": contents_list,
"first": sanitized['first'],
Expand Down Expand Up @@ -504,7 +504,7 @@ async def get_popular_torrents_channel(self, request):
contents_list.append(entry.to_simple_dict())

self.add_download_progress_to_metadata_list(contents_list)
self.add_tags_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"])
self.add_statements_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"])
response_dict = {
"results": contents_list,
"first": sanitized['first'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from dataclasses import asdict
from typing import Optional

from pony.orm import db_session
Expand Down Expand Up @@ -38,11 +39,11 @@


class MetadataEndpointBase(RESTEndpoint):
def __init__(self, metadata_store: MetadataStore, *args, tags_db: KnowledgeDatabase = None,
def __init__(self, metadata_store: MetadataStore, *args, knowledge_db: KnowledgeDatabase = None,
tag_rules_processor: KnowledgeRulesProcessor = None, **kwargs):
super().__init__(*args, **kwargs)
self.mds = metadata_store
self.tags_db: Optional[KnowledgeDatabase] = tags_db
self.knowledge_db: Optional[KnowledgeDatabase] = knowledge_db
self.tag_rules_processor: Optional[KnowledgeRulesProcessor] = tag_rules_processor

@classmethod
Expand Down Expand Up @@ -84,13 +85,15 @@ def extract_tags(self, entry):
self._logger.info(f'Generated {generated} tags for {hexlify(entry.infohash)}')

@db_session
def add_tags_to_metadata_list(self, contents_list, hide_xxx=False):
if self.tags_db is None:
self._logger.error(f'Cannot add tags to metadata list: tags_db is not set in {self.__class__.__name__}')
def add_statements_to_metadata_list(self, contents_list, hide_xxx=False):
if self.knowledge_db is None:
self._logger.error(f'Cannot add statements to metadata list: '
f'knowledge_db is not set in {self.__class__.__name__}')
return
for torrent in contents_list:
if torrent['type'] == REGULAR_TORRENT:
tags = self.tags_db.get_objects(torrent["infohash"], predicate=ResourceType.TAG)
statements = [asdict(stmt) for stmt in self.knowledge_db.get_statements(torrent["infohash"])]
if hide_xxx:
tags = [tag.lower() for tag in tags if not default_xxx_filter.isXXX(tag, isFilename=False)]
torrent["tags"] = tags
statements = [stmt for stmt in statements if not default_xxx_filter.isXXX(stmt["object"],
isFilename=False)]
torrent["statements"] = statements
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def build_snippets(self, search_results: List[Dict]) -> List[Dict]:
content_to_torrents: Dict[str, list] = defaultdict(list)
for search_result in search_results:
with db_session:
content_items: List[str] = self.tags_db.get_objects(search_result["infohash"],
predicate=ResourceType.TITLE)
content_items: List[str] = self.knowledge_db.get_objects(search_result["infohash"],
predicate=ResourceType.TITLE)
if content_items:
for content_id in content_items:
content_to_torrents[content_id].append(search_result)
Expand Down Expand Up @@ -131,8 +131,8 @@ def search_db():
try:
with db_session:
if tags:
infohash_set = self.tags_db.get_subjects_intersection(set(tags), predicate=ResourceType.TAG,
case_sensitive=False)
infohash_set = self.knowledge_db.get_subjects_intersection(set(tags), predicate=ResourceType.TAG,
case_sensitive=False)
if infohash_set:
sanitized['infohash_set'] = {bytes.fromhex(s) for s in infohash_set}

Expand All @@ -141,7 +141,7 @@ def search_db():
self._logger.exception("Error while performing DB search: %s: %s", type(e).__name__, e)
return RESTResponse(status=HTTP_BAD_REQUEST)

self.add_tags_to_metadata_list(search_results, hide_xxx=sanitized["hide_xxx"])
self.add_statements_to_metadata_list(search_results, hide_xxx=sanitized["hide_xxx"])

if sanitized["first"] == 1: # Only show a snippet on top
search_results = self.build_snippets(search_results)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def return_exc(*args, **kwargs):

mock_gigachannel_community.remote_select_channel_contents = return_exc
ep_args = [mock_dlmgr, mock_gigachannel_manager, mock_gigachannel_community, metadata_store]
ep_kwargs = {'tags_db': knowledge_db}
ep_kwargs = {'knowledge_db': knowledge_db}
collections_endpoint = ChannelsEndpoint(*ep_args, **ep_kwargs)
channels_endpoint = ChannelsEndpoint(*ep_args, **ep_kwargs)

Expand Down Expand Up @@ -713,7 +713,7 @@ async def test_get_my_channel_tags(metadata_store, mock_dlmgr_get_download, my_c

assert len(json_dict['results']) == 9
for item in json_dict['results']:
assert len(item["tags"]) >= 2
assert len(item["statements"]) >= 2


async def test_get_my_channel_tags_xxx(metadata_store, knowledge_db, mock_dlmgr_get_download, my_channel,
Expand All @@ -739,4 +739,4 @@ async def test_get_my_channel_tags_xxx(metadata_store, knowledge_db, mock_dlmgr_
)

assert len(json_dict['results']) == 1
assert len(json_dict['results'][0]["tags"]) == 1
assert len(json_dict['results'][0]["statements"]) == 1
19 changes: 15 additions & 4 deletions src/tribler/core/components/metadata_store/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ def tag_torrent(infohash, tags_db, tags=None, suggested_tags=None):
if tag not in suggested_tags:
suggested_tags.append(tag)

def _add_operation(_tag, _op, _key):
operation = StatementOperation(subject_type=ResourceType.TORRENT, subject=infohash, predicate=ResourceType.TAG,
object=_tag, operation=_op, clock=0, creator_public_key=_key.pub().key_to_bin())
def _add_operation(_obj, _op, _key, _predicate=ResourceType.TAG):
operation = StatementOperation(subject_type=ResourceType.TORRENT, subject=infohash, predicate=_predicate,
object=_obj, operation=_op, clock=0, creator_public_key=_key.pub().key_to_bin())
operation.clock = tags_db.get_clock(operation) + 1
tags_db.add_operation(operation, b"")

# Give each torrent some tags
# Give the torrent some tags
for tag in tags:
for key in [random_key_1, random_key_2]: # Each tag should be proposed by two unique users
_add_operation(tag, Operation.ADD, key)
Expand All @@ -73,6 +73,17 @@ def _add_operation(_tag, _op, _key):
_add_operation(tag, Operation.ADD, random_key_3)
_add_operation(tag, Operation.REMOVE, random_key_2)

# Give the torrent some simple attributes
random_title = generate_title(2)
random_year = "%s" % random.randint(1990, 2040)
random_description = generate_title(5)
random_lang = random.choice(["english", "russian", "dutch", "klingon", "valyerian"])
for key in [random_key_1, random_key_2]: # Each statement should be proposed by two unique users
_add_operation(random_title, Operation.ADD, key, _predicate=ResourceType.TITLE)
_add_operation(random_year, Operation.ADD, key, _predicate=ResourceType.DATE)
_add_operation(random_description, Operation.ADD, key, _predicate=ResourceType.DESCRIPTION)
_add_operation(random_lang, Operation.ADD, key, _predicate=ResourceType.LANGUAGE)


@db_session
def generate_torrent(metadata_store, tags_db, parent):
Expand Down
8 changes: 4 additions & 4 deletions src/tribler/core/components/restapi/restapi_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,18 @@ async def run(self):
self.maybe_add('/libtorrent', LibTorrentEndpoint, libtorrent_component.download_manager)
self.maybe_add('/torrentinfo', TorrentInfoEndpoint, libtorrent_component.download_manager)
self.maybe_add('/metadata', MetadataEndpoint, torrent_checker, metadata_store_component.mds,
tags_db=knowledge_component.knowledge_db,
knowledge_db=knowledge_component.knowledge_db,
tag_rules_processor=knowledge_component.rules_processor)
self.maybe_add('/channels', ChannelsEndpoint, libtorrent_component.download_manager, gigachannel_manager,
gigachannel_component.community, metadata_store_component.mds,
tags_db=knowledge_component.knowledge_db,
knowledge_db=knowledge_component.knowledge_db,
tag_rules_processor=knowledge_component.rules_processor)
self.maybe_add('/collections', ChannelsEndpoint, libtorrent_component.download_manager, gigachannel_manager,
gigachannel_component.community, metadata_store_component.mds,
tags_db=knowledge_component.knowledge_db,
knowledge_db=knowledge_component.knowledge_db,
tag_rules_processor=knowledge_component.rules_processor)
self.maybe_add('/search', SearchEndpoint, metadata_store_component.mds,
tags_db=knowledge_component.knowledge_db)
knowledge_db=knowledge_component.knowledge_db)
self.maybe_add('/remote_query', RemoteQueryEndpoint, gigachannel_component.community,
metadata_store_component.mds)
self.maybe_add('/knowledge', KnowledgeEndpoint, db=knowledge_component.knowledge_db,
Expand Down
47 changes: 40 additions & 7 deletions src/tribler/gui/dialogs/addtagsdialog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, Optional, List
from typing import Dict, List

from PyQt5 import uic
from PyQt5.QtCore import QModelIndex, QPoint, pyqtSignal
from PyQt5.QtCore import QModelIndex, QPoint, pyqtSignal, Qt
from PyQt5.QtWidgets import QSizePolicy, QWidget

from tribler.core.components.knowledge.db.knowledge_db import ResourceType
Expand All @@ -10,10 +10,13 @@
from tribler.gui.defs import TAG_HORIZONTAL_MARGIN
from tribler.gui.dialogs.dialogcontainer import DialogContainer
from tribler.gui.tribler_request_manager import TriblerNetworkRequest
from tribler.gui.utilities import connect, get_ui_file_path, tr
from tribler.gui.utilities import connect, get_ui_file_path, tr, get_objects_with_predicate
from tribler.gui.widgets.tagbutton import TagButton


METADATA_TABLE_PREDICATES = [ResourceType.TITLE, ResourceType.DESCRIPTION, ResourceType.DATE, ResourceType.LANGUAGE]


class AddTagsDialog(DialogContainer):
"""
This dialog enables a user to add new tags to/remove existing tags from content.
Expand All @@ -22,10 +25,11 @@ class AddTagsDialog(DialogContainer):
save_button_clicked = pyqtSignal(QModelIndex, list)
suggestions_loaded = pyqtSignal()

def __init__(self, parent: QWidget, infohash: str) -> None:
def __init__(self, parent: QWidget, index: QModelIndex) -> None:
DialogContainer.__init__(self, parent, left_right_margin=400)
self.index: Optional[QModelIndex] = None
self.infohash = infohash
self.index: QModelIndex = index
self.data_item = self.index.model().data_items[self.index.row()]
self.infohash = self.data_item["infohash"]

uic.loadUi(get_ui_file_path('add_tags_dialog.ui'), self.dialog_widget)

Expand All @@ -39,11 +43,30 @@ def __init__(self, parent: QWidget, infohash: str) -> None:
self.dialog_widget.error_text_label.hide()
self.dialog_widget.suggestions_container.hide()

connect(self.dialog_widget.edit_metadata_table.doubleClicked, self.on_edit_metadata_table_item_clicked)

# Fill in the metadata table and make the items in the 2nd column editable
for ind in range(self.dialog_widget.edit_metadata_table.topLevelItemCount()):
item = self.dialog_widget.edit_metadata_table.topLevelItem(ind)
objects = get_objects_with_predicate(self.data_item, METADATA_TABLE_PREDICATES[ind])
if objects:
item.setText(1, objects[0]) # TODO take the object with the highest creation count
item.setFlags(item.flags() | Qt.ItemIsEditable)

if get_objects_with_predicate(self.data_item, ResourceType.TAG):
self.dialog_widget.edit_tags_input.set_tags(get_objects_with_predicate(self.data_item, ResourceType.TAG))
self.dialog_widget.content_name_label.setText(self.data_item["name"])

# Fetch suggestions
TriblerNetworkRequest(f"knowledge/{infohash}/tag_suggestions", self.on_received_tag_suggestions)
TriblerNetworkRequest(f"knowledge/{self.infohash}/tag_suggestions", self.on_received_tag_suggestions)

self.update_window()

def on_edit_metadata_table_item_clicked(self, index):
if index.column() == 1:
item = self.dialog_widget.edit_metadata_table.topLevelItem(index.row())
self.dialog_widget.edit_metadata_table.editItem(item, index.column())

def on_save_tags_button_clicked(self, _) -> None:
statements: List[Dict] = []

Expand All @@ -65,6 +88,16 @@ def on_save_tags_button_clicked(self, _) -> None:
"object": tag,
})

# Convert the entries in the metadata table to statements
for ind in range(self.dialog_widget.edit_metadata_table.topLevelItemCount()):
item = self.dialog_widget.edit_metadata_table.topLevelItem(ind)
entered_text = item.text(1)
if entered_text:
statements.append({
"predicate": METADATA_TABLE_PREDICATES[ind],
"object": entered_text,
})

self.save_button_clicked.emit(self.index, statements)

def on_received_tag_suggestions(self, data: Dict) -> None:
Expand Down
Loading

0 comments on commit 69f04fb

Please sign in to comment.