Skip to content

Commit

Permalink
feat: add caching feature (#7)
Browse files Browse the repository at this point in the history
A basic caching feature has been added. It stores newly searched words
in a file named .cambd-cache.json in $HOME. And for every new word
lookup, it checks the cache file if the definition for that specific word
already exists. If yes then return immediately else fetch the new
definition and add it to the cache file for future reference.

fixes #6
  • Loading branch information
rocktimsaikia committed Nov 12, 2022
1 parent 7420de7 commit 9977b3e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 25 deletions.
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
dest_dir := /usr/local/src/cambd-cli
bin_file := /usr/bin/cambd
DIST = /usr/local/src/cambd-cli
BIN = /usr/bin/cambd

install:
cp -f cambd-cli/cambd.sh $(bin_file)
chmod +x $(bin_file)
mkdir -m 777 -p $(dest_dir)
cp -f cambd-cli/cambd.py $(dest_dir)/cambd.py
@echo "\nInstall successful"
cp -f cambd-cli/cambd.sh $(BIN)
chmod +x $(BIN)
mkdir -m 777 -p $(DIST)
cp -f cambd-cli/cambd.py $(DIST)
@echo "Install successful."

uninstall:
rm -rf $(bin_file)
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,23 @@ cambd neccesseery

> The above word spelling is incorrect. So cambd will suggest related words.
## Uninstall

To uninstall you can run the below command from the `cambd-cli` directory

```sh
sudo make uninstall
```

## TODO:

- [x] Add loading animation
- [x] Handle error for getting definition of words with spaces
- [x] Show only 2 examples per definition by default
- [ ] Implement a basic local caching mechanism
- [x] Implement a basic local caching mechanism
- [ ] Add flag to show all definitions. Default is 1

## Caching

This is how the basic caching feature is implemented

- Search definition for given word in the CLI.
- Check if the word was already looked up before in the cache file in `$HOME`.
- If yes then return it from the cache file immediently.
- If not fetch the new definition and add it to the cache file for future usecase.

## LICENSE

[MIT](./LICENSE) License © [Rocktim Saikia](https://rocktimsaikia.com) 2022
49 changes: 40 additions & 9 deletions cambd-cli/cambd.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
#!/bin/env python3

import os
import re
import sys
import yaml
import json
import requests
from bs4 import BeautifulSoup
from simple_term_menu import TerminalMenu
from halo import Halo
import yaml
import re

spinner = Halo(text="Loading", spinner="dots")

# Don't touch this. Unless you want to deal with permission issues in other places
cached_dictionary = os.path.expanduser("~") + "/.cambd-cache.json"

spellcheck_url = "https://dictionary.cambridge.org/spellcheck/english/?q="
definition_url = "https://dictionary.cambridge.org/dictionary/english/"
# This is needed to not get detected as a bot
headers = {"User-Agent": "Mozilla/5.0"}


Expand All @@ -31,11 +35,36 @@ def get_suggestions(word: str) -> list[str]:
return suggested_words


def is_cached(word: str):
# File exists and has content
if os.path.exists(cached_dictionary) and os.path.getsize(cached_dictionary) > 0:
with open(cached_dictionary, "r") as file:
file_data = json.load(file)
if word in file_data:
return file_data[word]


def cache_it(word, definitions):
# File does not exists or file is empty;
if not os.path.exists(cached_dictionary) or os.path.getsize(cached_dictionary) == 0:
with open(cached_dictionary, "w") as ofile:
json.dump({word: definitions}, ofile)
return

# Content already exists, append
with open(cached_dictionary, "r+") as ofile:
file_data = json.load(ofile)
file_data[word] = definitions
ofile.seek(0)
json.dump(file_data, ofile)


@spinner
def get_definitions(word: str) -> list[str]:
# In browser, getting definition for word with spaces inbetween automatically formats dashes (-) replacing the spaces
# Replicating the same behaviour here too for consistency
word = word.strip().replace(" ", "-")
# Return cahced version if available
cached_word = is_cached(word)
if cached_word is not None:
return cached_word

response = requests.get(definition_url + word, headers=headers)

Expand Down Expand Up @@ -76,7 +105,8 @@ def get_definitions(word: str) -> list[str]:

def main():
arg = sys.argv[1:][0]
definitions = get_definitions(arg)
word = arg.strip().replace(" ", "-").lower()
definitions = get_definitions(word)

if len(definitions) == 0:
suggestions = get_suggestions(arg)
Expand All @@ -86,12 +116,13 @@ def main():
menu_entry_index = terminal_menu.show()

if type(menu_entry_index) is int:
arg = suggestions[menu_entry_index]
definitions = get_definitions(arg)
suggested_word = suggestions[menu_entry_index]
definitions = get_definitions(suggested_word)

spinner.succeed(f"Showing definition for: \033[1m{arg}\033[0m")

print("\n" + yaml.dump(definitions[0], indent=3, sort_keys=False))
cache_it(word, definitions)


if __name__ == "__main__":
Expand Down

0 comments on commit 9977b3e

Please sign in to comment.