forecastfactor-publisher 1.0.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.
- forecastfactor_publisher-1.0.0/PKG-INFO +18 -0
- forecastfactor_publisher-1.0.0/README.md +299 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher/__init__.py +1 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher/publisher.py +80 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher.egg-info/PKG-INFO +18 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher.egg-info/SOURCES.txt +9 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher.egg-info/dependency_links.txt +1 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher.egg-info/requires.txt +2 -0
- forecastfactor_publisher-1.0.0/forecastfactor_publisher.egg-info/top_level.txt +1 -0
- forecastfactor_publisher-1.0.0/setup.cfg +4 -0
- forecastfactor_publisher-1.0.0/setup.py +20 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: forecastfactor_publisher
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A library for publishing forecast data to an API
|
|
5
|
+
Author: Ricardo Teixeira
|
|
6
|
+
Author-email: ricardo.teixeira@obiio.com
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
Requires-Dist: pandas
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: requires-dist
|
|
17
|
+
Dynamic: requires-python
|
|
18
|
+
Dynamic: summary
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Forecast Factor Publisher
|
|
2
|
+
|
|
3
|
+
A Python library for publishing forecast data to the Forecast Factor API. This package provides a simple interface to upload time series data, forecasts, and associated metadata to the forecast ingestion service.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Upload raw time series data with metadata
|
|
8
|
+
- Publish forecast data with confidence bounds
|
|
9
|
+
- Include model information and performance metrics
|
|
10
|
+
- Support for turning points analysis
|
|
11
|
+
- Comprehensive error handling and validation
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### From PyPI (Recommended)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install forecastfactor-publisher
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From Source
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone <repository-url>
|
|
25
|
+
cd forecastfactor_publisher
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import pandas as pd
|
|
33
|
+
from forecastfactor_publisher import forecastfactor_publish
|
|
34
|
+
|
|
35
|
+
# Prepare your data
|
|
36
|
+
raw_data = pd.DataFrame({
|
|
37
|
+
'date': ['2023-01-01', '2023-01-02'],
|
|
38
|
+
'value': [100, 110]
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
forecast_data = pd.DataFrame({
|
|
42
|
+
'date': ['2023-02-01', '2023-02-02'],
|
|
43
|
+
'value': [120, 130]
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
forecast_bounds = pd.DataFrame({
|
|
47
|
+
'date': ['2023-02-01', '2023-02-02'],
|
|
48
|
+
'lb95': [115, 125],
|
|
49
|
+
'ub95': [125, 135]
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
forecast_residuals = pd.DataFrame({
|
|
53
|
+
'date': ['2023-01-01', '2023-01-02'],
|
|
54
|
+
'value': [0.1, -0.2]
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
# Define metadata
|
|
58
|
+
turning_points = {
|
|
59
|
+
"2023-01-15": ["peak", "trough"],
|
|
60
|
+
"2023-02-01": ["peak"]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
metadata = {
|
|
64
|
+
"source": "internal_data",
|
|
65
|
+
"description": "Monthly sales forecast"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
model_inputs = {
|
|
69
|
+
"algorithm": "ARIMA",
|
|
70
|
+
"mae": "0.15",
|
|
71
|
+
"rmse": "0.23"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
model_info = {
|
|
75
|
+
"parameters": ["p=1", "d=1", "q=1"],
|
|
76
|
+
"features": ["lag_1", "trend"]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Optionally, you can specify a custom API endpoint:
|
|
80
|
+
# result = forecastfactor_publish(..., base_url="https://your-custom-api-endpoint")
|
|
81
|
+
|
|
82
|
+
# Publish to API
|
|
83
|
+
result = forecastfactor_publish(
|
|
84
|
+
api_key="your_api_key_here",
|
|
85
|
+
group_name="sales_forecasts",
|
|
86
|
+
series_name="monthly_sales",
|
|
87
|
+
model_name="arima_111",
|
|
88
|
+
transformation_name="none",
|
|
89
|
+
data_frequency="monthly",
|
|
90
|
+
raw_data=raw_data,
|
|
91
|
+
forecast_data=forecast_data,
|
|
92
|
+
forecast_bounds=forecast_bounds,
|
|
93
|
+
forecast_residuals=forecast_residuals,
|
|
94
|
+
turning_points=turning_points,
|
|
95
|
+
metadata=metadata,
|
|
96
|
+
model_inputs=model_inputs,
|
|
97
|
+
model_info=model_info
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
print(result)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API Reference
|
|
104
|
+
|
|
105
|
+
### `forecastfactor_publish()`
|
|
106
|
+
|
|
107
|
+
Posts time series data and metadata to the Forecast Factor API.
|
|
108
|
+
|
|
109
|
+
#### Parameters
|
|
110
|
+
|
|
111
|
+
- **api_key** (str): API key for authentication
|
|
112
|
+
- **group_name** (str): The name of the group the series belongs to
|
|
113
|
+
- **series_name** (str): The name of the series
|
|
114
|
+
- **model_name** (str): The name of the model
|
|
115
|
+
- **transformation_name** (str): The transformation applied to the original dataset (e.g., 'none', 'log')
|
|
116
|
+
- **data_frequency** (str): The series data frequency (monthly, weekly, daily, etc.)
|
|
117
|
+
- **raw_data** (pd.DataFrame): DataFrame with columns ['date', 'value']
|
|
118
|
+
- **forecast_data** (pd.DataFrame): DataFrame with columns ['date', 'value']
|
|
119
|
+
- **forecast_bounds** (pd.DataFrame): DataFrame with columns ['date', 'lb95', ..., 'ub95']
|
|
120
|
+
- **forecast_residuals** (pd.DataFrame): DataFrame with columns ['date', 'value']
|
|
121
|
+
- **turning_points** (Dict): Dictionary where keys are timestamps, values are lists of turning points
|
|
122
|
+
- **metadata** (Dict): Additional metadata characterizing the series
|
|
123
|
+
- **model_inputs** (Dict): Information about the forecasting model and performance metrics
|
|
124
|
+
- **model_info** (Dict): Dictionary where keys are model parameters, values are lists of values
|
|
125
|
+
- - **base_url** (str, optional): Override the default API endpoint URL. Defaults to the official endpoint.
|
|
126
|
+
|
|
127
|
+
#### Returns
|
|
128
|
+
|
|
129
|
+
- **Dict**: Response from the API with status and message
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
### Prerequisites
|
|
134
|
+
|
|
135
|
+
- Python 3.7 or higher
|
|
136
|
+
- pip
|
|
137
|
+
- twine (for publishing)
|
|
138
|
+
|
|
139
|
+
### Setup Development Environment
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Clone the repository
|
|
143
|
+
git clone <repository-url>
|
|
144
|
+
cd forecastfactor_publisher
|
|
145
|
+
|
|
146
|
+
# Create virtual environment
|
|
147
|
+
python -m venv venv
|
|
148
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
149
|
+
|
|
150
|
+
# Install dependencies
|
|
151
|
+
pip install -r requirements.txt
|
|
152
|
+
pip install -e .
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Publishing New Versions
|
|
156
|
+
|
|
157
|
+
### Step 1: Update Version Number
|
|
158
|
+
|
|
159
|
+
Before publishing a new version, you must update the version number in `setup.py`:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
# In setup.py, line 5
|
|
163
|
+
version="0.1.9", # Increment this version number
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Version Numbering Convention:**
|
|
167
|
+
- **Major.Minor.Patch** (e.g., 1.2.3)
|
|
168
|
+
- **Major**: Breaking changes
|
|
169
|
+
- **Minor**: New features, backward compatible
|
|
170
|
+
- **Patch**: Bug fixes, backward compatible
|
|
171
|
+
|
|
172
|
+
### Step 2: Update Changelog (Optional but Recommended)
|
|
173
|
+
|
|
174
|
+
Create or update a `CHANGELOG.md` file to document changes:
|
|
175
|
+
|
|
176
|
+
```markdown
|
|
177
|
+
# Changelog
|
|
178
|
+
|
|
179
|
+
## [0.1.9] - 2024-01-15
|
|
180
|
+
### Added
|
|
181
|
+
- New feature X
|
|
182
|
+
- Enhanced error handling
|
|
183
|
+
|
|
184
|
+
### Fixed
|
|
185
|
+
- Bug fix Y
|
|
186
|
+
|
|
187
|
+
## [0.1.8] - 2024-01-01
|
|
188
|
+
### Added
|
|
189
|
+
- Initial release
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Step 3: Commit Changes
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
git add .
|
|
196
|
+
git commit -m "Bump version to 0.1.9"
|
|
197
|
+
git tag v0.1.9
|
|
198
|
+
git push origin main
|
|
199
|
+
git push origin v0.1.9
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Step 4: Build and Publish
|
|
203
|
+
|
|
204
|
+
Use the provided release script:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Make sure you're in the project root directory
|
|
208
|
+
./release.sh
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Or manually:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Clean old builds
|
|
215
|
+
rm -rf build dist *.egg-info
|
|
216
|
+
|
|
217
|
+
# Create new build
|
|
218
|
+
python3 setup.py sdist bdist_wheel
|
|
219
|
+
|
|
220
|
+
# Upload to PyPI
|
|
221
|
+
twine upload dist/*
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Step 5: Verify Publication
|
|
225
|
+
|
|
226
|
+
Check that your package is available on PyPI:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install --upgrade forecastfactor-publisher
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Configuration
|
|
233
|
+
|
|
234
|
+
### PyPI Credentials
|
|
235
|
+
|
|
236
|
+
Before publishing, ensure you have PyPI credentials configured:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Create ~/.pypirc file
|
|
240
|
+
[distutils]
|
|
241
|
+
index-servers =
|
|
242
|
+
pypi
|
|
243
|
+
|
|
244
|
+
[pypi]
|
|
245
|
+
repository = https://upload.pypi.org/legacy/
|
|
246
|
+
username = your_username
|
|
247
|
+
password = your_password
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Or use environment variables:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
export TWINE_USERNAME=your_username
|
|
254
|
+
export TWINE_PASSWORD=your_password
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Testing
|
|
258
|
+
|
|
259
|
+
### Local Testing
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# Install in development mode
|
|
263
|
+
pip install -e .
|
|
264
|
+
|
|
265
|
+
# Test the package
|
|
266
|
+
python -c "from forecastfactor_publisher import forecastfactor_publish; print('Package imported successfully')"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Test PyPI (Optional)
|
|
270
|
+
|
|
271
|
+
For testing before publishing to production PyPI:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Upload to test PyPI first
|
|
275
|
+
twine upload --repository testpypi dist/*
|
|
276
|
+
|
|
277
|
+
# Install from test PyPI
|
|
278
|
+
pip install --index-url https://test.pypi.org/simple/ forecastfactor-publisher
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Troubleshooting
|
|
282
|
+
|
|
283
|
+
### Common Issues
|
|
284
|
+
|
|
285
|
+
1. **Version Already Exists**: If you get an error about the version already existing, increment the version number in `setup.py`.
|
|
286
|
+
|
|
287
|
+
2. **Authentication Failed**: Ensure your PyPI credentials are correct in `~/.pypirc` or environment variables.
|
|
288
|
+
|
|
289
|
+
3. **Build Errors**: Make sure all dependencies are installed and the package structure is correct.
|
|
290
|
+
|
|
291
|
+
4. **Import Errors**: Verify that `__init__.py` files are present in all directories.
|
|
292
|
+
|
|
293
|
+
### Getting Help
|
|
294
|
+
|
|
295
|
+
If you encounter issues:
|
|
296
|
+
|
|
297
|
+
1. Check the [PyPI documentation](https://packaging.python.org/)
|
|
298
|
+
2. Verify your package structure matches Python packaging standards
|
|
299
|
+
3. Test locally before publishing
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .publisher import forecastfactor_publish
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from typing import Dict, Optional
|
|
4
|
+
|
|
5
|
+
def forecastfactor_publish(
|
|
6
|
+
api_key: str,
|
|
7
|
+
group_name: str,
|
|
8
|
+
series_name: str,
|
|
9
|
+
model_name: str,
|
|
10
|
+
transformation_name: str,
|
|
11
|
+
data_frequency: str,
|
|
12
|
+
raw_data: pd.DataFrame,
|
|
13
|
+
forecast_data: pd.DataFrame,
|
|
14
|
+
forecast_bounds: pd.DataFrame,
|
|
15
|
+
forecast_residuals: pd.DataFrame,
|
|
16
|
+
turning_points: Dict[str, list],
|
|
17
|
+
metadata: Dict[str, str],
|
|
18
|
+
model_inputs: Dict[str, str],
|
|
19
|
+
model_info: Dict[str, list],
|
|
20
|
+
base_url: Optional[str] = None,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Posts time series data, metadata to an API.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
api_key (str): API key for authentication.
|
|
27
|
+
group_name (str): The name of the group the series belongs to.
|
|
28
|
+
series_name (str): The name of the series.
|
|
29
|
+
model_name (str): The name of the model.
|
|
30
|
+
transformation_name (str): The name of the transformation of the original dataset to feed the model (e.g., 'none', 'log', etc.).
|
|
31
|
+
data_frequency (str): The series data frequency (monthly, weekly, daily, etc.).
|
|
32
|
+
raw_data (pd.DataFrame): DataFrame of raw time series data with columns ['date', 'value'].
|
|
33
|
+
forecast_data (pd.DataFrame): DataFrame of forecast bounds with columns ['date', 'value'].
|
|
34
|
+
forecast_bounds (pd.DataFrame): DataFrame of forecast data with columns ['date', 'lb95', ..., 'up95'].
|
|
35
|
+
forecast_residuals (pd.DataFrame): DataFrame of forecast data with columns ['date', 'value'].
|
|
36
|
+
turning_points (Dict): Dictionary where keys are timestamps, and values are lists of turning points.
|
|
37
|
+
metadata (Dict): Additional metadata characterizing the series and input parameters.
|
|
38
|
+
model_inputs (Dict): Information about the forecasting model, including performance metrics.
|
|
39
|
+
model_info (Dict): Dictionary where keys are model parameters, and values are lists of multiple values.
|
|
40
|
+
base_url (str, optional): Override the default API endpoint URL. Defaults to None (uses the official endpoint).
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dict: Response from the API.
|
|
44
|
+
"""
|
|
45
|
+
# Validate turning_points format
|
|
46
|
+
if not isinstance(turning_points, dict) or not all(isinstance(v, list) for v in turning_points.values()):
|
|
47
|
+
return {"status": "error", "message": "Invalid format: 'turningPoints' should be a dictionary with lists as values."}
|
|
48
|
+
|
|
49
|
+
# Validate model_info format
|
|
50
|
+
if not isinstance(model_info, dict) or not all(isinstance(v, list) for v in model_info.values()):
|
|
51
|
+
return {"status": "error", "message": "Invalid format: 'modelInfo' should be a dictionary with lists as values."}
|
|
52
|
+
|
|
53
|
+
payload = {
|
|
54
|
+
"groupName": group_name,
|
|
55
|
+
"seriesName": series_name,
|
|
56
|
+
"modelName": model_name,
|
|
57
|
+
"transformationName": transformation_name,
|
|
58
|
+
"dataFrequency": data_frequency,
|
|
59
|
+
"rawData": raw_data.to_dict(orient="records"),
|
|
60
|
+
"forecastData": forecast_data.to_dict(orient="records"),
|
|
61
|
+
"forecastBounds": forecast_bounds.to_dict(orient="records"),
|
|
62
|
+
"forecastResiduals": forecast_residuals.to_dict(orient="records"),
|
|
63
|
+
"turningPoints": turning_points,
|
|
64
|
+
"metadata": metadata,
|
|
65
|
+
"modelInfo": model_info,
|
|
66
|
+
"modelInputs": model_inputs,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
headers = {
|
|
70
|
+
"Authorization": f"Bearer {api_key}",
|
|
71
|
+
"Content-Type": "application/json"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
url = base_url or "https://vu-forecast-ingestor.ricardofmteixeira.workers.dev"
|
|
76
|
+
response = requests.post(url, json=payload, headers=headers)
|
|
77
|
+
response.raise_for_status() # Raise HTTPError for bad responses (4xx, 5xx)
|
|
78
|
+
return {"status": "success", "message": "Forecast data successfully uploaded."}
|
|
79
|
+
except requests.exceptions.RequestException as e:
|
|
80
|
+
return {"status": "error", "message": f"Request failed: {str(e)}"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: forecastfactor_publisher
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A library for publishing forecast data to an API
|
|
5
|
+
Author: Ricardo Teixeira
|
|
6
|
+
Author-email: ricardo.teixeira@obiio.com
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
Requires-Dist: pandas
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: requires-dist
|
|
17
|
+
Dynamic: requires-python
|
|
18
|
+
Dynamic: summary
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
forecastfactor_publisher/__init__.py
|
|
4
|
+
forecastfactor_publisher/publisher.py
|
|
5
|
+
forecastfactor_publisher.egg-info/PKG-INFO
|
|
6
|
+
forecastfactor_publisher.egg-info/SOURCES.txt
|
|
7
|
+
forecastfactor_publisher.egg-info/dependency_links.txt
|
|
8
|
+
forecastfactor_publisher.egg-info/requires.txt
|
|
9
|
+
forecastfactor_publisher.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
forecastfactor_publisher
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="forecastfactor_publisher",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
description="A library for publishing forecast data to an API",
|
|
7
|
+
author="Ricardo Teixeira",
|
|
8
|
+
author_email="ricardo.teixeira@obiio.com",
|
|
9
|
+
packages=find_packages(include=["forecastfactor_publisher*"]),
|
|
10
|
+
install_requires=[
|
|
11
|
+
"requests",
|
|
12
|
+
"pandas",
|
|
13
|
+
],
|
|
14
|
+
classifiers=[
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
],
|
|
19
|
+
python_requires=">=3.7",
|
|
20
|
+
)
|