forked from tallguyjenks/fla.sh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflash
416 lines (352 loc) Β· 13.8 KB
/
flash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
#!/usr/bin/env bash
#==============================================================#
#
# AUTHOR:
# Bryan Jenks
# - www.bryanjenks.xyz
# - https://github.com/tallguyjenks/flash.sh
# Modified by Lucien Grondin
# - https://github.com/grondilu/fla.sh
#
# SOURCE:
# This file is based off of the one presented in this YouTube Video by nixcasts:
# - https://www.youtube.com/watch?v=lX8jqo70r1I
# Regarding ANSI escape sequences:
# - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
#
# PURPOSE:
# To have a command line flash card tool with minimal code, plain text input and output
#
# VISION:
# The goal i have for this script is a basic level emulation of ANKI to where i have a way to
# keep track of a score for each item in each selected deck so that it can pick from a selection
# of the lowest scoring items and shuf them to the user for reenforcement of active recall.
#
# DEPENDENCIES:
# fzf - https://github.com/junegunn/fzf
# bat - https://github.com/sharkdp/bat
#==============================================================#
# USER CUSTOMIZABLE VARIABLES
declare -i CARD_POOL_SIZE=10 ######## How large the pool size is for shuf to draw from
declare -i SEARCH_DEPTH=999 ######### How many levels to recursively search for .csv's in .local/share/flash
declare PREVIEWER='bat' ########## What fzf previewer to use when searching through decks
declare CURR_DECK_DISPLAY="file" # Options are 'file' or 'path'
# ANSI FOREGROUND ESCAPE COLORS
declare RED=$'\033[0;31m'
declare LRED=$'\033[1;31m'
declare YELLOW=$'\033[1;33m'
declare GREEN=$'\033[0;32m'
declare LGREEN=$'\033[1;32m'
declare LBLUE=$'\033[1;34m'
declare CYAN=$'\033[0;36m'
declare LCYAN=$'\033[1;36m'
declare ORANGE=$'\033[0;33m'
declare LGREY=$'\033[0;37m'
declare WHITE=$'\033[1;37m'
declare NC=$'\033[0m' # No Color
declare REVERSE=$'\033[7m'
# ANSI BACKGROUND ESCAPE COLORS
declare WHITEBG=$'\033[1;47m'
# FONT FORMAT EXCAPE CODES
declare BOLD=$'\e[1m'
print-usage() {
cat <<-END_USAGE
${LCYAN}fla.sh --- Flash card system by Bryan Jenks${NC} ${LBLUE}<[email protected]>${NC}
${YELLOW}${BOLD}Usage:${NC}
${GREEN}flash -h:${NC} Print this help text
${GREEN}flash -i:${NC} Print Information about the flashcard system
${GREEN}flash -v:${NC} Print version Number
${GREEN}flash -p [BINARY]:${NC} Change the previewer when selecting decks.
Default: ${GREEN}bat${NC}
Supported: ${GREEN}bat${NC}, ${GREEN}cat${NC}
END_USAGE
}
print-info() {
cat <<-END_INFO
This flash card system works via colon ${YELLOW}:${NC} separated ${YELLOW}.csv${NC} files
with entries that look like this: ${LGREY}category:question:answer:0${NC}
These ${YELLOW}.csv${NC} files should be stored in your
${YELLOW}\$XDG_DATA_HOME/flash${NC}
OR
${YELLOW}~/.local/share/flash${NC} for Linux
${YELLOW}~/Library/Application Support/flash${NC} for mac
-------------------------------------------------------------------------------
${LCYAN}Exiting:${NC}
You can exit the application at any time by pressing either ${RED}q${NC} or ${RED}Q${NC}
${LCYAN}Usage:${NC}
You will first be prompted with a question and will need to press ${RED}[Enter]${NC}
to reveal the answer. Once the answer is revealed you will need to either
quit, or give a rating to how difficult the question was.
${LCYAN}The Scoring System:${NC}
The last field in the ${YELLOW}.csv${NC} files is the current point score of the 'card'
Every time you rate a card the point score will either increase, decrease,
or stay the same. zero is the lowest value, and the upper limit is over
100 billion.
${CYAN}Scoring Results:${NC}
[${LRED}Hard${NC}] -2 points
[${RED}Difficult${NC}] -1 point
[${YELLOW}Normal${NC}] No Change
[${GREEN}Mild${NC}] +1 point
[${LGREEN}Easy${NC}] +2 points
The cards with the lowest scores (you scored them as ${LRED}Hard${NC}/${RED}Difficult${NC}) are
sorted to the top, a pool of them are picked and from that pool 1 card is
randomly drawn. As you become more familair with the material and rate it
as easier, the point values will go up and the cards will appear less
frequently making room for those cards that are still difficult and have
lower point values.
It is a good idea to start all cards off at ${YELLOW}0${NC} initially so that they all
have an equal chance of being drawn initially.
END_INFO
}
# Remember User's Starting Directory
# PWD="$(pwd)" # not used?
# Where Decks Are Located for linux & mac
# Prefer XDG Configuration before hard coded file paths
case "$(uname)" in
Darwin) DIR="${XDG_DATA_HOME:-$HOME/Library/Application Support}/flash" && alias shuf=gshuf ;;
Linux) DIR="${XDG_DATA_HOME:-$HOME/.local/share}/flash" ;;
*) DIR="${XDG_DATA_HOME:-$HOME/.local/share}/flash" ;;
esac
# Where the example deck will be placed and named
EXAMPLE_DECK="$DIR/deck.csv"
# Track High score
HIGH_SCORE="$DIR/.highscore"
# Track number of cards reviewed per session
REVIEW_LOG="$DIR/.reviews"
# Iterator for Count of cards reveiwed
declare -i COUNTER=0
# User has .local/share directory but no decks inside
NO_DECKS="
No decks were found, please make a new deck
using ${ORANGE}:${NC} as a delimiter in a ${ORANGE}.csv${NC} file in
the ${LRED}$DIR${NC} directory.
An example of a card:
${GREEN}Math:What is the square root of 4?:2:0${NC}
"
# Define setup process in a function and create necessary files for user
setup() {
# Success message of setup process
local -r DIR_MADE_MSG="
Your ${LRED}$DIR${NC} directory has been made and
your ${ORANGE}deck.csv${NC} file is ready for you to enter your flashcard data
If you want to see information about ${ORANGE}fla.sh${NC} then use the option
${YELLOW}flash -i${NC}."
if mkdir "$DIR"
then
if touch "$EXAMPLE_DECK" "$HIGH_SCORE" "$REVIEW_LOG"
then
if cat <<-END_TEMPLATE >> "$EXAMPLE_DECK"
History:When was the declaration of independence signed?:1776:0
Math:What is the square root of 4?:2:4
Science:What is the charge on a proton?:Positive 1:2
Philosophy:What was Socrates known as?:The Gadfly of Athens:0
Programming:What is the typical starting index of an array?:0:5
History:What did Abraham Lincoln typically keep in his hat?:Mail:0
Math:What is the value of PI to 2 decimal places?:3.14:4
Science:What is the charge on an electron?:Negative 1:3
Programming:What does OOP stand for?:Object Oriented Programming:2
History:What did Socrates drink to commit suicide?:Hemlock Tea:2
Math:What is the general equation for the slope of a line?:y=mx+b:4
Science:What is the charge on a neutron?:Neutral:1
Programming:What is Vim?:God's Text Editor:999
History:What were the British known by during the American Revolution?:The Redcoats:1
Math:What is the value of this equation - log10(100)?:2:0
Science:What are protons and neutrons made of?:quarks:5
Programming:What does RAM stand for?:Random Access Memory:1
History:What was the year 2000 also known as?:Y2K:0
Math:What is the formula for the mean?:Sum/count:4
Science:What is cold?:The absense of heat:3
Programming:What languages are the worst?:Proprietary:999
History:When did man land on the moon?:1969:4
Math:10^3=?:1000:1
Science:The _____ ______ Project mapped all of man's genes.:Human Genome:3
Programming:What is the best computer to program on?:Thinkpad:999
History:When was fla.sh created?:April 2020:999
Math:What do you call a number only divisible by itself and 1?:Prime:0
Science:What is the distance between the Earth and Sol called?:An Astronomical Unit (AU):1
Programming:What is the best operating system?:Arch, because BTW i run Arch:999"
END_TEMPLATE
then echo -e "$DIR_MADE_MSG"
fi
fi
fi
}
# Test if .local/share exists and wether to offer setup process
if [ ! -d "$DIR" ]
then
echo -e "No ${LRED}$DIR${NC} directory, make it? ${LGREEN}Y${NC}/${LRED}N${NC}"
# shellcheck disable=SC2162
read
case "${REPLY,,}" in
[qn]) return 0 ;;
y) setup && return 0 ;;
*) >&2 echo -e "invalid choice, please select either ${LGREEN}y${NC} or ${LRED}n${NC}" && exit 1 ;;
esac
fi
# go to the flashcard decks directory
#cd "$DIR" || exit 1
pushd "$DIR"
# If there are no flashcard decks available return user to starting location
# while also displaying explanatory text of issue
if
! find . -maxdepth "$SEARCH_DEPTH" -iname "*.csv" -print -quit |
read
then
>&2 echo "$NO_DECKS"
return 1
fi
# if highscore file was removed, remake it.
[ -e "$HIGH_SCORE" ] || : touch "$HIGH_SCORE"
# if reviewed file was removed, remake it.
[ -e "$REVIEW_LOG" ] || : touch "$REVIEW_LOG"
process-args() {
local OPTIND OPTARG o
while getopts 'hivp:' o
do
echo "option $o"
case "$o" in
h) print-usage && return 0 ;;
i) print-info && return 0 ;;
v) echo -e "\n${YELLOW}fla.sh Current Version:${NC} ${RED}1.2${NC}\n" && return 0 ;;
p) { [[ $(command -v "$OPTARG" 2>&1) ]] && PREVIEWER=$OPTARG; } ||
>&2 echo "Unable to find previewer $OPTARG. Exiting..." &&
return 1 ;;
*) print-usage && return 1 ;;
esac
done
}
# Set some parameters for preview command used by FZF
while [ -z "$PREVIEWER_PARAMTERS" ]
do
case "$PREVIEWER" in
bat) PREVIEWER_PARAMTERS="--theme='Solarized (dark)' --style=numbers --color=always" ;;
cat) PREVIEWER_PARAMTERS="-b" ;;
*) echo -e "${RED}$PREVIEWER${NC} is not a valid previewer. Use '${GREEN}bat${NC}' or '${GREEN}cat${NC}'. Falling back on default...\n" &&
read -r -s -p 'Press [Enter] to continue...' &&
echo -e "\n" &&
PREVIEWER='bat' ;;
esac
done
# Show pretty FZF preview of decks using $PREVIEWER. Default: BAT
DECK="$(find . -maxdepth "$SEARCH_DEPTH" -iname "*.csv" | fzf --preview="$PREVIEWER $PREVIEWER_PARAMTERS {} | head -500")"
# Creating new entries in the log for the average score generation
add-usage-entry() {
if [ ! "$COUNTER" = 0 ]
then
# Create a New Entry
TIME_STAMP=$(date +"%Y%m%d %H:%M:%S")
printf -v ENTRY "TimeStamp: %s Deck: %s cardsReviewed: %s" "$TIME_STAMP" "${DECK##*/}" "$COUNTER"
echo "$ENTRY" >>"$REVIEW_LOG"
fi
}
function center() {
fmt -w59 <<<"$1" |
while read -r
do printf "%$(( (59 - ${#REPLY})/2 ))s%s\n" ' ' "$REPLY"
done
}
# If no deck is selected in fzf menu
# return user to start location
# and exit quietly
[ -z "$DECK" ] && return 1
quiz() {
clear
# hiding cursor
echo -e "\e[?25l"
sort "$DECK" -n --field-separator=: --key=4 |
{
local -i PICK
mapfile -t
echo "${MAPFILE[
RANDOM%(${#MAPFILE[@]} < CARD_POOL_SIZE ? ${#MAPFILE[@]} : CARD_POOL_SIZE)
]}"
} |
{
local -a q
local -i DIFFICULTY_SCORE
IFS=: read -r -a q
cat <<-EOF
$REVERSE$BOLD Fla.sh - Flash Cards In Your Terminal $BOLD$REVERSE
${ORANGE}${BOLD} Current Deck:${BOLD}${NC} $(basename -s .csv "${DECK##*/}")
${ORANGE}${BOLD}Cards Reviewed:${BOLD}${NC} $COUNTER
${ORANGE}${BOLD}High Score:${BOLD}${NC} $(cat "$HIGH_SCORE")
${ORANGE}${BOLD}Avg review:${BOLD}${NC} $(awk '{ sum += $7; n++ } END { if (n > 0) print sum / n; }' "$REVIEW_LOG")
${LGREY}Category:${NC} ${q[0]}
___________________________________________________________
EOF
center "${q[1]}"
echo -e "\n${LGREY}ββββββββββββββββ Press [Enter] to continue ββββββββββββββββ${NC}"
while
read -sn 1 < /dev/tty
! [[ "$REPLY" =~ ^[qQ]?$ ]]
do :
done
echo -ne "\e[1F"
[[ "${REPLY,}" = q ]] && add-usage-entry && exit 0
cat <<-EOF
___________________________________________________________
$(center "${q[2]}")
${REVERSE}===========================================================${REVERSE}
${LGREY}How Difficult Was This Question?${NC}
${LRED}[H]ard${NC} ${RED}[D]ifficult${NC} ${YELLOW}[N]ormal${NC} ${GREEN}[M]ild${NC} ${LGREEN}[E]asy${NC}
${LGREY}Select a letter continue, or${NC} ${LRED}Q${NC} ${LGREY}to quit...${NC}"
EOF
local -Ari difficulty=([H]=0 [D]=1 [N]=2 [M]=3 [E]=4)
# shellcheck disable=SC2162
while
read -sn 1 </dev/tty
[[ ! "${REPLY^}" =~ [12345QHDNME] ]]
do :
done
[[ "${REPLY,}" = q ]] && add-usage-entry && exit 0
if [[ "${REPLY^}" =~ [HDNME] ]]
then DIFFICULTY_SCORE=${difficulty[${REPLY^}]}
else DIFFICULTY_SCORE=REPLY
fi
# Increment count for card review count increment
((COUNTER++))
local -i NEW_ITEM_SCORE
if (( q[3] == 0 ))
then
NEW_ITEM_SCORE=0
case "$DIFFICULTY_SCORE" in
[123]) NEW_ITEM_SCORE=0 ;; #HARD DIFFICULTY & NORMAL
4) NEW_ITEM_SCORE=1 ;; #MILD
5) NEW_ITEM_SCORE=2 ;; #EASY
*) NEW_ITEM_SCORE=0 ;; #INVALID
esac
elif (( q[3] == 1 ))
then
case "$DIFFICULTY_SCORE" in
1) NEW_ITEM_SCORE=0 ;; #HARD
2) NEW_ITEM_SCORE=0 ;; #DIFFICULT
3) NEW_ITEM_SCORE=1 ;; #NORMAL
4) NEW_ITEM_SCORE=2 ;; #MILD
5) NEW_ITEM_SCORE=3 ;; #EASY
*) NEW_ITEM_SCORE=1 ;; #INVALID
esac
else
case "$DIFFICULTY_SCORE" in
1) NEW_ITEM_SCORE="$((q[3] - 2))" ;; #HARD
2) NEW_ITEM_SCORE="$((q[3] - 1))" ;; #DIFFICULTY
3) NEW_ITEM_SCORE="${q[3]}" ;; #NORMAL
4) NEW_ITEM_SCORE="$((q[3] + 1))" ;; #MILD
5) NEW_ITEM_SCORE="$((q[3] + 2))" ;; #EASY
*) NEW_ITEM_SCORE="${q[3]}" ;; #INVALID
esac
fi
# Update item score for each flashcard item
sed -i "$((PICK+1))s/:[^:]*$/:$NEW_ITEM_SCORE/" "$DECK"
# If no highscore currently set, set it.
[ -z "$(cat "$HIGH_SCORE")" ] && echo "$COUNTER" >"$HIGH_SCORE"
local -i high_score
read high_score < "$HIGH_SCORE"
# If Cards Reviewed > Current High Score, Update
((COUNTER > high_score)) && echo "$COUNTER" >"$HIGH_SCORE"
}
# restoring cursor
echo -e "\e[?25h\e[?47l"
}
#while true
#do main
#done