Skip to content

Commit

Permalink
Merge pull request #19 from 9dogs/fixes-for-new-anki-version
Browse files Browse the repository at this point in the history
Fixes for the new Anki version
  • Loading branch information
9dogs authored Apr 9, 2023
2 parents fbecf7d + bca66fe commit 4933de1
Show file tree
Hide file tree
Showing 7 changed files with 822 additions and 761 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install dependencies
Expand All @@ -26,12 +26,18 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install system dependencies
run: |
sudo apt update
sudo apt install -y libglib2.0-dev libxkbfile1 libgl1-mesa-glx libegl1 libxkbcommon-dev qt6-base-dev libnss3 libasound2
- name: Install dependencies
env:
DISABLE_QT5_COMPAT: 1
run: |
python -m pip install --upgrade poetry
poetry install
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ lint:
poetry run isort --check-only $(FILES)
poetry run flake8 $(FILES)
poetry run pydocstyle $(FILES)
poetry run mypy --show-error-codes --sqlite-cache $(FILES)
poetry run mypy $(FILES)

TEST_OUTPUT ?= .
test:
Expand Down
40 changes: 28 additions & 12 deletions notion_sync_addon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

from anki.collection import Collection
from aqt import mw
from aqt.hooks_gen import main_window_did_init
from aqt.gui_hooks import main_window_did_init
from aqt.utils import showCritical, showInfo
from jsonschema import ValidationError, validate
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
from PyQt5.QtWidgets import QAction, QMessageBox
from PyQt6.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QMenu, QMessageBox

from .helpers import (
BASE_DIR,
Expand Down Expand Up @@ -57,7 +58,7 @@ def __init__(self):
enable_logging_to_file()
self.logger = get_logger(self.__class__.__name__, self.debug)
self.logger.info('Config loaded: %s', self.config)
# Anki collection and note manager
# Anki's collection and note manager
self.collection: Optional[Collection] = None
self._collection_seeded = False
self.notes_manager: Optional[NotesManager] = None
Expand All @@ -71,7 +72,7 @@ def __init__(self):
self.existing_note_ids: Set[int] = set()
self._remove_obsolete_on_sync = False
# Add action to Anki menu
self.notion_menu = None
self.notion_menu: Optional[QMenu] = None
self.add_actions()
# Add callback to seed the collection then it's ready
main_window_did_init.append(self.seed_collection)
Expand All @@ -90,19 +91,24 @@ def __init__(self):
self.timer.timeout.connect(self.auto_sync)
self.timer.start()

def _validate_config(self, config: Dict[str, Any]):
def _validate_config(self, config: Optional[Dict[str, Any]]):
"""Validate config.
:param config: config
:raises ValidationError: if config is invalid
"""
if not config:
raise ValidationError('Config is empty')
# Load schema and validate configuration
with open(
BASE_DIR / 'schemas/config_schema.json', encoding='utf8'
) as s:
schema = json.load(s)
validate(config, schema)

def get_valid_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
def get_valid_config(
self, config: Optional[Dict[str, Any]]
) -> Dict[str, Any]:
"""Get valid configuration.
:param config: configuration
Expand All @@ -117,19 +123,24 @@ def get_valid_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
default_config = mw.addonManager.addonConfigDefaults(str(BASE_DIR))
return cast(Dict[str, Any], default_config)
else:
assert config # mypy
return config

def reload_config(self, new_config: Dict[str, Any]) -> None:
def reload_config(self, new_config: Optional[Dict[str, Any]]) -> None:
"""Reload configuration.
:param new_config: new configuration
"""
if not new_config:
assert mw # mypy
new_config = mw.addonManager.getConfig(__name__)
try:
self._validate_config(new_config)
except ValidationError as exc:
self.logger.error('Config update error', exc_info=exc)
showCritical(str(exc), title='Notion loader config update error')
else:
assert new_config # mypy
self.config = new_config

def add_actions(self):
Expand All @@ -150,6 +161,7 @@ def add_actions(self):

def seed_collection(self):
"""Init collection and note manager after Anki loaded."""
assert mw # mypy
self.collection = mw.col
if not self.collection:
self.logger.error('Collection is empty')
Expand Down Expand Up @@ -207,6 +219,7 @@ def handle_sync_finished(self) -> None:
self._alive_workers -= 1
if self._alive_workers:
return
assert self.notion_menu # mypy
self.notion_menu.setTitle('Notion')
# Show errors if manual sync
if self._sync_errors:
Expand All @@ -222,13 +235,15 @@ def handle_sync_finished(self) -> None:
f'Will delete {len(ids_to_remove)} obsolete note(s), '
f'continue?'
)
assert mw # mypy
do_delete = QMessageBox.question(
mw,
'Confirm deletion',
msg,
QMessageBox.Yes | QMessageBox.No,
QMessageBox.StandardButton.Yes
| QMessageBox.StandardButton.No,
)
if do_delete == QMessageBox.Yes:
if do_delete == QMessageBox.StandardButton.Yes.value:
self.notes_manager.remove_notes(ids_to_remove)
self._deleted += len(ids_to_remove)
self.collection.save(trx=False)
Expand Down Expand Up @@ -266,7 +281,7 @@ def auto_sync(self) -> None:
self.logger.info('Auto sync started')
# Reload config
assert mw # mypy
self.config = mw.addonManager.getConfig(__name__)
self.reload_config(None)
self._is_auto_sync = True
self._sync()

Expand All @@ -275,7 +290,7 @@ def sync(self) -> None:
self.logger.info('Sync started')
# Reload config
assert mw # mypy
self.config = mw.addonManager.getConfig(__name__)
self.reload_config(None)
if not self._alive_workers:
self._is_auto_sync = False
self._sync()
Expand Down Expand Up @@ -312,6 +327,7 @@ def _sync(self) -> None:
self.seed_collection()
if not self._collection_seeded:
return
assert self.notion_menu # mypy
self.notion_menu.setTitle('Notion (syncing...)')
for page_spec in self.config.get('notion_pages', []):
page_id, recursive = page_spec['page_id'], page_spec['recursive']
Expand Down
10 changes: 5 additions & 5 deletions notion_sync_addon/notion_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def __init__(self, token: str, debug: bool = False) -> None:
"""
self.logger = get_logger(self.__class__.__name__, debug)
self.cookies: Dict[str, str] = {'token_v2': token}
self.session = requests.Session()
self.session.cookies.update(self.cookies)

def enqueue_export_task(
self, page_id: str, recursive: bool = False
Expand Down Expand Up @@ -78,10 +80,9 @@ def enqueue_export_task(
data = None
while attempts_count < self.NOTION_MAX_RETRIES:
try:
resp = requests.post(
resp = self.session.post(
self.NOTION_ENQUEUE_TASK_ENDPOINT,
json=payload,
cookies=self.cookies,
)
except CONNECTION_EXCEPTIONS as exc:
raise NotionClientError('Request error') from exc
Expand Down Expand Up @@ -128,10 +129,9 @@ def get_task_result(self, task_id: str) -> Optional[str]:
attempts_count = 0
while attempts_count < self.NOTION_MAX_RETRIES:
try:
resp = requests.post(
resp = self.session.post(
self.NOTION_GET_TASK_ENDPOINT,
json={'taskIds': [task_id]},
cookies=self.cookies,
)
except CONNECTION_EXCEPTIONS as exc:
raise NotionClientError('Request error') from exc
Expand Down Expand Up @@ -181,7 +181,7 @@ def export_page(
'Export complete, downloading file on URL: %s',
export_url,
)
with requests.get(export_url, stream=True) as r:
with self.session.get(export_url, stream=True) as r:
r.raise_for_status()
with open(destination, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
Expand Down
Loading

0 comments on commit 4933de1

Please sign in to comment.