wikinator 0.6.2__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,16 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wikinator
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: Convert Google docs to markdown
5
+ Requires-Dist: confuse>=2.2.0
6
+ Requires-Dist: cryptography>=46.0.5
5
7
  Requires-Dist: dotenv>=0.9.9
6
8
  Requires-Dist: google-api-python-client>=2.175.0
7
9
  Requires-Dist: google-auth-httplib2>=0.2.0
8
10
  Requires-Dist: google-auth-oauthlib>=1.2.2
9
11
  Requires-Dist: gql[all]>=3.5.3
12
+ Requires-Dist: humanize>=4.13.0
13
+ Requires-Dist: pillow>=11.3.0
10
14
  Requires-Dist: pytest>=8.4.1
11
15
  Requires-Dist: python-docx>=1.2.0
12
16
  Requires-Dist: typer-slim>=0.16.0
@@ -23,24 +27,22 @@ Convert a Google drive download into a markdown-based wiki.
23
27
  **Note**: This is a work in progress, and not all features will be supported or working properly.
24
28
 
25
29
  ## tl;dr
30
+ [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/) and then:
26
31
  ```
27
- uvx wikinator upload --help
28
- Usage: wikinator upload [OPTIONS] SOURCE WIKIROOT
29
-
30
- Convert and upload a file hierarchy to a GraphQL wiki. Given a source
31
- directrory, walk the directory tree and for each file: - if MD or image
32
- file, upload at the same path relative to the wikiroot path - If DOCX,
33
- convert to MD and upload at the same path... - Unknown files are skipped.
34
- For example, with source=/src and wikiroot=/wiki/root, A DOCX file at
35
- /src/dir/some_file.docx will be uploaded to /wiki/root/dir/some_file on the
36
- wiki.
37
-
38
- Arguments:
39
- SOURCE [required]
40
- WIKIROOT defaults to root of wiki, /
32
+ uvx wikinator --help
33
+ Usage: wikinator [OPTIONS] COMMAND [ARGS]...
41
34
 
42
35
  Options:
43
- --help Show this message and exit.
36
+ --version Display version and exit.
37
+ -v Show verbose logging.
38
+ -vv Show debug logging.
39
+ -vvv Show full trace logging.
40
+ --help Show this message and exit.
41
+
42
+ Commands:
43
+ upload Convert and upload a file hierarchy to a GraphQL wiki.
44
+ convert Given the URL of a specific gdoc:
45
+ config View or set configuration settings.
44
46
  ```
45
47
 
46
48
  Given a directory, convert supported file types into markdown-based files while maintaining names and directory structure. This can then be uploaded into various wiki systems.
@@ -61,44 +63,50 @@ The development log will be kept here until the 1.0 release.
61
63
 
62
64
  ## Usage
63
65
  ```
66
+ uxv wikinator convert https://gdoc/full/url path=test
67
+
64
68
  uvx wikinator upload target_dir
65
69
  uvx wikinator upload target_file.md new/path
66
70
  ```
67
71
 
72
+ `convert` will take a single URL to a google doc, convert it to markdown, and
73
+ upload that to the configured GraphQL server. An optional `path` option is provided
74
+ to specify to path in the wiki to upload the document to.
75
+
68
76
  `upload` loads a full directory into the wiki. In the above examples:
69
77
  - Upload the directoy tree at `target_dir` into the wikipath `target_dir`
70
- - Upload the file `target_file.md` into the wiki path `new/path/target_file`
78
+ - Upload the file `target_file.docx` into the wiki path `new/path/target_file`
71
79
 
72
80
  Assuming the `en` locale, the final paths in the wiki will be:
73
81
  - $GRAPH_DB/en/target_dir/...
74
82
  - $GRAPH_DB/en/new/path/target_file
75
83
 
76
- ## Install & Configure
84
+ ## Configuration
77
85
  There is nothing to install, the `wikinator` command can be run from anywhere [`uvx` is installed](https://docs.astral.sh/uv/getting-started/installation/).
78
86
 
79
- To upload to your wiki, you must have the URL and an authorization token.
80
-
81
- Create a file in your home directory: `~/.config/wikinator.env`
87
+ To upload to your wiki, you must have:
88
+ 1. the URL
89
+ 2. an authentication token
82
90
 
83
- In that file, add your wiki GraphQL URL and the access token:
91
+ Configuring the values in wikinator:
84
92
  ```
85
- GRAPH_DB=https://wiki.example.org/graphql
86
- AUTH_TOKEN=your-authorization-token
93
+ uvx wikinator config db_url https://db.example.com/graphql
94
+ uvx wikinator config db_token <authentication-token-for-your-graphdb>
87
95
  ```
88
96
 
97
+ When accessing Google docs, `wikinator` will confirm access to the requested files with a browser-based user authentication. These details will be stored in the configuration directory (`uvx wikinator config config_dir`) in `token.json` for future use.
98
+
89
99
  ### wiki.js
90
100
 
91
- TODO: Details on setting up creds for wikijs.
101
+ This section is specific to the getting configuration values for a wiki.js server.
92
102
 
93
- Once you have the URL and authorization token from wiki,js, add them to an `.env` file:
94
- ```
95
- GRAPH_DB=https://wiki.example.com/graphql
96
- AUTH_TOKEN=kfhgdkfjhgkdjfhgkdjhfgk...
97
- ```
103
+ You'll need the URL of the server, and the [authentication token](https://docs.requarks.io/dev/api#authentication) for you account.
98
104
 
99
- Once this file is set up correctly, `wikinator` is run with:
105
+ With those values, configure the `db_url` and `db_token` with the `wikinator config` command, as above.
106
+
107
+ Once this file is set up correctly, confirm with with:
100
108
  ```
101
- uvx wikinator upload --help
109
+ uvx wikinator config
102
110
  ```
103
111
 
104
112
 
@@ -119,6 +127,11 @@ uvx wikinator upload --help
119
127
 
120
128
  ## Development Log
121
129
 
130
+ ### 2026-03-08
131
+ Adding `config` and `convert` commands.
132
+ - `config` helps manage config
133
+ - `convert` will read, convert and upload a google doc
134
+
122
135
  ### 2025-08-07
123
136
  Refactored and disabled (for now) the convert, extract and teleport commands. The code remains in place, but the
124
137
  current focus is on convert and upload to graphql, and I wan to disable any code that's not in that path while testing.
@@ -5,24 +5,22 @@ Convert a Google drive download into a markdown-based wiki.
5
5
  **Note**: This is a work in progress, and not all features will be supported or working properly.
6
6
 
7
7
  ## tl;dr
8
+ [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/) and then:
8
9
  ```
9
- uvx wikinator upload --help
10
- Usage: wikinator upload [OPTIONS] SOURCE WIKIROOT
11
-
12
- Convert and upload a file hierarchy to a GraphQL wiki. Given a source
13
- directrory, walk the directory tree and for each file: - if MD or image
14
- file, upload at the same path relative to the wikiroot path - If DOCX,
15
- convert to MD and upload at the same path... - Unknown files are skipped.
16
- For example, with source=/src and wikiroot=/wiki/root, A DOCX file at
17
- /src/dir/some_file.docx will be uploaded to /wiki/root/dir/some_file on the
18
- wiki.
19
-
20
- Arguments:
21
- SOURCE [required]
22
- WIKIROOT defaults to root of wiki, /
10
+ uvx wikinator --help
11
+ Usage: wikinator [OPTIONS] COMMAND [ARGS]...
23
12
 
24
13
  Options:
25
- --help Show this message and exit.
14
+ --version Display version and exit.
15
+ -v Show verbose logging.
16
+ -vv Show debug logging.
17
+ -vvv Show full trace logging.
18
+ --help Show this message and exit.
19
+
20
+ Commands:
21
+ upload Convert and upload a file hierarchy to a GraphQL wiki.
22
+ convert Given the URL of a specific gdoc:
23
+ config View or set configuration settings.
26
24
  ```
27
25
 
28
26
  Given a directory, convert supported file types into markdown-based files while maintaining names and directory structure. This can then be uploaded into various wiki systems.
@@ -43,44 +41,50 @@ The development log will be kept here until the 1.0 release.
43
41
 
44
42
  ## Usage
45
43
  ```
44
+ uxv wikinator convert https://gdoc/full/url path=test
45
+
46
46
  uvx wikinator upload target_dir
47
47
  uvx wikinator upload target_file.md new/path
48
48
  ```
49
49
 
50
+ `convert` will take a single URL to a google doc, convert it to markdown, and
51
+ upload that to the configured GraphQL server. An optional `path` option is provided
52
+ to specify to path in the wiki to upload the document to.
53
+
50
54
  `upload` loads a full directory into the wiki. In the above examples:
51
55
  - Upload the directoy tree at `target_dir` into the wikipath `target_dir`
52
- - Upload the file `target_file.md` into the wiki path `new/path/target_file`
56
+ - Upload the file `target_file.docx` into the wiki path `new/path/target_file`
53
57
 
54
58
  Assuming the `en` locale, the final paths in the wiki will be:
55
59
  - $GRAPH_DB/en/target_dir/...
56
60
  - $GRAPH_DB/en/new/path/target_file
57
61
 
58
- ## Install & Configure
62
+ ## Configuration
59
63
  There is nothing to install, the `wikinator` command can be run from anywhere [`uvx` is installed](https://docs.astral.sh/uv/getting-started/installation/).
60
64
 
61
- To upload to your wiki, you must have the URL and an authorization token.
62
-
63
- Create a file in your home directory: `~/.config/wikinator.env`
65
+ To upload to your wiki, you must have:
66
+ 1. the URL
67
+ 2. an authentication token
64
68
 
65
- In that file, add your wiki GraphQL URL and the access token:
69
+ Configuring the values in wikinator:
66
70
  ```
67
- GRAPH_DB=https://wiki.example.org/graphql
68
- AUTH_TOKEN=your-authorization-token
71
+ uvx wikinator config db_url https://db.example.com/graphql
72
+ uvx wikinator config db_token <authentication-token-for-your-graphdb>
69
73
  ```
70
74
 
75
+ When accessing Google docs, `wikinator` will confirm access to the requested files with a browser-based user authentication. These details will be stored in the configuration directory (`uvx wikinator config config_dir`) in `token.json` for future use.
76
+
71
77
  ### wiki.js
72
78
 
73
- TODO: Details on setting up creds for wikijs.
79
+ This section is specific to the getting configuration values for a wiki.js server.
74
80
 
75
- Once you have the URL and authorization token from wiki,js, add them to an `.env` file:
76
- ```
77
- GRAPH_DB=https://wiki.example.com/graphql
78
- AUTH_TOKEN=kfhgdkfjhgkdjfhgkdjhfgk...
79
- ```
81
+ You'll need the URL of the server, and the [authentication token](https://docs.requarks.io/dev/api#authentication) for you account.
80
82
 
81
- Once this file is set up correctly, `wikinator` is run with:
83
+ With those values, configure the `db_url` and `db_token` with the `wikinator config` command, as above.
84
+
85
+ Once this file is set up correctly, confirm with with:
82
86
  ```
83
- uvx wikinator upload --help
87
+ uvx wikinator config
84
88
  ```
85
89
 
86
90
 
@@ -101,6 +105,11 @@ uvx wikinator upload --help
101
105
 
102
106
  ## Development Log
103
107
 
108
+ ### 2026-03-08
109
+ Adding `config` and `convert` commands.
110
+ - `config` helps manage config
111
+ - `convert` will read, convert and upload a google doc
112
+
104
113
  ### 2025-08-07
105
114
  Refactored and disabled (for now) the convert, extract and teleport commands. The code remains in place, but the
106
115
  current focus is on convert and upload to graphql, and I wan to disable any code that's not in that path while testing.
@@ -1,15 +1,19 @@
1
1
  [project]
2
2
  name = "wikinator"
3
- version = "0.6.2"
3
+ version = "0.7.0"
4
4
  description = "Convert Google docs to markdown"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
+ "confuse>=2.2.0",
9
+ "cryptography>=46.0.5",
8
10
  "dotenv>=0.9.9",
9
11
  "google-api-python-client>=2.175.0",
10
12
  "google-auth-httplib2>=0.2.0",
11
13
  "google-auth-oauthlib>=1.2.2",
12
14
  "gql[all]>=3.5.3",
15
+ "humanize>=4.13.0",
16
+ "pillow>=11.3.0",
13
17
  "pytest>=8.4.1",
14
18
  "python-docx>=1.2.0",
15
19
  "typer-slim>=0.16.0",
@@ -1,22 +1,19 @@
1
1
  import importlib.metadata
2
2
  import logging
3
- import json
3
+
4
4
  import typer
5
- from typing import Optional
6
5
  from typing_extensions import Annotated
7
- import os
8
- from dotenv import load_dotenv
9
-
10
6
 
11
- from .wiki import GraphIngester, GraphDB
7
+ from wikinator.config import AppConfig, __app_name__
8
+ from wikinator.gdrive import GoogleDrive
9
+ from wikinator.wiki import GraphDB as GraphDB
10
+ from wikinator.wiki import GraphIngester
12
11
 
13
-
14
- __app_name__ = "wikinator"
15
- __app_version__ = importlib.metadata.version(__app_name__)
12
+ #__app_version__ = importlib.metadata.version(__app_name__)
16
13
 
17
14
 
15
+ app_config = AppConfig()
18
16
  log = logging.getLogger(__name__)
19
-
20
17
  app = typer.Typer(add_completion=False)
21
18
 
22
19
 
@@ -45,13 +42,17 @@ def init_logging(level:int) -> None:
45
42
  logging.basicConfig(level=logging.WARNING, format=fmt, style='{', handlers=(typer_handler,))
46
43
  # gql.transport.aiohttp = INFO for full transport logging
47
44
  log.setLevel(level)
48
- log.info(f"starting {__app_name__} v{__app_version__}")
49
- log.debug("debug logging enabled.")
45
+ log.info(f"starting {app_config.value('app_info')}")
46
+
47
+
48
+ # initialize logging from config
49
+ init_logging(logging.getLevelName(app_config.get('log_level').upper()))
50
+ log.debug("debug logging enabled")
50
51
 
51
52
 
52
53
  def version_callback(value: bool) -> None:
53
54
  if value:
54
- typer.echo(f"{__app_name__} v{__app_version__}")
55
+ print(f"{app_config.value('app_info')}")
55
56
  raise typer.Exit()
56
57
 
57
58
 
@@ -77,8 +78,8 @@ def trace_callback(value: bool) -> None:
77
78
  def upload(
78
79
  source: str,
79
80
  wikiroot: str = "/",
80
- #url: Annotated[str, typer.Option("--db", help="URL of the GraphQL database")] = os.getenv("GRAPH_DB"),
81
- #token: Annotated[str, typer.Option("--token", help="URL of the GraphQL database")] = os.getenv("AUTH_TOKEN"),
81
+ db_url: Annotated[str, typer.Option("--db", help="URL of the GraphQL database")] = app_config.get('db_url'),
82
+ db_token: Annotated[str, typer.Option("--token", help="URL of the GraphQL database")] = app_config.get('db_token'),
82
83
  output: Annotated[bool, typer.Option("-o", help="Make a local copy of the converted file")] = False,
83
84
  ) -> None:
84
85
  """
@@ -91,12 +92,9 @@ def upload(
91
92
  For example, with source=/src and wikiroot=/wiki/root,
92
93
  a DOCX file at /src/dir/some_file.docx will be uploaded to /wiki/root/dir/some_file on the wiki.
93
94
  """
94
- load_dotenv(dotenv_path=os.path.expanduser("~/.config/wikinator.env"))
95
- url = os.getenv("GRAPH_DB")
96
- token = os.getenv("AUTH_TOKEN")
97
- log.debug(f"Loaded env, db={url}")
95
+ GraphIngester(url=db_url, token=db_token, output=output).convert_directory(source, wikiroot)
96
+ raise typer.Exit()
98
97
 
99
- GraphIngester(url=url, token=token, output=output).convert_directory(source, wikiroot)
100
98
 
101
99
 
102
100
  # Setup global options using callbacks
@@ -115,16 +113,71 @@ def common(
115
113
  pass
116
114
 
117
115
 
116
+ @app.command()
117
+ def convert(
118
+ doc_url: Annotated[str, typer.Argument(help="URL of the google-doc")],
119
+ db_url: Annotated[str, typer.Option("--db", help="URL of the GraphQL database.")] = app_config.get('db_url'),
120
+ path: Annotated[str, typer.Option("--path", help="Path to upload to. Defaults to '/' (root)")] = None,
121
+ name: Annotated[str, typer.Option("--name", help="Name of the uploaded file. Defaults to '<document-name>.md'")] = None,
122
+ token: Annotated[str, typer.Option("--token", help="Secure token for GraphQL database.")] = app_config.get('db_token'),
123
+ ) -> None:
124
+ """
125
+ Given the URL of a google doc, and options upload path: download the document, convert it to markdown, and upload
126
+ the converted file to a GraphQL wiki with /path/name.
127
+
128
+ Example:
129
+
130
+ uvx wikinator https://example.com/docker_tutorials.docx --path edu/tutorials
131
+
132
+ will create a new wiki entry '/edu/tutorials/docker_tutorials.md' from https://example.com/docker_tutorials.docx
118
133
 
119
- # TODO
134
+ GraphQL credentials can be set on the command line or configured with `wikinator config`. Loading a Googledoc will
135
+ invoke an authorization flow in the browser to confirm access to document. See README.md#Configuration for details.
136
+ """
137
+ log.debug(f"downloading {doc_url}")
138
+ page = GoogleDrive(*app_config.config_dir()).get_doc_url(doc_url)
120
139
 
140
+ if name is not None:
141
+ page.path = name
121
142
 
122
- # @app.command()
123
- # def convert(
124
- # source: str,
125
- # destination: Annotated[str, typer.Argument()] = "."
126
- # ) -> None:
127
- # DocxitConverter().convert_directory(source, destination)
143
+ if path is not None:
144
+ page.update_path(path)
145
+
146
+ log.debug(f"uploading to {db_url}/{page.path}")
147
+ db = GraphDB(db_url, token)
148
+ db.update(page)
149
+
150
+ raise typer.Exit()
151
+
152
+
153
+ @app.command()
154
+ def config(
155
+ name: Annotated[str, typer.Argument(help="Display or set the name of a config value")] = None,
156
+ value: Annotated[str, typer.Argument(help="If provided, set the supplied name to this value")] = None,
157
+ ) -> None:
158
+ """
159
+ View or set configuration settings.
160
+
161
+ Example:
162
+
163
+ uvx wikinator config db_url https://mydb.example.com/graphql
164
+
165
+ uvx wikinator config db_token <authorization-token-for-graphdb>
166
+
167
+ uvx wikinator config
168
+ """
169
+ if name and value:
170
+ app_config.set(name, value)
171
+ app_config.write()
172
+ log.info(f"Value set: {name} = {value}")
173
+ raise typer.Exit()
174
+
175
+ if name:
176
+ print(app_config.value(name))
177
+ raise typer.Exit()
178
+
179
+ for k in app_config.keys():
180
+ print(f"{k}: {app_config.value(k)}")
128
181
 
129
182
 
130
183
  #- extract : from googledocs -> file system
@@ -151,4 +204,4 @@ def common(
151
204
  # ) -> None:
152
205
  # wiki = GraphDB()
153
206
  # for page in GoogleDrive().list_files("application/vnd.google-apps.document"):
154
- # wiki.upload(page, wikiroot)
207
+ # wiki.upload(page, wikiroot)
@@ -0,0 +1,71 @@
1
+ import importlib
2
+
3
+ import confuse
4
+ import os
5
+
6
+
7
+ __app_name__ = "wikinator"
8
+ __app_version__ = importlib.metadata.version(__app_name__)
9
+
10
+ __config__ = confuse.Configuration(__app_name__, __name__)
11
+
12
+
13
+ class AppConfig:
14
+ def __init__(self):
15
+ self._config = self._load()
16
+
17
+
18
+ def config_dir(self):
19
+ from .gdrive import config_link
20
+ return __config__.config_dir(), config_link
21
+
22
+
23
+ def _load(self):
24
+ cdir, _ = self.config_dir()
25
+ template = {
26
+ "db_url": f"Configure db_url in ${cdir}",
27
+ "db_token": f"Configure db_token in ${cdir}",
28
+ "log_level": "warning",
29
+ }
30
+ __config__.set_env()
31
+ config = __config__.get(template)
32
+ config['app_info'] = f"{__app_name__} v{__app_version__}"
33
+ config['config_dir'] = __config__.config_dir()
34
+ config['config_file'] = os.path.join(__config__.config_dir(), confuse.CONFIG_FILENAME)
35
+
36
+ # config["key"].redact = True
37
+ #config.dump(redact=True)
38
+ return config
39
+
40
+
41
+ def keys(self):
42
+ return self._config.keys()
43
+
44
+
45
+ def get(self, name:str, default:str = "") -> str:
46
+ return self._config.get(name, default)
47
+
48
+
49
+ def set(self, name:str, value) -> None:
50
+ self._config[name] = value
51
+
52
+
53
+ def value(self, name:str, default:str = "") -> str:
54
+ val = self._config.get(name, default)
55
+ if "secret" in name or "token" in name:
56
+ return f"{val[:6]}...{val[-6:]}"
57
+ else:
58
+ return val
59
+
60
+
61
+ _SKIP_NAMES = ["app_info", "config_dir", "config_file"]
62
+ def write(self):
63
+ # FIXME copy and remove values: app_info: config_dir: config_file:
64
+
65
+ config_filename = os.path.join(__config__.config_dir(), confuse.CONFIG_FILENAME)
66
+ with open(config_filename, "w") as f:
67
+ #yaml.safe_dump(self._config, f) #, default_flow_style=False)
68
+ #f.write(self._config.dump())
69
+ for k, v in self._config.items():
70
+ if k not in self._SKIP_NAMES:
71
+ f.write(f"{k}: {v}\n")
@@ -0,0 +1,5 @@
1
+
2
+ db_url: http://example.com/graphdb
3
+ db_token: FIXME-WITH-A-REAL-TOKEN
4
+
5
+ log_level: warning