amitlals commited on
Commit
a75037d
·
1 Parent(s): dcfc478

Deploy to HF Spaces - Gradio app with Azure Container Apps support

Browse files
Files changed (10) hide show
  1. .dockerignore +74 -0
  2. DEPLOYMENT.md +0 -0
  3. DEPLOYMENT_INSTRUCTIONS.md +257 -0
  4. Dockerfile +48 -0
  5. README.md +26 -134
  6. README_HF.md +37 -0
  7. app.py +0 -451
  8. app_gradio.py +7 -1
  9. deploy-azure.ps1 +135 -0
  10. requirements.txt +1 -2
.dockerignore ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .venv/
28
+
29
+ # IDE
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # Git
37
+ .git/
38
+ .gitignore
39
+
40
+ # Environment files (secrets)
41
+ .env
42
+ .env.local
43
+ .env.*.local
44
+
45
+ # Testing
46
+ .pytest_cache/
47
+ .coverage
48
+ htmlcov/
49
+ .tox/
50
+
51
+ # Documentation
52
+ docs/
53
+ *.md
54
+ !README.md
55
+
56
+ # Logs
57
+ *.log
58
+ logs/
59
+
60
+ # Temporary files
61
+ tmp/
62
+ temp/
63
+ *.tmp
64
+
65
+ # OS files
66
+ .DS_Store
67
+ Thumbs.db
68
+
69
+ # Deployment files (not needed in container)
70
+ .github/
71
+ *.yml
72
+ !requirements.txt
73
+ azure-deploy.sh
74
+ deploy.ps1
DEPLOYMENT.md ADDED
File without changes
DEPLOYMENT_INSTRUCTIONS.md ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SAP Finance Dashboard - Azure Container Apps Deployment Guide
2
+
3
+ ## Prerequisites
4
+
5
+ 1. **Azure CLI** - Install from: https://aka.ms/installazurecliwindows
6
+ 2. **Azure Subscription** - Active Azure account
7
+ 3. **Git** (optional) - For code updates
8
+
9
+ ## Quick Deploy Steps
10
+
11
+ ### Option 1: Automated Deployment Script
12
+
13
+ Open PowerShell as Administrator and run:
14
+
15
+ ```powershell
16
+ cd "c:\Users\amlal\Downloads\VSCode-SAP-AI-Copilot-Projects2025\SAP-RPT-1-OSS-App"
17
+ .\deploy-azure.ps1
18
+ ```
19
+
20
+ The script will:
21
+ - ✅ Create Azure Resource Group
22
+ - ✅ Create Azure Container Registry
23
+ - ✅ Build Docker image in Azure (no local Docker needed)
24
+ - ✅ Create Container Apps Environment
25
+ - ✅ Deploy your application
26
+ - ✅ Provide public URL
27
+
28
+ **Deployment Time:** ~10-15 minutes
29
+
30
+ ### Option 2: Manual Deployment (Step-by-Step)
31
+
32
+ If the automated script doesn't work, follow these manual commands:
33
+
34
+ #### 1. Login to Azure
35
+ ```bash
36
+ az login
37
+ ```
38
+
39
+ #### 2. Set Variables
40
+ ```bash
41
+ set RESOURCE_GROUP=rg-sap-finance-dashboard
42
+ set LOCATION=eastus
43
+ set ACR_NAME=acrsapfinance%RANDOM%
44
+ set ENVIRONMENT_NAME=env-sap-finance
45
+ set APP_NAME=sap-finance-dashboard
46
+ ```
47
+
48
+ #### 3. Create Resource Group
49
+ ```bash
50
+ az group create --name %RESOURCE_GROUP% --location %LOCATION%
51
+ ```
52
+
53
+ #### 4. Create Container Registry
54
+ ```bash
55
+ az acr create --resource-group %RESOURCE_GROUP% --name %ACR_NAME% --sku Basic --admin-enabled true
56
+ ```
57
+
58
+ #### 5. Build Docker Image (Using Azure - No local Docker needed)
59
+ ```bash
60
+ az acr build --registry %ACR_NAME% --image sap-finance-dashboard:latest .
61
+ ```
62
+
63
+ #### 6. Create Container Apps Environment
64
+ ```bash
65
+ az containerapp env create --name %ENVIRONMENT_NAME% --resource-group %RESOURCE_GROUP% --location %LOCATION%
66
+ ```
67
+
68
+ #### 7. Deploy Container App
69
+ ```bash
70
+ az containerapp create ^
71
+ --name %APP_NAME% ^
72
+ --resource-group %RESOURCE_GROUP% ^
73
+ --environment %ENVIRONMENT_NAME% ^
74
+ --image %ACR_NAME%.azurecr.io/sap-finance-dashboard:latest ^
75
+ --registry-server %ACR_NAME%.azurecr.io ^
76
+ --target-port 7862 ^
77
+ --ingress external ^
78
+ --cpu 2 ^
79
+ --memory 4Gi ^
80
+ --min-replicas 1 ^
81
+ --max-replicas 3
82
+ ```
83
+
84
+ #### 8. Get Application URL
85
+ ```bash
86
+ az containerapp show --name %APP_NAME% --resource-group %RESOURCE_GROUP% --query properties.configuration.ingress.fqdn -o tsv
87
+ ```
88
+
89
+ ## Post-Deployment Configuration
90
+
91
+ ### Add Hugging Face Token (Required for AI Features)
92
+
93
+ 1. Go to Azure Portal: https://portal.azure.com
94
+ 2. Navigate to your Container App
95
+ 3. Go to **Settings** > **Secrets**
96
+ 4. Add secret:
97
+ - Name: `huggingface-token`
98
+ - Value: Your Hugging Face token (get from https://huggingface.co/settings/tokens)
99
+ 5. Go to **Settings** > **Containers** > **Environment variables**
100
+ 6. Add environment variable:
101
+ - Name: `HUGGINGFACE_TOKEN`
102
+ - Source: Reference a secret
103
+ - Value: Select `huggingface-token`
104
+ 7. Click **Save** and wait for app to restart
105
+
106
+ ### Optional: Add SAP OData Credentials
107
+
108
+ If connecting to SAP systems:
109
+ - `SAP_ODATA_BASE_URL`: Your SAP OData endpoint
110
+ - `SAP_USERNAME`: SAP username (as secret)
111
+ - `SAP_PASSWORD`: SAP password (as secret)
112
+
113
+ ## Application Architecture
114
+
115
+ ```
116
+ ┌─────────────────────────────────────────┐
117
+ │ Azure Container Apps │
118
+ │ ┌───────────────────────────────────┐ │
119
+ │ │ SAP Finance Dashboard │ │
120
+ │ │ - Gradio Web Interface │ │
121
+ │ │ - RPT-1-OSS AI Model │ │
122
+ │ │ - Port 7862 │ │
123
+ │ └───────────────────────────────────┘ │
124
+ │ ↕ │
125
+ │ ┌───────────────────────────────────┐ │
126
+ │ │ Azure Container Registry │ │
127
+ │ │ - Docker Image Storage │ │
128
+ │ └───────────────────────────────────┘ │
129
+ └─────────────────────────────────────────┘
130
+ ```
131
+
132
+ ## Application Features
133
+
134
+ Once deployed, you'll have access to:
135
+
136
+ 1. **📊 Dashboard** - Financial metrics and visualizations
137
+ 2. **🔍 Data Explorer** - Browse datasets
138
+ 3. **📤 Upload** - Upload custom CSV files
139
+ 4. **🤖 AI Predictions** - SAP-RPT-1-OSS powered predictions
140
+ 5. **🔗 OData** - Connect to SAP systems
141
+ 6. **🎮 Playground** - Custom model training
142
+
143
+ ## Scaling Configuration
144
+
145
+ The default configuration:
146
+ - **Min Replicas:** 1 (always running)
147
+ - **Max Replicas:** 3 (auto-scale under load)
148
+ - **CPU:** 2 cores
149
+ - **Memory:** 4 GB
150
+
151
+ To adjust scaling:
152
+ ```bash
153
+ az containerapp update ^
154
+ --name %APP_NAME% ^
155
+ --resource-group %RESOURCE_GROUP% ^
156
+ --min-replicas 1 ^
157
+ --max-replicas 5 ^
158
+ --cpu 4 ^
159
+ --memory 8Gi
160
+ ```
161
+
162
+ ## Cost Optimization
163
+
164
+ **Estimated Monthly Cost (East US):**
165
+ - Container Apps (1 replica, 2 vCPU, 4GB): ~$60/month
166
+ - Container Registry (Basic): ~$5/month
167
+ - **Total:** ~$65/month
168
+
169
+ **To reduce costs:**
170
+ 1. Set min-replicas to 0 (app sleeps when not in use)
171
+ 2. Use smaller CPU/memory allocation
172
+ 3. Delete when not needed:
173
+ ```bash
174
+ az group delete --name %RESOURCE_GROUP% --yes
175
+ ```
176
+
177
+ ## Monitoring & Logs
178
+
179
+ ### View Live Logs
180
+ ```bash
181
+ az containerapp logs show --name %APP_NAME% --resource-group %RESOURCE_GROUP% --follow
182
+ ```
183
+
184
+ ### View Metrics (Azure Portal)
185
+ 1. Go to your Container App
186
+ 2. Click **Monitoring** > **Metrics**
187
+ 3. View:
188
+ - CPU usage
189
+ - Memory usage
190
+ - Request count
191
+ - Response time
192
+
193
+ ## Updating Your Application
194
+
195
+ When you make code changes:
196
+
197
+ ```bash
198
+ # Build new image
199
+ az acr build --registry %ACR_NAME% --image sap-finance-dashboard:latest .
200
+
201
+ # Update container app
202
+ az containerapp update ^
203
+ --name %APP_NAME% ^
204
+ --resource-group %RESOURCE_GROUP% ^
205
+ --image %ACR_NAME%.azurecr.io/sap-finance-dashboard:latest
206
+ ```
207
+
208
+ ## Troubleshooting
209
+
210
+ ### App not starting?
211
+ Check logs:
212
+ ```bash
213
+ az containerapp logs show --name %APP_NAME% --resource-group %RESOURCE_GROUP% --tail 100
214
+ ```
215
+
216
+ ### Out of memory?
217
+ Increase memory allocation:
218
+ ```bash
219
+ az containerapp update --name %APP_NAME% --resource-group %RESOURCE_GROUP% --memory 8Gi
220
+ ```
221
+
222
+ ### Port issues?
223
+ Verify port is set to 7862:
224
+ ```bash
225
+ az containerapp show --name %APP_NAME% --resource-group %RESOURCE_GROUP% --query properties.configuration.ingress
226
+ ```
227
+
228
+ ### Can't access URL?
229
+ Check ingress is enabled:
230
+ ```bash
231
+ az containerapp ingress show --name %APP_NAME% --resource-group %RESOURCE_GROUP%
232
+ ```
233
+
234
+ ## Security Best Practices
235
+
236
+ 1. **Use Managed Identity** for Azure service connections
237
+ 2. **Store secrets** in Azure Key Vault
238
+ 3. **Enable HTTPS only** (default enabled)
239
+ 4. **Restrict ingress** to specific IPs if needed
240
+ 5. **Rotate tokens** regularly
241
+
242
+ ## Support
243
+
244
+ - **Azure Container Apps Docs:** https://learn.microsoft.com/azure/container-apps/
245
+ - **SAP-RPT-1-OSS:** https://github.com/SAP-samples/sap-rpt-1-oss
246
+ - **Gradio Docs:** https://gradio.app/docs
247
+
248
+ ## Clean Up
249
+
250
+ To delete all resources and stop charges:
251
+ ```bash
252
+ az group delete --name %RESOURCE_GROUP% --yes --no-wait
253
+ ```
254
+
255
+ ---
256
+
257
+ **Ready to deploy?** Run the automated script or follow the manual steps above! 🚀
Dockerfile ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for SAP Finance Dashboard with RPT-1-OSS Model
2
+ # Optimized for Azure Container Apps deployment
3
+
4
+ FROM python:3.11-slim
5
+
6
+ # Set environment variables
7
+ ENV PYTHONDONTWRITEBYTECODE=1
8
+ ENV PYTHONUNBUFFERED=1
9
+ ENV GRADIO_SERVER_NAME=0.0.0.0
10
+ ENV GRADIO_SERVER_PORT=7862
11
+
12
+ # Install system dependencies
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ git \
15
+ curl \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+ # Set working directory
19
+ WORKDIR /app
20
+
21
+ # Copy requirements first for better caching
22
+ COPY requirements.txt .
23
+
24
+ # Install Python dependencies
25
+ RUN pip install --no-cache-dir --upgrade pip && \
26
+ pip install --no-cache-dir -r requirements.txt
27
+
28
+ # Install SAP-RPT-1-OSS from GitHub
29
+ RUN pip install --no-cache-dir git+https://github.com/SAP-samples/sap-rpt-1-oss
30
+
31
+ # Force Gradio 4.x to be installed LAST (override any conflicting dependencies)
32
+ RUN pip install --no-cache-dir --force-reinstall "gradio>=4.0.0"
33
+
34
+ # Copy application code
35
+ COPY . .
36
+
37
+ # Create data directory if it doesn't exist
38
+ RUN mkdir -p /app/data
39
+
40
+ # Expose port
41
+ EXPOSE 7862
42
+
43
+ # Health check
44
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
45
+ CMD curl -f http://localhost:7862/ || exit 1
46
+
47
+ # Run the application
48
+ CMD ["python", "app_gradio.py"]
README.md CHANGED
@@ -1,145 +1,37 @@
1
- # SAP OpenSource Model Playground | SAP-RPT-1-OSS Model Beta
2
-
3
- A comprehensive SAP Playground application built with Gradio that integrates the SAP-RPT-1-OSS model for predictive analysis on SAP datasets. Features include synthetic data generation, interactive visualizations, live OData connectivity, AI-powered insights, and a playground for training custom models.
4
- <img width="839" height="540" alt="image" src="https://github.com/user-attachments/assets/d6376079-2b83-422a-b4fd-51d517431ec2" />
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  ## Features
7
 
8
- - **Multiple Synthetic SAP Datasets**: General Ledger accounts, Financial Statements (P&L, Balance Sheet), and Sales Orders
9
- - **Data Upload**: Upload custom CSV, Parquet, or JSON datasets for analysis
10
- - **Interactive Visualizations**: Financial charts and graphs using Plotly
11
- - **SAP-RPT-1-OSS Model Integration**: AI-powered predictions and analysis
12
- - **Live OData Connection**: Connect to SAP systems to fetch real-time sales order data
13
- - **Playground Tab**: Upload datasets, configure model parameters, train, and download predictions
14
- - **Modern UI**: Built with Gradio, a Python-based web framework
15
-
16
- ## Installation
17
-
18
- ### Prerequisites
19
-
20
- - Python 3.11 or higher
21
- - Hugging Face account (for SAP model access)
22
- - SAP OData credentials (optional, for live data connection)
23
-
24
- ### Setup Steps
25
-
26
- 1. **Clone the repository**:
27
- ```bash
28
- git clone <repository-url>
29
- cd SAP-RPT-1-OSS-App
30
- ```
31
-
32
- 2. **Create a virtual environment**:
33
- ```bash
34
- python -m venv venv
35
- source venv/bin/activate # On Windows: venv\Scripts\activate
36
- ```
37
-
38
- 3. **Install dependencies**:
39
- ```bash
40
- pip install -r requirements.txt
41
- ```
42
-
43
- 4. **Install SAP-RPT-OSS package**:
44
- ```bash
45
- pip install git+https://github.com/SAP-samples/sap-rpt-1-oss
46
- ```
47
-
48
- 5. **Set up environment variables**:
49
- - Copy `.env.example` to `.env`
50
- - Fill in your SAP OData credentials
51
- - Add your Hugging Face token for model access
52
-
53
- 6. **Authenticate with Hugging Face**:
54
- ```bash
55
- pip install huggingface_hub
56
- huggingface-cli login
57
- ```
58
- Or set the `HUGGINGFACE_TOKEN` in your `.env` file.
59
-
60
- 7. **Generate synthetic data** (optional, if not already generated):
61
- ```bash
62
- python -c "from utils.data_generator import generate_all_datasets; generate_all_datasets()"
63
- ```
64
 
65
  ## Usage
66
 
67
- ### Running the Application
68
-
69
- Start the Gradio application:
70
-
71
- ```bash
72
- python app_gradio.py
73
- ```
74
-
75
- The application will be available at `http://localhost:7862` (default Gradio port).
76
 
77
- ### Application Tabs
78
 
79
- 1. **Dashboard**: Overview with key financial metrics and visualizations
80
- 2. **Data Explorer**: Browse and filter datasets (GL, Financial Statements, Sales Orders)
81
- 3. **Upload**: Upload custom CSV datasets for analysis
82
- 4. **Predictions**: Use SAP-RPT-1-OSS model for predictions and analysis with pre-configured scenarios
83
- 5. **OData**: Connect to SAP OData services and fetch live data
84
- 6. **Playground**: Upload datasets, configure model parameters (task type, target column, test split, context size, bagging, GPU), train models, and download predictions
85
-
86
- ## SAP OData Connection Setup
87
-
88
- 1. Set the following environment variables in your `.env` file:
89
- - `SAP_USERNAME`: Your SAP username
90
- - `SAP_PASSWORD`: Your SAP password
91
- - `SAP_SERVER`: SAP server URL (default: `https://sapes5.sapdevcenter.com/`)
92
- - `SAP_CLIENT`: SAP client number (default: `002`)
93
-
94
- 2. The OData connector uses the base URL:
95
- `https://sapes5.sapdevcenter.com/sap/opu/odata/IWBEP/GWSAMPLE_BASIC`
96
-
97
- 3. Available endpoints:
98
- - Sales Orders: `SalesOrderSet`
99
- - Products: `ProductSet`
100
- - Line Items: `SalesOrderLineItemSet`
101
- - Business Partners: `BusinessPartnerSet`
102
-
103
- ## Model Configuration
104
-
105
- The SAP-RPT-1-OSS model supports both classification and regression tasks. For best performance:
106
-
107
- - **Recommended**: GPU with at least 80 GB memory, context size 8192, bagging factor 8
108
- - **Lightweight**: CPU with context size 2048, bagging factor 1
109
-
110
- The application automatically detects available resources and adjusts settings accordingly.
111
-
112
- ## Project Structure
113
-
114
- ```
115
- SAP-RPT-1-OSS-App/
116
- ├── app_gradio.py # Main Gradio application
117
- ├── models/
118
- │ └── rpt_model.py # SAP-RPT-1-OSS model wrapper
119
- ├── data/
120
- │ ├── synthetic_gl_accounts.csv
121
- │ ├── synthetic_financial_statements.csv
122
- │ └── synthetic_sales_orders.csv
123
- ├── utils/
124
- │ ├── data_generator.py # Generate synthetic SAP finance data
125
- │ ├── visualizations.py # Chart generation functions
126
- │ ├── odata_connector.py # OData connection utilities
127
- │ └── playground.py # Playground utilities for model training
128
- ├── requirements.txt
129
- ├── README.md
130
- └── .env.example
131
- ```
132
 
133
  ## License
134
 
135
- This project is licensed under the Apache Software License, version 2.0.
136
-
137
- ## Support
138
-
139
- For issues or questions, please create an issue in this repository.
140
-
141
- ## Acknowledgments
142
-
143
- - SAP-RPT-1-OSS model: [Hugging Face](https://huggingface.co/SAP/sap-rpt-1-oss)
144
- - Gradio framework: [Gradio Documentation](https://www.gradio.app/docs/)
145
-
 
1
+ ---
2
+ title: SAP Finance Dashboard with RPT-1-OSS
3
+ emoji: 📊
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app_gradio.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # SAP Finance Dashboard with RPT-1-OSS Model
14
+
15
+ A comprehensive financial dashboard application built with Gradio that integrates the SAP-RPT-1-OSS model for predictive analysis on SAP finance datasets.
16
 
17
  ## Features
18
 
19
+ - **Multiple Synthetic SAP Finance Datasets**: General Ledger accounts, Financial Statements, Sales Orders
20
+ - **Interactive Visualizations**: Financial charts using Plotly
21
+ - **SAP-RPT-1-OSS Model Integration**: AI-powered predictions
22
+ - **Playground Tab**: Upload datasets, configure model parameters, train models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  ## Usage
25
 
26
+ 1. **Dashboard**: View key financial metrics and visualizations
27
+ 2. **Data Explorer**: Browse and filter datasets
28
+ 3. **Predictions**: Use SAP-RPT-1-OSS model for AI predictions
29
+ 4. **Playground**: Upload custom datasets and train models
 
 
 
 
 
30
 
31
+ ## Model
32
 
33
+ This Space uses the [SAP-RPT-1-OSS](https://huggingface.co/SAP/sap-rpt-1-oss) model for predictions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  ## License
36
 
37
+ Apache 2.0
 
 
 
 
 
 
 
 
 
 
README_HF.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: SAP Finance Dashboard with RPT-1-OSS
3
+ emoji: 📊
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app_gradio.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # SAP Finance Dashboard with RPT-1-OSS Model
14
+
15
+ A comprehensive financial dashboard application built with Gradio that integrates the SAP-RPT-1-OSS model for predictive analysis on SAP finance datasets.
16
+
17
+ ## Features
18
+
19
+ - **Multiple Synthetic SAP Finance Datasets**: General Ledger accounts, Financial Statements, Sales Orders
20
+ - **Interactive Visualizations**: Financial charts using Plotly
21
+ - **SAP-RPT-1-OSS Model Integration**: AI-powered predictions
22
+ - **Playground Tab**: Upload datasets, configure model parameters, train models
23
+
24
+ ## Usage
25
+
26
+ 1. **Dashboard**: View key financial metrics and visualizations
27
+ 2. **Data Explorer**: Browse and filter datasets
28
+ 3. **Predictions**: Use SAP-RPT-1-OSS model for AI predictions
29
+ 4. **Playground**: Upload custom datasets and train models
30
+
31
+ ## Model
32
+
33
+ This Space uses the [SAP-RPT-1-OSS](https://huggingface.co/SAP/sap-rpt-1-oss) model for predictions.
34
+
35
+ ## License
36
+
37
+ Apache 2.0
app.py DELETED
@@ -1,451 +0,0 @@
1
- """
2
- SAP Finance Dashboard with RPT-1-OSS Model
3
-
4
- Main Mesop application with multiple pages:
5
- - Dashboard: Overview with metrics and charts
6
- - Data Explorer: Browse datasets
7
- - Upload: Upload custom datasets
8
- - Predictions: AI-powered predictions using SAP-RPT-1-OSS
9
- - OData: Connect to SAP OData services
10
- """
11
-
12
- import os
13
- import mesop as me
14
- import pandas as pd
15
- import numpy as np
16
- from pathlib import Path
17
- import json
18
- import base64
19
- from typing import Optional, Dict, Any
20
- import plotly.graph_objects as go
21
- import plotly.io as pio
22
-
23
- # Import utilities
24
- from utils.data_generator import generate_all_datasets
25
- from utils.visualizations import (
26
- create_revenue_expense_chart,
27
- create_balance_sheet_chart,
28
- create_gl_summary_chart,
29
- create_sales_analytics_chart,
30
- create_sales_trend_chart,
31
- get_summary_metrics
32
- )
33
- from utils.odata_connector import SAPFinanceConnector
34
- from models.rpt_model import RPTModelWrapper, create_model
35
-
36
- # Global state
37
- from dataclasses import field
38
-
39
- @me.stateclass
40
- class State:
41
- gl_data: pd.DataFrame = field(default_factory=pd.DataFrame)
42
- financial_data: pd.DataFrame = field(default_factory=pd.DataFrame)
43
- sales_data: pd.DataFrame = field(default_factory=pd.DataFrame)
44
- uploaded_data: pd.DataFrame = field(default_factory=pd.DataFrame)
45
- current_dataset_type: str = ""
46
- odata_connector: Optional[SAPFinanceConnector] = None
47
- odata_connected: bool = False
48
- odata_data: pd.DataFrame = field(default_factory=pd.DataFrame)
49
- model_wrapper: Optional[RPTModelWrapper] = None
50
- predictions: Optional[np.ndarray] = None
51
- prediction_proba: Optional[np.ndarray] = None
52
- connection_message: str = ""
53
- fetch_message: str = ""
54
- model_initialized: bool = False
55
- model_trained: bool = False
56
-
57
-
58
- def load_datasets(state: State):
59
- """Load synthetic datasets if they exist."""
60
- data_dir = Path("data")
61
-
62
- if (data_dir / "synthetic_gl_accounts.csv").exists():
63
- state.gl_data = pd.read_csv(data_dir / "synthetic_gl_accounts.csv")
64
-
65
- if (data_dir / "synthetic_financial_statements.csv").exists():
66
- state.financial_data = pd.read_csv(data_dir / "synthetic_financial_statements.csv")
67
-
68
- if (data_dir / "synthetic_sales_orders.csv").exists():
69
- state.sales_data = pd.read_csv(data_dir / "synthetic_sales_orders.csv")
70
-
71
-
72
- def plotly_to_html(fig_dict: Dict[str, Any]) -> str:
73
- """Convert Plotly figure dict to HTML string."""
74
- if not fig_dict:
75
- return "<p>No chart data available</p>"
76
-
77
- try:
78
- fig = go.Figure(fig_dict)
79
- html_str = pio.to_html(fig, include_plotlyjs='cdn', div_id="plotly-div")
80
- return html_str
81
- except Exception as e:
82
- return f"<p>Error rendering chart: {str(e)}</p>"
83
-
84
-
85
- @me.page(path="/", title="SAP Finance Dashboard")
86
- def dashboard_page():
87
- """Main dashboard page with overview metrics and charts."""
88
- state = me.state(State)
89
-
90
- me.text("SAP Finance Dashboard", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
91
-
92
- # Load datasets if not loaded
93
- if state.gl_data.empty and state.financial_data.empty and state.sales_data.empty:
94
- load_datasets(state)
95
-
96
- # Generate datasets if they don't exist
97
- if state.gl_data.empty or state.financial_data.empty or state.sales_data.empty:
98
- with me.box(style=me.Style(padding=16, background="#fff3cd", border_radius=8, margin=me.Margin(bottom=16))):
99
- me.text("Generating synthetic datasets...", style=me.Style(color="#856404"))
100
- generate_all_datasets()
101
- load_datasets(state)
102
-
103
- # Summary metrics
104
- with me.box(style=me.Style(display="grid", grid_template_columns="repeat(4, 1fr)", gap=16, margin=me.Margin(bottom=24))):
105
- if not state.gl_data.empty:
106
- gl_metrics = get_summary_metrics(state.gl_data, "gl")
107
- with me.box(style=me.Style(padding=16, background="#f8f9fa", border_radius=8)):
108
- me.text("GL Transactions", style=me.Style(font_weight="bold"))
109
- me.text(f"{gl_metrics.get('Total Transactions', 0):,}")
110
-
111
- if not state.financial_data.empty:
112
- fin_metrics = get_summary_metrics(state.financial_data, "financial")
113
- with me.box(style=me.Style(padding=16, background="#f8f9fa", border_radius=8)):
114
- me.text("Latest Revenue", style=me.Style(font_weight="bold"))
115
- me.text(f"${fin_metrics.get('Latest Revenue', 0):,.2f}")
116
-
117
- if not state.sales_data.empty:
118
- sales_metrics = get_summary_metrics(state.sales_data, "sales")
119
- with me.box(style=me.Style(padding=16, background="#f8f9fa", border_radius=8)):
120
- me.text("Total Sales", style=me.Style(font_weight="bold"))
121
- me.text(f"${sales_metrics.get('Total Sales', 0):,.2f}")
122
-
123
- with me.box(style=me.Style(padding=16, background="#f8f9fa", border_radius=8)):
124
- me.text("Datasets", style=me.Style(font_weight="bold"))
125
- count = sum([
126
- not state.gl_data.empty,
127
- not state.financial_data.empty,
128
- not state.sales_data.empty,
129
- not state.uploaded_data.empty
130
- ])
131
- me.text(f"{count} loaded")
132
-
133
- # Charts
134
- if not state.financial_data.empty:
135
- with me.box(style=me.Style(margin=me.Margin(bottom=24))):
136
- me.text("Financial Trends", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
137
- chart_data = create_revenue_expense_chart(state.financial_data)
138
- if chart_data:
139
- html_chart = plotly_to_html(chart_data)
140
- me.html(html_chart)
141
-
142
- if not state.financial_data.empty:
143
- with me.box(style=me.Style(margin=me.Margin(bottom=24))):
144
- me.text("Balance Sheet", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
145
- chart_data = create_balance_sheet_chart(state.financial_data)
146
- if chart_data:
147
- html_chart = plotly_to_html(chart_data)
148
- me.html(html_chart)
149
-
150
- if not state.sales_data.empty:
151
- with me.box(style=me.Style(margin=me.Margin(bottom=24))):
152
- me.text("Sales Analytics", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
153
- chart_data = create_sales_analytics_chart(state.sales_data)
154
- if chart_data:
155
- html_chart = plotly_to_html(chart_data)
156
- me.html(html_chart)
157
-
158
-
159
- @me.page(path="/explorer", title="Data Explorer")
160
- def explorer_page():
161
- """Data explorer page to browse and filter datasets."""
162
- state = me.state(State)
163
-
164
- me.text("Data Explorer", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
165
-
166
- # Dataset selector
167
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
168
- me.text("Select Dataset:", style=me.Style(font_weight="bold", margin=me.Margin(bottom=8)))
169
- dataset_options = [
170
- ("GL Accounts", "gl"),
171
- ("Financial Statements", "financial"),
172
- ("Sales Orders", "sales"),
173
- ("Uploaded Data", "uploaded")
174
- ]
175
-
176
- me.select(
177
- label="Dataset",
178
- options=[me.SelectOption(label=label, value=value) for label, value in dataset_options],
179
- on_selection_change=on_dataset_selection_change,
180
- value=state.current_dataset_type
181
- )
182
-
183
- # Display selected dataset
184
- if state.current_dataset_type:
185
- display_dataset(state, state.current_dataset_type)
186
-
187
-
188
- def on_dataset_selection_change(e: me.SelectSelectionChangeEvent):
189
- """Handle dataset selection change."""
190
- state = me.state(State)
191
- state.current_dataset_type = e.value
192
-
193
-
194
- def display_dataset(state: State, dataset_type: str):
195
- """Display the selected dataset."""
196
- if dataset_type == "gl" and not state.gl_data.empty:
197
- df = state.gl_data
198
- me.text(f"GL Accounts ({len(df)} records)", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
199
- chart_data = create_gl_summary_chart(df)
200
- if chart_data:
201
- html_chart = plotly_to_html(chart_data)
202
- me.html(html_chart)
203
- me.table(data=df.head(100).to_dict("records"), style=me.Style(margin=me.Margin(top=16)))
204
-
205
- elif dataset_type == "financial" and not state.financial_data.empty:
206
- df = state.financial_data
207
- me.text(f"Financial Statements ({len(df)} records)", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
208
- chart_data = create_revenue_expense_chart(df)
209
- if chart_data:
210
- html_chart = plotly_to_html(chart_data)
211
- me.html(html_chart)
212
- me.table(data=df.to_dict("records"), style=me.Style(margin=me.Margin(top=16)))
213
-
214
- elif dataset_type == "sales" and not state.sales_data.empty:
215
- df = state.sales_data
216
- me.text(f"Sales Orders ({len(df)} records)", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
217
- chart_data = create_sales_trend_chart(df)
218
- if chart_data:
219
- html_chart = plotly_to_html(chart_data)
220
- me.html(html_chart)
221
- me.table(data=df.head(100).to_dict("records"), style=me.Style(margin=me.Margin(top=16)))
222
-
223
- elif dataset_type == "uploaded" and not state.uploaded_data.empty:
224
- df = state.uploaded_data
225
- me.text(f"Uploaded Data ({len(df)} records)", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
226
- me.table(data=df.head(100).to_dict("records"), style=me.Style(margin=me.Margin(top=16)))
227
-
228
- else:
229
- me.text("No data available for this dataset type.", style=me.Style(color="#dc3545"))
230
-
231
-
232
- @me.page(path="/upload", title="Upload Data")
233
- def upload_page():
234
- """Upload page for custom datasets."""
235
- state = me.state(State)
236
-
237
- me.text("Upload Dataset", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
238
-
239
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
240
- me.text("Upload a CSV file to analyze:", style=me.Style(margin=me.Margin(bottom=8)))
241
- me.file_upload(
242
- label="Choose CSV File",
243
- accept=".csv",
244
- on_upload=handle_file_upload
245
- )
246
-
247
- if not state.uploaded_data.empty:
248
- me.text("Uploaded Data Preview:", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(top=16, bottom=8)))
249
- me.table(data=state.uploaded_data.head(50).to_dict("records"))
250
-
251
-
252
- def handle_file_upload(e: me.UploadEvent):
253
- """Handle file upload."""
254
- state = me.state(State)
255
- try:
256
- if e.files:
257
- file = e.files[0]
258
- df = pd.read_csv(file.getvalue())
259
- state.uploaded_data = df
260
- except Exception as ex:
261
- pass
262
-
263
-
264
- @me.page(path="/predictions", title="Predictions")
265
- def predictions_page():
266
- """Predictions page using SAP-RPT-1-OSS model."""
267
- state = me.state(State)
268
-
269
- me.text("AI Predictions with SAP-RPT-1-OSS", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
270
-
271
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
272
- me.text("Model Configuration", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
273
-
274
- me.select(
275
- label="Model Type",
276
- options=[
277
- me.SelectOption(label="Classifier", value="classifier"),
278
- me.SelectOption(label="Regressor", value="regressor")
279
- ]
280
- )
281
-
282
- me.checkbox(label="Use GPU (requires 80GB memory)", checked=False)
283
-
284
- me.button("Initialize Model", on_click=on_init_model)
285
-
286
- if state.model_initialized:
287
- me.text("Model initialized successfully!", style=me.Style(color="#28a745", margin=me.Margin(bottom=16)))
288
-
289
- # Dataset selection for training
290
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
291
- me.text("Select Training Data", style=me.Style(font_weight="bold", margin=me.Margin(bottom=8)))
292
- dataset_options = [
293
- ("GL Accounts", "gl"),
294
- ("Financial Statements", "financial"),
295
- ("Sales Orders", "sales"),
296
- ("Uploaded Data", "uploaded")
297
- ]
298
-
299
- me.select(
300
- label="Dataset",
301
- options=[me.SelectOption(label=label, value=value) for label, value in dataset_options]
302
- )
303
-
304
- me.button("Train Model", on_click=on_train_model)
305
-
306
- if state.model_trained:
307
- me.text("Model trained successfully!", style=me.Style(color="#28a745", margin=me.Margin(top=16)))
308
-
309
- if state.predictions is not None:
310
- me.text("Predictions:", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(top=16, bottom=8)))
311
- me.text(str(state.predictions[:10])) # Show first 10 predictions
312
-
313
- else:
314
- with me.box(style=me.Style(padding=16, background="#fff3cd", border_radius=8)):
315
- me.text("Please initialize the model first.", style=me.Style(color="#856404"))
316
-
317
-
318
- def on_init_model(e: me.ClickEvent):
319
- """Initialize the model."""
320
- state = me.state(State)
321
- try:
322
- state.model_wrapper = create_model(model_type="classifier", use_gpu=False)
323
- state.model_initialized = True
324
- except Exception as ex:
325
- state.connection_message = f"Error initializing model: {str(ex)}"
326
-
327
-
328
- def on_train_model(e: me.ClickEvent):
329
- """Train the model."""
330
- state = me.state(State)
331
- try:
332
- if state.model_wrapper and not state.gl_data.empty:
333
- # Simple example: use GL data
334
- X = state.gl_data.select_dtypes(include=[np.number]).dropna()
335
- if len(X) > 0:
336
- # Create a simple target for classification
337
- y = (X.iloc[:, 0] > X.iloc[:, 0].median()).astype(int)
338
- state.model_wrapper.fit(X, y)
339
- state.model_trained = True
340
- except Exception as ex:
341
- state.connection_message = f"Error training model: {str(ex)}"
342
-
343
-
344
- @me.page(path="/odata", title="OData Connection")
345
- def odata_page():
346
- """OData connection page for SAP data."""
347
- state = me.state(State)
348
-
349
- me.text("SAP OData Connection", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
350
-
351
- # Connection status
352
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
353
- if state.odata_connector is None:
354
- state.odata_connector = SAPFinanceConnector()
355
-
356
- me.button("Test Connection", on_click=on_test_odata_connection)
357
-
358
- if state.connection_message:
359
- color = "#28a745" if state.odata_connected else "#dc3545"
360
- me.text(state.connection_message, style=me.Style(color=color, margin=me.Margin(top=8)))
361
- elif state.odata_connected:
362
- me.text("✓ Connected to SAP OData", style=me.Style(color="#28a745", margin=me.Margin(top=8)))
363
- else:
364
- me.text("Not connected", style=me.Style(color="#dc3545", margin=me.Margin(top=8)))
365
-
366
- # Fetch options
367
- if state.odata_connected:
368
- with me.box(style=me.Style(margin=me.Margin(bottom=16))):
369
- me.text("Fetch Data", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(bottom=8)))
370
-
371
- top_count = me.number_input(label="Number of records", value=100, min_value=1, max_value=1000)
372
-
373
- with me.box(style=me.Style(display="grid", grid_template_columns="repeat(2, 1fr)", gap=8, margin=me.Margin(top=8))):
374
- me.button("Fetch Sales Orders", on_click=lambda e: on_fetch_odata(e, "orders", top_count))
375
- me.button("Fetch Products", on_click=lambda e: on_fetch_odata(e, "products", top_count))
376
- me.button("Fetch Line Items", on_click=lambda e: on_fetch_odata(e, "line_items", top_count))
377
- me.button("Fetch Partners", on_click=lambda e: on_fetch_odata(e, "partners", top_count))
378
-
379
- if state.fetch_message:
380
- me.text(state.fetch_message, style=me.Style(color="#28a745", margin=me.Margin(top=8)))
381
-
382
- # Display fetched data
383
- if not state.odata_data.empty:
384
- me.text("Fetched Data:", style=me.Style(font_size=20, font_weight="bold", margin=me.Margin(top=16, bottom=8)))
385
- me.table(data=state.odata_data.head(100).to_dict("records"))
386
-
387
-
388
- def on_test_odata_connection(e: me.ClickEvent):
389
- """Test OData connection."""
390
- state = me.state(State)
391
- try:
392
- if state.odata_connector is None:
393
- state.odata_connector = SAPFinanceConnector()
394
-
395
- connected, message = state.odata_connector.test_connection()
396
- state.odata_connected = connected
397
- state.connection_message = message
398
- except Exception as ex:
399
- state.connection_message = f"Error: {str(ex)}"
400
- state.odata_connected = False
401
-
402
-
403
- def on_fetch_odata(e: me.ClickEvent, entity_type: str, top: int):
404
- """Fetch data from OData service."""
405
- state = me.state(State)
406
- try:
407
- if not state.odata_connected:
408
- state.fetch_message = "Please connect first!"
409
- return
410
-
411
- if entity_type == "orders":
412
- state.odata_data = state.odata_connector.fetch_orders_df(top)
413
- elif entity_type == "products":
414
- state.odata_data = state.odata_connector.fetch_products_df(top)
415
- elif entity_type == "line_items":
416
- state.odata_data = state.odata_connector.fetch_line_items_df(top)
417
- elif entity_type == "partners":
418
- state.odata_data = state.odata_connector.fetch_partners_df(top)
419
-
420
- state.fetch_message = f"Fetched {len(state.odata_data)} records"
421
- except Exception as ex:
422
- state.fetch_message = f"Error fetching data: {str(ex)}"
423
-
424
-
425
- # Navigation
426
- @me.page(path="/nav", title="Navigation")
427
- def nav_page():
428
- """Navigation page."""
429
- me.text("Navigation", style=me.Style(font_size=32, font_weight="bold", margin=me.Margin(bottom=16)))
430
-
431
- nav_links = [
432
- ("Dashboard", "/"),
433
- ("Data Explorer", "/explorer"),
434
- ("Upload", "/upload"),
435
- ("Predictions", "/predictions"),
436
- ("OData", "/odata")
437
- ]
438
-
439
- for label, path in nav_links:
440
- with me.box(style=me.Style(margin=me.Margin(bottom=8))):
441
- me.link(label, path=path, style=me.Style(font_size=18, text_decoration="none"))
442
-
443
-
444
- if __name__ == "__main__":
445
- # Generate datasets if they don't exist
446
- data_dir = Path("data")
447
- if not (data_dir / "synthetic_gl_accounts.csv").exists():
448
- generate_all_datasets()
449
-
450
- me.run()
451
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app_gradio.py CHANGED
@@ -1415,8 +1415,14 @@ with gr.Blocks(title="SAP Finance Dashboard", theme=gr.themes.Soft(), css="""
1415
 
1416
 
1417
  if __name__ == "__main__":
 
 
1418
  # Load datasets on startup
1419
  load_datasets()
1420
 
 
 
 
 
1421
  # Launch the app
1422
- app.launch(share=False, server_name="127.0.0.1", server_port=7862, quiet=False)
 
1415
 
1416
 
1417
  if __name__ == "__main__":
1418
+ import os
1419
+
1420
  # Load datasets on startup
1421
  load_datasets()
1422
 
1423
+ # Get server configuration from environment variables (for container deployment)
1424
+ server_name = os.environ.get("GRADIO_SERVER_NAME", "0.0.0.0")
1425
+ server_port = int(os.environ.get("GRADIO_SERVER_PORT", 7862))
1426
+
1427
  # Launch the app
1428
+ app.launch(share=False, server_name=server_name, server_port=server_port, quiet=False)
deploy-azure.ps1 ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Azure Container Apps Deployment Script for SAP Finance Dashboard
2
+ # Run this script in PowerShell after logging in to Azure CLI
3
+
4
+ # ============================================
5
+ # CONFIGURATION - Update these values
6
+ # ============================================
7
+ $RESOURCE_GROUP = "rg-sap-finance-dashboard"
8
+ $LOCATION = "eastus"
9
+ $ACR_NAME = "acrsapfinance$(Get-Random -Maximum 9999)" # Must be globally unique
10
+ $ENVIRONMENT_NAME = "env-sap-finance"
11
+ $APP_NAME = "sap-finance-dashboard"
12
+
13
+ # Optional: Set your secrets (or configure later in Azure Portal)
14
+ $HUGGINGFACE_TOKEN = "" # Your Hugging Face token
15
+ $SAP_USERNAME = "" # SAP OData username
16
+ $SAP_PASSWORD = "" # SAP OData password
17
+
18
+ # ============================================
19
+ # STEP 1: Login to Azure (if not already logged in)
20
+ # ============================================
21
+ Write-Host "Step 1: Checking Azure login..." -ForegroundColor Cyan
22
+ $account = az account show 2>$null | ConvertFrom-Json
23
+ if (-not $account) {
24
+ Write-Host "Please login to Azure..." -ForegroundColor Yellow
25
+ az login
26
+ }
27
+ Write-Host "Logged in as: $($account.user.name)" -ForegroundColor Green
28
+
29
+ # ============================================
30
+ # STEP 2: Create Resource Group
31
+ # ============================================
32
+ Write-Host "`nStep 2: Creating Resource Group..." -ForegroundColor Cyan
33
+ az group create --name $RESOURCE_GROUP --location $LOCATION
34
+ Write-Host "Resource Group '$RESOURCE_GROUP' created in $LOCATION" -ForegroundColor Green
35
+
36
+ # ============================================
37
+ # STEP 3: Create Azure Container Registry
38
+ # ============================================
39
+ Write-Host "`nStep 3: Creating Azure Container Registry..." -ForegroundColor Cyan
40
+ az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic --admin-enabled true
41
+ Write-Host "Container Registry '$ACR_NAME' created" -ForegroundColor Green
42
+
43
+ # Get ACR credentials
44
+ $ACR_USERNAME = az acr credential show --name $ACR_NAME --query username -o tsv
45
+ $ACR_PASSWORD = az acr credential show --name $ACR_NAME --query "passwords[0].value" -o tsv
46
+ $ACR_LOGIN_SERVER = az acr show --name $ACR_NAME --query loginServer -o tsv
47
+
48
+ Write-Host "ACR Login Server: $ACR_LOGIN_SERVER" -ForegroundColor Yellow
49
+
50
+ # ============================================
51
+ # STEP 4: Build and Push Docker Image
52
+ # ============================================
53
+ Write-Host "`nStep 4: Building and pushing Docker image..." -ForegroundColor Cyan
54
+ Write-Host "This may take several minutes..." -ForegroundColor Yellow
55
+
56
+ # Build using ACR Tasks (no local Docker required)
57
+ az acr build --registry $ACR_NAME --image "${APP_NAME}:latest" .
58
+
59
+ Write-Host "Docker image built and pushed to ACR" -ForegroundColor Green
60
+
61
+ # ============================================
62
+ # STEP 5: Create Container Apps Environment
63
+ # ============================================
64
+ Write-Host "`nStep 5: Creating Container Apps Environment..." -ForegroundColor Cyan
65
+ az containerapp env create `
66
+ --name $ENVIRONMENT_NAME `
67
+ --resource-group $RESOURCE_GROUP `
68
+ --location $LOCATION
69
+
70
+ Write-Host "Container Apps Environment '$ENVIRONMENT_NAME' created" -ForegroundColor Green
71
+
72
+ # ============================================
73
+ # STEP 6: Deploy Container App
74
+ # ============================================
75
+ Write-Host "`nStep 6: Deploying Container App..." -ForegroundColor Cyan
76
+
77
+ # Build the secrets parameter if tokens are provided
78
+ $secretsParam = ""
79
+ $envVarsParam = ""
80
+
81
+ if ($HUGGINGFACE_TOKEN) {
82
+ $secretsParam = "--secrets huggingface-token=$HUGGINGFACE_TOKEN"
83
+ $envVarsParam = "--env-vars HUGGINGFACE_TOKEN=secretref:huggingface-token"
84
+ }
85
+
86
+ # Create the container app
87
+ az containerapp create `
88
+ --name $APP_NAME `
89
+ --resource-group $RESOURCE_GROUP `
90
+ --environment $ENVIRONMENT_NAME `
91
+ --image "${ACR_LOGIN_SERVER}/${APP_NAME}:latest" `
92
+ --registry-server $ACR_LOGIN_SERVER `
93
+ --registry-username $ACR_USERNAME `
94
+ --registry-password $ACR_PASSWORD `
95
+ --target-port 7862 `
96
+ --ingress external `
97
+ --cpu 2 `
98
+ --memory 4Gi `
99
+ --min-replicas 1 `
100
+ --max-replicas 3 `
101
+ --query properties.configuration.ingress.fqdn
102
+
103
+ Write-Host "`nContainer App deployed!" -ForegroundColor Green
104
+
105
+ # ============================================
106
+ # STEP 7: Get Application URL
107
+ # ============================================
108
+ Write-Host "`nStep 7: Getting Application URL..." -ForegroundColor Cyan
109
+ $APP_URL = az containerapp show `
110
+ --name $APP_NAME `
111
+ --resource-group $RESOURCE_GROUP `
112
+ --query properties.configuration.ingress.fqdn -o tsv
113
+
114
+ Write-Host "`n============================================" -ForegroundColor Green
115
+ Write-Host "DEPLOYMENT COMPLETE!" -ForegroundColor Green
116
+ Write-Host "============================================" -ForegroundColor Green
117
+ Write-Host "`nYour SAP Finance Dashboard is available at:" -ForegroundColor Cyan
118
+ Write-Host "https://$APP_URL" -ForegroundColor Yellow
119
+ Write-Host "`n============================================" -ForegroundColor Green
120
+
121
+ # ============================================
122
+ # NEXT STEPS
123
+ # ============================================
124
+ Write-Host "`nNEXT STEPS:" -ForegroundColor Cyan
125
+ Write-Host "1. Configure secrets in Azure Portal:" -ForegroundColor White
126
+ Write-Host " - HUGGINGFACE_TOKEN: Your Hugging Face API token" -ForegroundColor Gray
127
+ Write-Host " - SAP_USERNAME: SAP OData username (optional)" -ForegroundColor Gray
128
+ Write-Host " - SAP_PASSWORD: SAP OData password (optional)" -ForegroundColor Gray
129
+ Write-Host "`n2. To update the app after code changes:" -ForegroundColor White
130
+ Write-Host " az acr build --registry $ACR_NAME --image ${APP_NAME}:latest ." -ForegroundColor Gray
131
+ Write-Host " az containerapp update --name $APP_NAME --resource-group $RESOURCE_GROUP --image ${ACR_LOGIN_SERVER}/${APP_NAME}:latest" -ForegroundColor Gray
132
+ Write-Host "`n3. To view logs:" -ForegroundColor White
133
+ Write-Host " az containerapp logs show --name $APP_NAME --resource-group $RESOURCE_GROUP --follow" -ForegroundColor Gray
134
+ Write-Host "`n4. To delete all resources:" -ForegroundColor White
135
+ Write-Host " az group delete --name $RESOURCE_GROUP --yes --no-wait" -ForegroundColor Gray
requirements.txt CHANGED
@@ -1,4 +1,3 @@
1
- mesop>=0.4.0
2
  pandas>=2.0.0
3
  numpy>=1.24.0
4
  #sap-rpt-oss
@@ -7,7 +6,7 @@ plotly>=5.17.0
7
  requests>=2.31.0
8
  python-dotenv>=1.0.0
9
  scikit-learn>=1.3.0
10
- gradio
11
  pyarrow>=10.0.0
12
  pyzmq>=25.0.0
13
 
 
 
1
  pandas>=2.0.0
2
  numpy>=1.24.0
3
  #sap-rpt-oss
 
6
  requests>=2.31.0
7
  python-dotenv>=1.0.0
8
  scikit-learn>=1.3.0
9
+ gradio>=4.0.0
10
  pyarrow>=10.0.0
11
  pyzmq>=25.0.0
12