Skip to content

Instantly share code, notes, and snippets.

@0xpizza
Created October 15, 2019 21:29
Show Gist options
  • Select an option

  • Save 0xpizza/7de2b2fdd557b4a64834dc82fa0fe657 to your computer and use it in GitHub Desktop.

Select an option

Save 0xpizza/7de2b2fdd557b4a64834dc82fa0fe657 to your computer and use it in GitHub Desktop.
Designed to be run on a daily basis by Windows Task Scheduler or *nix chron.
#!python3
# coding: utf-8
import os
import datetime
import itertools
from tkinter import *
from tkinter import _tkinter # for errors
from tkinter.ttk import *
from tkinter.messagebox import *
from tkinter.scrolledtext import ScrolledText
import requests
from bs4 import BeautifulSoup
__all__ = ['wotd_cli', 'wotd_gui', 'get_wotd']
TIMESTAMP_FILE_NAME = 'wotd-timestamp'
__TEST_WOTD = {
'word': 'стильный',
'meaning': 'stylish, classy, trendy',
'phonetic': "[STEEL'-niy]",
'examples': [
(
'Жа́нна лю́бит сти́льную оде́жду.',
'Zhanna loves stylish clothes.'
),
(
'Андре́й купи́л сти́льные чёрные очки́.',
'Andrey bought stylish black glasses.'
),
(
'У Ната́ши сти́льная су́мка.',
'Natasha has a stylish handbag.'
),
(
'Он был о́чень сти́льным челове́ком.',
'He was a very stylish person.'
),
]
}
PRONUNCIATION_GUIDE = '''
A A - "A"
K k - "K"
M m - "M"
O o - "O"
T t - "T"
В в - "V"
Е е - "yë"
Н н - "N"
Р р - "R"
С с - "S"
У у - "oo" (as in "spoon")
Х х - "gutteral H as in 'Bach' or 'Loch'"
Б б - "B"
Г г - "G"
Д д - "D"
З з - "Z"
И и - "Ē" "ee"
Л л - "L"
П п - "P"
Ф ф - "F"
Э э - "Ë"
Ю ю - "you"
Я я - "yä"
Ё ё - "yo"
Ж ж - "zh"
Ц ц - "ts"
Ч ч - "ch"
Ш ш - "sh"
Щ щ - Pronounced like "sh" but with your tongue on the roof of your mouth. Try putting your tongue in the same position as you would to say "ch" but say "sh" instead. English speakers may find it hard to define the difference between "ш" and "щ".
Ы ы - Pronounced like the "i" in "bit" or "ill". (Said with your tongue slightly back in your mouth.)
Й й - This letter is used to form diphthongs. So "oй" is like the "oy" sound in "boy" or "aй" is like the "igh" in "sigh".
Ъ ъ - The 'Hard Sign' is rarely used. It indicates a slight pause between sylables.
Ь ь - The 'Soft Sign' makes the previous letter 'soft'. Think of the "p" sound in the word "pew". (Try inflecting a very slight "y" sound onto letter before it.)
'''
class PronunciationGuide(ScrolledText):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.insert(1.0, PRONUNCIATION_GUIDE)
self.configure(state=DISABLED)
class Flashcard(Label):
def __init__(self, *args, **kwargs):
self._text = kwargs.pop('text', '')
self._answer = kwargs.pop('answer', '')
super().__init__(*args, **kwargs)
self._textvar = StringVar()
self.configure(textvar=self._textvar)
self._textvar.set(self._text)
self._text_values = itertools.cycle([self._answer, self._text])
self.bind('<Button-1>', self.toggle_text)
def toggle_text(self, event=None):
self._textvar.set(next(self._text_values))
class WotdGui(Frame):
def __init__(self, *args, **kwargs):
self.wotd = kwargs.pop('wotd')
super().__init__(*args, **kwargs)
self.render_wotd()
Frame(self).pack(expand=True, fill=Y) # spacer for guide placement
self.guide = Label(self, text='cyrillic guide')
self.guide.bind('<Button-1>', self.show_guide)
self.guide.pack(anchor='e')
self._root().geometry('700x500')
def show_guide(self, event=None):
top = Toplevel(self._root())
PronunciationGuide(top).pack(expand=True, fill=BOTH)
def render_wotd(self):
Label(
self,
text=self.wotd['word'].title(),
style='Big.TLabel',
).pack()
Label(
self,
text=self.wotd['meaning'],
style='G.TLabel',
).pack()
Flashcard(
self,
text='<pronunciation>',
answer=self.wotd['phonetic'],
style='G.TLabel',
).pack()
for sentence in self.wotd['examples']:
Flashcard(
self,
text=sentence[0],
answer=sentence[1],
style='Med.TLabel',
).pack()
def save_timestamp(t):
'''
time stamp saved in ISO format as a string
'''
with open(TIMESTAMP_FILE_NAME, 'w') as f:
f.write(t.isoformat())
def load_timestamp():
'''
ISO formatted string loaded from a file.
'''
while True:
try:
with open(TIMESTAMP_FILE_NAME) as f:
return datetime.datetime.fromisoformat(f.read())
except FileNotFoundError:
return None
except ValueError:
# parse error on file, delete and try again
print('file corruption detected. recreating file...')
os.remove(TIMESTAMP_FILE_NAME)
continue
def next_timestamp(current_date=None):
if current_date is None:
current_date = datetime.datetime.now()
# get tomorrow's date
next_date = current_date + datetime.timedelta(days=1)
# ajust it for early in the morning
next_date = datetime.datetime(
year=next_date.year,
month=next_date.month,
day=next_date.day,
hour=4,
)
return next_date
def check_timestamp():
'''
Returns True at or after the date determined by next_timestamp.
'''
t = load_timestamp()
# first time running, or missing file. create and return True
if t is None:
save_timestamp(next_timestamp())
return True
# file exists, check to see if it's a future time
if t < datetime.datetime.now():
t = next_timestamp(t) # hasn't been run today, increment day to 6am
save_timestamp(t)
return True
# already ran today, return False
return False
def show_error(msg):
showerror('Error', msg)
def init_tk():
global root
root = Tk()
# configure our ttk styles
s = Style(root)
s.configure('Big.TLabel', font='times 40 bold')
s.configure('Med.TLabel', font='times 25')
s.configure('G.TLabel', font='times 20', color='red')
# prevent tk window from popping up until we want it to
root.withdraw()
def get_wotd():
'''
queries masterrussian.com for the word of the day, translation, phonetics,
and examples and returns them all as a dict.
'''
#return __TEST_WOTD
url = 'http://masterrussian.com/blword.shtml'
r = requests.get(url)
r.raise_for_status()
soup = BeautifulSoup(r.content, 'html.parser')
wotd = {
'word' : soup.select('table h4')[0].text,
'meaning' : soup.select('div#wod_transl')[0].text,
'phonetic' : soup.select('div#wod_pron')[0].text,
}
# examples are <li> tags alternating between a sentence and its translation
# this code will get the block of <li>'s and group them as tuples
examples = soup.select('ul.phrase_plain li')
e = iter(examples)
examples = [(next(e).text, next(e).text) for _ in range(len(examples)//2)]
wotd['examples'] = examples # add to our word object
return wotd
def wotd_cli(wotd):
'''
prints the word of the day out as formatted text meant for a CLI
'''
examples = wotd.get('examples', None)
word = wotd.get('word', None)
meaning = wotd.get('meaning', None)
phonetic = wotd.get('phonetic', None)
fmt = '''
########################################
{word:^45s}
########################################
=========
{phonetic}
{meaning}
'''
print(fmt.format(word=word, meaning=meaning, phonetic=phonetic))
if examples is not None:
print('\n----------------')
for e,t in examples:
print(f'{e}\n{t}\n---')
def wotd_gui(wotd):
'''
displays the word of the day as a tkinter GUI
'''
global root
root.iconify()
WotdGui(root, wotd=wotd).pack(expand=True, fill=BOTH)
root.mainloop()
def main():
'''
check timestamp and display mode, showing the word according to whether
check_timestamp returns True, and prints to CLI if mode is '--cli',
otherwise defaults to the GUI.
'''
import sys
init_tk()
mode = '--gui'
if len(sys.argv) > 1:
wotd = get_wotd()
mode = sys.argv[1]
else:
if check_timestamp():
wotd = get_wotd()
else:
return
if mode == '--cli':
wotd_cli(wotd)
else:
wotd_gui(wotd)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment