Last active
May 13, 2020 15:58
-
-
Save loliee/3c03e755f65baf2c42981a05849653fc to your computer and use it in GitHub Desktop.
A simple naming assistant.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: UTF-8 -*- | |
# Need a first name ? Curl that script and start a brainstorming session | |
# 🇫🇷 https://www.data.gouv.fr/fr/datasets/liste-de-prenoms/ | |
import os | |
import sys | |
import random | |
import csv | |
import pickle | |
import hashlib | |
import subprocess | |
import urllib.request | |
from argparse import ArgumentParser | |
C = { | |
'blue': '\033[94m', | |
'green': '\033[92m', | |
'yellow': '\033[93m', | |
'red': '\033[91m', | |
'bold': '\033[1m', | |
'reset': '\033[0m' | |
} | |
CSV_URL = 'https://www.data.gouv.fr/fr/datasets/r/55cd803a-998d-4a5c-9741-4cd0ee0a7699' | |
CSV_SHA256 = 'e3d77a4ceab1e477eeafb410e7390393cec2ba8a4b9dd764e31e7a2b2b79b187' | |
def sha256sum(filepath): | |
'''sha256sum a file''' | |
blocksize = 65536 | |
hasher = hashlib.sha256() | |
with open(filepath, 'rb') as afile: | |
buf = afile.read(blocksize) | |
while len(buf) > 0: | |
hasher.update(buf) | |
buf = afile.read(blocksize) | |
return hasher.hexdigest() | |
def format_csv_line(obj): | |
'''Convert csv line to utf-8 dictionary''' | |
key = obj[0] | |
meta = { | |
'gender': obj[1], | |
'origin': obj[2], | |
'freq': float(obj[3]) | |
} | |
return key, meta | |
class BabyNaming(object): | |
def __init__(self, args): | |
self._args = args | |
self._app_dir = os.path.join(os.environ.get('HOME'), '.babynaming') | |
self._csv_file = os.path.join(self._app_dir, 'base.csv') | |
self._init_app_dir() | |
self._is_say() | |
if self._args.print_only: | |
self._load_csv() | |
self._create_dbe() | |
elif self._args.session and os.path.exists(self._get_session_path()): | |
self._load_db() | |
else: | |
if not self._args.session: | |
self._args.session = input('\nEnter a session name: ') | |
if os.path.exists(self._get_session_path()): | |
self._load_db() | |
else: | |
print('{}Create new session file "{}" {}'.format( | |
C['blue'], self._get_session_path(), C['reset'] | |
)) | |
self._load_csv() | |
self._create_dbe() | |
self._save() | |
def _load_csv(self): | |
data = {} | |
with open(self._csv_file, 'r', encoding='ISO-8859-1') as csvfile: | |
reader = csv.reader(csvfile, delimiter=';') | |
next(reader) | |
for line in reader: | |
key, meta = format_csv_line(line) | |
data[key] = meta | |
self._db = data | |
def _load_db(self): | |
print('{}Load session file "{}" {}'.format( | |
C['blue'], self._get_session_path(), C['reset'] | |
)) | |
self._db = pickle.load(open(self._get_session_path(), 'rb')) | |
self._create_dbe() | |
def _is_say(self): | |
try: | |
subprocess.check_output( | |
'hash say', | |
shell=True, | |
stderr=subprocess.STDOUT, | |
) | |
except subprocess.CalledProcessError: | |
print('{}"say" command is not available {}'.format( | |
C['yellow'], C['reset'] | |
)) | |
self._args.voice = None | |
def _init_app_dir(self): | |
if not os.path.exists(self._app_dir): | |
os.mkdir(self._app_dir) | |
if not os.path.exists(self._csv_file): | |
print('{}Download "{}" to "{}"{}'.format( | |
C['blue'], CSV_URL, self._csv_file, C['reset'] | |
)) | |
urllib.request.urlretrieve(CSV_URL, self._csv_file) | |
if sha256sum(self._csv_file) != CSV_SHA256: | |
print('{}CSV checksum is incorrect "{}""{}'.format( | |
C['red'], self._csv_file, C['reset'] | |
)) | |
def _create_dbe(self): | |
self._dbe = { | |
k: {'gender': v['gender'], | |
'origin': v['origin'], | |
'freq': v['freq']} | |
for k, v in self._db.items() if self._filter_key(k) | |
} | |
def _get_session_path(self): | |
return os.path.join(self._app_dir, self._args.session + '.pkl') | |
def _save(self): | |
with open(self._get_session_path(), 'wb+') as dbf: | |
pickle.dump( | |
self._db, dbf, pickle.HIGHEST_PROTOCOL | |
) | |
def _filter_key(self, key): | |
ret = True | |
if self._args.origin and ( | |
self._args.origin not in self._db[key]['origin']): | |
ret = False | |
if self._args.gender and ( | |
self._args.gender not in self._db[key]['gender']): | |
ret = False | |
return ret | |
def _print_all(self): | |
names = self._dbe.keys() | |
names.sort() | |
for name in names: | |
self._print(name) | |
def _print(self, name): | |
print('\n{}{}\n{}{}\nfreq: {}{}{}\norigin: {}{}{}\n{}'.format( | |
C['bold'], name.capitalize(), '-' * len(name), C['reset'], | |
C['green'], self._db[name]['freq'], C['reset'], | |
C['blue'], self._db[name]['origin'], C['reset'], | |
'-' * (len(self._dbe[name]['origin']) + 8) | |
)) | |
def _say(self, name): | |
if self._args.voice: | |
subprocess.Popen( | |
'say --quality=127 --rate=0.01 -v {} "{}"'.format( | |
self._args.voice, name | |
), shell=True | |
) | |
def _count(self): | |
print('Still {}{}{}{} names'.format( | |
C['bold'], C['red'], len(self._dbe.keys()), C['reset'] | |
)) | |
def _get_answer(self, name): | |
while True: | |
answer = input('Keep this name ? (y/n) ? ') | |
if answer == 'n': | |
del self._db[name] | |
del self._dbe[name] | |
self._save() | |
break | |
elif answer == 'y': | |
self._dbe[name]['viewed'] = True | |
break | |
def start(self): | |
'''This can be long :/''' | |
if self._args.print_only: | |
self._print_all() | |
sys.exit(0) | |
while True: | |
names = [k for k, v in self._dbe.items() if 'viewed' not in v] | |
if names == []: | |
print('\n{}Your selection:\n - {}{}'.format( | |
C['bold'], C['reset'], | |
'\n - '.join(self._dbe.keys()) | |
)) | |
sys.exit(0) | |
name = random.choice(names) | |
self._print(name) | |
self._say(name) | |
self._get_answer(name) | |
self._count() | |
def main(): | |
global C | |
parser = ArgumentParser( | |
description='Play and record a baby naming session. CTRL-C to exit.' | |
) | |
parser.add_argument( | |
'-s', '--session', dest='session', default=None, | |
help='Session file name, saved in ~/.babynaming directory.' | |
) | |
parser.add_argument( | |
'-g', '--gender', dest='gender', default=None, | |
help='Filter on gender (m|f).' | |
) | |
parser.add_argument( | |
'-o', '--origin', dest='origin', default=None, | |
help='Filter on origin (french|english|portuguese|...).' | |
) | |
parser.add_argument( | |
'-v', '--voice', dest='voice', default=None, | |
help='Voice name, will enable vocal reading with "say" command.' | |
) | |
parser.add_argument( | |
'-p', '--print-only', dest='print_only', default=None, | |
action='store_true', | |
help='Print all names and exit.' | |
) | |
parser.add_argument( | |
'--no-color', dest='no_color', default=None, action='store_true', | |
help='Disable color display' | |
) | |
args = parser.parse_args() | |
if args.no_color: | |
C = {k: '' for k, v in C.items()} | |
try: | |
babynamingparty = BabyNaming(args) | |
babynamingparty.start() | |
except KeyboardInterrupt: | |
print('{}\nExit...{}'.format(C['yellow'], C['reset'])) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Install
curl -s -o babynaming.py https://gist.githubusercontent.com/loliee/3c03e755f65baf2c42981a05849653fc/raw/43f5abfdd1d91ac1f63e36b50f2023634022c05e/babynaming.py && chmod u+x babynaming.py
Dependency free, should be executed as a non privileged user.
Screenshot