Skip to content

Commit c27c599

Browse files
author
Pseudonium
authored
Merge pull request #53 from Pseudonium/develop
Automation update
2 parents 9219bb6 + b3575b9 commit c27c599

3 files changed

Lines changed: 101 additions & 4 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ The script needs to be able to:
5050

5151
## Usage
5252

53-
**Apart from editing the config file, all operations of the script require Anki to be running.**
53+
**Apart from editing the config file, all operations of the script require Anki to be running.**
54+
New in v2.7.0 - if you supply Anki Path and Anki Profile, the script will automatically attempt to open Anki if it isn't already running.
5455

5556
The GUI of the script looks like this:
5657
![GUI](Images/GUI.png)
@@ -91,6 +92,8 @@ Allows you to change the default deck and tag of the script.
9192
New in v2.2.2 - allows you to enable/disable the 'CurlyCloze' option, which is explained in [Cloze formatting](#cloze-formatting)
9293
New in v2.4.0 - allows you to enable/disable the GUI of the script - see [Command line usage](#command-line-usage).
9394
New in v2.5.0 - allows you to enable/disable IDs being embedded in HTML comments. The script can read IDs whether or not they are in a HTML comment.
95+
New in v2.5.0 - allows you to have regex mode on by default.
96+
New in v2.7.0 - Anki Path and Anki Profile. If you supply both the absolute path to the Anki executable, and your profile on Anki, the script will attempt to open Anki when run if it's not already running. Useful for automation - see [Technical](#technical)
9497

9598
### Syntax
9699
Note that START, END, TARGET DECK, FILE TAGS and DELETE all require an **exact match** on the line - you cannot have spaces afterwards.
@@ -364,3 +367,15 @@ You may also want to prepend the following shebang to the start of the file:
364367
`#!/usr/bin/env python`
365368

366369
For more information, see [this pull request](https://github.com/Pseudonium/Obsidian_to_Anki/pull/13).
370+
371+
New in v2.7.0 - the script can now automatically open Anki if Anki Path and Anki Profile are supplied. This would allow you to schedule the script to run at a certain time without needing to worry about whether Anki was open at that time.
372+
373+
Note that the script will never close Anki by itself, so you may find Anki open when you return to your computer!
374+
375+
### Scheduling
376+
377+
On Windows, check out [Task Scheduler](https://www.windowscentral.com/how-create-automated-task-using-task-scheduler-windows-10).
378+
379+
On macOS/Linux, check out Corey Schafer's excellent tutorial on [cron](https://www.windowscentral.com/how-create-automated-task-using-task-scheduler-windows-10).
380+
381+
As an example, you could schedule the script to run recursively over your top-level notes folder, at midnight every day.

obsidian_to_anki.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import base64
1212
import argparse
1313
import html
14+
import time
15+
import socket
16+
import subprocess
1417
try:
1518
import gooey
1619
GOOEY = True
@@ -52,6 +55,8 @@
5255
extensions=['extra', 'nl2br', 'sane_lists']
5356
)
5457

58+
ANKI_PORT = 8765
59+
5560

5661
def write_safe(filename, contents):
5762
"""
@@ -118,6 +123,66 @@ def findignore(pattern, string, ignore_spans):
118123
)
119124

120125

126+
def wait_for_port(port, host='localhost', timeout=5.0):
127+
"""Wait until a port starts accepting TCP connections.
128+
Args:
129+
port (int): Port number.
130+
host (str): Host address on which the port should exist.
131+
timeout (float): In seconds. How long to wait before raising errors.
132+
Raises:
133+
TimeoutError: The port isn't accepting connection after time specified
134+
in `timeout`.
135+
"""
136+
start_time = time.perf_counter()
137+
while True:
138+
try:
139+
with socket.create_connection((host, port), timeout=timeout):
140+
break
141+
except OSError as ex:
142+
time.sleep(0.01)
143+
if time.perf_counter() - start_time >= timeout:
144+
raise TimeoutError(
145+
'Waited too long for the port {} on host {} to'
146+
'start accepting connections.'.format(port, host)
147+
) from ex
148+
149+
150+
def load_anki():
151+
"""Attempt to load anki in the correct profile."""
152+
try:
153+
Config.load_config()
154+
except Exception as e:
155+
print("Error when loading config:", e)
156+
print("Please open Anki before running script again.")
157+
return False
158+
if CONFIG_DATA["Path"] and CONFIG_DATA["Profile"]:
159+
print("Anki Path and Anki Profile provided.")
160+
print("Attempting to open Anki in selected profile...")
161+
subprocess.Popen(
162+
[CONFIG_DATA["Path"], "-p", CONFIG_DATA["Profile"]]
163+
)
164+
try:
165+
wait_for_port(ANKI_PORT)
166+
except TimeoutError:
167+
print("Opened Anki, but can't connect! Is AnkiConnect working?")
168+
return False
169+
else:
170+
print("Opened and connected to Anki successfully!")
171+
return True
172+
else:
173+
print(
174+
"Must provide both Anki Path and Anki Profile",
175+
"in order to open Anki automatically"
176+
)
177+
178+
179+
def main():
180+
"""Main functionality of script."""
181+
if not os.path.exists(CONFIG_PATH):
182+
Config.update_config()
183+
App()
184+
185+
121186
class AnkiConnect:
122187
"""Namespace for AnkiConnect functions."""
123188

@@ -626,6 +691,12 @@ def update_config():
626691
config["Defaults"].setdefault(
627692
"ID Comments", "True"
628693
)
694+
config["Defaults"].setdefault(
695+
"Anki Path", ""
696+
)
697+
config["Defaults"].setdefault(
698+
"Anki Profile", ""
699+
)
629700
# Setting up Custom Regexps
630701
config.setdefault("Custom Regexps", dict())
631702
for note in note_types:
@@ -693,6 +764,8 @@ def load_config():
693764
CONFIG_DATA["Comment"] = config.getboolean(
694765
"Defaults", "ID Comments"
695766
)
767+
CONFIG_DATA["Path"] = config["Defaults"]["Anki Path"]
768+
CONFIG_DATA["Profile"] = config["Defaults"]["Anki Profile"]
696769
Config.config = config # Can access later if need be
697770
print("Loaded successfully!")
698771

@@ -1408,6 +1481,13 @@ def requests_2(self):
14081481

14091482

14101483
if __name__ == "__main__":
1411-
if not os.path.exists(CONFIG_PATH):
1412-
Config.update_config()
1413-
App()
1484+
print("Attempting to connect to Anki...")
1485+
try:
1486+
wait_for_port(ANKI_PORT)
1487+
except TimeoutError:
1488+
print("Couldn't connect to Anki, attempting to open Anki...")
1489+
if load_anki():
1490+
main()
1491+
else:
1492+
print("Connected!")
1493+
main()

obsidian_to_anki_config.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ CurlyCloze = False
4242
GUI = True
4343
Regex = False
4444
ID Comments = True
45+
Anki Path =
46+
Anki Profile =
4547

4648
[Custom Regexps]
4749
Basic = ^Q: ((?:[^\n][\n]?)+)\n+A: ((?:[^\n][\n]?)+)

0 commit comments

Comments
 (0)