|
1 |
| -# Copyright 2020 The Hyp2Rem Authors |
2 |
| - |
3 |
| -# Use of this source code is governed by an MIT-style |
4 |
| -# license that can be found in the LICENSE file or at |
5 |
| -# https://opensource.org/licenses/MIT. |
6 |
| - |
7 |
| - |
8 |
| -"""Command line interface to sync Hypothes.is and RemNote. |
9 |
| -
|
10 |
| -This module provides a command line utility that retrieves newly created or |
11 |
| -updated annotations in a Hypothes.is group, compares them to existing Rem's in |
12 |
| -a RemNote account, and uploads them. |
13 |
| -
|
14 |
| -Note: |
15 |
| - For this module to work, it is recommended that you store your credentials |
16 |
| - as environment variables. See the package documentation for more |
17 |
| - information. |
18 |
| -""" |
19 |
| - |
20 |
| - |
21 |
| -import json |
22 |
| -from datetime import datetime |
23 |
| -from enum import Enum |
24 |
| -from typing import Optional |
25 |
| - |
26 |
| -import log # type: ignore |
27 |
| -from typer import Option, Typer, echo |
28 |
| - |
29 |
| -import hyp2rem.utils |
30 |
| -from hyp2rem import hypothesis as hyp |
31 |
| - |
32 |
| -app = Typer() |
33 |
| - |
34 |
| - |
35 |
| -class SortOption(str, Enum): |
36 |
| - """Sorting options supported by Hypothes.is API.""" |
37 |
| - |
38 |
| - created: str = "created" |
39 |
| - updated: str = "updated" |
40 |
| - |
41 |
| - |
42 |
| -@app.command() |
43 |
| -def main( # type: ignore |
44 |
| - hyp_group: Optional[str] = Option( |
45 |
| - None, |
46 |
| - help="Name of the Hypothes.is group where annotations are stored", |
47 |
| - show_default=False, |
48 |
| - ), |
49 |
| - sort: SortOption = Option( |
50 |
| - SortOption.created, help="Metric to sort results by" |
51 |
| - ), |
52 |
| - after: Optional[datetime] = Option( |
53 |
| - None, |
54 |
| - help="Search for annotations created ou updated after the given date", |
55 |
| - ), |
56 |
| - hyp_key: str = Option( |
57 |
| - ..., |
58 |
| - envvar="HYP_KEY", |
59 |
| - prompt=True, |
60 |
| - help="API key for Hypothes.is account", |
61 |
| - show_default=False, |
62 |
| - ), |
63 |
| - rem_user: str = Option( |
64 |
| - ..., |
65 |
| - envvar="REM_USER", |
66 |
| - prompt=True, |
67 |
| - help="User ID for RemNote account", |
68 |
| - show_default=False, |
69 |
| - ), |
70 |
| - rem_key: str = Option( |
71 |
| - ..., |
72 |
| - envvar="REM_KEY", |
73 |
| - prompt=True, |
74 |
| - help="API key for RemNote account", |
75 |
| - show_default=False, |
76 |
| - ), |
77 |
| - uri: Optional[str] = Option( |
78 |
| - None, |
79 |
| - help="A web page address (URL) or a URN representing another kind " |
80 |
| - + "of resource such as DOI (Digital Object Identifier) or a PDF " |
81 |
| - + "fingerprint.", |
82 |
| - show_default=False, |
83 |
| - ), |
84 |
| - quiet: bool = Option( |
85 |
| - False, |
86 |
| - help="Silences the output printed to the terminal.", |
87 |
| - ), |
88 |
| - verbose: bool = Option( |
89 |
| - False, |
90 |
| - help="Increases the output printed to the terminal.", |
91 |
| - ), |
92 |
| - debug: bool = Option( |
93 |
| - False, |
94 |
| - help="Print every step to the terminal, for debugging purposes.", |
95 |
| - ), |
96 |
| -): |
97 |
| - """ |
98 |
| - Sync Hypothes.is annotations with Rem's in a RemNote account. |
99 |
| - """ |
100 |
| - # pylint: disable=too-many-locals |
101 |
| - if quiet: |
102 |
| - verbosity: int = 0 # Only errors (ERROR) |
103 |
| - elif verbose: |
104 |
| - verbosity = 2 # Errors, warnings and information (INFO) |
105 |
| - elif debug: |
106 |
| - verbosity = 3 # All possible output (DEBUG) |
107 |
| - else: |
108 |
| - verbosity = 1 # Errors and warnings (WARN) - Default |
109 |
| - log.reset() |
110 |
| - log.init(verbosity=verbosity) |
111 |
| - # get group id, if a group name was provided |
112 |
| - group_id = None |
113 |
| - if hyp_group is not None: |
114 |
| - group = hyp.get_group_by_name(key=hyp_key, name=hyp_group) |
115 |
| - if group is not None: |
116 |
| - group_id = group["id"] |
117 |
| - else: |
118 |
| - log.error( |
119 |
| - "Group name was set, but not found in server. Cannot proceed." |
120 |
| - ) |
121 |
| - raise ValueError |
122 |
| - # `after` option back to ISO string |
123 |
| - if isinstance(after, datetime): |
124 |
| - after = after.isoformat() # type: ignore |
125 |
| - # fetch relevant annotations |
126 |
| - annotations = hyp.get_annotations( |
127 |
| - key=hyp_key, |
128 |
| - group=group_id, |
129 |
| - sort=sort, |
130 |
| - order="asc", |
131 |
| - search_after=after, |
132 |
| - uri=uri, |
133 |
| - ) |
134 |
| - if verbosity > 2: |
135 |
| - echo(json.dumps(annotations, indent=4)) |
136 |
| - for annotation in annotations: |
137 |
| - document = hyp2rem.utils.document_for_source( |
138 |
| - rem_key, rem_user, annotation |
139 |
| - ) |
140 |
| - if verbosity > 2: |
141 |
| - echo(document) |
142 |
| - |
143 |
| - |
144 |
| -if __name__ == "__main__": |
145 |
| - app(prog_name="hyp2rem") |
| 1 | +# Copyright 2020 The Hyp2Rem Authors |
| 2 | + |
| 3 | +# Use of this source code is governed by an MIT-style |
| 4 | +# license that can be found in the LICENSE file or at |
| 5 | +# https://opensource.org/licenses/MIT. |
| 6 | + |
| 7 | + |
| 8 | +"""Command line interface to sync Hypothes.is and RemNote. |
| 9 | +
|
| 10 | +This module provides a command line utility that retrieves newly created or |
| 11 | +updated annotations in a Hypothes.is group, compares them to existing Rem's in |
| 12 | +a RemNote account, and uploads them. |
| 13 | +
|
| 14 | +Note: |
| 15 | + For this module to work, it is recommended that you store your credentials |
| 16 | + as environment variables. See the package documentation for more |
| 17 | + information. |
| 18 | +""" |
| 19 | + |
| 20 | + |
| 21 | +from datetime import datetime |
| 22 | +from enum import Enum |
| 23 | +from typing import Literal, Optional |
| 24 | + |
| 25 | +import log # type: ignore |
| 26 | +from dotenv import load_dotenv |
| 27 | +from typer import Option, Typer, echo |
| 28 | + |
| 29 | +from hyp2rem.hyp2rem import Bridge |
| 30 | +from hyp2rem.hypothesis.clients import HypothesisV1Client |
| 31 | +from hyp2rem.remnote.clients import RemNoteV0Client |
| 32 | + |
| 33 | +app = Typer() |
| 34 | + |
| 35 | + |
| 36 | +class SortOption(str, Enum): |
| 37 | + """Sorting options supported by Hypothes.is API.""" |
| 38 | + |
| 39 | + CREATED: Literal["created"] = "created" |
| 40 | + UPDATED: Literal["updated"] = "updated" |
| 41 | + |
| 42 | + |
| 43 | +@app.command() |
| 44 | +def main( # type: ignore |
| 45 | + group: Optional[str] = Option( # TODO: Accept multiple groups |
| 46 | + None, |
| 47 | + help="Name of the Hypothes.is group where annotations are stored", |
| 48 | + show_default=False, |
| 49 | + ), |
| 50 | + sort: SortOption = Option( |
| 51 | + SortOption.UPDATED, |
| 52 | + help="Metric to sort results by", |
| 53 | + case_sensitive=False, |
| 54 | + ), |
| 55 | + after: Optional[datetime] = Option( |
| 56 | + None, |
| 57 | + help="Search for annotations created ou updated after the given date", |
| 58 | + ), |
| 59 | + uri: Optional[str] = Option( |
| 60 | + None, |
| 61 | + help="A web page address (URL) or a URN representing another kind " |
| 62 | + + "of resource whose annotations should be synced.", |
| 63 | + show_default=False, |
| 64 | + ), |
| 65 | + hyp_key: str = Option( |
| 66 | + ..., |
| 67 | + envvar="HYP_KEY", |
| 68 | + prompt=True, |
| 69 | + help="API key for Hypothes.is account", |
| 70 | + show_default=False, |
| 71 | + ), |
| 72 | + rem_user: str = Option( |
| 73 | + ..., |
| 74 | + envvar="REM_USER", |
| 75 | + prompt=True, |
| 76 | + help="User ID for RemNote account", |
| 77 | + show_default=False, |
| 78 | + ), |
| 79 | + rem_key: str = Option( |
| 80 | + ..., |
| 81 | + envvar="REM_KEY", |
| 82 | + prompt=True, |
| 83 | + help="API key for RemNote account", |
| 84 | + show_default=False, |
| 85 | + ), |
| 86 | + quiet: bool = Option( |
| 87 | + False, |
| 88 | + help="Silences the output printed to the terminal.", |
| 89 | + ), |
| 90 | + verbose: bool = Option( |
| 91 | + False, |
| 92 | + help="Increases the output printed to the terminal.", |
| 93 | + ), |
| 94 | + debug: bool = Option( |
| 95 | + False, |
| 96 | + help="Print every step to the terminal, for debugging purposes.", |
| 97 | + ), |
| 98 | +): |
| 99 | + """ |
| 100 | + Sync Hypothes.is annotations with Rem's in a RemNote account. |
| 101 | + """ |
| 102 | + # pylint: disable=too-many-locals |
| 103 | + |
| 104 | + # set verbosity levels and initialize logs |
| 105 | + verbosity: int = 1 # Errors and warnings (WARN) - Default |
| 106 | + if quiet: |
| 107 | + verbosity = 0 # Only errors (ERROR) |
| 108 | + elif verbose: |
| 109 | + verbosity = 2 # Errors, warnings and information (INFO) |
| 110 | + elif debug: |
| 111 | + verbosity = 3 # All possible output (DEBUG) |
| 112 | + log.reset() |
| 113 | + log.init(verbosity=verbosity) |
| 114 | + |
| 115 | + # set up Hypothes.is and RemNote connections |
| 116 | + hyp_client: HypothesisV1Client = HypothesisV1Client( |
| 117 | + group_name=group, |
| 118 | + sort=sort, |
| 119 | + order="asc", |
| 120 | + search_after=after, |
| 121 | + uri=uri, |
| 122 | + key=hyp_key, |
| 123 | + ) |
| 124 | + rem_client: RemNoteV0Client = RemNoteV0Client( |
| 125 | + key=rem_key, |
| 126 | + user_id=rem_user, |
| 127 | + ) |
| 128 | + |
| 129 | + # set up Bridge object between fetched annotations and RemNote |
| 130 | + bridge: Bridge = Bridge(hyp_client, rem_client) |
| 131 | + bridge.sync_all() |
| 132 | + if verbosity > 1: |
| 133 | + echo(bridge.stats) |
| 134 | + |
| 135 | + |
| 136 | +if __name__ == "__main__": |
| 137 | + load_dotenv() |
| 138 | + app(prog_name="hyp2rem") |
0 commit comments