JarrettYe commited on
Commit
1d0f43c
1 Parent(s): cc2ce93

support csv

Browse files
Files changed (3) hide show
  1. app.py +85 -32
  2. markdown.py +3 -3
  3. utilities.py +6 -4
app.py CHANGED
@@ -1,18 +1,22 @@
1
  import gradio as gr
2
  import pytz
3
  import os
 
 
4
  import matplotlib.pyplot as plt
5
  from datetime import datetime
6
  from markdown import instructions_markdown, faq_markdown
7
  from fsrs_optimizer import Optimizer
8
  from pathlib import Path
9
  from utilities import cleanup
10
- import re
11
 
12
  with open("./requirements.txt", "r") as f:
13
  txt = f.read().strip()
14
  version = re.search(r"FSRS-Optimizer==(.*)", txt).group(1)
15
 
 
 
 
16
  def get_w_markdown(w):
17
  return f"""
18
  # Updated Parameters
@@ -26,33 +30,64 @@ def get_w_markdown(w):
26
  """
27
 
28
 
29
- def anki_optimizer(file: gr.File, timezone, next_day_starts_at, revlog_start_date, filter_out_suspended_cards, requestRetention,
30
- progress=gr.Progress(track_tqdm=True)):
31
- os.chdir('/home/user/app')
32
- if file is None or (not file.name.endswith(".apkg") and not file.name.endswith(".colpkg")):
33
- raise ValueError("Please upload a deck/collection file.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  if timezone == "":
35
  raise ValueError("Please select a timezone.")
36
  now = datetime.now()
37
- files = ['prediction.tsv', 'revlog.csv', 'revlog_history.tsv', 'stability_for_analysis.tsv',
38
- 'expected_time.csv', 'evaluation.tsv']
39
- prefix = now.strftime(f'%Y_%m_%d_%H_%M_%S')
40
- suffix = file.name.split('/')[-1].replace(".", "_").replace("@", "_")
41
- proj_dir = Path(f'projects/{prefix}/{suffix}')
 
 
 
 
 
 
42
  proj_dir.mkdir(parents=True, exist_ok=True)
43
  os.chdir(proj_dir)
44
  optimizer = Optimizer()
45
- optimizer.anki_extract(file.name, filter_out_suspended_cards)
46
- analysis_markdown = optimizer.create_time_series(timezone, revlog_start_date, next_day_starts_at).replace("\n", "\n\n")
 
 
 
 
 
 
47
  optimizer.define_model()
48
  optimizer.pretrain(verbose=False)
49
  optimizer.train(verbose=False)
50
  print(optimizer.w)
51
  w_markdown = get_w_markdown(optimizer.w)
52
  optimizer.predict_memory_states()
53
- difficulty_distribution = optimizer.difficulty_distribution.to_string().replace("\n", "\n\n")
 
 
54
  plot_output = optimizer.find_optimal_retention()[0]
55
- suggested_retention_markdown = f"""# Suggested Retention: `{optimizer.optimal_retention:.2f}`"""
 
 
56
  rating_markdown = optimizer.preview(requestRetention).replace("\n", "\n\n")
57
  loss_before, loss_after = optimizer.evaluate()
58
  loss_markdown = f"""
@@ -73,15 +108,15 @@ def anki_optimizer(file: gr.File, timezone, next_day_starts_at, revlog_start_dat
73
  # Ratings
74
  {rating_markdown}
75
  """
76
- os.chdir('/home/user/app')
77
  files_out = [proj_dir / file for file in files if (proj_dir / file).exists()]
78
  cleanup(proj_dir, files)
79
- plt.close('all')
80
  return w_markdown, markdown_out, plot_output, files_out
81
 
82
 
83
  description = f"""
84
- # FSRS4Anki Optimizer App - v{version}
85
  Based on the [tutorial](https://medium.com/@JarrettYe/how-to-use-the-next-generation-spaced-repetition-algorithm-fsrs-on-anki-5a591ca562e2)
86
  of [Jarrett Ye](https://github.com/L-M-Sherlock). This application can give you personalized anki parameters without having to code.
87
 
@@ -89,25 +124,34 @@ Read the `Instructions` if its your first time using the app.
89
  """
90
 
91
  with gr.Blocks() as demo:
92
- with gr.Tab("FSRS4Anki Optimizer"):
93
  with gr.Box():
94
  gr.Markdown(description)
95
  with gr.Box():
96
  with gr.Row():
97
  with gr.Column():
98
- file = gr.File(label='Review Logs (Step 1)')
99
  with gr.Column():
100
- next_day_starts_at = gr.Number(value=4,
101
- label="Next Day Starts at (Step 2)",
102
- precision=0)
103
- timezone = gr.Dropdown(label="Timezone (Step 3.1)", choices=pytz.all_timezones)
104
- filter_out_suspended_cards = gr.Checkbox(value=False, label="Filter out suspended cards")
 
 
 
 
105
  with gr.Accordion(label="Advanced Settings (Step 3.2)", open=False):
106
- requestRetention = gr.Number(value=.9, label="Desired Retention: Recommended to set between 0.8 0.9")
107
- revlog_start_date = gr.Textbox(value="2006-10-05",
108
- label="Revlog Start Date: Optimize review logs after this date.")
 
 
 
 
 
109
  with gr.Row():
110
- btn_plot = gr.Button('Optimize your Anki!')
111
  with gr.Row():
112
  w_output = gr.Markdown()
113
  with gr.Tab("Instructions"):
@@ -122,8 +166,17 @@ with gr.Blocks() as demo:
122
  with gr.Tab("FAQ"):
123
  gr.Markdown(faq_markdown)
124
 
125
- btn_plot.click(anki_optimizer,
126
- inputs=[file, timezone, next_day_starts_at, revlog_start_date, filter_out_suspended_cards, requestRetention],
127
- outputs=[w_output, markdown_output, plot_output, files_output])
 
 
 
 
 
 
 
 
 
128
 
129
  demo.queue().launch(show_error=True)
 
1
  import gradio as gr
2
  import pytz
3
  import os
4
+ import shutil
5
+ import re
6
  import matplotlib.pyplot as plt
7
  from datetime import datetime
8
  from markdown import instructions_markdown, faq_markdown
9
  from fsrs_optimizer import Optimizer
10
  from pathlib import Path
11
  from utilities import cleanup
 
12
 
13
  with open("./requirements.txt", "r") as f:
14
  txt = f.read().strip()
15
  version = re.search(r"FSRS-Optimizer==(.*)", txt).group(1)
16
 
17
+ home_path = os.getcwd()
18
+
19
+
20
  def get_w_markdown(w):
21
  return f"""
22
  # Updated Parameters
 
30
  """
31
 
32
 
33
+ def optimizer(
34
+ file: gr.File,
35
+ timezone,
36
+ next_day_starts_at,
37
+ revlog_start_date,
38
+ filter_out_suspended_cards,
39
+ requestRetention,
40
+ progress=gr.Progress(track_tqdm=True),
41
+ ):
42
+ os.chdir(home_path)
43
+ if file is None:
44
+ raise ValueError("Please upload a deck/collection/csv file.")
45
+ if file.name.endswith(".apkg") or file.name.endswith(".colpkg"):
46
+ mode = "anki"
47
+ elif file.name.endswith(".csv"):
48
+ mode = "csv"
49
+ else:
50
+ raise ValueError(
51
+ "File must be an Anki deck/collection file (.apkg or .colpkg) or a csv file."
52
+ )
53
  if timezone == "":
54
  raise ValueError("Please select a timezone.")
55
  now = datetime.now()
56
+ files = [
57
+ "prediction.tsv",
58
+ "revlog.csv",
59
+ "revlog_history.tsv",
60
+ "stability_for_analysis.tsv",
61
+ "expected_time.csv",
62
+ "evaluation.tsv",
63
+ ]
64
+ prefix = now.strftime(f"%Y_%m_%d_%H_%M_%S")
65
+ suffix = file.name.split("/")[-1].replace(".", "_").replace("@", "_")
66
+ proj_dir = Path(f"projects/{prefix}/{suffix}")
67
  proj_dir.mkdir(parents=True, exist_ok=True)
68
  os.chdir(proj_dir)
69
  optimizer = Optimizer()
70
+ if mode == "anki":
71
+ optimizer.anki_extract(file.name, filter_out_suspended_cards)
72
+ else:
73
+ print(file.name)
74
+ shutil.copyfile(file.name, "./revlog.csv")
75
+ analysis_markdown = optimizer.create_time_series(
76
+ timezone, revlog_start_date, next_day_starts_at
77
+ ).replace("\n", "\n\n")
78
  optimizer.define_model()
79
  optimizer.pretrain(verbose=False)
80
  optimizer.train(verbose=False)
81
  print(optimizer.w)
82
  w_markdown = get_w_markdown(optimizer.w)
83
  optimizer.predict_memory_states()
84
+ difficulty_distribution = optimizer.difficulty_distribution.to_string().replace(
85
+ "\n", "\n\n"
86
+ )
87
  plot_output = optimizer.find_optimal_retention()[0]
88
+ suggested_retention_markdown = (
89
+ f"""# Suggested Retention: `{optimizer.optimal_retention:.2f}`"""
90
+ )
91
  rating_markdown = optimizer.preview(requestRetention).replace("\n", "\n\n")
92
  loss_before, loss_after = optimizer.evaluate()
93
  loss_markdown = f"""
 
108
  # Ratings
109
  {rating_markdown}
110
  """
111
+ os.chdir(home_path)
112
  files_out = [proj_dir / file for file in files if (proj_dir / file).exists()]
113
  cleanup(proj_dir, files)
114
+ plt.close("all")
115
  return w_markdown, markdown_out, plot_output, files_out
116
 
117
 
118
  description = f"""
119
+ # FSRS Optimizer - v{version}
120
  Based on the [tutorial](https://medium.com/@JarrettYe/how-to-use-the-next-generation-spaced-repetition-algorithm-fsrs-on-anki-5a591ca562e2)
121
  of [Jarrett Ye](https://github.com/L-M-Sherlock). This application can give you personalized anki parameters without having to code.
122
 
 
124
  """
125
 
126
  with gr.Blocks() as demo:
127
+ with gr.Tab("FSRS Optimizer"):
128
  with gr.Box():
129
  gr.Markdown(description)
130
  with gr.Box():
131
  with gr.Row():
132
  with gr.Column():
133
+ file = gr.File(label="Review Logs (Step 1)")
134
  with gr.Column():
135
+ next_day_starts_at = gr.Number(
136
+ value=4, label="Next Day Starts at (Step 2)", precision=0
137
+ )
138
+ timezone = gr.Dropdown(
139
+ label="Timezone (Step 3.1)", choices=pytz.all_timezones
140
+ )
141
+ filter_out_suspended_cards = gr.Checkbox(
142
+ value=False, label="Filter out suspended cards"
143
+ )
144
  with gr.Accordion(label="Advanced Settings (Step 3.2)", open=False):
145
+ requestRetention = gr.Number(
146
+ value=0.9,
147
+ label="Desired Retention: Recommended to set between 0.8 0.9",
148
+ )
149
+ revlog_start_date = gr.Textbox(
150
+ value="2006-10-05",
151
+ label="Revlog Start Date: Optimize review logs after this date.",
152
+ )
153
  with gr.Row():
154
+ btn_plot = gr.Button("Optimize!")
155
  with gr.Row():
156
  w_output = gr.Markdown()
157
  with gr.Tab("Instructions"):
 
166
  with gr.Tab("FAQ"):
167
  gr.Markdown(faq_markdown)
168
 
169
+ btn_plot.click(
170
+ optimizer,
171
+ inputs=[
172
+ file,
173
+ timezone,
174
+ next_day_starts_at,
175
+ revlog_start_date,
176
+ filter_out_suspended_cards,
177
+ requestRetention,
178
+ ],
179
+ outputs=[w_output, markdown_output, plot_output, files_output],
180
+ )
181
 
182
  demo.queue().launch(show_error=True)
markdown.py CHANGED
@@ -1,7 +1,7 @@
1
  instructions_markdown = """
2
- # How to get personalized FSRS Anki parameters
3
  If you have been using Anki for some time and have accumulated a lot of review logs, you can try this
4
- FSRS4Anki optimizer app to generate parameters for you.
5
 
6
  This is based on the amazing work of [Jarrett Ye](https://github.com/L-M-Sherlock). My goal is to further
7
  democratize this technology so anyone can use it!
@@ -41,4 +41,4 @@ You can find it here: [https://www.maimemo.com/paper/](https://www.maimemo.com/p
41
  What is the original author's research story?
42
 
43
  You can find it here: [https://medium.com/@JarrettYe/how-did-i-publish-a-paper-in-acmkdd-as-an-undergraduate-c0199baddf31](https://medium.com/@JarrettYe/how-did-i-publish-a-paper-in-acmkdd-as-an-undergraduate-c0199baddf31)
44
- """
 
1
  instructions_markdown = """
2
+ # How to get personalized FSRS parameters
3
  If you have been using Anki for some time and have accumulated a lot of review logs, you can try this
4
+ FSRS optimizer app to generate parameters for you.
5
 
6
  This is based on the amazing work of [Jarrett Ye](https://github.com/L-M-Sherlock). My goal is to further
7
  democratize this technology so anyone can use it!
 
41
  What is the original author's research story?
42
 
43
  You can find it here: [https://medium.com/@JarrettYe/how-did-i-publish-a-paper-in-acmkdd-as-an-undergraduate-c0199baddf31](https://medium.com/@JarrettYe/how-did-i-publish-a-paper-in-acmkdd-as-an-undergraduate-c0199baddf31)
44
+ """
utilities.py CHANGED
@@ -5,12 +5,15 @@ from pathlib import Path
5
 
6
  # Extract the collection file or deck file to get the .anki21 database.
7
  def extract(file, prefix):
8
- proj_dir = Path(f'projects/{prefix}_{file.orig_name.replace(".", "_").replace("@", "_")}')
9
- with ZipFile(file, 'r') as zip_ref:
 
 
10
  zip_ref.extractall(proj_dir)
11
  # print(f"Extracted {file.orig_name} successfully!")
12
  return proj_dir
13
 
 
14
  def cleanup(proj_dir: Path, files):
15
  """
16
  Delete all files in prefix that dont have filenames in files
@@ -18,7 +21,6 @@ def cleanup(proj_dir: Path, files):
18
  :param files:
19
  :return:
20
  """
21
- for file in proj_dir.glob('*'):
22
  if file.name not in files:
23
  os.remove(file)
24
-
 
5
 
6
  # Extract the collection file or deck file to get the .anki21 database.
7
  def extract(file, prefix):
8
+ proj_dir = Path(
9
+ f'projects/{prefix}_{file.orig_name.replace(".", "_").replace("@", "_")}'
10
+ )
11
+ with ZipFile(file, "r") as zip_ref:
12
  zip_ref.extractall(proj_dir)
13
  # print(f"Extracted {file.orig_name} successfully!")
14
  return proj_dir
15
 
16
+
17
  def cleanup(proj_dir: Path, files):
18
  """
19
  Delete all files in prefix that dont have filenames in files
 
21
  :param files:
22
  :return:
23
  """
24
+ for file in proj_dir.glob("*"):
25
  if file.name not in files:
26
  os.remove(file)