-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfinncards.py
966 lines (927 loc) · 38.7 KB
/
finncards.py
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Feb 16 12:26:19 2019
@author: toddbilsborough
Finnish flashcards application with spaced repetition and retrieval of word
forms from the internet
"""
import datetime
from numpy import nan
import os
import pandas as pd
import random
from shutil import copy
from bs4 import BeautifulSoup
from requests_html import HTMLSession
# General settings
# Multiply the interval for a correct answer by this
CORRECT_INTERVAL = 1.4
# Multiply the interval for an incorrect answer by this
INCORRECT_INTERVAL = 0.5
# Lowest possible interval
MINIMUM_INTERVAL = pd.to_timedelta('1 days 00:00:00')
# Highest possible interval
MAXIMUM_INTERVAL = pd.to_timedelta('365 days 00:00:00')
# Highest interval after getting something wrong
MAX_AFTER_WRONG = pd.to_timedelta('6 days 00:00:00')
# 1 out of every n cards is tested further (on form and sentence use)
FURTHER_TESTING_RATE = 4
# Anything in this list always does further testing
FURTHER_TESTING = ['verbs']
# Additional columns for nominals
NOMINAL_COLUMNS = [
"Nominative singular",
"English"
]
# Additional columns for verbs
VERB_COLUMNS = [
"Infinitive",
"English present",
"English simple past",
"English past participle",
"English present participle"
]
# Columns for invariants
INVARIANT_COLUMNS = [
"Finnish",
"English",
"Pre / Post",
"Rection"
]
# Columns for phrases
PHRASE_COLUMNS = [
"Finnish",
"English"
]
# Statistics
STATS = [
"Last reviewed",
"Next review",
"Interval",
"Correct?",
"Times correct",
"Times incorrect"
]
# Keys: Finnish noun forms
# Values: The HTML tags used to retrieve them
NOMINAL_FORMS = {
"Nominative plural": "form-of plural-nominative-form-of lang-fi",
"Partitive singular": "form-of singular-partitive-form-of lang-fi",
"Partitive plural": "form-of plural-partitive-form-of lang-fi",
"Genitive singular": "form-of singular-genitive-form-of lang-fi",
"Genitive plural": "form-of plural-genitive-form-of lang-fi",
"Illative singular": "form-of singular-illative-form-of lang-fi",
"Illative plural": "form-of plural-illative-form-of lang-fi",
"Inessive singular": "form-of singular-inessive-form-of lang-fi",
"Inessive plural": "form-of plural-inessive-form-of lang-fi",
"Elative singular": "form-of singular-elative-form-of lang-fi",
"Elative plural": "form-of plural-elative-form-of lang-fi",
"Adessive singular": "form-of singular-adessive-form-of lang-fi",
"Adessive plural": "form-of plural-adessive-form-of lang-fi",
"Ablative singular": "form-of singular-ablative-form-of lang-fi",
"Ablative plural": "form-of plural-ablative-form-of lang-fi",
"Allative singular": "form-of singular-allative-form-of lang-fi",
"Allative plural": "form-of plural-allative-form-of lang-fi",
"Essive singular": "form-of singular-essive-form-of lang-fi",
"Essive plural": "form-of plural-essive-form-of lang-fi",
"Translative singular": "form-of singular-translative-form-of lang-fi",
"Translative plural": "form-of plural-translative-form-of lang-fi",
"Abessive singular": "form-of singular-abessive-form-of lang-fi",
"Abessive plural": "form-of plural-abessive-form-of lang-fi"
}
# Keys: Finnish verb forms
# Values: [Their English translation, associated English form]
# English form key:
# 0: Simple present / present participle
# 1: Simple present only
# 2: Simple past
# 3: Past participle
# 4: Present participle only
VERB_FORMS = {
"First person": ["I / I am", 0],
"First person negative": ["I don't / I'm not", 0],
"First person perfect": ["I have", 3],
"First person perfect negative": ["I haven't", 3],
"Second person": ["You / You are", 0],
"Second person negative": ["You don't / You're not", 0],
"Second person perfect": ["You have", 3],
"Second person perfect negative": ["You haven't", 3],
"Third person": ["He / He is", 0],
"Third person negative": ["She doesn't / She isn't", 0],
"Third person perfect": ["She has", 3],
"Third person perfect negative": ["He hasn't", 3],
"First person plural": ["We / We are", 0],
"First person plural negative": ["We don't / We're not", 0],
"First person plural perfect": ["We have", 3],
"First person plural perfect negative": ["We haven't", 3],
"Second person plural": ["You all / You all are", 0],
"Second person plural negative": ["You all don't / You all aren't", 0],
"Second person plural perfect": ["You all have", 3],
"Second person plural perfect negative": ["You all haven't", 3],
"Third person plural": ["They / They are", 0],
"Third person plural negative": ["They don't / They're not", 0],
"Third person plural perfect": ["They have", 3],
"Third person plural perfect negative": ["They haven't", 3],
"Passive": ["One / One is", 0],
"Passive negative": ["One doesn't / One isn't", 0],
"Passive perfect": ["One has", 3],
"Passive perfect negative": ["One hasn't", 3],
"First person past": ["I", 2],
"First person past negative": ["I didn't", 1],
"First person pluperfect": ["I had", 3],
"First person pluperfect negative": ["I hadn't", 3],
"Second person past": ["You", 2],
"Second person past negative": ["You didn't", 1],
"Second person pluperfect": ["You had", 3],
"Second person pluperfect negative": ["You hadn't", 3],
"Third person past": ["She", 2],
"Third person past negative": ["He didn't", 1],
"Third person pluperfect": ["He had", 3],
"Third person pluperfect negative": ["She hadn't", 3],
"First person past plural": ["We", 2],
"First person past plural negative": ["We didn't", 1],
"First person pluperfect plural": ["We had", 3],
"First person pluperfect plural negative": ["We hadn't", 3],
"Second person past plural": ["You all", 2],
"Second person past plural negative": ["You all didn't", 1],
"Second person pluperfect plural": ["You all had", 3],
"Second person pluperfect plural negative": ["You all hadn't", 3],
"Third person past plural": ["They", 2],
"Third person past plural negative": ["They didn't", 1],
"Third person pluperfect plural": ["They had", 3],
"Third person pluperfect plural negative": ["They hadn't", 3],
"Passive past": ["One", 2],
"Passive past negative": ["One didn't", 1],
"Passive pluperfect": ["One had", 3],
"Passive pluperfect negative": ["One hadn't", 3],
"First person conditional": ["I would", 1],
"First person conditional negative": ["I wouldn't", 1],
"First person conditional perfect": ["I would have", 3],
"First person conditional perfect negative": ["I wouldn't have", 3],
"Second person conditional": ["You would", 1],
"Second person conditional negative": ["You wouldn't", 1],
"Second person conditional perfect": ["You would have", 3],
"Second person conditional perfect negative": ["You wouldn't have", 3],
"Third person conditional": ["He would", 1],
"Third person conditional negative": ["She wouldn't", 1],
"Third person conditional perfect": ["She would have", 3],
"Third person conditional perfect negative": ["He wouldn't have", 3],
"First person conditional plural": ["We would", 1],
"First person conditional plural negative": ["We wouldn't", 1],
"First person conditional plural perfect": ["We would have", 3],
"First person conditional plural perfect negative":
["We wouldn't have", 3],
"Second person conditional plural": ["You all would", 1],
"Second person conditional plural negative": ["You all wouldn't", 1],
"Second person conditional plural perfect": ["You all would have", 3],
"Second person conditional plural perfect negative":
["You all wouldn't have", 3],
"Third person conditional plural": ["They would", 1],
"Third person conditional plural negative": ["They wouldn't", 1],
"Third person conditional plural perfect": ["They would have", 3],
"Third person conditional plural perfect negative":
["They wouldn't have", 3],
"Passive conditional": ["One would", 1],
"Passive conditional negative": ["One wouldn't", 1],
"Passive conditional perfect": ["One would have", 3],
"Passive conditional perfect negative": ["One wouldn't have", 3],
"Second person imperative": ["(You)", 1],
"Second person imperative negative": ["Don't", 1],
"Second person imperative perfect": ["You must have", 3],
"Second person imperative perfect negative": ["You musn't have", 3],
"Third person imperative": ["She must", 1],
"Third person imperative negative": ["He musn't", 1],
"Third person imperative perfect": ["He must have", 3],
"Third person imperative perfect negative": ["She musn't have", 3],
"First person imperative plural": ["Let's", 1],
"First person imperative plural negative": ["Let's not", 1],
"First person imperative plural perfect": ["Let's have", 3],
"First person imperative plural perfect negative": ["Let's not have", 3],
"Second person imperative plural": ["(You all)", 1],
"Second person imperative plural negative": ["(You all) don't", 1],
"Second person imperative plural perfect":
["(You all) have ___ done:", 4],
"Second person imperative plural perfect negative":
["(You all) don't have ___ done:", 4],
"Third person imperative plural": ["They must", 1],
"Third person imperative plural negative": ["They musn't", 1],
"Third person imperative plural perfect": ["They must have", 3],
"Third person imperative plural perfect negative":
["They musn't have", 3],
"Passive imperative": ["One must", 1],
"Passive imperative negative": ["One musn't", 1],
"Passive imperative perfect": ["One must have", 3],
"Passive imperative perfect negative": ["One musn't have", 3],
"First person potential": ["I could", 1],
"First person potential negative": ["I couldn't", 1],
"First person potential perfect": ["I could have", 3],
"First person potential perfect negative": ["I couldn't have", 3],
"Second person potential": ["You could", 1],
"Second person potential negative": ["You couldn't", 1],
"Second person potential perfect": ["You could have", 3],
"Second person potential perfect negative": ["You couldn't have", 3],
"Third person potential": ["She could", 1],
"Third person potential negative": ["He couldn't", 1],
"Third person potential perfect": ["He could have", 3],
"Third person potential perfect negative": ["He couldn't have", 3],
"First person potential plural": ["We could", 1],
"First person potential plural negative": ["We couldn't", 1],
"First person potential plural perfect": ["We could have", 3],
"First person potential plural perfect negative": ["We couldn't have", 3],
"Second person potential plural": ["You all could", 1],
"Second person potential plural negative": ["You all couldn't", 1],
"Second person potential plural perfect": ["You all could have", 3],
"Second person potential plural perfect negative":
["You all couldn't have", 3],
"Third person potential plural": ["They could", 1],
"Third person potential plural negative": ["They couldn't", 1],
"Third person potential plural perfect": ["They could have", 3],
"Third person potential plural perfect negative":
["They couldn't have", 3],
"Passive potential": ["One could", 1],
"Passive potential negative": ["One couldn't", 1],
"Passive potential perfect": ["One could have", 3],
"Passive potential perfect negative": ["One wouldn't have", 3],
"1st infinitive": ["[skip]", nan],
"Present active participle": ["-ing", 4],
"Present passive participle": ["Which is to be", 3],
"A infinitive, long form": ["[skip]", nan],
"Past active participle": ["Did this:", 1],
"Past passive participle": ["Which is to be done by one:", 0],
"E infinitive inessive": ["When someone does this:", 0],
"E infinitive inessive passive": ["When one does this:", 0],
"Agent participle": ["Past adjectival form:", 0],
"E infinitive instructive": ["Doing:", 4],
"Negative participle": ["Without:", 4],
"MA infinitive inessive": ["Ongoing action or process:", 4],
"MA infinitive elative": ["From doing this:", 4],
"MA infinitive illative": ["About to begin", 4],
"MA infinitive adessive": ["By doing this:", 4],
"MA infinitive abessive": ["Without doing this: ", 4],
"MA infinitive instructive": ["Solemn necessity:", 4],
"MA infinitive instructive passive": ["One's solemn necessity:", 1],
"MINEN infinitive nominative": ["[skip]", nan],
"MINEN infinitive partitive": ["[skip]", nan],
"5th infinitive": ["[skip]", nan]
}
def backup_files():
"""Backs up data files"""
ts = datetime.datetime.today()
day = ts.day if int(ts.day) > 9 else "0{}".format(ts.day)
month = ts.month if int(ts.month) > 9 else "0{}".format(ts.month)
timestamp = "_{}{}{}".format(ts.year, month, day)
newpath = r'backups/backup{}/'.format(timestamp)
if not os.path.exists(newpath):
os.makedirs(newpath)
print(r"Backing up data files to /backups/backup{}".format(timestamp))
copy('verbs.csv', newpath)
copy('nominals.csv', newpath)
copy('invariants.csv', newpath)
copy('phrases.csv', newpath)
print("Backup completed")
def load_invariants():
"""Loads the invariants file"""
invariants = pd.read_csv('invariants.csv',
index_col='Finnish',
parse_dates=['Last reviewed', 'Next review'],
infer_datetime_format=True)
invariants.sort_index(inplace=True)
return invariants
def load_nominals():
"""Load the nominals file"""
nominals = pd.read_csv('nominals.csv',
index_col="Nominative singular",
parse_dates=["Last reviewed", "Next review"],
infer_datetime_format=True)
nominals.sort_index(inplace=True)
return nominals
def load_verbs():
"""Loads the verbs file"""
verbs = pd.read_csv('verbs.csv',
index_col="Infinitive",
parse_dates=["Last reviewed", "Next review"],
infer_datetime_format=True)
verbs.sort_index(inplace=True)
return verbs
def load_phrases():
"""Loads the phrases file"""
phrases = pd.read_csv('phrases.csv',
index_col=0,
parse_dates=["Last reviewed", "Next review"],
infer_datetime_format=True)
phrases.sort_index(inplace=True)
return phrases
def in_file(word=None, words_df=None, category="", english=False):
"""Checks to see if a word is in a given file"""
if word is None:
word = input("Word: ").lower()
while category not in ['invariant', 'nominal', 'verb']:
category = input("Category: ").lower()
if words_df is None:
if category == 'invariant':
words_df = load_invariants()
elif category == 'nominal':
words_df = load_nominals()
elif category == 'verb':
words_df = load_verbs()
if english:
if category == 'verb':
return (True if word in list(words_df['English present']) else
False)
else:
return True if word in list(words_df['English']) else False
else:
return True if word in list(words_df.index) else False
def save_invariant(invariant=None, english=None):
"""Save an invariant and at it to the file"""
invariants = load_invariants()
if invariant is None:
invariant = input("Invariant (Finnish): ")
if in_file(word=invariant, words_df=invariants,
category='invariant'):
print("{} already in file".format(invariant))
return None
if english is None:
english = input("English translation of {}: ".format(invariant))
if in_file(word=english, words_df=invariants,
category='invariant', english=True):
print("{} already in file".format(invariant))
return None
last_reviewed = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
next_review = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
interval = pd.to_timedelta("1 days")
correct = True
times_correct = 0
times_incorrect = 0
stats = [last_reviewed, next_review, interval, correct, times_correct,
times_incorrect]
pre_post = input("Pre or post? ").lower()
rection = input("Rection: ").lower()
data = [invariant, english, pre_post, rection] + stats
columns = INVARIANT_COLUMNS + STATS
entry = pd.DataFrame(data=[data], columns=columns)
entry.set_index(keys="Finnish", inplace=True)
invariants = invariants.append(entry, verify_integrity=True)
conf = input("Adding {}. Continue? ".format(invariant)).lower()
if conf == 'y':
invariants.to_csv('invariants.csv')
print("File saved")
return True
else:
print("No updates made")
return None
def save_nominal(nominal=None, english=None, forms=None):
"""Save a nominal and its forms to the file"""
nominals = load_nominals()
if nominal is None:
nominal = input("Nominal (Finnish): ")
if in_file(word=nominal, words_df=nominals,
category='nominal'):
print("{} already in file".format(nominal))
return None
if english is None:
english = input("English translation of {}: ".format(nominal))
if in_file(word=english, words_df=nominals,
category='nominal', english=True):
print("{} already in file".format(english))
return None
if forms is None:
forms = retrieve_nominal(nominal, skip_save=True)
last_reviewed = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
next_review = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
interval = pd.to_timedelta("1 days")
correct = True
times_correct = 0
times_incorrect = 0
stats = [last_reviewed, next_review, interval, correct, times_correct,
times_incorrect]
data = [nominal, english] + forms + stats
columns = NOMINAL_COLUMNS + list(NOMINAL_FORMS.keys()) + STATS
entry = pd.DataFrame(data=[data], columns=columns)
entry.set_index(keys="Nominative singular", inplace=True)
nominals = nominals.append(entry, verify_integrity=True)
conf = input("Adding {}. Continue? ".format(nominal)).lower()
if conf == 'y':
nominals.to_csv('nominals.csv')
print("File saved")
return True
else:
print("No updates made")
return None
def save_verb(verb=None, english=None, forms=None):
"""Save a verb and its forms to the file"""
verbs = load_verbs()
if verb is None:
verb = input("Verb (Finnish): ")
if in_file(word=verb, words_df=verbs,
category='verb'):
print("{} already in file".format(verb))
return None
if english is None:
e_present = input("English present: ")
if in_file(word=e_present, words_df=verbs,
category='verb', english=True):
print("{} already in file".format(e_present))
return None
e_simple_past = input("English simple past: ")
e_past_part = input("English past participle: ")
e_present_part = input("English present participle: ")
if (english is not None and not
isinstance(english, list) and
len(english) != 4):
print("Wrong number of entries in English list")
return None
if english is not None:
e_present = english[0]
e_simple_past = english[1]
e_past_part = english[2]
e_present_part = english[3]
if forms is None:
forms = retrieve_verb(verb, skip_save=True)
last_reviewed = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
next_review = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
interval = pd.to_timedelta("1 days")
correct = True
times_correct = 0
times_incorrect = 0
stats = [last_reviewed, next_review, interval, correct, times_correct,
times_incorrect]
data = [verb, e_present, e_simple_past, e_past_part,
e_present_part] + forms + stats
columns = VERB_COLUMNS + list(VERB_FORMS.keys()) + STATS
entry = pd.DataFrame(data=[data], columns=columns)
entry.set_index(keys="Infinitive", inplace=True)
verbs = verbs.append(entry, verify_integrity=True)
conf = input("Adding {}. Continue? ".format(verb)).lower()
if conf == 'y':
verbs.to_csv('verbs.csv')
print("File saved")
return True
else:
print("No updates made")
return None
def save_phrase(phrase=None, english=None):
"""Save a phrase to the file"""
if phrase is None:
finnish = input("Phrase (Finnish): ").lower()
if english is None:
english = input("English: ").lower()
last_reviewed = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
next_review = pd.Timestamp(pd.to_datetime(datetime.datetime.now()))
interval = pd.to_timedelta("1 days")
correct = True
times_correct = 0
times_incorrect = 0
stats = [last_reviewed, next_review, interval, correct, times_correct,
times_incorrect]
data = [finnish, english] + stats
columns = PHRASE_COLUMNS + STATS
entry = pd.DataFrame(data=[data], columns=columns)
phrases = load_phrases()
phrases = phrases.append(entry)
phrases.reset_index(drop=True, inplace=True)
conf = input("Adding phrase. Continue? ").lower()
if conf == 'y':
phrases.to_csv('phrases.csv')
print("File saved")
return True
else:
return None
def retrieve_nominal(nominal, skip_save=False):
"""Get a noun's forms from wiktionary"""
session = HTMLSession()
link_string = "https://en.wiktionary.org/wiki/{}".format(nominal)
try:
r = session.get(link_string)
except Exception as ex:
print("There was a problem getting the web page: {}".format(ex))
return None
soup = BeautifulSoup(r.text, 'html.parser')
forms = []
for key, value in NOMINAL_FORMS.items():
try:
span = soup.find(class_=value)
link = span.find('a')
form = link['title'].split(" ")[0]
forms.append(form)
except Exception as ex:
print("There was a problem finding {}: {}".format(key, ex))
forms.append("")
continue
print(*forms, sep=', ')
nominals = load_nominals()
if not skip_save and nominal not in nominals.index:
conf = input("No entry for {} in file. Add? ".format(nominal)).lower()
if conf == 'y':
save_nominal(nominal, forms=forms)
return forms
def retrieve_verb(verb, skip_save=False):
"""Get a verb's forms from wiktionary"""
session = HTMLSession()
link_string = "https://en.wiktionary.org/wiki/{}".format(verb)
try:
r = session.get(link_string)
except Exception as ex:
print("There was a problem getting the web page: {}".format(ex))
return None
soup = BeautifulSoup(r.text, 'html.parser')
spans=soup.find_all(lang='fi')
adding = False
forms = []
for span in spans:
# First valid entry will be first person singular
# which will always end in an 'n'
if span.text[-1] == 'n' and span.parent.name == 'td':
adding = True
if adding:
forms.append(span.text)
# There are extraneous entries at the end
forms = forms[:157]
print(*forms, sep=', ')
verbs = load_verbs()
if not skip_save and verb not in verbs.index:
conf = input("No entry for {} in file. Add? ".format(verb)).lower()
if conf == 'y':
save_verb(verb, forms=forms)
return forms
def add_words():
"""Looping function for adding words"""
running = True
while running:
word = input("Word: ").lower()
if word == '#q':
return None
if word == '#s':
if in_file():
print("That word is in the file")
continue
else:
print("That word is not in the file")
continue
cat = ""
while cat not in ['i', 'n', 'v']:
cat = input("Category: ").lower()[0]
if cat == 'i':
save_invariant(invariant=word)
elif cat == 'n':
save_nominal(nominal=word)
elif cat == 'v':
save_verb(verb=word)
def add_phrases():
"""Looping function for adding phrases"""
running = True
while running:
phrase = input("Phrase (English): ").lower()
if phrase == '#q':
return None
save_phrase(english=phrase)
def edit_word(word=None, cat=None):
"""Edit an word entry"""
if word is None:
word = input("Word (Finnish): ").lower()
if cat is None:
cat = ""
while cat not in ['invariant', 'nominal', 'verb']:
cat = input("Category: ").lower()
if cat == 'invariant':
words = load_invariants()
save_string = 'invariants.csv'
elif cat == 'nominal':
words = load_nominals()
save_string = 'nominals.csv'
elif cat == 'verb':
words = load_verbs()
save_string = 'verbs.csv'
if word not in words.index():
print("No entry for {}".format(word))
return None
current_english = words.loc[word, 'english']
print("{}: {}".format(word, current_english))
new_english = input("New value: ").lower()
print("{}: {}".format(word, new_english))
conf = input("Update entry?: ").lower()
if conf == 'y':
words.loc[word, 'english'] = new_english
words.to_csv(save_string)
print("File saved")
return True
def edit_phrase(search=None, cat=None):
"""Edit a phrase entry"""
if search is None:
search = input("Search (English): ").lower()
phrases = load_phrases()
english_list = list(phrases['English'])
matches = []
for english in english_list:
if search in english:
matches.append(english)
finnish = list(phrases[phrases['English'] ==
english]['Finnish'])[0]
print("{}. {}: {}".format(len(matches), english, finnish))
selection = -1
while selection not in range(1, len(matches)+1):
selection = int(input("Selection: "))
english = matches[selection-1]
new_finnish = input("New Finnish: ").lower()
index = phrases[phrases['English'] == english].index.values[0]
print("{}: {}".format(english, new_finnish))
conf = input("Update entry?: ").lower()
if conf == 'y':
phrases.loc[index, 'Finnish'] = new_finnish
phrases.to_csv('phrases.csv')
print("File saved")
return True
def generate_words_list(load_all=True):
"""Generate a list of words to review"""
invariants = load_invariants()
nominals = load_nominals()
verbs = load_verbs()
now = pd.to_datetime(datetime.datetime.now())
invariants_to_review = [[word, 'invariant'] for
word in list(invariants.index) if
now > invariants.loc[word, 'Next review']]
nominals_to_review = [[word, 'nominal'] for
word in list(nominals.index) if
now > nominals.loc[word, 'Next review']]
verbs_to_review = [[word, 'verb'] for
word in list(verbs.index) if
now > verbs.loc[word, 'Next review']]
words = invariants_to_review + nominals_to_review + verbs_to_review
if load_all:
return words, invariants, nominals, verbs
else:
return words
def generate_phrases_list():
"""Generate a list of phrases to review"""
phrases = load_phrases()
now = pd.to_datetime(datetime.datetime.now())
phrases_to_review = [phrase_i for phrase_i in list(phrases.index) if
now > phrases.loc[phrase_i, 'Next review']]
return phrases_to_review
def process_correct(word, words_df, cat):
"""Process a correct answer"""
print("Correct")
words_df.loc[word, 'Last reviewed'] = pd.to_datetime(datetime
.datetime.now())
# Do not increase the interval if the word was previously incorrect
if words_df.loc[word, 'Correct?']:
words_df.loc[word, 'Interval'] = (pd.to_timedelta(words_df.loc[word,
'Interval']) * CORRECT_INTERVAL)
# Make sure the interval doesn't exceed the max
if pd.to_timedelta(words_df.loc[word, 'Interval']) > MAXIMUM_INTERVAL:
words_df.loc[word, 'Interval'] = MAXIMUM_INTERVAL
words_df.loc[word, 'Next review'] = (
pd.to_datetime(datetime.datetime.now() +
pd.to_timedelta(words_df.loc[word, 'Interval'])))
print("Next review: {}".format(words_df.loc[word, 'Next review']))
words_df.loc[word, 'Correct?'] = True
words_df.loc[word, 'Times correct'] += 1
if cat == 'invariant':
words_df.to_csv('invariants.csv')
elif cat == 'nominal':
words_df.to_csv('nominals.csv')
elif cat == 'verb':
words_df.to_csv('verbs.csv')
elif cat == 'phrase':
words_df.to_csv('phrases.csv')
return True
def process_incorrect(word, words_df, cat):
"""Process an incorrect answer"""
if cat == 'invariant' or cat == 'nominal':
print("Incorrect. {} is {}".format(words_df.loc[word,
'English'], word))
elif cat == 'verb':
print("Incorrect. {} is {}".format(words_df.loc[word,
'English present'], word))
elif cat == 'phrase':
print("Incorrect. {}\nis\n{}".format(words_df.loc[word, 'English'],
words_df.loc[word, 'Finnish']))
words_df.loc[word, 'Last reviewed'] = pd.to_datetime(datetime
.datetime.now())
if (pd.to_timedelta(words_df.loc[word, 'Interval']) >
pd.to_timedelta('1 days')):
words_df.loc[word, 'Interval'] = (pd.to_timedelta(words_df.loc[word,
'Interval']) * INCORRECT_INTERVAL)
# Make sure interval is not less than the minimum
if pd.to_timedelta(words_df.loc[word, 'Interval']) < MINIMUM_INTERVAL:
words_df.loc[word, 'Interval'] = MINIMUM_INTERVAL
# Make sure interval is not greater than the maximum after wrong
if pd.to_timedelta(words_df.loc[word, 'Interval']) > MAX_AFTER_WRONG:
words_df.loc[word, 'Interval'] = MAX_AFTER_WRONG
words_df.loc[word, 'Correct?'] = False
words_df.loc[word, 'Times incorrect'] += 1
if cat == 'invariant':
words_df.to_csv('invariants.csv')
elif cat == 'nominal':
words_df.to_csv('nominals.csv')
elif cat == 'verb':
words_df.to_csv('verbs.csv')
elif cat == 'phrase':
words_df.to_csv('phrases.csv')
return True
def flash_invariant(word, invariants):
"""Do an invariant flashcard"""
english_list = invariants.loc[word, 'English'].split(', ')
english = random.choice(english_list)
print(english)
answer = input("Soumeksi: ").lower()
if answer == '#q':
return False
correct = True if answer == word else False
if correct:
process_correct(word, invariants, cat='invariant')
else:
process_incorrect(word, invariants, cat='invariant')
# 1 in 4 chance to contintue with form testing
if (correct and
'invariants' not in FURTHER_TESTING and
random.randint(1, FURTHER_TESTING_RATE) > 1):
return True
return True
def flash_nominal(word, nominals):
"""Do a nominal flashcard"""
english_list = nominals.loc[word, 'English'].split(', ')
english = random.choice(english_list)
print(english)
answer = input("Suomeksi: ").lower()
if answer == '#q':
return False
correct = True if answer == word else False
if correct:
process_correct(word, nominals, cat='nominal')
else:
process_incorrect(word, nominals, cat='nominal')
# 1 in 4 chance to contintue with form testing unless incorrect
if (correct and
'nominals' not in FURTHER_TESTING and
random.randint(1, FURTHER_TESTING_RATE) > 1):
return True
form_name = random.choice(list(NOMINAL_FORMS.keys()))
form_value = nominals.loc[word, form_name]
answer = input("{}: ".format(form_name)).lower()
if answer == '#q':
return False
if answer == form_value:
print("Correct")
else:
print("Incorrect. {} of {} is {}".format(form_name, word, form_value))
return True
def tps_conjugation(english):
"""Conjugates an English verb in the 3rd person singular"""
es_list_last_one = ['o', 's', 'x']
es_list_last_two = ['ch', 'sh']
vowels = "aeiou"
if english[-1] in es_list_last_one or english[-2:] in es_list_last_two:
english += 'es'
return english
if english[-2] not in vowels and english[-1] == 'y':
english = english[:-1] + 'ies'
return english
english += 's'
return english
def flash_verb(word, verbs, form_only=False):
"""Do a verb flashcard"""
if not form_only:
english = verbs.loc[word, 'English present']
print("To {}".format(english))
answer = input("Soumeksi: ").lower()
if answer == 'q':
return False
correct = True if answer == word else False
if correct:
process_correct(word, verbs, cat='verb')
else:
process_incorrect(word, verbs, cat='verb')
# 1 in 4 chance to contintue with form testing
if (correct and
'verbs' not in FURTHER_TESTING and
random.randint(1, FURTHER_TESTING_RATE) > 1):
return True
form_choices = [form for form in VERB_FORMS.keys() if
VERB_FORMS[form] != "[skip]"]
form_name = random.choice(form_choices)
while VERB_FORMS[form_name][0] == '[skip]':
form_name = random.choice(form_choices)
form_value = verbs.loc[word, form_name]
# English form key:
# 0: Simple present / present participle
# 1: Simple present only
# 2: Simple past
# 3: Past participle
# 4: Present participle only
if VERB_FORMS[form_name][1] == 0:
verb_phrase_subjects = VERB_FORMS[form_name][0].split(" / ")
if form_name == 'Third person' or form_name == 'Passive':
conjugation = tps_conjugation(verbs.loc[word, 'English present'])
else:
conjugation = verbs.loc[word, 'English present']
if len(verb_phrase_subjects) == 2:
english_verb_phrase = "{} - \"{} {} / {} {}\"".format(form_name,
verb_phrase_subjects[0],
conjugation,
verb_phrase_subjects[1],
verbs.loc[word,
'English present participle'])
elif len(verb_phrase_subjects) == 1:
english_verb_phrase = "{} - \"{} {}\"".format(form_name,
verb_phrase_subjects[0],
conjugation)
else:
print("{} has an improper English verb structure!"
.format(form_name))
return False
elif VERB_FORMS[form_name][1] == 1:
english_verb_phrase = "{} - \"{} {}\"".format(form_name,
VERB_FORMS[form_name][0],
verbs.loc[word, 'English present'])
elif VERB_FORMS[form_name][1] == 2:
english_verb_phrase = "{} - \"{} {}\"".format(form_name,
VERB_FORMS[form_name][0],
verbs.loc[word, 'English simple past'])
elif VERB_FORMS[form_name][1] == 3:
english_verb_phrase = "{} - \"{} {}\"".format(form_name,
VERB_FORMS[form_name][0],
verbs.loc[word, 'English past participle'])
elif VERB_FORMS[form_name][1] == 4:
english_verb_phrase = "{} - \"{} {}\"".format(form_name,
VERB_FORMS[form_name][0],
verbs.loc[word, 'English present participle'])
else:
print("{} does not have a proper verb form assignment!"
.format(form_name))
return False
answer = input("{}: ".format(english_verb_phrase)).lower()
if answer == '#q':
return False
if answer == form_value:
print("Correct")
else:
print("Incorect. {} of {} is {}".format(form_name, word,
form_value))
return True
def flash_phrase(phrase_i, phrases):
"""Do a phrase flashcard"""
english = phrases.loc[phrase_i, 'English']
print(english)
answer = input("Soumeksi: ").lower()
if answer == '#q':
return False
if answer == phrases.loc[phrase_i, 'Finnish']:
process_correct(phrase_i, phrases, cat='phrase')
else:
process_incorrect(phrase_i, phrases, cat='phrase')
return True
def flashcards():
"""The core flashcards function"""
words, invariants, nominals, verbs = generate_words_list()
while True:
print("{} words due".format(len(words)))
random.shuffle(words)
for word in words:
if word[1] == 'invariant':
if not flash_invariant(word[0], invariants):
print("Quitting")
return None
elif word[1] == 'nominal':
if not flash_nominal(word[0], nominals):
print("Quitting")
return None
elif word[1] == 'verb':
if not flash_verb(word[0], verbs):
print("Quitting")
return None
words, invariants, nominals, verbs = generate_words_list()
# Keep going if there are still wrong words
correct_list = (invariants['Correct?'] + nominals['Correct?'] +
verbs['Correct?'])
if not all(correct_list):
continue
else:
break
print("No more flashcards")
def phrasecards():
"""Flashcards for phrases"""
phrase_indices = generate_phrases_list()
phrases = load_phrases()
random.shuffle(phrase_indices)
for phrase_i in phrase_indices:
if not flash_phrase(phrase_i, phrases):
print("Quitting")
return None
print("No more flashcards")
def verb_forms_quiz(verb=None):
"""A quiz on verb forms using a single verb"""
verbs = load_verbs()
if verb is None:
verb = input('Verb (#r for random): ').lower()
if verb == '#r':
verb = random.choice(list(verbs.index))
print(verb)
if verb not in list(verbs.index):
add = input("Verb not in file. Add? ").lower()
if add == 'y':
save_verb(verb=verb)
verbs = load_verbs()
while flash_verb(verb, verbs, form_only=True):
continue