scitex 2.17.0__py3-none-any.whl → 2.17.4__py3-none-any.whl
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.
- scitex/_dev/__init__.py +122 -0
- scitex/_dev/_config.py +391 -0
- scitex/_dev/_dashboard/__init__.py +11 -0
- scitex/_dev/_dashboard/_app.py +89 -0
- scitex/_dev/_dashboard/_routes.py +182 -0
- scitex/_dev/_dashboard/_scripts.py +422 -0
- scitex/_dev/_dashboard/_styles.py +295 -0
- scitex/_dev/_dashboard/_templates.py +130 -0
- scitex/_dev/_dashboard/static/version-dashboard-favicon.svg +12 -0
- scitex/_dev/_ecosystem.py +109 -0
- scitex/_dev/_github.py +360 -0
- scitex/_dev/_mcp/__init__.py +11 -0
- scitex/_dev/_mcp/handlers.py +182 -0
- scitex/_dev/_rtd.py +122 -0
- scitex/_dev/_ssh.py +362 -0
- scitex/_dev/_versions.py +272 -0
- scitex/_mcp_tools/__init__.py +2 -0
- scitex/_mcp_tools/dev.py +186 -0
- scitex/audio/_audio_check.py +84 -41
- scitex/cli/capture.py +45 -22
- scitex/cli/dev.py +494 -0
- scitex/cli/main.py +2 -0
- scitex/cli/stats.py +48 -20
- scitex/cli/verify.py +33 -36
- scitex/plt/__init__.py +16 -6
- scitex/scholar/_mcp/crossref_handlers.py +45 -7
- scitex/scholar/_mcp/openalex_handlers.py +45 -7
- scitex/scholar/config/default.yaml +2 -0
- scitex/scholar/local_dbs/__init__.py +5 -1
- scitex/scholar/local_dbs/export.py +93 -0
- scitex/scholar/local_dbs/unified.py +505 -0
- scitex/scholar/metadata_engines/ScholarEngine.py +11 -0
- scitex/scholar/metadata_engines/individual/OpenAlexLocalEngine.py +346 -0
- scitex/scholar/metadata_engines/individual/__init__.py +1 -0
- scitex/template/__init__.py +18 -1
- scitex/template/clone_research_minimal.py +111 -0
- scitex/verify/README.md +0 -12
- scitex/verify/__init__.py +0 -4
- scitex/verify/_visualize.py +0 -4
- scitex/verify/_viz/__init__.py +0 -18
- {scitex-2.17.0.dist-info → scitex-2.17.4.dist-info}/METADATA +2 -1
- {scitex-2.17.0.dist-info → scitex-2.17.4.dist-info}/RECORD +45 -24
- scitex/verify/_viz/_plotly.py +0 -193
- {scitex-2.17.0.dist-info → scitex-2.17.4.dist-info}/WHEEL +0 -0
- {scitex-2.17.0.dist-info → scitex-2.17.4.dist-info}/entry_points.txt +0 -0
- {scitex-2.17.0.dist-info → scitex-2.17.4.dist-info}/licenses/LICENSE +0 -0
scitex/_dev/__init__.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2026-02-02
|
|
3
|
+
# File: scitex/_dev/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
SciTeX Developer Utilities (Internal Module).
|
|
7
|
+
|
|
8
|
+
This module provides internal developer tools for managing the scitex ecosystem.
|
|
9
|
+
|
|
10
|
+
Functions
|
|
11
|
+
---------
|
|
12
|
+
list_versions : List versions across ecosystem packages
|
|
13
|
+
check_versions : Check version consistency
|
|
14
|
+
load_config : Load developer configuration
|
|
15
|
+
check_all_hosts : Check versions on SSH hosts
|
|
16
|
+
check_all_remotes : Check versions on GitHub remotes
|
|
17
|
+
run_dashboard : Run the Flask version dashboard
|
|
18
|
+
|
|
19
|
+
Examples
|
|
20
|
+
--------
|
|
21
|
+
>>> from scitex._dev import list_versions
|
|
22
|
+
>>> versions = list_versions()
|
|
23
|
+
>>> versions["scitex"]["local"]["pyproject_toml"]
|
|
24
|
+
'2.17.1'
|
|
25
|
+
|
|
26
|
+
>>> from scitex._dev import check_versions
|
|
27
|
+
>>> result = check_versions(["scitex", "figrecipe"])
|
|
28
|
+
>>> result["summary"]["ok"]
|
|
29
|
+
2
|
|
30
|
+
|
|
31
|
+
>>> from scitex._dev import load_config
|
|
32
|
+
>>> config = load_config()
|
|
33
|
+
>>> len(config.packages)
|
|
34
|
+
8
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from ._config import (
|
|
38
|
+
DevConfig,
|
|
39
|
+
GitHubRemote,
|
|
40
|
+
HostConfig,
|
|
41
|
+
PackageConfig,
|
|
42
|
+
PyPIAccount,
|
|
43
|
+
create_default_config,
|
|
44
|
+
get_config_path,
|
|
45
|
+
get_enabled_hosts,
|
|
46
|
+
get_enabled_remotes,
|
|
47
|
+
load_config,
|
|
48
|
+
)
|
|
49
|
+
from ._ecosystem import ECOSYSTEM, get_all_packages, get_local_path
|
|
50
|
+
from ._github import (
|
|
51
|
+
check_all_remotes,
|
|
52
|
+
compare_with_local,
|
|
53
|
+
get_github_latest_tag,
|
|
54
|
+
get_github_release,
|
|
55
|
+
get_github_tags,
|
|
56
|
+
)
|
|
57
|
+
from ._ssh import (
|
|
58
|
+
check_all_hosts,
|
|
59
|
+
get_remote_version,
|
|
60
|
+
get_remote_versions,
|
|
61
|
+
test_host_connection,
|
|
62
|
+
)
|
|
63
|
+
from ._versions import check_versions, list_versions
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
# Versions
|
|
67
|
+
"list_versions",
|
|
68
|
+
"check_versions",
|
|
69
|
+
# Ecosystem
|
|
70
|
+
"ECOSYSTEM",
|
|
71
|
+
"get_all_packages",
|
|
72
|
+
"get_local_path",
|
|
73
|
+
# Config
|
|
74
|
+
"load_config",
|
|
75
|
+
"get_config_path",
|
|
76
|
+
"create_default_config",
|
|
77
|
+
"get_enabled_hosts",
|
|
78
|
+
"get_enabled_remotes",
|
|
79
|
+
"DevConfig",
|
|
80
|
+
"HostConfig",
|
|
81
|
+
"GitHubRemote",
|
|
82
|
+
"PackageConfig",
|
|
83
|
+
"PyPIAccount",
|
|
84
|
+
# SSH
|
|
85
|
+
"check_all_hosts",
|
|
86
|
+
"get_remote_version",
|
|
87
|
+
"get_remote_versions",
|
|
88
|
+
"test_host_connection",
|
|
89
|
+
# GitHub
|
|
90
|
+
"check_all_remotes",
|
|
91
|
+
"compare_with_local",
|
|
92
|
+
"get_github_tags",
|
|
93
|
+
"get_github_latest_tag",
|
|
94
|
+
"get_github_release",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def run_dashboard(
|
|
99
|
+
host: str = "127.0.0.1",
|
|
100
|
+
port: int = 5000,
|
|
101
|
+
debug: bool = False,
|
|
102
|
+
open_browser: bool = True,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Run the Flask version dashboard.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
host : str
|
|
109
|
+
Host to bind to.
|
|
110
|
+
port : int
|
|
111
|
+
Port to listen on.
|
|
112
|
+
debug : bool
|
|
113
|
+
Enable debug mode.
|
|
114
|
+
open_browser : bool
|
|
115
|
+
Open browser automatically.
|
|
116
|
+
"""
|
|
117
|
+
from ._dashboard import run_dashboard as _run
|
|
118
|
+
|
|
119
|
+
_run(host=host, port=port, debug=debug, open_browser=open_browser)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# EOF
|
scitex/_dev/_config.py
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2026-02-02
|
|
3
|
+
# File: scitex/_dev/_config.py
|
|
4
|
+
|
|
5
|
+
"""Configuration management for scitex developer utilities."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class HostConfig:
|
|
17
|
+
"""SSH host configuration."""
|
|
18
|
+
|
|
19
|
+
name: str
|
|
20
|
+
hostname: str
|
|
21
|
+
user: str
|
|
22
|
+
role: str = "dev" # dev, staging, prod
|
|
23
|
+
enabled: bool = True
|
|
24
|
+
ssh_key: str | None = None
|
|
25
|
+
port: int = 22
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class GitHubRemote:
|
|
30
|
+
"""GitHub remote configuration."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
org: str
|
|
34
|
+
enabled: bool = True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class PyPIAccount:
|
|
39
|
+
"""PyPI account configuration."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
enabled: bool = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class PackageConfig:
|
|
47
|
+
"""Package configuration."""
|
|
48
|
+
|
|
49
|
+
name: str
|
|
50
|
+
local_path: str
|
|
51
|
+
pypi_name: str
|
|
52
|
+
github_repo: str | None = None
|
|
53
|
+
import_name: str | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class DevConfig:
|
|
58
|
+
"""Full developer configuration."""
|
|
59
|
+
|
|
60
|
+
packages: list[PackageConfig] = field(default_factory=list)
|
|
61
|
+
hosts: list[HostConfig] = field(default_factory=list)
|
|
62
|
+
github_remotes: list[GitHubRemote] = field(default_factory=list)
|
|
63
|
+
pypi_accounts: list[PyPIAccount] = field(default_factory=list)
|
|
64
|
+
branches: list[str] = field(default_factory=lambda: ["main", "develop"])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _get_default_config_path() -> Path:
|
|
68
|
+
"""Get default config file path."""
|
|
69
|
+
return Path.home() / ".scitex" / "dev_config.yaml"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _load_yaml(path: Path) -> dict[str, Any]:
|
|
73
|
+
"""Load YAML file."""
|
|
74
|
+
if not path.exists():
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
import yaml
|
|
79
|
+
|
|
80
|
+
with open(path) as f:
|
|
81
|
+
return yaml.safe_load(f) or {}
|
|
82
|
+
except ImportError:
|
|
83
|
+
# Fallback: basic YAML parsing for simple configs
|
|
84
|
+
content = path.read_text()
|
|
85
|
+
# Very basic parsing - handles simple key: value pairs
|
|
86
|
+
result: dict[str, Any] = {}
|
|
87
|
+
current_key = None
|
|
88
|
+
current_list: list[Any] = []
|
|
89
|
+
|
|
90
|
+
for line in content.split("\n"):
|
|
91
|
+
line = line.rstrip()
|
|
92
|
+
if not line or line.startswith("#"):
|
|
93
|
+
continue
|
|
94
|
+
if line.startswith(" - "):
|
|
95
|
+
# List item
|
|
96
|
+
current_list.append(line[4:].strip())
|
|
97
|
+
elif line.startswith(" "):
|
|
98
|
+
# Nested dict item - skip for basic parsing
|
|
99
|
+
continue
|
|
100
|
+
elif ":" in line:
|
|
101
|
+
if current_key and current_list:
|
|
102
|
+
result[current_key] = current_list
|
|
103
|
+
current_list = []
|
|
104
|
+
key, val = line.split(":", 1)
|
|
105
|
+
current_key = key.strip()
|
|
106
|
+
val = val.strip()
|
|
107
|
+
if val:
|
|
108
|
+
result[current_key] = val
|
|
109
|
+
if current_key and current_list:
|
|
110
|
+
result[current_key] = current_list
|
|
111
|
+
return result
|
|
112
|
+
except Exception:
|
|
113
|
+
return {}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _parse_host_config(data: dict[str, Any]) -> HostConfig:
|
|
117
|
+
"""Parse host config from dict."""
|
|
118
|
+
return HostConfig(
|
|
119
|
+
name=data.get("name", "unknown"),
|
|
120
|
+
hostname=data.get("hostname", "localhost"),
|
|
121
|
+
user=data.get("user", os.getenv("USER", "user")),
|
|
122
|
+
role=data.get("role", "dev"),
|
|
123
|
+
enabled=data.get("enabled", True),
|
|
124
|
+
ssh_key=data.get("ssh_key"),
|
|
125
|
+
port=data.get("port", 22),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _parse_github_remote(data: dict[str, Any]) -> GitHubRemote:
|
|
130
|
+
"""Parse GitHub remote from dict."""
|
|
131
|
+
return GitHubRemote(
|
|
132
|
+
name=data.get("name", "unknown"),
|
|
133
|
+
org=data.get("org", ""),
|
|
134
|
+
enabled=data.get("enabled", True),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _parse_pypi_account(data: dict[str, Any]) -> PyPIAccount:
|
|
139
|
+
"""Parse PyPI account from dict."""
|
|
140
|
+
return PyPIAccount(
|
|
141
|
+
name=data.get("name", ""),
|
|
142
|
+
enabled=data.get("enabled", True),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _parse_package_config(data: dict[str, Any]) -> PackageConfig:
|
|
147
|
+
"""Parse package config from dict."""
|
|
148
|
+
return PackageConfig(
|
|
149
|
+
name=data.get("name", "unknown"),
|
|
150
|
+
local_path=data.get("local_path", ""),
|
|
151
|
+
pypi_name=data.get("pypi_name", data.get("name", "")),
|
|
152
|
+
github_repo=data.get("github_repo"),
|
|
153
|
+
import_name=data.get("import_name"),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def load_config(config_path: str | Path | None = None) -> DevConfig:
|
|
158
|
+
"""Load config from YAML with environment variable overrides.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
config_path : str | Path | None
|
|
163
|
+
Path to config file. If None, uses SCITEX_DEV_CONFIG env var
|
|
164
|
+
or ~/.scitex/dev_config.yaml
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
DevConfig
|
|
169
|
+
Loaded configuration.
|
|
170
|
+
"""
|
|
171
|
+
# Determine config path
|
|
172
|
+
if config_path is None:
|
|
173
|
+
config_path = os.getenv("SCITEX_DEV_CONFIG")
|
|
174
|
+
if config_path is None:
|
|
175
|
+
config_path = _get_default_config_path()
|
|
176
|
+
else:
|
|
177
|
+
config_path = Path(config_path).expanduser()
|
|
178
|
+
|
|
179
|
+
# Load YAML
|
|
180
|
+
data = _load_yaml(config_path)
|
|
181
|
+
|
|
182
|
+
# Parse packages
|
|
183
|
+
packages = []
|
|
184
|
+
if "packages" in data and isinstance(data["packages"], list):
|
|
185
|
+
for pkg_data in data["packages"]:
|
|
186
|
+
if isinstance(pkg_data, dict):
|
|
187
|
+
packages.append(_parse_package_config(pkg_data))
|
|
188
|
+
|
|
189
|
+
# If no packages in config, use ecosystem defaults
|
|
190
|
+
if not packages:
|
|
191
|
+
from ._ecosystem import ECOSYSTEM
|
|
192
|
+
|
|
193
|
+
for name, info in ECOSYSTEM.items():
|
|
194
|
+
packages.append(
|
|
195
|
+
PackageConfig(
|
|
196
|
+
name=name,
|
|
197
|
+
local_path=info.get("local_path", ""),
|
|
198
|
+
pypi_name=info.get("pypi_name", name),
|
|
199
|
+
github_repo=info.get("github_repo"),
|
|
200
|
+
import_name=info.get("import_name"),
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Parse hosts
|
|
205
|
+
hosts = []
|
|
206
|
+
if "hosts" in data and isinstance(data["hosts"], list):
|
|
207
|
+
for host_data in data["hosts"]:
|
|
208
|
+
if isinstance(host_data, dict):
|
|
209
|
+
hosts.append(_parse_host_config(host_data))
|
|
210
|
+
|
|
211
|
+
# Override from env
|
|
212
|
+
env_hosts = os.getenv("SCITEX_DEV_HOSTS", "").strip()
|
|
213
|
+
if env_hosts:
|
|
214
|
+
enabled_names = set(env_hosts.split(","))
|
|
215
|
+
for host in hosts:
|
|
216
|
+
host.enabled = host.name in enabled_names
|
|
217
|
+
|
|
218
|
+
# Parse GitHub remotes
|
|
219
|
+
github_remotes = []
|
|
220
|
+
if "github_remotes" in data and isinstance(data["github_remotes"], list):
|
|
221
|
+
for remote_data in data["github_remotes"]:
|
|
222
|
+
if isinstance(remote_data, dict):
|
|
223
|
+
github_remotes.append(_parse_github_remote(remote_data))
|
|
224
|
+
|
|
225
|
+
# Default GitHub remote from ecosystem
|
|
226
|
+
if not github_remotes:
|
|
227
|
+
github_remotes.append(GitHubRemote(name="ywatanabe1989", org="ywatanabe1989"))
|
|
228
|
+
|
|
229
|
+
# Override from env
|
|
230
|
+
env_remotes = os.getenv("SCITEX_DEV_GITHUB_REMOTES", "").strip()
|
|
231
|
+
if env_remotes:
|
|
232
|
+
enabled_names = set(env_remotes.split(","))
|
|
233
|
+
for remote in github_remotes:
|
|
234
|
+
remote.enabled = remote.name in enabled_names
|
|
235
|
+
|
|
236
|
+
# Parse PyPI accounts
|
|
237
|
+
pypi_accounts = []
|
|
238
|
+
if "pypi_accounts" in data and isinstance(data["pypi_accounts"], list):
|
|
239
|
+
for acct_data in data["pypi_accounts"]:
|
|
240
|
+
if isinstance(acct_data, dict):
|
|
241
|
+
pypi_accounts.append(_parse_pypi_account(acct_data))
|
|
242
|
+
|
|
243
|
+
if not pypi_accounts:
|
|
244
|
+
pypi_accounts.append(PyPIAccount(name="ywatanabe1989"))
|
|
245
|
+
|
|
246
|
+
# Parse branches
|
|
247
|
+
branches = data.get("branches", ["main", "develop"])
|
|
248
|
+
if not isinstance(branches, list):
|
|
249
|
+
branches = ["main", "develop"]
|
|
250
|
+
|
|
251
|
+
return DevConfig(
|
|
252
|
+
packages=packages,
|
|
253
|
+
hosts=hosts,
|
|
254
|
+
github_remotes=github_remotes,
|
|
255
|
+
pypi_accounts=pypi_accounts,
|
|
256
|
+
branches=branches,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def get_enabled_hosts(config: DevConfig | None = None) -> list[HostConfig]:
|
|
261
|
+
"""Get list of enabled hosts.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
config : DevConfig | None
|
|
266
|
+
Configuration to use. If None, loads default config.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
list[HostConfig]
|
|
271
|
+
List of enabled hosts.
|
|
272
|
+
"""
|
|
273
|
+
if config is None:
|
|
274
|
+
config = load_config()
|
|
275
|
+
return [h for h in config.hosts if h.enabled]
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def get_enabled_remotes(config: DevConfig | None = None) -> list[GitHubRemote]:
|
|
279
|
+
"""Get list of enabled GitHub remotes.
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
config : DevConfig | None
|
|
284
|
+
Configuration to use. If None, loads default config.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
list[GitHubRemote]
|
|
289
|
+
List of enabled remotes.
|
|
290
|
+
"""
|
|
291
|
+
if config is None:
|
|
292
|
+
config = load_config()
|
|
293
|
+
return [r for r in config.github_remotes if r.enabled]
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def get_config_path() -> Path:
|
|
297
|
+
"""Get the config file path (may not exist)."""
|
|
298
|
+
path = os.getenv("SCITEX_DEV_CONFIG")
|
|
299
|
+
if path:
|
|
300
|
+
return Path(path).expanduser()
|
|
301
|
+
return _get_default_config_path()
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def create_default_config() -> Path:
|
|
305
|
+
"""Create default config file if it doesn't exist.
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
Path
|
|
310
|
+
Path to the config file.
|
|
311
|
+
"""
|
|
312
|
+
config_path = _get_default_config_path()
|
|
313
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
314
|
+
|
|
315
|
+
if config_path.exists():
|
|
316
|
+
return config_path
|
|
317
|
+
|
|
318
|
+
default_config = """\
|
|
319
|
+
# SciTeX Developer Configuration
|
|
320
|
+
# Timestamp: 2026-02-02
|
|
321
|
+
|
|
322
|
+
# Ecosystem packages to track
|
|
323
|
+
packages:
|
|
324
|
+
- name: scitex
|
|
325
|
+
local_path: ~/proj/scitex-python
|
|
326
|
+
pypi_name: scitex
|
|
327
|
+
github_repo: ywatanabe1989/scitex-python
|
|
328
|
+
import_name: scitex
|
|
329
|
+
- name: figrecipe
|
|
330
|
+
local_path: ~/proj/figrecipe
|
|
331
|
+
pypi_name: figrecipe
|
|
332
|
+
github_repo: ywatanabe1989/figrecipe
|
|
333
|
+
import_name: figrecipe
|
|
334
|
+
- name: scitex-cloud
|
|
335
|
+
local_path: ~/proj/scitex-cloud
|
|
336
|
+
pypi_name: scitex-cloud
|
|
337
|
+
github_repo: ywatanabe1989/scitex-cloud
|
|
338
|
+
import_name: scitex_cloud
|
|
339
|
+
- name: scitex-writer
|
|
340
|
+
local_path: ~/proj/scitex-writer
|
|
341
|
+
pypi_name: scitex-writer
|
|
342
|
+
github_repo: ywatanabe1989/scitex-writer
|
|
343
|
+
import_name: scitex_writer
|
|
344
|
+
- name: crossref-local
|
|
345
|
+
local_path: ~/proj/crossref-local
|
|
346
|
+
pypi_name: crossref-local
|
|
347
|
+
github_repo: ywatanabe1989/crossref-local
|
|
348
|
+
import_name: crossref_local
|
|
349
|
+
|
|
350
|
+
# Hosts to check via SSH
|
|
351
|
+
hosts:
|
|
352
|
+
- name: ywata-note-win
|
|
353
|
+
hostname: localhost
|
|
354
|
+
user: ywatanabe
|
|
355
|
+
role: dev
|
|
356
|
+
enabled: true
|
|
357
|
+
- name: nas
|
|
358
|
+
hostname: nas.local
|
|
359
|
+
user: ywatanabe
|
|
360
|
+
role: staging
|
|
361
|
+
enabled: true
|
|
362
|
+
- name: scitex-cloud
|
|
363
|
+
hostname: scitex.ai
|
|
364
|
+
user: deploy
|
|
365
|
+
role: prod
|
|
366
|
+
enabled: false
|
|
367
|
+
|
|
368
|
+
# GitHub remotes to check
|
|
369
|
+
github_remotes:
|
|
370
|
+
- name: ywatanabe1989
|
|
371
|
+
org: ywatanabe1989
|
|
372
|
+
enabled: true
|
|
373
|
+
- name: scitex-ai
|
|
374
|
+
org: scitex-ai
|
|
375
|
+
enabled: false
|
|
376
|
+
|
|
377
|
+
# PyPI accounts
|
|
378
|
+
pypi_accounts:
|
|
379
|
+
- name: ywatanabe1989
|
|
380
|
+
enabled: true
|
|
381
|
+
|
|
382
|
+
# Branches to track
|
|
383
|
+
branches:
|
|
384
|
+
- main
|
|
385
|
+
- develop
|
|
386
|
+
"""
|
|
387
|
+
config_path.write_text(default_config)
|
|
388
|
+
return config_path
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
# EOF
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2026-02-02
|
|
3
|
+
# File: scitex/_dev/_dashboard/_app.py
|
|
4
|
+
|
|
5
|
+
"""Flask application factory for the dashboard."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from flask import Flask
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_app() -> Flask:
|
|
16
|
+
"""Create and configure the Flask application.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
Flask
|
|
21
|
+
Configured Flask application.
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
from flask import Flask
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise ImportError(
|
|
27
|
+
"Flask is required for the dashboard. Install with: pip install flask"
|
|
28
|
+
) from e
|
|
29
|
+
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
static_folder = Path(__file__).parent / "static"
|
|
33
|
+
app = Flask(__name__, static_folder=str(static_folder), static_url_path="/static")
|
|
34
|
+
|
|
35
|
+
# Disable JSON key sorting to preserve insertion order (Flask 2.2+)
|
|
36
|
+
app.json.sort_keys = False
|
|
37
|
+
|
|
38
|
+
# Register routes
|
|
39
|
+
from ._routes import register_routes
|
|
40
|
+
|
|
41
|
+
register_routes(app)
|
|
42
|
+
|
|
43
|
+
return app
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run_dashboard(
|
|
47
|
+
host: str = "127.0.0.1",
|
|
48
|
+
port: int = 5000,
|
|
49
|
+
debug: bool = False,
|
|
50
|
+
open_browser: bool = True,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Run the Flask dashboard server.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
host : str
|
|
57
|
+
Host to bind to. Default "127.0.0.1".
|
|
58
|
+
port : int
|
|
59
|
+
Port to listen on. Default 5000.
|
|
60
|
+
debug : bool
|
|
61
|
+
Enable Flask debug mode.
|
|
62
|
+
open_browser : bool
|
|
63
|
+
Open browser automatically.
|
|
64
|
+
"""
|
|
65
|
+
app = create_app()
|
|
66
|
+
|
|
67
|
+
url = f"http://{host}:{port}"
|
|
68
|
+
print(f"Starting SciTeX Version Dashboard at {url}")
|
|
69
|
+
print("Press Ctrl+C to stop.")
|
|
70
|
+
|
|
71
|
+
if open_browser:
|
|
72
|
+
import threading
|
|
73
|
+
import webbrowser
|
|
74
|
+
|
|
75
|
+
def open_url():
|
|
76
|
+
import time
|
|
77
|
+
|
|
78
|
+
time.sleep(1) # Wait for server to start
|
|
79
|
+
webbrowser.open(url)
|
|
80
|
+
|
|
81
|
+
threading.Thread(target=open_url, daemon=True).start()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
app.run(host=host, port=port, debug=debug, threaded=True)
|
|
85
|
+
except KeyboardInterrupt:
|
|
86
|
+
print("\nDashboard stopped.")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# EOF
|