Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions weasyl/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ def __init__(self):
self.required_user_excludes = set()
self.ratings = set()
self.find = None
self.sha256: str | None = None

def add_criterion(self, criterion):
def add_criterion(self, criterion: str):
def add_nonempty(s, item):
if item:
s.add(item)
Expand All @@ -65,6 +66,9 @@ def add_nonempty(s, item):
elif criterion.startswith("-user:"):
user = d.get_sysname(criterion.split(":", 1)[1])
add_nonempty(self.required_user_excludes, user)
elif criterion.startswith("sha256:"):
sha256 = criterion.split(":", 1)[1]
self.sha256 = sha256 or None
elif criterion.startswith("+"):
tag = d.get_search_tag(criterion[1:])
add_nonempty(self.required_includes, tag)
Expand All @@ -85,6 +89,7 @@ def __bool__(self):
self.required_excludes or
self.required_user_includes or
self.required_user_excludes or
self.sha256 or
self.ratings)

@classmethod
Expand Down Expand Up @@ -135,7 +140,7 @@ def select_users(q):


def _find_without_media(userid, rating, limit,
search, within, cat, subcat, backid, nextid):
search: Query, within, cat, subcat, backid, nextid):
type_code, type_letter, table, select, subtype = _TABLE_INFORMATION[search.find]

# Begin statement
Expand Down Expand Up @@ -163,6 +168,12 @@ def _find_without_media(userid, rating, limit,
elif cat:
statement_where.append("AND content.subtype >= %(category)s AND content.subtype < %(category)s + 1000")

if search.find == "submit" and search.sha256:
statement_from.append("INNER JOIN submission_media_links ON submission_media_links.submitid = content.{select}")
statement_from.append("INNER JOIN media ON media.mediaid = submission_media_links.mediaid")
statement_where.append("AND submission_media_links.link_type = 'submission'")
statement_where.append("AND media.sha256 = %(sha256)s")

if userid:
if within == "notify":
# Search within notifications
Expand Down Expand Up @@ -265,6 +276,7 @@ def make_statement(statement_select, statement_additional_where, statement_order
]).format(
table=table,
find=search.find,
sha256=search.sha256,
select=select,
subtype=subtype,
title_field="char_name" if search.find == "char" else "title",
Expand Down Expand Up @@ -302,6 +314,7 @@ def get_ids(names):
"required_excludes": get_ids(search.required_excludes),
"required_user_includes": list(search.required_user_includes),
"required_user_excludes": list(search.required_user_excludes),
"sha256": search.sha256,
"type": type_letter,
"userid": userid,
"rating": rating,
Expand Down
9 changes: 9 additions & 0 deletions weasyl/templates/help/searching.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ <h4>Search modifiers (can only be used with at least one search term above)</h4>
<td>Limits results to the <em>content</em> level specified. <em>Use as many of these per search as you wish.</em></td>
</tr>
</table>

<h4>Advanced search types</h4>

<table>
<tr>
<th><pre>sha256:8c474ca2ebfa54b1439a5c6f025f58ab443e6e8ba8a0ed885046d6375fbf4e8a</pre></th>
<td>Search for a submission whose media matches the given SHA-256 hash. Given a piece of artwork, this can be used to find the original submission on Weasyl, in a process known as <em>reverse image search</em>.</td>
</tr>
</table>
</div>
</div>
</div>
49 changes: 48 additions & 1 deletion weasyl/test/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from libweasyl.models.helpers import CharSettings
from libweasyl import ratings
from libweasyl import media, ratings
from weasyl.test import db_utils
from weasyl import search

Expand Down Expand Up @@ -217,3 +217,50 @@ def _select(userid: int):

assert len(_select(user1_id)) == 1
assert len(_select(user1_id)) == 1


def test_search_sha256(db):
userid = db_utils.create_user()
tagid = db_utils.create_tag('test')

def _select(term: str):
results, _, _ = search.select(
search=search.Query.parse(term, 'submit'),
userid=userid, rating=ratings.EXPLICIT.code, limit=100,
cat=None, subcat=None, within='', backid=None, nextid=None,
)

return results

submitid_foo = db_utils.create_submission(userid, 'Foo')
db_utils.create_submission_tag(tagid, submitid_foo)
with open('libweasyl/test/data/1x70.jpg', 'rb') as file_foo:
data_foo = file_foo.read()
media_foo = media.MediaItem.fetch_or_create(data_foo, file_type='jpg')
media.SubmissionMediaLink.make_or_replace_link(submitid_foo, 'submission', media_foo)

submitid_bar = db_utils.create_submission(userid, 'Bar')
db_utils.create_submission_tag(tagid, submitid_bar)
with open('libweasyl/test/data/2x233.gif', 'rb') as file_bar:
data_bar = file_bar.read()
media_bar = media.MediaItem.fetch_or_create(data_bar, file_type='gif')
media.SubmissionMediaLink.make_or_replace_link(submitid_bar, 'submission', media_bar)

submitid_baz = db_utils.create_submission(userid, 'Baz')
db_utils.create_submission_tag(tagid, submitid_baz)
with open('libweasyl/test/data/2x233.gif', 'rb') as file_baz:
data_baz = file_baz.read()
media_baz = media.MediaItem.fetch_or_create(data_baz, file_type='gif')
media.SubmissionMediaLink.make_or_replace_link(submitid_baz, 'submission', media_baz)

results_foo = _select('sha256:a4e6ff301f671e8afb2397b4967c6a7004657447a5877a08d2dc29af58fccf3b')
results_bar = _select('sha256:fa6bd1dacf7b3d351172051826bb6d123113cfb7314322732a3ee1a917531672')
results_unknown = _select('sha256:0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f')

assert len(results_foo) == 1
assert results_foo[0]['title'] == 'Foo'

assert len(results_bar) == 2
assert sorted(map(lambda x: x['title'], results_bar)) == ['Bar', 'Baz']

assert len(results_unknown) == 0