Skip to content

Commit 5446ee3

Browse files
committed
Allow to use custom snapshot command
1 parent 9073811 commit 5446ee3

8 files changed

Lines changed: 94 additions & 3 deletions

File tree

crowd_anki/config/config_dialog.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ def setup_snapshot_options(self):
4747
self.form.textedit_snapshot_root_decks.appendPlainText(list_to_cs_string(self.config.snapshot_root_decks))
4848
self.form.textedit_snapshot_root_decks.textChanged.connect(self.changed_textedit_snapshot_root_decks)
4949

50+
self.form.textedit_custom_snapshot_command.setText(self.config.custom_snapshot_command)
51+
self.form.textedit_custom_snapshot_command.textChanged.connect(self.changed_textedit_custom_snapshot_command)
52+
5053
def setup_export_options(self):
5154
self.form.cb_reverse_sort.setChecked(self.config.export_notes_reverse_order)
5255
self.form.cb_reverse_sort.stateChanged.connect(self.toggle_reverse_sort)
@@ -83,3 +86,6 @@ def changed_textedit_snapshot_root_decks(self):
8386

8487
def changed_textedit_snapshot_path(self):
8588
self.config.snapshot_path = self.form.textedit_snapshot_path.text()
89+
90+
def changed_textedit_custom_snapshot_command(self):
91+
self.config.custom_snapshot_command = self.form.textedit_custom_snapshot_command.text()

crowd_anki/config/config_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ConfigSettings:
3232
export_note_sort_methods: list
3333
export_create_deck_subdirectory: bool
3434
import_notes_ignore_deck_movement: bool
35+
custom_snapshot_command: str
3536

3637
@property
3738
def formatted_export_note_sort_methods(self) -> list:
@@ -48,6 +49,7 @@ class Properties(Enum):
4849
EXPORT_NOTES_REVERSE_ORDER = ConfigEntry("export_notes_reverse_order", False)
4950
EXPORT_CREATE_DECK_SUBDIRECTORY = ConfigEntry("export_create_deck_subdirectory", True)
5051
IMPORT_NOTES_IGNORE_DECK_MOVEMENT = ConfigEntry("import_notes_ignore_deck_movement", False)
52+
CUSTOM_SNAPSHOT_COMMAND = ConfigEntry("custom_snapshot_command", "")
5153

5254
def __init__(self, addon_manager=None, init_values=None, profile_manager=None):
5355
self._profile_manager = profile_manager or mw.pm

crowd_anki/config/config_ui_qt5.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Form implementation generated from reading ui file 'ui_files/config.ui'
44
#
5-
# Created by: PyQt5 UI code generator 5.15.9
5+
# Created by: PyQt5 UI code generator 5.15.11
66
#
77
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
88
# run again. Do not edit this file unless you know what you are doing.
@@ -43,6 +43,15 @@ def setupUi(self, Dialog):
4343
self.textedit_snapshot_root_decks = QtWidgets.QPlainTextEdit(self.group_snapshot)
4444
self.textedit_snapshot_root_decks.setObjectName("textedit_snapshot_root_decks")
4545
self.verticalLayout_3.addWidget(self.textedit_snapshot_root_decks)
46+
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
47+
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
48+
self.lbl_custom_snapshot_command = QtWidgets.QLabel(self.group_snapshot)
49+
self.lbl_custom_snapshot_command.setObjectName("lbl_custom_snapshot_command")
50+
self.horizontalLayout_3.addWidget(self.lbl_custom_snapshot_command)
51+
self.textedit_custom_snapshot_command = QtWidgets.QLineEdit(self.group_snapshot)
52+
self.textedit_custom_snapshot_command.setObjectName("textedit_custom_snapshot_command")
53+
self.horizontalLayout_3.addWidget(self.textedit_custom_snapshot_command)
54+
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
4655
self.verticalLayout_2.addWidget(self.group_snapshot)
4756
self.group_deck_import = QtWidgets.QGroupBox(Dialog)
4857
self.group_deck_import.setObjectName("group_deck_import")
@@ -93,6 +102,7 @@ def retranslateUi(self, Dialog):
93102
self.lbl_snapshot_path.setText(_translate("Dialog", "Snapshot Path:"))
94103
self.cb_automated_snapshot.setText(_translate("Dialog", "Automated Snapshot"))
95104
self.lbl_snapshot.setText(_translate("Dialog", "Snapshot Root Decks (separated by comma)"))
105+
self.lbl_custom_snapshot_command.setText(_translate("Dialog", "Custom command:"))
96106
self.group_deck_import.setTitle(_translate("Dialog", "Import"))
97107
self.cb_ignore_move_cards.setText(_translate("Dialog", "Do Not Move Existing Cards"))
98108
self.group_deck_export.setTitle(_translate("Dialog", "Export"))
@@ -112,6 +122,7 @@ def retranslateUi(self, Dialog):
112122
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600;\">Snapshot Root Decks</span>: A list of names of the decks that should be considered `root`. When CrowdAnki creates a snapshot it\'ll create a separate git repository for each `root` deck.</p>\n"
113123
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-style:italic;\">Default</span>: Each deck with no children is considered `root`.</p>\n"
114124
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" text-decoration: underline;\">Example</span>: Let\'s assume that you have the following decks in your collection: `a` (with sub-decks `b` and `c`), and `d`. By default CrowdAnki is going to create 3 separate repositories - `a::b`, `a::c` and `d`. If you are to add `a` to `snapshot_root_decks` then CrowdAnki would create 2 repositories instead - `a` and `d`. The information for sub-decks `b` and `c` would be stored within repository `a` in this case.</p>\n"
125+
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600;\">Custom command</span>: Shell command to run instead of the normal `git-commit` procedure. The `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.</p>\n"
115126
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
116127
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
117128
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:16pt;\">Import</span></p>\n"

crowd_anki/config/config_ui_qt6.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Form implementation generated from reading ui file 'ui_files/config.ui'
22
#
3-
# Created by: PyQt6 UI code generator 6.5.0
3+
# Created by: PyQt6 UI code generator 6.9.1
44
#
55
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
66
# run again. Do not edit this file unless you know what you are doing.
@@ -41,6 +41,15 @@ def setupUi(self, Dialog):
4141
self.textedit_snapshot_root_decks = QtWidgets.QPlainTextEdit(parent=self.group_snapshot)
4242
self.textedit_snapshot_root_decks.setObjectName("textedit_snapshot_root_decks")
4343
self.verticalLayout_3.addWidget(self.textedit_snapshot_root_decks)
44+
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
45+
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
46+
self.lbl_custom_snapshot_command = QtWidgets.QLabel(parent=self.group_snapshot)
47+
self.lbl_custom_snapshot_command.setObjectName("lbl_custom_snapshot_command")
48+
self.horizontalLayout_3.addWidget(self.lbl_custom_snapshot_command)
49+
self.textedit_custom_snapshot_command = QtWidgets.QLineEdit(parent=self.group_snapshot)
50+
self.textedit_custom_snapshot_command.setObjectName("textedit_custom_snapshot_command")
51+
self.horizontalLayout_3.addWidget(self.textedit_custom_snapshot_command)
52+
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
4453
self.verticalLayout_2.addWidget(self.group_snapshot)
4554
self.group_deck_import = QtWidgets.QGroupBox(parent=Dialog)
4655
self.group_deck_import.setObjectName("group_deck_import")
@@ -91,6 +100,7 @@ def retranslateUi(self, Dialog):
91100
self.lbl_snapshot_path.setText(_translate("Dialog", "Snapshot Path:"))
92101
self.cb_automated_snapshot.setText(_translate("Dialog", "Automated Snapshot"))
93102
self.lbl_snapshot.setText(_translate("Dialog", "Snapshot Root Decks (separated by comma)"))
103+
self.lbl_custom_snapshot_command.setText(_translate("Dialog", "Custom command:"))
94104
self.group_deck_import.setTitle(_translate("Dialog", "Import"))
95105
self.cb_ignore_move_cards.setText(_translate("Dialog", "Do Not Move Existing Cards"))
96106
self.group_deck_export.setTitle(_translate("Dialog", "Export"))
@@ -110,6 +120,7 @@ def retranslateUi(self, Dialog):
110120
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600;\">Snapshot Root Decks</span>: A list of names of the decks that should be considered `root`. When CrowdAnki creates a snapshot it\'ll create a separate git repository for each `root` deck.</p>\n"
111121
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-style:italic;\">Default</span>: Each deck with no children is considered `root`.</p>\n"
112122
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" text-decoration: underline;\">Example</span>: Let\'s assume that you have the following decks in your collection: `a` (with sub-decks `b` and `c`), and `d`. By default CrowdAnki is going to create 3 separate repositories - `a::b`, `a::c` and `d`. If you are to add `a` to `snapshot_root_decks` then CrowdAnki would create 2 repositories instead - `a` and `d`. The information for sub-decks `b` and `c` would be stored within repository `a` in this case.</p>\n"
123+
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600;\">Custom command</span>: Shell command to run instead of the normal `git-commit` procedure. The `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.</p>\n"
113124
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
114125
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
115126
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:16pt;\">Import</span></p>\n"

crowd_anki/history/anki_repo.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ def stage_all(self):
99
@abstractmethod
1010
def commit(self, message: str = None):
1111
pass
12+
13+
@abstractmethod
14+
def close(self):
15+
pass

crowd_anki/history/archiver_vendor.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .anki_deck_archiver import AnkiDeckArchiver
66
from .archiver import AllDeckArchiver
7+
from .custom_command_repo import CustomCommandRepo
78
from .dulwich_repo import DulwichAnkiRepo
89
from ..anki.adapters.deck_manager import AnkiStaticDeckManager, DeckManager
910
from ..anki.ui.utils import progress_indicator
@@ -24,12 +25,18 @@ def deck_manager(self) -> DeckManager:
2425
return AnkiStaticDeckManager(self.window.col.decks)
2526

2627
def all_deck_archiver(self):
28+
if self.config.custom_snapshot_command:
29+
repo_provider = lambda path: CustomCommandRepo(
30+
path, self.config.custom_snapshot_command)
31+
else:
32+
repo_provider = DulwichAnkiRepo
33+
2734
return AllDeckArchiver(
2835
self.deck_manager,
2936
lambda deck: AnkiDeckArchiver(deck,
3037
self.config.full_snapshot_path,
3138
AnkiJsonExporter(self.window.col, self.config),
32-
DulwichAnkiRepo))
39+
repo_provider))
3340

3441
def snapshot_path(self):
3542
return Path(self.config.snapshot_path)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import logging
2+
import os
3+
import subprocess
4+
5+
from dataclasses import dataclass
6+
from pathlib import Path
7+
8+
from .anki_repo import AnkiRepo
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
@dataclass
14+
class CustomCommandRepo(AnkiRepo):
15+
repo_path: Path
16+
command: str
17+
18+
def stage_all(self):
19+
pass
20+
21+
def commit(self, message: str = None):
22+
env = os.environ.copy()
23+
env["CROWD_ANKI_COMMIT_MESSAGE"] = message
24+
25+
try:
26+
retcode = subprocess.call(self.command, shell=True, env=env)
27+
except OSError as e:
28+
raise RuntimeError("Failed to execute custom snapshot command: %s" % e)
29+
30+
if retcode != 0:
31+
raise RuntimeError("Custom snapshot command exited with "
32+
"non-zero exit code: %d" % retcode)
33+
34+
def close(self):
35+
pass

ui_files/config.ui

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@
5555
<item>
5656
<widget class="QPlainTextEdit" name="textedit_snapshot_root_decks"/>
5757
</item>
58+
<item>
59+
<layout class="QHBoxLayout" name="horizontalLayout_3">
60+
<item>
61+
<widget class="QLabel" name="lbl_custom_snapshot_command">
62+
<property name="text">
63+
<string>Custom command:</string>
64+
</property>
65+
</widget>
66+
</item>
67+
<item>
68+
<widget class="QLineEdit" name="textedit_custom_snapshot_command"/>
69+
</item>
70+
</layout>
71+
</item>
5872
</layout>
5973
</widget>
6074
</item>
@@ -128,6 +142,7 @@ p, li { white-space: pre-wrap; }
128142
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Snapshot Root Decks&lt;/span&gt;: A list of names of the decks that should be considered `root`. When CrowdAnki creates a snapshot it'll create a separate git repository for each `root` deck.&lt;/p&gt;
129143
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Default&lt;/span&gt;: Each deck with no children is considered `root`.&lt;/p&gt;
130144
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;Example&lt;/span&gt;: Let's assume that you have the following decks in your collection: `a` (with sub-decks `b` and `c`), and `d`. By default CrowdAnki is going to create 3 separate repositories - `a::b`, `a::c` and `d`. If you are to add `a` to `snapshot_root_decks` then CrowdAnki would create 2 repositories instead - `a` and `d`. The information for sub-decks `b` and `c` would be stored within repository `a` in this case.&lt;/p&gt;
145+
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Custom command&lt;/span&gt;: Shell command to run instead of the normal `git-commit` procedure. The `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.&lt;/p&gt;
131146
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
132147
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
133148
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;Import&lt;/span&gt;&lt;/p&gt;

0 commit comments

Comments
 (0)