lizeyan/Zotero-meta-update

windows10 运行的时候出错了

handsomeallan opened this issue · 3 comments

运行 python update_zotero_meta
提示:OSError: [WinError 123] 文件名、目录名或卷标语法不正确。: '..\output\run-2023-11-28T17:15:07.343973'

怎么办?

应该是因为我给目录的命名不符合 windows 的规范。这个你可以尝试自己改改,我没有 windows 电脑,我只在 macOS 和 Linux 上测试过....

应该是因为我给目录的命名不符合 windows 的规范。这个你可以尝试自己改改,我没有 windows 电脑,我只在 macOS 和 Linux 上测试过....

目测应该是因为windows要求文件名不能有冒号

安装完后运行这个就行

#!C:\Users\24871\mambaforge\envs\zotero\python.exe
import json
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional

from click import command, option
from loguru import logger
from pytz import timezone

from zotero import download_items
from zotero.metadata import get_updated_zotero_meta_for_item, get_update_time
from zotero.request import write_metadata_to_zotero


def check_difference(a, b, skip_confirmation: bool) -> bool:
    print(f"itemType: {a['itemType']=:15}, {b['itemType']=:15}")
    print(f"Missing keys:    {set(a.keys()) - set(b.keys())=}")
    print(f"Additional keys: {set(b.keys()) - set(a.keys())=}")
    print(f"Title: {b['title']}")
    nothing_changed = True
    for key in (set(a.keys()) | set(b.keys())) - {'extra', 'dateModified'}:
        if a.get(key, "NaN") != b.get(key, 'NaN'):
            nothing_changed = False
            print(f"{key:10} changed from {a.get(key, 'NaN')!r:10} to {b.get(key, 'NaN')!r:10}")
    if nothing_changed and get_update_time(a) is not None:
        """
        If get_update_time(a) is not None, the original metadata has no mark that it has been updated
        We want to mark it to skip online search in future runs
        """
        logger.info(f"Skip write because nothing changed")
        return False
    if skip_confirmation:
        return True
    choice = input("Skip (s, default) or write to server (w)")
    if choice.lower() == "write" or choice.lower() == "w":
        return True
    else:
        return False


@command("Zotero Metadata Update")
@option(
    "--output-dir", "-o", type=Path, default=Path("../output") / f'run-{datetime.now().isoformat()}',
    help="The downloaded original metadata files and the updated ones will be saved to this directory."
)
@option(
    "--min-update-interval-days", "-d", type=int, default=7,
    help="If an item has been updated in `min-update-interval-days` days, it will be skipped."
)
@option(
    "--skip-download", "-s", is_flag=True, default=False,
    help="If true, we skip download original metadata files from Zotero server."
)
@option(
    "--skip-online-update", is_flag=True, default=False,
    help="If true, we skip update online and just read the newest updated metadata."
)
@option(
    "--write", "-w", is_flag=True, default=False,
    help="If true, we write the updated metadata files to Zotero server."
)
@option(
    "--skip-confirmation", is_flag=True, default=False,
    help="If true, each item would be write to server (only when --write) without confirmation"
)
def main(
        output_dir: Path, min_update_interval_days: int, skip_download: bool, write: bool,
        skip_online_update: bool, skip_confirmation: bool,
):
    # replace all : to - in the output_dir name
    output_dir = Path(str(output_dir).replace(":", "-"))
    output_dir.mkdir(exist_ok=True, parents=True)
    logger.add(output_dir / "log.txt", rotation="1 week", retention="1 month")
    logger.info(f"=========================START===============================")
    dt_threshold = datetime.now(timezone("Asia/Shanghai")) - timedelta(days=min_update_interval_days)
    logger.info(f"{dt_threshold=}")
    if not skip_download:
        logger.info("Downloading original metadata files from Zotero server...")
        download_items(output_dir=output_dir)
    else:
        logger.info("Skip download original metadata files from Zotero server.")

    paths = output_dir.glob("*")
    # paths = [output_dir / 'D2BLJAUK']  # DEBUG
    failed_items = []

    for item_path in paths:
        if not item_path.is_dir():
            continue
        handler_id = logger.add(item_path / "log.txt", rotation="1 MB", enqueue=True)
        try:
            logger.info(f"========================================================")
            logger.info(f"Processing {item_path}")
            try:
                with open(item_path / "original.json", "r", encoding="utf-8") as f:
                    original_meta = json.load(f)['data']
            except Exception as e:
                logger.error(f"Cannot load {item_path / 'original.json'}: {e}")
                continue
            date_modified: Optional[datetime] = get_update_time(original_meta)
            logger.info(f"item {original_meta['key']} is modified at {date_modified}")
            if date_modified is not None and date_modified > dt_threshold:
                logger.info(f"Skip {item_path.name} since it has been updated recently.")
                continue

            if 'lock' in original_meta.get("extra", ""):
                logger.info(f"Skip item {original_meta['key']} because it is locked: {original_meta['extra']=}")
                continue

            if original_meta["itemType"] == "attachment":
                logger.error(f"Skip {item_path.name} since it is an attachment.")
                continue
            if skip_online_update:
                try:
                    with open(item_path / "updated_meta.json", "r") as f:
                        new_meta = json.load(f)
                    logger.info(f"Read updated metadata from {item_path / 'updated_meta.json'}")
                except Exception as e:
                    logger.error(f"Cannot load {item_path / 'updated_meta.json'}: {e}")
                    new_meta = None
            else:
                logger.info(f"Updating metadata for {item_path.name}")
                new_meta = get_updated_zotero_meta_for_item(original_meta)
            if new_meta is not None:
                with open(item_path / "updated_meta.json", "w+") as f:
                    json.dump(new_meta, f, indent=2)
                confirmation = check_difference(original_meta, new_meta, skip_confirmation=skip_confirmation)
                if write and confirmation:
                    logger.info("Write to server")
                    write_metadata_to_zotero(original_meta['key'], new_meta)
                else:
                    logger.info("Skip writing to server")
            else:
                failed_items.append(item_path)
        except Exception as e:
            logger.exception(f"Exception encountered when processing {item_path=}", exception=e)
            failed_items.append(item_path)
        finally:
            logger.remove(handler_id)
    logger.info(f"failed_items: {failed_items}")
    logger.info(f"=========================START===============================")


if __name__ == '__main__':
    main()