Skip to content

Commit

Permalink
Merge pull request #1 from cibere/v2
Browse files Browse the repository at this point in the history
Publish V2
  • Loading branch information
cibere authored Oct 27, 2024
2 parents 5983e43 + 8a4086b commit 58611bf
Show file tree
Hide file tree
Showing 32 changed files with 748 additions and 182 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/Publish Release.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: Publish Release

on:
workflow_dispatch:
push:
branches: [ main ]
paths-ignore:
- .github/workflows/*
paths:
- 'plugin.json'
workflow_dispatch:

jobs:
publish:
Expand All @@ -15,22 +15,27 @@ jobs:

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: get version
id: version
uses: notiz-dev/github-action-json-property@release
with:
path: 'plugin.json'
prop_path: 'Version'

- run: echo ${{steps.version.outputs.prop}}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r ./requirements.txt -t ./lib
zip -r Flow.Launcher.Plugin.WordNikDictionary.zip . -x '*.git*'
- name: Publish
if: success()
uses: softprops/action-gh-release@v1
Expand Down
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
__pycache__
venv
__pycache__/*
venv/*
lib/*
wordnik.logs
*.pyc
Binary file added Images/discord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/filter_by_part_of_speech_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/find_similiar_word_categories_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/get_definition_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/get_definition_information_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/get_syllables_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/github.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/invalid_api_key_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/log_file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/unexpected_error_handler_showcase.mp4
Binary file not shown.
Binary file added Images/word_not_found_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,54 @@
This is a plugin for [Flow Launcher](https://github.com/Flow-Launcher/Flow.Launcher) that lets you easily see the definitions of words using [wordnik](https://wordnik.com).

## Get an API Key
To get an API key, head to [developer.wordnik.com](https://developer.wordnik.com/), and create an account. Once you've created your account, you'll be able to fill out a form to request an api key.
To get an API key, head to [developer.wordnik.com](https://developer.wordnik.com/), and create an account. Once you've created your account, you'll be able to fill out a form to request an api key.

## Features

### Feature List
1. [Get the definition of a word](#get-the-definition-of-a-word)
2. [Get information about definition of a word](#get-information-about-definition-of-a-word)
3. [Search Modifiers](#search-modifiers)
- [Filter by parts of speech](#filter-by-parts-of-speech)
- [Get the syllables of a word](#get-the-syllables-of-a-word)
- [Get similiar word by category](#get-similiar-word-by-category)
4. [Advanced Error Handler](#advanced-error-handler)
- [Expected Errors](#expected-errors)
- [Unexpected Errors](#unexpected-errors)

### Get the definition of a word
Get a list of definitions for your word from various sources. Syntax: `def word`
![Example showing the result of the search `def vague`](Images/get_definition_example.png)
### Get information about definition of a word
Get information about a certain definition, and easy access to the source. This is a context menu that is avalible for all definitions.
![Example showing the context menu of a word definition](Images/get_definition_information_example.png)
### Search Modifiers
You can use search modifiers to filter your results by part of speech, or to get different types of information about a word. Search modifiers have the following syntax: `word!modifier`
#### Filter by parts of speech
You can filter results by parts of speech by inputting the part of speech as a modifier.
Syntax: `word!part_of_speech`.
List of acceptable parts of speech modifiers:
```
noun, adjective, verb, adverb, interjection, pronoun, preposition, abbreviation, affix, article, auxiliary-verb, conjunction, definite-article, family-name, given-name, idiom, imperative, noun-plural, noun-posessive, past-participle, phrasal-prefix, proper-noun, proper-noun-plural, proper-noun-posessive, suffix, verb-intransitive, verb-transitive
```
![Example showing the useage of the parts of speech search modifier with the `def vague!noun` query](Images/filter_by_part_of_speech_example.png)
#### Get the syllables of a word
We can use the `syllables` search modifier to get the syllables of a word. Syntax: `def word!syllables`
![Example showing the result of the search `def developer!syllable`](Images/get_syllables_example.png)
#### Get categories of similiar words
To find the categories of avalible similiar words for a given word, use the following command: `def word!similiar`. To see all of the words in a given category, see the section below.
![](Images/find_similiar_word_categories_example.png)
#### Get similiar word by category
To find all of the words that are similiar to a word in a specific category, use the following command: `def word!rel-category`. For a list of avalible categories for a given word, see the above section.
![](Images/find_similiar_words_by_category_example.png)

### Advanced Error Handler

#### Expected Errors
Expected errors will return a short, simple, and stylish error message.
![](Images/word_not_found_example.png)
![](Images/invalid_api_key_example.png)
#### Unexpected Errors
When unexpected errors occur, our error handler redirects it to the logs and prompts you to notify us by creating a github issue or discord thread with the logfile.

https://github.com/cibere/Flow.Launcher.Plugin.WordNikDictionary/raw/refs/heads/v2/Images/unexpected_error_handler_showcase.mp4
8 changes: 1 addition & 7 deletions SettingsTemplate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,4 @@ body:
attributes:
name: results
label: Number of results to display
defaultValue: 20
- type: checkbox
attributes:
name: debug_mode
label: Debug Mode
description: If checked, data will be written to temp files for review in the case of an error.
defaultValue: false
defaultValue: 20
13 changes: 13 additions & 0 deletions WordnikDictionary/attributions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from __future__ import annotations

__all__ = ("Attribution",)


class Attribution:
def __init__(self, text: str | None, url: str | None) -> None:
self.text = text or ""
self.url = url

@classmethod
def from_json(cls: type[Attribution], data: dict) -> Attribution:
return cls(data["attributionText"], data["attributionUrl"])
185 changes: 185 additions & 0 deletions WordnikDictionary/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import inspect
import json
import os
import re
import sys
import webbrowser
from logging import getLogger
from typing import Any

from flowlauncher import FlowLauncher, FlowLauncherAPI

from .definition import Definition
from .errors import InternalException
from .http import HTTPClient
from .options import Option
from .utils import convert_options, handle_plugin_exception
from .word_relationship import WordRelationship

LOG = getLogger(__name__)
QUERY_REGEX = re.compile(r"^(?P<word>[a-zA-Z]+)(!(?P<filter>[a-zA-Z-]+))?$")

parts_of_speech = [
"noun",
"adjective",
"verb",
"adverb",
"interjection",
"pronoun",
"preposition",
"abbreviation",
"affix",
"article",
"auxiliary-verb",
"conjunction",
"definite-article",
"family-name",
"given-name",
"idiom",
"imperative",
"noun-plural",
"noun-posessive",
"past-participle",
"phrasal-prefix",
"proper-noun",
"proper-noun-plural",
"proper-noun-posessive",
"suffix",
"intransitive-verb",
"transitive-verb",
]


class WordnikDictionaryPlugin(FlowLauncher):
def __init__(self, args: str | None = None):
self.http = HTTPClient(self)

# defalut jsonrpc
self.rpc_request = {"method": "query", "parameters": [""]}
self.debugMessage = ""

if args is None and len(sys.argv) > 1:

# Gets JSON-RPC from Flow Launcher process.
self.rpc_request = json.loads(sys.argv[1])
LOG.debug(f"Received RPC request: {json.dumps(self.rpc_request)}")

# proxy is not working now
# self.proxy = self.rpc_request.get("proxy", {})

request_method_name = self.rpc_request.get("method", "query")
request_parameters = self.rpc_request.get("parameters", [])

methods = inspect.getmembers(self, predicate=inspect.ismethod)
request_method = dict(methods)[request_method_name]
results = request_method(*request_parameters)

if request_method_name in ("query", "context_menu"):
data = {"result": results, "debugMessage": self.debugMessage}

try:
payload = json.dumps(data)
except TypeError as e:
LOG.error(
f"Error occured while trying to convert payload for flow through json.dumps. Data: {data!r}",
exc_info=e,
)
raise InternalException() from e
else:
LOG.debug(f"Sending data to flow: {payload}")
print(payload)

@property
def settings(self) -> dict:
return self.rpc_request["settings"]

def get_definitions(self, word: str) -> list[Definition]:
raw = self.http.fetch_definitions(word)
final = []
for data in raw:
definition = Definition.from_json(word, data)
if definition:
final.append(definition)
return final

def get_syllables(self, word: str) -> list[str]:
raw = self.http.fetch_syllables(word)
final = []
for data in sorted(raw, key=lambda d: d["seq"]):
final.append(data["text"])
return final

def get_word_relationships(self, word: str) -> list[WordRelationship]:
raw = self.http.fetch_similiar_words(word)
final = []
for data in raw:
item = WordRelationship.from_json(word, data)
if item:
final.append(item)
return final

@handle_plugin_exception
@convert_options
def query(self, query: str):
LOG.info(f"Received query: {query!r}")

if not query.strip():
return [Option.wnf()]

word = query
filter_query = None
matches = QUERY_REGEX.match(query)
if matches:
word = matches["word"]
filter_query = matches.group("filter")
LOG.info(f"Match found. {word=}, {filter_query=}")

if filter_query:
if filter_query == "syllables":
syllables = self.get_syllables(word)
return [Option(title="-".join(syllables))] or [Option.wnf()]
elif filter_query == "similiar":
return self.get_word_relationships(word) or [Option.wnf()]
elif filter_query.startswith("rel-"):
rel_type = filter_query.removeprefix("rel-")
relationships = self.get_word_relationships(word)
for relationship in relationships:
if relationship.type == rel_type:
return relationship.get_word_options() or [Option.wnf()]

definitions = self.get_definitions(word)

if filter_query:
if filter_query in parts_of_speech:
temp = filter_query.replace("-", " ")
definitions = filter(lambda d: d.part_of_speech == temp, definitions)
else:
return [
Option(
title="Unknown Search Modifier Given",
sub="Press ENTER to open search modifier index",
callback="open_url",
params=[
"https://github.com/cibere/Flow.Launcher.Plugin.WordNikDictionary/tree/v2?tab=readme-ov-file#search-modifiers"
],
)
]

return definitions or [Option.wnf()]

@handle_plugin_exception
def context_menu(self, data: list[Any]):
LOG.debug(f"Context menu received: {data=}")
return data

def open_url(self, url):
webbrowser.open(url)

def open_settings_menu(self):
FlowLauncherAPI.open_setting_dialog()

def change_query(self, query: str):
FlowLauncherAPI.change_query(query)

def open_log_file_folder(self):
os.system(f'explorer.exe /select, "wordnik.logs"')
24 changes: 24 additions & 0 deletions WordnikDictionary/dataclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import annotations

from typing import Self

from .options import Option

__all__ = ("Dataclass",)


class Dataclass:
@classmethod
def from_json(cls: type[Self], word: str, data: dict) -> Self | None:
raise RuntimeError("This must be overriden")

def _generate_base_option(self) -> Option:
raise RuntimeError("This must be overriden")

def _generate_context_menu_options(self) -> list[Option]:
return []

def to_option(self) -> Option:
opt = self._generate_base_option()
opt.context_data = self._generate_context_menu_options()
return opt
Loading

0 comments on commit 58611bf

Please sign in to comment.