Skip to content

Initial advanced filtering#218

Open
HornyHornofHornyness wants to merge 47 commits into
WillyJL:mainfrom
HornyHornofHornyness:main
Open

Initial advanced filtering#218
HornyHornofHornyness wants to merge 47 commits into
WillyJL:mainfrom
HornyHornofHornyness:main

Conversation

@HornyHornofHornyness

@HornyHornofHornyness HornyHornofHornyness commented Jan 19, 2025

Copy link
Copy Markdown

Implementing #55
Turns the search bar into a filter bar with support for logical searches (AND, OR, NOT). I don't think it's very stable right now, but it works if you don't try to break it.
It is stable, but it can probably still be broken. Don't have the time to try breaking it really hard.

Formatting
tag:{tagname} - Defaults to AND
tag:({tag1} or {tag2} or ...)
label:"{labelname}" - Requires quotes if label has spaces
cat or dog cat | dog cat || dog - Any of these work for OR
status:complete - All currently supported filters are implemented
cat or (dog and (fox or squirrel)) - Nesting is supported

I will be adding support for inequalities (> >= < <=) and some other filtering options Added

@HornyHornofHornyness HornyHornofHornyness marked this pull request as draft January 19, 2025 03:14
@WillyJL

WillyJL commented Jan 20, 2025

Copy link
Copy Markdown
Owner

cat or dog cat | dog cat || dog - Any of these work for OR
status:complete - All currently supported filters are implemented
cat or (dog and (fox or squirrel)) - Nesting is supported

im assuming here cat and dog and such refer to any kind of filter, so something like status:complete and (label:xyz or tag:abc) would work?

@HornyHornofHornyness

Copy link
Copy Markdown
Author

Yeah I was just using that for examples. They could be any filter.

@HornyHornofHornyness

HornyHornofHornyness commented Jan 25, 2025

Copy link
Copy Markdown
Author

I've added most of the filters I could think of, and it seems stable now.
Currently supported filters:

  • Logic

    • and
    • or
    • not
  • Basic filter

    • Supports wildcard character *, will match any number of characters
      • t*t will match tt, tet test
    • Matches against version, developer, name, and notes
  • Boolean filters (true, false) (e.g.archived:true)

    • archived
    • custom
    • updated
    • outdated
    • is (e.g.is:archived)
      • archived
      • custom
      • updated
      • outdated
      • finished
      • installed
  • Custom filters (e.g.exe:invalid)

    • exe, image
      • invalid
      • valid
      • selected
      • unset
    • finished, isfinished, installed, isinstalled
      • true
      • false
      • old_version, outdated
      • any
  • Number filters (<, <=, =, >=, >) (e.g.score>=3)

    • id
    • rating
    • score
    • votes
    • wscore, weight, scoreweight, weightedscore
  • String filters (e.g.dev=Caribdis)

    • Supports wildcard character *
    • name, title
    • dev, developer
    • ver, version
    • note, notes
    • desc, description
    • url
    • downloads
      • Matches hoster name (Mega) or download title (Win/Linux)
  • Enum filters (e.g.status:completed)

    • Will match any that include the query, unless an exact match is found

    • Supports wildcard character *

    • tag

      • tag:(3dcg animated) matches all by default
    • label

      • label:(label1 label2) matches all by default
    • tab

    • status

    • type

  • Date filters (<, <=, =, >=, >) (e.g.added>=10/10/2024)

    • added
    • updated
    • launched
    • All Timeline Events
      • gameadded
      • gamelaunched
      • gamefinished or finished
      • gameinstalled or installed
      • changedname
      • changedstatus
      • changedversion
      • changeddeveloper
      • changedtype
      • tagsadded
      • tagsremoved
      • scoreincreased
      • scoredecreased
      • recheckexpired
      • recheckuserreq
  • Amount filters (<, <=, =, >=, >) (e.g.tags>=20)

    • tags
    • labels
    • exes, executables

@HornyHornofHornyness HornyHornofHornyness marked this pull request as ready for review January 25, 2025 21:33
@WillyJL

WillyJL commented Jan 25, 2025

Copy link
Copy Markdown
Owner

I've added most of the filters I could think of, and it seems stable now.
Currently supported filters:

this is incredibly impressive, amazing work! and in just 370 lines too!

one thing i will note is number, string, and date filters might benefit from : being an acceptable alternative to =. seems fitting to follow the pattern, that way you can do title:The*Whatever. for inequalities like >= it makes sense to use = but considering everything else uses : might be more intuitive to allow tag:xyz title:abc rating:5. also maybe ==? might be more intuitive for programmers haha

last thing that perplexes me is the amounts. having the same name might be a bit confusing. i realized while writing this that the actual filters are singular like tag:xyz and the amounts are plural like tags=10. then only note here is the same as above, with allowing : and == for equality.

last thing is finished:old_version and friends. finished:outdated reflects what is currently used in gui, but i agree old version might be more intuitive. maybe finished:oldversion to follow the theme of the other selectors without the _? and possibly just finished:old too?

@HornyHornofHornyness

Copy link
Copy Markdown
Author

:, =, and == are completely interchangeable. Forgot to mention that part.
I agree that old_version isn't the best for it, its just what I went with originally and never changed it. I'll do another pass over and add some more alternates.

@HornyHornofHornyness HornyHornofHornyness marked this pull request as draft January 27, 2025 07:52
@HornyHornofHornyness

Copy link
Copy Markdown
Author

Alright I think this is finally done. Added support for timeline data matching tagsadded:newtag and added a couple alternate matches. Also added a button to convert basic filters to advanced.
image
image

@HornyHornofHornyness HornyHornofHornyness marked this pull request as ready for review January 29, 2025 07:21
@FaceCrap

FaceCrap commented Feb 2, 2025

Copy link
Copy Markdown
Contributor

got it to crash... repeatable.

The intention was to filter type not renpy but not being totally clear with expected syntax and seeing that it didn't seem to make a difference if there was a colon present, I started typing type:no but before I could type the rest it bombed.

Exception has occurred: TypeError
'in <string>' requires string as left operand, not Type
  File "Y:\Code\VN\FaceCrap-Filters\modules\utils.py", line 549, in <genexpr>
    key = lambda game, f: (and_or(attr_for(f.token, game) in node.token for node in f.nodes))
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Y:\Code\VN\FaceCrap-Filters\modules\utils.py", line 549, in <lambda>
    key = lambda game, f: (and_or(attr_for(f.token, game) in node.token for node in f.nodes))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Y:\Code\VN\FaceCrap-Filters\modules\utils.py", line 597, in <lambda>
    base_ids = set(filter(functools.partial(lambda f, k, id: f.invert != k(globals.games[id], f), head, key), base_ids))
                                                                         ^^^^^^^^^^^^^^^^^^^^^^^
  File "Y:\Code\VN\FaceCrap-Filters\modules\utils.py", line 597, in parse_query
    base_ids = set(filter(functools.partial(lambda f, k, id: f.invert != k(globals.games[id], f), head, key), base_ids))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Y:\Code\VN\FaceCrap-Filters\modules\gui.py", line 3008, in calculate_ids
    base_ids = utils.parse_query(query, base_ids)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Y:\Code\VN\FaceCrap-Filters\modules\gui.py", line 3160, in draw_games_list
    self.calculate_ids(table_id, imgui.table_get_sort_specs())
  File "Y:\Code\VN\FaceCrap-Filters\modules\gui.py", line 955, in main_loop
    self.draw_games_list()
  File "Y:\Code\VN\FaceCrap-Filters\main-debug.py", line 50, in main
    globals.gui.main_loop()
  File "Y:\Code\VN\FaceCrap-Filters\main-debug.py", line 95, in <module>
    main()
TypeError: 'in <string>' requires string as left operand, not Type

It will bomb on everything typed after type: that's not a type...

Again, not being 100% sure on syntax I next tried status:not onhold but instead of excluding, I got only onhold. So I started editing and backspacing intending to change it to status:onhold...
But the moment it became status:notonhold it bombed too... in the same location but slightly different error

Exception has occurred: TypeError
'in <string>' requires string as left operand, not Status
#
# lines removed because they are identical to the previous stack trace, just different cause
#
TypeError: 'in <string>' requires string as left operand, not Status

Seems there needs to be some kind of safeguarding if arguments consisting of enums are used.

About filtering on images... it's now linked to the same enum as filtering on exes. But there's also this...

                    except aiohttp.ClientResponseError as exc:
                        if exc.status < 400:
                            raise  # Not error status
                        if image_url.startswith("https://i.imgur.com"):
                            thread["image_url"] = "blocked"
                        else:
                            thread["image_url"] = "dead"

@HornyHornofHornyness

HornyHornofHornyness commented Feb 2, 2025

Copy link
Copy Markdown
Author

got it to crash... repeatable.

The intention was to filter type not renpy but not being totally clear with expected syntax and seeing that it didn't seem to make a difference if there was a colon present, I started typing type:no but before I could type the rest it bombed.
It will bomb on everything typed after type: that's not a type...

Thank you, fixed this (as well as for Status and Tab)

Again, not being 100% sure on syntax I next tried status:not onhold but instead of excluding, I got only onhold. So I started editing and backspacing intending to change it to status:onhold... But the moment it became status:notonhold it bombed too... in the same location but slightly different error

Syntax for this would be not status:onhold
status:not onhold is trying to find games with a status of not, and also searching for onhold in the name, dev, version, or notes
You could also do status:(not onhold) (wasn't working before). Here you are inverting the term onhold, and then checking that the status matches the already inverted term. Same result as not status:onhold, just a different syntax.
status:(not onhold) means - Status is (anything except onhold)
not status:onhold means - Status is not (onhold).
Mostly useful for nesting terms.

Anything like something:value will try to find games with a something attribute matching value. not will negate the next term it finds (something:value is one term)

About filtering on images... it's now linked to the same enum as filtering on exes. But there's also this...

                    except aiohttp.ClientResponseError as exc:
                        if exc.status < 400:
                            raise  # Not error status
                        if image_url.startswith("https://i.imgur.com"):
                            thread["image_url"] = "blocked"
                        else:
                            thread["image_url"] = "dead"

I don't understand this part. Neither images nor exes use enums, and that code is not something I wrote.

@FaceCrap

FaceCrap commented Feb 3, 2025

Copy link
Copy Markdown
Contributor

About filtering on images... it's now linked to the same enum as filtering on exes. But there's also this...

                    except aiohttp.ClientResponseError as exc:
                        if exc.status < 400:
                            raise  # Not error status
                        if image_url.startswith("https://i.imgur.com"):
                            thread["image_url"] = "blocked"
                        else:
                            thread["image_url"] = "dead"

I don't understand this part. Neither images nor exes use enums, and that code is not something I wrote.
Yeah sorry, was still looking at structs... where exe state is an enum.
Forgot there's actually a second, similar check

                        if check_host(f95_domain) and not check_host(get_url_domain(image_url)):
                            # Link is actually dead
                            thread["image_url"] = "dead"

I know you did not write both bits, the reason I referred to it is because these two values could end up someone's data. But they can't be filtered against.

@HornyHornofHornyness

HornyHornofHornyness commented Feb 3, 2025

Copy link
Copy Markdown
Author

I know you did not write both bits, the reason I referred to it is because these two values could end up someone's data. But they can't be filtered against.

Ok I understand now. I've added a filter for image_url, as well as added onto regular image filters to support dead and broken
image_url:imgur - matches against url
image_url:dead image:dead
image_url:broken image:broken

@WillyJL

WillyJL commented Jun 8, 2026

Copy link
Copy Markdown
Owner

(just bumping in hopes of github figuring itself out, it decided to hide 5 PRs and updating something in them seems to make them show again - your PR wasnt showing)
image

im so sorry for the absolute silence. i hope to eventually get to reviewing this, but sadly this project is not my main focus since a long time and large changes are hard to justify. also considering i would ideally like to move forward with the rewrite, but that too has gotten no attention for a long time. not enough free time with work now :(

@WillyJL WillyJL added the feature New feature or request label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants