reheat 0.0.1__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.
@@ -0,0 +1 @@
1
+ __pycache__/
reheat-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,263 @@
1
+ Metadata-Version: 2.4
2
+ Name: reheat
3
+ Version: 0.0.1
4
+ Summary: Python CLI for SEO analysis. Cluster search intent, surface content gaps.
5
+ Home-page: https://www.bayis.co.uk/reheat
6
+ Author: Edward Grundy
7
+ Author-email: ed@bayis.co.uk
8
+ License: MIT
9
+ Project-URL: Source, https://github.com/bayinfosys/reheat
10
+ Project-URL: PyPI, https://pypi.org/project/reheat
11
+ Project-URL: Company, https://bayis.co.uk
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Internet :: WWW/HTTP
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Classifier: Environment :: Console
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Operating System :: OS Independent
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: dynawrap
26
+ Requires-Dist: requests
27
+ Requires-Dist: fastembed
28
+ Requires-Dist: numpy
29
+ Requires-Dist: scipy
30
+ Requires-Dist: scikit-learn
31
+ Requires-Dist: umap-learn
32
+ Requires-Dist: google-auth
33
+ Requires-Dist: google-auth-httplib2
34
+ Requires-Dist: google-api-python-client
35
+ Requires-Dist: google-auth-oauthlib
36
+ Requires-Dist: tqdm
37
+ Requires-Dist: fastapi
38
+ Requires-Dist: uvicorn
39
+ Requires-Dist: python-dotenv
40
+ Provides-Extra: local
41
+ Requires-Dist: sentence-transformers; extra == "local"
42
+ Requires-Dist: torch; extra == "local"
43
+ Provides-Extra: openai
44
+ Requires-Dist: openai>=1.0; extra == "openai"
45
+ Provides-Extra: anthropic
46
+ Requires-Dist: anthropic>=0.20; extra == "anthropic"
47
+ Provides-Extra: postgres
48
+ Requires-Dist: psycopg2-binary; extra == "postgres"
49
+ Provides-Extra: dev
50
+ Requires-Dist: build; extra == "dev"
51
+ Requires-Dist: twine; extra == "dev"
52
+ Requires-Dist: setuptools-scm; extra == "dev"
53
+ Requires-Dist: black; extra == "dev"
54
+ Requires-Dist: flake8; extra == "dev"
55
+ Requires-Dist: isort; extra == "dev"
56
+ Provides-Extra: all
57
+ Requires-Dist: sentence-transformers; extra == "all"
58
+ Requires-Dist: torch; extra == "all"
59
+ Requires-Dist: openai>=1.0; extra == "all"
60
+ Requires-Dist: anthropic>=0.20; extra == "all"
61
+ Requires-Dist: psycopg2-binary; extra == "all"
62
+ Dynamic: author
63
+ Dynamic: author-email
64
+ Dynamic: classifier
65
+ Dynamic: description
66
+ Dynamic: description-content-type
67
+ Dynamic: home-page
68
+ Dynamic: license
69
+ Dynamic: project-url
70
+ Dynamic: provides-extra
71
+ Dynamic: requires-dist
72
+ Dynamic: requires-python
73
+ Dynamic: summary
74
+
75
+ # Reheat
76
+
77
+ Python CLI for SEO analysis. Pulls search queries from Google Search Console,
78
+ enriches them with related searches and People Also Ask data, clusters by
79
+ semantic intent, and surfaces content gaps and opportunities.
80
+
81
+ Built by [Edward Grundy](https://bayis.co.uk) at
82
+ [Bay Information Systems](https://bayis.co.uk).
83
+
84
+ - Install: `pip install reheat`
85
+ - Docs: [bayinfosys.github.io/reheat](https://bayinfosys.github.io/reheat)
86
+ - PyPI: [pypi.org/project/reheat](https://pypi.org/project/reheat)
87
+
88
+ ---
89
+
90
+ ## Getting started
91
+
92
+ ### Install
93
+
94
+ ```bash
95
+ pip install reheat
96
+ ```
97
+
98
+ Requires Python 3.10+. For the local web interface you also need a running
99
+ postgres instance (or use the JSON file backend for local development
100
+ without postgres).
101
+
102
+ ```bash
103
+ docker run -d \
104
+ --name reheat-pg \
105
+ -e POSTGRES_USER=reheat \
106
+ -e POSTGRES_PASSWORD=reheat \
107
+ -e POSTGRES_DB=reheat \
108
+ -p 5432:5432 \
109
+ postgres:16
110
+
111
+ export DATABASE_URL=postgresql://reheat:reheat@localhost:5432/reheat
112
+ ```
113
+
114
+ ### Configure a Google Search Console source
115
+
116
+ Download OAuth2 credentials from Google Cloud Console (APIs and Services >
117
+ Credentials > OAuth 2.0 Client IDs, Desktop app type) and save as
118
+ `~/.reheat/google-search-console.json`. Then:
119
+
120
+ ```bash
121
+ reheat sources create \
122
+ --source-type google_search_console \
123
+ --domain yourdomain.com \
124
+ --client-secrets-path ~/.reheat/google-search-console.json
125
+ ```
126
+
127
+ ### Authenticate
128
+
129
+ ```bash
130
+ reheat sources auth
131
+ ```
132
+
133
+ Opens a browser for the Google OAuth2 consent flow. The token is persisted
134
+ to `~/.reheat/gsc_token.json` and refreshed automatically on subsequent runs.
135
+
136
+ ### Configure a SerpAPI source (optional)
137
+
138
+ Required for PAA and related search enrichment. Get an API key at
139
+ [serpapi.com](https://serpapi.com).
140
+
141
+ ```bash
142
+ reheat sources create \
143
+ --source-type serp \
144
+ --api-key YOUR_SERP_API_KEY
145
+ ```
146
+
147
+ ### Run the pipeline
148
+
149
+ ```bash
150
+ # Fetch queries from Search Console
151
+ reheat runs create
152
+
153
+ # Enrich and process
154
+ reheat enrich tags
155
+ reheat enrich embed
156
+ reheat enrich cluster
157
+ reheat enrich gap
158
+ reheat analyse opportunities
159
+
160
+ # Build report data
161
+ reheat project create
162
+ reheat report scatter create
163
+ reheat report summary create
164
+ reheat report coverage create
165
+
166
+ # Start the web interface
167
+ reheat serve
168
+ ```
169
+
170
+ Open [http://localhost:8000](http://localhost:8000).
171
+
172
+ ---
173
+
174
+ ## Inference providers
175
+
176
+ `reheat analyse summarise` labels intent clusters using an LLM. Configure
177
+ one of the following.
178
+
179
+ ### OpenAI
180
+
181
+ Set `OPENAI_API_KEY` in your environment or `.env` file, or:
182
+
183
+ ```bash
184
+ reheat config set --key openai_api_key --value sk-...
185
+ ```
186
+
187
+ ### Anthropic
188
+
189
+ Set `ANTHROPIC_API_KEY` in your environment or `.env` file, or:
190
+
191
+ ```bash
192
+ reheat config set --key anthropic_api_key --value sk-ant-...
193
+ ```
194
+
195
+ ### Marigold
196
+
197
+ [Marigold](https://marigold.run) is a private inference API built by
198
+ Bay Information Systems. Set endpoint and key when available:
199
+
200
+ ```bash
201
+ reheat config set --key marigold_endpoint --value https://api.marigold.run
202
+ reheat config set --key marigold_api_key --value <key>
203
+ ```
204
+
205
+ ---
206
+
207
+ ## CLI reference
208
+
209
+ ```
210
+ reheat config show / set
211
+ reheat sources create / list / show / update / delete / auth
212
+ reheat runs create / list / show / delete
213
+ reheat enrichments list / show / delete
214
+ reheat enrich tags / embed / cluster / gap
215
+ reheat analyse summarise / opportunities
216
+ reheat project create / read
217
+ reheat report scatter / summary / coverage create / read
218
+ reheat serve
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Architecture
224
+
225
+ reheat has three layers.
226
+
227
+ **Commands** in `reheat/commands/` are the single source of truth for the
228
+ application surface. Each command is a Python function decorated with
229
+ `@command`, registered in a central registry, and exposed automatically
230
+ through both the CLI and the HTTP API.
231
+
232
+ **Pipeline** functions in `reheat/pipeline/` are pure data transforms:
233
+ embedding, clustering, gap analysis, report building. No persistence, no
234
+ side effects.
235
+
236
+ **Persistence** uses [dynawrap](https://github.com/bayinfosys/dynawrap),
237
+ a lightweight key-value library with identical interfaces over PostgreSQL
238
+ and DynamoDB. Tables are passed at call time; models are backend-agnostic.
239
+
240
+ The web interface is a static SPA served by FastAPI. All pages share a
241
+ single stylesheet and a common `api.js` module that is the single source
242
+ of truth for API endpoint calls.
243
+
244
+ ---
245
+
246
+ ## Requirements
247
+
248
+ - Python 3.10+
249
+ - PostgreSQL (or JSON file backend for local development)
250
+ - Google Search Console OAuth2 credentials
251
+ - SerpAPI key (optional, for PAA enrichment)
252
+ - OpenAI, Anthropic, or Marigold key (optional, for cluster summarisation)
253
+
254
+ ---
255
+
256
+ ## License
257
+
258
+ MIT. See [LICENSE](LICENSE).
259
+
260
+ ---
261
+
262
+ Built by [Edward Grundy](https://bayis.co.uk) --
263
+ [Bay Information Systems](https://bayis.co.uk)
reheat-0.0.1/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # Reheat
2
+
3
+ Python CLI for SEO analysis. Pulls search queries from Google Search Console,
4
+ enriches them with related searches and People Also Ask data, clusters by
5
+ semantic intent, and surfaces content gaps and opportunities.
6
+
7
+ Built by [Edward Grundy](https://bayis.co.uk) at
8
+ [Bay Information Systems](https://bayis.co.uk).
9
+
10
+ - Install: `pip install reheat`
11
+ - Docs: [bayinfosys.github.io/reheat](https://bayinfosys.github.io/reheat)
12
+ - PyPI: [pypi.org/project/reheat](https://pypi.org/project/reheat)
13
+
14
+ ---
15
+
16
+ ## Getting started
17
+
18
+ ### Install
19
+
20
+ ```bash
21
+ pip install reheat
22
+ ```
23
+
24
+ Requires Python 3.10+. For the local web interface you also need a running
25
+ postgres instance (or use the JSON file backend for local development
26
+ without postgres).
27
+
28
+ ```bash
29
+ docker run -d \
30
+ --name reheat-pg \
31
+ -e POSTGRES_USER=reheat \
32
+ -e POSTGRES_PASSWORD=reheat \
33
+ -e POSTGRES_DB=reheat \
34
+ -p 5432:5432 \
35
+ postgres:16
36
+
37
+ export DATABASE_URL=postgresql://reheat:reheat@localhost:5432/reheat
38
+ ```
39
+
40
+ ### Configure a Google Search Console source
41
+
42
+ Download OAuth2 credentials from Google Cloud Console (APIs and Services >
43
+ Credentials > OAuth 2.0 Client IDs, Desktop app type) and save as
44
+ `~/.reheat/google-search-console.json`. Then:
45
+
46
+ ```bash
47
+ reheat sources create \
48
+ --source-type google_search_console \
49
+ --domain yourdomain.com \
50
+ --client-secrets-path ~/.reheat/google-search-console.json
51
+ ```
52
+
53
+ ### Authenticate
54
+
55
+ ```bash
56
+ reheat sources auth
57
+ ```
58
+
59
+ Opens a browser for the Google OAuth2 consent flow. The token is persisted
60
+ to `~/.reheat/gsc_token.json` and refreshed automatically on subsequent runs.
61
+
62
+ ### Configure a SerpAPI source (optional)
63
+
64
+ Required for PAA and related search enrichment. Get an API key at
65
+ [serpapi.com](https://serpapi.com).
66
+
67
+ ```bash
68
+ reheat sources create \
69
+ --source-type serp \
70
+ --api-key YOUR_SERP_API_KEY
71
+ ```
72
+
73
+ ### Run the pipeline
74
+
75
+ ```bash
76
+ # Fetch queries from Search Console
77
+ reheat runs create
78
+
79
+ # Enrich and process
80
+ reheat enrich tags
81
+ reheat enrich embed
82
+ reheat enrich cluster
83
+ reheat enrich gap
84
+ reheat analyse opportunities
85
+
86
+ # Build report data
87
+ reheat project create
88
+ reheat report scatter create
89
+ reheat report summary create
90
+ reheat report coverage create
91
+
92
+ # Start the web interface
93
+ reheat serve
94
+ ```
95
+
96
+ Open [http://localhost:8000](http://localhost:8000).
97
+
98
+ ---
99
+
100
+ ## Inference providers
101
+
102
+ `reheat analyse summarise` labels intent clusters using an LLM. Configure
103
+ one of the following.
104
+
105
+ ### OpenAI
106
+
107
+ Set `OPENAI_API_KEY` in your environment or `.env` file, or:
108
+
109
+ ```bash
110
+ reheat config set --key openai_api_key --value sk-...
111
+ ```
112
+
113
+ ### Anthropic
114
+
115
+ Set `ANTHROPIC_API_KEY` in your environment or `.env` file, or:
116
+
117
+ ```bash
118
+ reheat config set --key anthropic_api_key --value sk-ant-...
119
+ ```
120
+
121
+ ### Marigold
122
+
123
+ [Marigold](https://marigold.run) is a private inference API built by
124
+ Bay Information Systems. Set endpoint and key when available:
125
+
126
+ ```bash
127
+ reheat config set --key marigold_endpoint --value https://api.marigold.run
128
+ reheat config set --key marigold_api_key --value <key>
129
+ ```
130
+
131
+ ---
132
+
133
+ ## CLI reference
134
+
135
+ ```
136
+ reheat config show / set
137
+ reheat sources create / list / show / update / delete / auth
138
+ reheat runs create / list / show / delete
139
+ reheat enrichments list / show / delete
140
+ reheat enrich tags / embed / cluster / gap
141
+ reheat analyse summarise / opportunities
142
+ reheat project create / read
143
+ reheat report scatter / summary / coverage create / read
144
+ reheat serve
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Architecture
150
+
151
+ reheat has three layers.
152
+
153
+ **Commands** in `reheat/commands/` are the single source of truth for the
154
+ application surface. Each command is a Python function decorated with
155
+ `@command`, registered in a central registry, and exposed automatically
156
+ through both the CLI and the HTTP API.
157
+
158
+ **Pipeline** functions in `reheat/pipeline/` are pure data transforms:
159
+ embedding, clustering, gap analysis, report building. No persistence, no
160
+ side effects.
161
+
162
+ **Persistence** uses [dynawrap](https://github.com/bayinfosys/dynawrap),
163
+ a lightweight key-value library with identical interfaces over PostgreSQL
164
+ and DynamoDB. Tables are passed at call time; models are backend-agnostic.
165
+
166
+ The web interface is a static SPA served by FastAPI. All pages share a
167
+ single stylesheet and a common `api.js` module that is the single source
168
+ of truth for API endpoint calls.
169
+
170
+ ---
171
+
172
+ ## Requirements
173
+
174
+ - Python 3.10+
175
+ - PostgreSQL (or JSON file backend for local development)
176
+ - Google Search Console OAuth2 credentials
177
+ - SerpAPI key (optional, for PAA enrichment)
178
+ - OpenAI, Anthropic, or Marigold key (optional, for cluster summarisation)
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT. See [LICENSE](LICENSE).
185
+
186
+ ---
187
+
188
+ Built by [Edward Grundy](https://bayis.co.uk) --
189
+ [Bay Information Systems](https://bayis.co.uk)
@@ -0,0 +1,10 @@
1
+ title: reheat
2
+ description: >
3
+ Open source Python CLI for SEO analysis. Pull Google Search Console queries,
4
+ cluster by semantic intent, and surface content gaps and opportunities.
5
+ url: https://bayinfosys.github.io
6
+ baseurl: /reheat
7
+ theme: minima
8
+ author:
9
+ name: Edward Grundy
10
+ url: https://bayis.co.uk
@@ -0,0 +1,74 @@
1
+ ---
2
+ layout: default
3
+ title: reheat
4
+ description: >
5
+ reheat is an open source Python CLI for SEO analysis. Pull queries from
6
+ Google Search Console, cluster by intent, and surface content gaps.
7
+ ---
8
+
9
+ # reheat
10
+
11
+ Open source Python CLI for SEO analysis. Pull your Google Search Console
12
+ queries, enrich them with related searches and People Also Ask data, cluster
13
+ by semantic intent, and surface content gaps and opportunities.
14
+
15
+ Built by [Edward Grundy](https://bayis.co.uk) at
16
+ [Bay Information Systems](https://bayis.co.uk).
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install reheat
24
+ ```
25
+
26
+ Requires Python 3.10+.
27
+
28
+ ---
29
+
30
+ ## Quick start
31
+
32
+ ```bash
33
+ # Configure your Search Console source
34
+ reheat sources create \
35
+ --source-type google_search_console \
36
+ --domain yourdomain.com \
37
+ --client-secrets-path ~/.reheat/google-search-console.json
38
+
39
+ # Authenticate
40
+ reheat sources auth
41
+
42
+ # Run the pipeline
43
+ reheat runs create
44
+ reheat enrich tags
45
+ reheat enrich embed
46
+ reheat enrich cluster
47
+ reheat enrich gap
48
+ reheat analyse opportunities
49
+ reheat project create
50
+ reheat report scatter create
51
+ reheat report summary create
52
+ reheat report coverage create
53
+
54
+ # Open the web interface
55
+ reheat serve
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Inference providers
61
+
62
+ Cluster summarisation (`reheat analyse summarise`) works with
63
+ [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), or
64
+ [Marigold](https://marigold.run). Set the relevant API key in your
65
+ environment or `.env` file and reheat will pick it up automatically.
66
+
67
+ ---
68
+
69
+ ## Links
70
+
71
+ - [GitHub repository](https://github.com/bayinfosys/reheat)
72
+ - [PyPI](https://pypi.org/project/reheat)
73
+ - [Bay Information Systems](https://bayis.co.uk)
74
+ - [dynawrap](https://github.com/bayinfosys/dynawrap) -- persistence layer
File without changes
@@ -0,0 +1,46 @@
1
+ import logging
2
+ import sys
3
+
4
+ from reheat.state import init_backend
5
+
6
+ import reheat.commands.config
7
+ import reheat.commands.sources
8
+ import reheat.commands.runs
9
+ import reheat.commands.enrichments
10
+ import reheat.commands.enrich
11
+ import reheat.commands.analyse
12
+ import reheat.commands.project
13
+ import reheat.commands.report
14
+ import reheat.commands.serve
15
+
16
+ from reheat.adapters.argparse_adapter import build_parser, dispatch
17
+
18
+
19
+ def main():
20
+ parser = build_parser()
21
+ args = parser.parse_args()
22
+
23
+ logging.basicConfig(
24
+ format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
25
+ datefmt="%H:%M:%S",
26
+ level=logging.DEBUG if getattr(args, "verbose", False) else logging.INFO,
27
+ )
28
+
29
+ try:
30
+ backend = init_backend(getattr(args, "config", None))
31
+ except Exception as e:
32
+ print(f"error: failed to initialise backend: {e}")
33
+ sys.exit(1)
34
+
35
+ try:
36
+ dispatch(args, backend)
37
+ except KeyboardInterrupt:
38
+ print("\ninterrupted")
39
+ sys.exit(130)
40
+ except Exception:
41
+ logging.getLogger(__name__).exception("command failed")
42
+ sys.exit(1)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
@@ -0,0 +1,34 @@
1
+ from typing import TypeVar, Generic, Callable, Dict, Optional
2
+
3
+ T = TypeVar("T")
4
+
5
+
6
+ class Resource(Generic[T]):
7
+ """Parameter is a resource identifier. Maps to a URL path segment."""
8
+ pass
9
+
10
+
11
+ class Payload(Generic[T]):
12
+ """Parameter is request payload. Maps to a POST body field."""
13
+ pass
14
+
15
+
16
+ _registry: Dict[str, Callable] = {}
17
+
18
+
19
+ def command(help: str = "", interactive_only: bool = False):
20
+ def decorator(fn: Callable) -> Callable:
21
+ parts = fn.__name__.split("_")
22
+
23
+ if parts[0] != "cmd":
24
+ raise ValueError(f"command functions must start with cmd_, got {fn.__name__}")
25
+
26
+ path = ".".join(parts[1:])
27
+ _registry[path] = fn
28
+ fn._cli_path = path
29
+ fn._cli_help = help
30
+ fn._interactive_only = interactive_only
31
+
32
+ return fn
33
+
34
+ return decorator
@@ -0,0 +1,24 @@
1
+ from reheat.state.execution import SourceConfig
2
+ from reheat.sources.base import SourceProvider, SourceError
3
+
4
+
5
+ def get_source_provider(config: SourceConfig) -> SourceProvider:
6
+ """
7
+ Factory: construct the correct SourceProvider for a SourceConfig.
8
+ Importing here keeps the registry central and avoids circular imports.
9
+ """
10
+ from reheat.sources.google_search_console import GoogleSearchConsoleProvider
11
+ from reheat.sources.serp import SerpAPIProvider
12
+
13
+ providers = {
14
+ "google_search_console": GoogleSearchConsoleProvider,
15
+ "serp": SerpAPIProvider,
16
+ }
17
+
18
+ cls = providers.get(config.source_type)
19
+ if cls is None:
20
+ raise SourceError(
21
+ f"unknown source type {config.source_type!r}. "
22
+ f"Available: {', '.join(providers)}"
23
+ )
24
+ return cls(config)
@@ -0,0 +1,51 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, List
3
+ from reheat.state.execution import QueryRecord, SourceConfig
4
+
5
+
6
+ class SourceProvider(ABC):
7
+ """
8
+ Base class for data source providers.
9
+
10
+ Each provider knows how to:
11
+ - validate its configuration
12
+ - execute a fetch and return QueryRecords
13
+ """
14
+
15
+ source_type: str = ""
16
+
17
+ def __init__(self, config: SourceConfig) -> None:
18
+ self.config = config
19
+ self.validate()
20
+
21
+ def validate(self) -> None:
22
+ """
23
+ Raise ValueError if the SourceConfig is missing required credentials
24
+ or settings. Called at construction time.
25
+ """
26
+ pass
27
+
28
+ @abstractmethod
29
+ def fetch(self) -> List[QueryRecord]:
30
+ """
31
+ Pull data from the source and return QueryRecords.
32
+ Raises SourceError on failure.
33
+ """
34
+ ...
35
+
36
+ def _credential(self, key: str) -> str:
37
+ """Return a credential value, raising ValueError if absent."""
38
+ value = self.config.credentials.get(key, "")
39
+ if not value:
40
+ raise ValueError(
41
+ f"source {self.config.source_id!r} missing credential {key!r}"
42
+ )
43
+ return value
44
+
45
+ def _setting(self, key: str, default: Any = None) -> Any:
46
+ """Return a setting value with optional default."""
47
+ return self.config.settings.get(key, default)
48
+
49
+
50
+ class SourceError(Exception):
51
+ """Raised when a source provider fails to fetch data."""