Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crowd_anki/config/config_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def setup_snapshot_options(self):
self.form.textedit_snapshot_root_decks.appendPlainText(list_to_cs_string(self.config.snapshot_root_decks))
self.form.textedit_snapshot_root_decks.textChanged.connect(self.changed_textedit_snapshot_root_decks)

self.form.textedit_snapshot_custom_command.setText(self.config.snapshot_custom_command)
self.form.textedit_snapshot_custom_command.textChanged.connect(self.changed_textedit_snapshot_custom_command)

def setup_export_options(self):
self.form.cb_reverse_sort.setChecked(self.config.export_notes_reverse_order)
self.form.cb_reverse_sort.stateChanged.connect(self.toggle_reverse_sort)
Expand Down Expand Up @@ -83,3 +86,6 @@ def changed_textedit_snapshot_root_decks(self):

def changed_textedit_snapshot_path(self):
self.config.snapshot_path = self.form.textedit_snapshot_path.text()

def changed_textedit_snapshot_custom_command(self):
self.config.snapshot_custom_command = self.form.textedit_snapshot_custom_command.text()
2 changes: 2 additions & 0 deletions crowd_anki/config/config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ConfigSettings:
export_note_sort_methods: list
export_create_deck_subdirectory: bool
import_notes_ignore_deck_movement: bool
snapshot_custom_command: str

@property
def formatted_export_note_sort_methods(self) -> list:
Expand All @@ -48,6 +49,7 @@ class Properties(Enum):
EXPORT_NOTES_REVERSE_ORDER = ConfigEntry("export_notes_reverse_order", False)
EXPORT_CREATE_DECK_SUBDIRECTORY = ConfigEntry("export_create_deck_subdirectory", True)
IMPORT_NOTES_IGNORE_DECK_MOVEMENT = ConfigEntry("import_notes_ignore_deck_movement", False)
SNAPSHOT_CUSTOM_COMMAND = ConfigEntry("snapshot_custom_command", "")

def __init__(self, addon_manager=None, init_values=None, profile_manager=None):
self._profile_manager = profile_manager or mw.pm
Expand Down
14 changes: 13 additions & 1 deletion crowd_anki/config/config_ui_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Form implementation generated from reading ui file 'ui_files/config.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
Expand Down Expand Up @@ -43,6 +43,15 @@ def setupUi(self, Dialog):
self.textedit_snapshot_root_decks = QtWidgets.QPlainTextEdit(self.group_snapshot)
self.textedit_snapshot_root_decks.setObjectName("textedit_snapshot_root_decks")
self.verticalLayout_3.addWidget(self.textedit_snapshot_root_decks)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.lbl_snapshot_custom_command = QtWidgets.QLabel(self.group_snapshot)
self.lbl_snapshot_custom_command.setObjectName("lbl_snapshot_custom_command")
self.horizontalLayout_3.addWidget(self.lbl_snapshot_custom_command)
self.textedit_snapshot_custom_command = QtWidgets.QLineEdit(self.group_snapshot)
self.textedit_snapshot_custom_command.setObjectName("textedit_snapshot_custom_command")
self.horizontalLayout_3.addWidget(self.textedit_snapshot_custom_command)
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.verticalLayout_2.addWidget(self.group_snapshot)
self.group_deck_import = QtWidgets.QGroupBox(Dialog)
self.group_deck_import.setObjectName("group_deck_import")
Expand Down Expand Up @@ -93,6 +102,7 @@ def retranslateUi(self, Dialog):
self.lbl_snapshot_path.setText(_translate("Dialog", "Snapshot Path:"))
self.cb_automated_snapshot.setText(_translate("Dialog", "Automated Snapshot"))
self.lbl_snapshot.setText(_translate("Dialog", "Snapshot Root Decks (separated by comma)"))
self.lbl_snapshot_custom_command.setText(_translate("Dialog", "Custom commit command:"))
self.group_deck_import.setTitle(_translate("Dialog", "Import"))
self.cb_ignore_move_cards.setText(_translate("Dialog", "Do Not Move Existing Cards"))
self.group_deck_export.setTitle(_translate("Dialog", "Export"))
Expand All @@ -112,6 +122,8 @@ def retranslateUi(self, Dialog):
"<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"
"<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"
"<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"
"<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 commit command</span>: Optional shell command to run instead of the built-in `git-commit` procedure. During this command execution, the `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.</p>\n"
"<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>: Blank, meaning the built-in `git-commit` procedure is run.</p>\n"
"<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"
"<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"
"<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"
Expand Down
14 changes: 13 additions & 1 deletion crowd_anki/config/config_ui_qt6.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'ui_files/config.ui'
#
# Created by: PyQt6 UI code generator 6.5.0
# Created by: PyQt6 UI code generator 6.9.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
Expand Down Expand Up @@ -41,6 +41,15 @@ def setupUi(self, Dialog):
self.textedit_snapshot_root_decks = QtWidgets.QPlainTextEdit(parent=self.group_snapshot)
self.textedit_snapshot_root_decks.setObjectName("textedit_snapshot_root_decks")
self.verticalLayout_3.addWidget(self.textedit_snapshot_root_decks)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.lbl_snapshot_custom_command = QtWidgets.QLabel(parent=self.group_snapshot)
self.lbl_snapshot_custom_command.setObjectName("lbl_snapshot_custom_command")
self.horizontalLayout_3.addWidget(self.lbl_snapshot_custom_command)
self.textedit_snapshot_custom_command = QtWidgets.QLineEdit(parent=self.group_snapshot)
self.textedit_snapshot_custom_command.setObjectName("textedit_snapshot_custom_command")
self.horizontalLayout_3.addWidget(self.textedit_snapshot_custom_command)
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.verticalLayout_2.addWidget(self.group_snapshot)
self.group_deck_import = QtWidgets.QGroupBox(parent=Dialog)
self.group_deck_import.setObjectName("group_deck_import")
Expand Down Expand Up @@ -91,6 +100,7 @@ def retranslateUi(self, Dialog):
self.lbl_snapshot_path.setText(_translate("Dialog", "Snapshot Path:"))
self.cb_automated_snapshot.setText(_translate("Dialog", "Automated Snapshot"))
self.lbl_snapshot.setText(_translate("Dialog", "Snapshot Root Decks (separated by comma)"))
self.lbl_snapshot_custom_command.setText(_translate("Dialog", "Custom commit command:"))
self.group_deck_import.setTitle(_translate("Dialog", "Import"))
self.cb_ignore_move_cards.setText(_translate("Dialog", "Do Not Move Existing Cards"))
self.group_deck_export.setTitle(_translate("Dialog", "Export"))
Expand All @@ -110,6 +120,8 @@ def retranslateUi(self, Dialog):
"<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"
"<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"
"<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"
"<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 commit command</span>: Optional shell command to run instead of the built-in `git-commit` procedure. During this command execution, the `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.</p>\n"
"<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>: Blank, meaning the built-in `git-commit` procedure is run.</p>\n"
"<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"
"<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"
"<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"
Expand Down
4 changes: 4 additions & 0 deletions crowd_anki/history/anki_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ def stage_all(self):
@abstractmethod
def commit(self, message: str = None):
pass

@abstractmethod
def close(self):
pass
9 changes: 8 additions & 1 deletion crowd_anki/history/archiver_vendor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .anki_deck_archiver import AnkiDeckArchiver
from .archiver import AllDeckArchiver
from .custom_command_repo import CustomCommandRepo
from .dulwich_repo import DulwichAnkiRepo
from ..anki.adapters.deck_manager import AnkiStaticDeckManager, DeckManager
from ..anki.ui.utils import progress_indicator
Expand All @@ -24,12 +25,18 @@ def deck_manager(self) -> DeckManager:
return AnkiStaticDeckManager(self.window.col.decks)

def all_deck_archiver(self):
if self.config.snapshot_custom_command:
repo_provider = lambda path: CustomCommandRepo(
path, self.config.snapshot_custom_command)
else:
repo_provider = DulwichAnkiRepo

return AllDeckArchiver(
self.deck_manager,
lambda deck: AnkiDeckArchiver(deck,
self.config.full_snapshot_path,
AnkiJsonExporter(self.window.col, self.config),
DulwichAnkiRepo))
repo_provider))

def snapshot_path(self):
return Path(self.config.snapshot_path)
Expand Down
32 changes: 32 additions & 0 deletions crowd_anki/history/custom_command_repo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
import subprocess

from dataclasses import dataclass
from pathlib import Path

from .anki_repo import AnkiRepo


@dataclass
class CustomCommandRepo(AnkiRepo):
repo_path: Path
command: str

def stage_all(self):
pass

def commit(self, message: str = None):
env = os.environ.copy()
env["CROWD_ANKI_COMMIT_MESSAGE"] = message

try:
retcode = subprocess.call(self.command, shell=True, env=env)
except OSError as e:
raise RuntimeError("Failed to execute custom snapshot command: %s" % e)

if retcode != 0:
raise RuntimeError("Custom snapshot command exited with "
"non-zero exit code: %d" % retcode)

def close(self):
pass
16 changes: 16 additions & 0 deletions ui_files/config.ui
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@
<item>
<widget class="QPlainTextEdit" name="textedit_snapshot_root_decks"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lbl_snapshot_custom_command">
<property name="text">
<string>Custom commit command:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textedit_snapshot_custom_command"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
Expand Down Expand Up @@ -128,6 +142,8 @@ p, li { white-space: pre-wrap; }
&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;
&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;
&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;
&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 commit command&lt;/span&gt;: Optional shell command to run instead of the built-in `git-commit` procedure. During this command execution, the `CROWD_ANKI_COMMIT_MESSAGE` environment variable will be set to the commit message that would be used normally.&lt;/p&gt;
&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;: Blank, meaning the built-in `git-commit` procedure is run.&lt;/p&gt;
&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;
&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;
&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;
Expand Down