Initial commit: Meta File Server Query Application
- Flask web application for querying Oliver metafile server - Replicates Make.com workflow for job data retrieval - Two-stage process: XML client extraction → JSON data retrieval - Clean HTML interface with responsive design - Comprehensive error handling and SSL fixes - Complete documentation and installation guides 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
dd77b199e0
9 changed files with 2571 additions and 0 deletions
62
.gitignore
vendored
Normal file
62
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# Virtual Environment
|
||||
meta-server-venv/
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Flask
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
1538
AI COMPANION Metafile Server Grabber.blueprint.json
Normal file
1538
AI COMPANION Metafile Server Grabber.blueprint.json
Normal file
File diff suppressed because it is too large
Load diff
285
INSTALL.md
Normal file
285
INSTALL.md
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
# Installation Guide - Meta File Server Query Application
|
||||
|
||||
This guide will help you install and run the Meta File Server Query application on different systems.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Software
|
||||
- **Python 3.7 or higher** - [Download Python](https://www.python.org/downloads/)
|
||||
- **Git** (optional) - For cloning the repository
|
||||
- **Modern web browser** - Chrome, Firefox, Safari, or Edge
|
||||
|
||||
### System Requirements
|
||||
- **RAM**: 512MB minimum, 1GB recommended
|
||||
- **Storage**: 100MB free space
|
||||
- **Network**: Internet access to reach metafile.oliver.solutions
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### Method 1: Quick Install (Recommended)
|
||||
|
||||
1. **Download/Copy the application folder** to your desired location
|
||||
2. **Open Terminal/Command Prompt** and navigate to the folder:
|
||||
```bash
|
||||
cd /path/to/META-FILE-SERVER-QUERY
|
||||
```
|
||||
3. **Run the startup script**:
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
This will automatically:
|
||||
- Create the virtual environment
|
||||
- Install all dependencies
|
||||
- Start the application
|
||||
|
||||
### Method 2: Manual Installation
|
||||
|
||||
1. **Navigate to the application directory**:
|
||||
```bash
|
||||
cd /path/to/META-FILE-SERVER-QUERY
|
||||
```
|
||||
|
||||
2. **Create Python virtual environment**:
|
||||
```bash
|
||||
python3 -m venv meta-server-venv
|
||||
```
|
||||
|
||||
3. **Activate the virtual environment**:
|
||||
|
||||
**On macOS/Linux**:
|
||||
```bash
|
||||
source meta-server-venv/bin/activate
|
||||
```
|
||||
|
||||
**On Windows**:
|
||||
```cmd
|
||||
meta-server-venv\Scripts\activate
|
||||
```
|
||||
|
||||
4. **Install dependencies**:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
5. **Run the application**:
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
## Platform-Specific Instructions
|
||||
|
||||
### macOS
|
||||
|
||||
1. **Install Python 3** (if not already installed):
|
||||
```bash
|
||||
# Using Homebrew (recommended)
|
||||
brew install python3
|
||||
|
||||
# Or download from python.org
|
||||
```
|
||||
|
||||
2. **Make startup script executable**:
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
```
|
||||
|
||||
3. **Run the application**:
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. **Install Python 3** from [python.org](https://www.python.org/downloads/windows/)
|
||||
- ✅ Check "Add Python to PATH" during installation
|
||||
|
||||
2. **Open Command Prompt or PowerShell**:
|
||||
```cmd
|
||||
cd C:\path\to\META-FILE-SERVER-QUERY
|
||||
```
|
||||
|
||||
3. **Create virtual environment**:
|
||||
```cmd
|
||||
python -m venv meta-server-venv
|
||||
```
|
||||
|
||||
4. **Activate virtual environment**:
|
||||
```cmd
|
||||
meta-server-venv\Scripts\activate
|
||||
```
|
||||
|
||||
5. **Install dependencies**:
|
||||
```cmd
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
6. **Run application**:
|
||||
```cmd
|
||||
python run.py
|
||||
```
|
||||
|
||||
### Linux (Ubuntu/Debian)
|
||||
|
||||
1. **Install Python 3 and pip**:
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install python3 python3-pip python3-venv
|
||||
```
|
||||
|
||||
2. **Navigate and setup**:
|
||||
```bash
|
||||
cd /path/to/META-FILE-SERVER-QUERY
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Linux (CentOS/RHEL/Fedora)
|
||||
|
||||
1. **Install Python 3**:
|
||||
```bash
|
||||
# CentOS/RHEL
|
||||
sudo yum install python3 python3-pip
|
||||
|
||||
# Fedora
|
||||
sudo dnf install python3 python3-pip
|
||||
```
|
||||
|
||||
2. **Run setup**:
|
||||
```bash
|
||||
cd /path/to/META-FILE-SERVER-QUERY
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
## Docker Installation (Optional)
|
||||
|
||||
If you prefer using Docker:
|
||||
|
||||
1. **Create Dockerfile**:
|
||||
```dockerfile
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["python", "run.py"]
|
||||
```
|
||||
|
||||
2. **Build and run**:
|
||||
```bash
|
||||
docker build -t meta-file-server .
|
||||
docker run -p 5000:5000 meta-file-server
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After installation, verify the application is working:
|
||||
|
||||
1. **Check the startup output** - you should see:
|
||||
```
|
||||
Meta File Server Query Application
|
||||
====================================
|
||||
Starting server on http://localhost:5000
|
||||
Press Ctrl+C to stop the server
|
||||
```
|
||||
|
||||
2. **Open your web browser** and go to: `http://localhost:5000`
|
||||
|
||||
3. **Test with a job number** (e.g., `6046034`)
|
||||
|
||||
4. **Check the API directly**: `http://localhost:5000/api/health`
|
||||
|
||||
## Troubleshooting Installation
|
||||
|
||||
### Python Not Found
|
||||
**Error**: `python3: command not found`
|
||||
|
||||
**Solution**:
|
||||
- Install Python 3 from python.org
|
||||
- On Windows, try `python` instead of `python3`
|
||||
- Make sure Python is added to your system PATH
|
||||
|
||||
### Permission Denied
|
||||
**Error**: `Permission denied: ./start.sh`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
```
|
||||
|
||||
### Module Not Found
|
||||
**Error**: `ModuleNotFoundError: No module named 'flask'`
|
||||
|
||||
**Solution**:
|
||||
- Make sure you activated the virtual environment
|
||||
- Run: `pip install -r requirements.txt`
|
||||
|
||||
### Port Already in Use
|
||||
**Error**: `OSError: [Errno 48] Address already in use`
|
||||
|
||||
**Solution**:
|
||||
- Stop any other applications using port 5000
|
||||
- Or modify `run.py` to use a different port:
|
||||
```python
|
||||
app.run(debug=True, host='0.0.0.0', port=5001) # Change port
|
||||
```
|
||||
|
||||
### SSL Certificate Warnings
|
||||
**Warning**: `InsecureRequestWarning: Unverified HTTPS request`
|
||||
|
||||
**This is expected** - The application disables SSL verification for the metafile server due to certificate issues. This is safe for this specific use case.
|
||||
|
||||
## Uninstallation
|
||||
|
||||
To remove the application:
|
||||
|
||||
1. **Stop the application** (Ctrl+C)
|
||||
2. **Delete the entire folder**:
|
||||
```bash
|
||||
rm -rf /path/to/META-FILE-SERVER-QUERY
|
||||
```
|
||||
|
||||
The application is self-contained in its directory and doesn't install anything system-wide.
|
||||
|
||||
## Moving to Another Machine
|
||||
|
||||
To transfer the application to another computer:
|
||||
|
||||
1. **Copy the entire META-FILE-SERVER-QUERY folder**
|
||||
2. **Install Python 3** on the target machine
|
||||
3. **Run the installation** using Method 1 or 2 above
|
||||
|
||||
The `meta-server-venv` folder can be deleted before copying - it will be recreated during installation.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production use on a server:
|
||||
|
||||
1. **Install using Method 2** (manual installation)
|
||||
2. **Install a production WSGI server**:
|
||||
```bash
|
||||
pip install gunicorn
|
||||
```
|
||||
3. **Run with Gunicorn**:
|
||||
```bash
|
||||
gunicorn -w 4 -b 0.0.0.0:5000 app:app
|
||||
```
|
||||
4. **Set up reverse proxy** (nginx/Apache) and SSL certificate
|
||||
5. **Configure firewall** to allow traffic on your chosen port
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues during installation:
|
||||
|
||||
1. Check the **Troubleshooting** section above
|
||||
2. Verify your Python version: `python3 --version`
|
||||
3. Check that all required files are present in the application directory
|
||||
4. Ensure you have internet connectivity to download dependencies
|
||||
|
||||
---
|
||||
|
||||
**Need help?** The application includes comprehensive error messages and logging to help diagnose issues.
|
||||
151
README.md
Normal file
151
README.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# Meta File Server Query Application
|
||||
|
||||
A Python Flask web application that replicates the Make.com workflow for querying the Oliver metafile server to retrieve job information based on deliverable numbers.
|
||||
|
||||
## Features
|
||||
|
||||
- **Two-stage workflow**: First fetches XML to extract client information, then retrieves JSON data
|
||||
- **Secure authentication**: Uses MD5 checksum authentication matching the original blueprint
|
||||
- **Clean web interface**: Simple HTML form with responsive design
|
||||
- **Error handling**: Comprehensive error handling for network issues, SSL problems, and parsing errors
|
||||
- **Portable**: Self-contained with virtual environment for easy deployment
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Use the startup script
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Option 2: Manual setup
|
||||
```bash
|
||||
# Create and activate virtual environment
|
||||
python3 -m venv meta-server-venv
|
||||
source meta-server-venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run the application
|
||||
python run.py
|
||||
```
|
||||
|
||||
Then open your browser to: `http://localhost:5000`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Enter a job/deliverable number in the web form
|
||||
2. Click "Query Job Information"
|
||||
3. The application will:
|
||||
- Fetch the XML file from the metafile server
|
||||
- Extract the client name from the jobpath
|
||||
- Retrieve the corresponding JSON data using the client information
|
||||
- Display the results in a formatted view
|
||||
|
||||
## API Endpoints
|
||||
|
||||
- `GET /` - Main web interface
|
||||
- `GET /api/job/<job_number>` - Retrieve job information as JSON
|
||||
- `GET /api/health` - Health check endpoint
|
||||
|
||||
## Example API Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"job_number": "6046034",
|
||||
"client": "ADIDAS",
|
||||
"data": {
|
||||
"JobSpecification": {
|
||||
// ... job data ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The application uses these default settings:
|
||||
|
||||
- **Metafile Server**: `https://metafile.oliver.solutions/getFile`
|
||||
- **API Key**: `$14W0TF~8FL` (from original blueprint)
|
||||
- **Port**: 5000
|
||||
- **SSL Verification**: Disabled (due to certificate issues)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
META-FILE-SERVER-QUERY/
|
||||
├── app.py # Main Flask application
|
||||
├── run.py # Application runner
|
||||
├── start.sh # Startup script
|
||||
├── requirements.txt # Python dependencies
|
||||
├── templates/
|
||||
│ └── index.html # Web interface
|
||||
├── static/ # Static assets (empty)
|
||||
└── meta-server-venv/ # Python virtual environment
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Workflow Process
|
||||
|
||||
1. **XML Retrieval**:
|
||||
- Constructs URL: `{base_url}?root=XML&path={job_number}.xml&keyid=1&cs={checksum}`
|
||||
- Checksum: `MD5(api_key + "XML/" + filename)`
|
||||
|
||||
2. **Client Extraction**:
|
||||
- Parses XML for `Set-Property` with `Property="jobpath"`
|
||||
- Extracts client name using regex: `([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)`
|
||||
|
||||
3. **JSON Retrieval**:
|
||||
- Constructs URL: `{base_url}?root=JSON_STORE&sub={client}&path={job_number}.json&keyid=1&cs={checksum}`
|
||||
- Checksum: `MD5(api_key + "JSON_STORE/" + client + "/" + filename)`
|
||||
|
||||
### Error Handling
|
||||
|
||||
- SSL certificate verification disabled for metafile.oliver.solutions
|
||||
- Network timeout protection (30 seconds)
|
||||
- XML parsing error handling
|
||||
- JSON parsing error handling
|
||||
- HTTP status code validation
|
||||
|
||||
## Moving to Production
|
||||
|
||||
To deploy this application:
|
||||
|
||||
1. **Copy the entire directory** to your target server
|
||||
2. **Install Python 3.7+** on the target system
|
||||
3. **Run the startup script**: `./start.sh`
|
||||
4. **For production**: Consider using a WSGI server like Gunicorn:
|
||||
```bash
|
||||
pip install gunicorn
|
||||
gunicorn -w 4 -b 0.0.0.0:5000 app:app
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSL Certificate Issues
|
||||
The application automatically handles SSL certificate verification issues by disabling verification. This is necessary because the metafile server has certificate problems.
|
||||
|
||||
### Connection Timeouts
|
||||
The application has a 30-second timeout for requests. If you experience frequent timeouts, check your network connection to the metafile server.
|
||||
|
||||
### Missing Job Data
|
||||
If a job number returns an error, verify:
|
||||
1. The job number exists in the XML system
|
||||
2. The corresponding JSON file exists in the JSON_STORE
|
||||
3. The client name extraction is working correctly
|
||||
|
||||
## Development
|
||||
|
||||
To modify or extend the application:
|
||||
|
||||
1. Activate the virtual environment: `source meta-server-venv/bin/activate`
|
||||
2. Make your changes to `app.py` or `templates/index.html`
|
||||
3. Test your changes: `python run.py`
|
||||
4. The server will automatically reload in debug mode
|
||||
|
||||
---
|
||||
|
||||
*Converted from Make.com blueprint: "AI COMPANION Metafile Server Grabber"*
|
||||
197
app.py
Normal file
197
app.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
import hashlib
|
||||
import re
|
||||
import requests
|
||||
import xml.etree.ElementTree as ET
|
||||
from flask import Flask, render_template, jsonify, request
|
||||
from urllib.parse import quote
|
||||
import urllib3
|
||||
|
||||
# Disable SSL warnings when using verify=False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Configuration
|
||||
METAFILE_BASE_URL = "https://metafile.oliver.solutions/getFile"
|
||||
METAFILE_KEY = "$14W0TF~8FL"
|
||||
|
||||
class MetafileClient:
|
||||
def __init__(self, base_url, api_key):
|
||||
self.base_url = base_url
|
||||
self.api_key = api_key
|
||||
|
||||
def _generate_checksum(self, root, path, sub=None):
|
||||
"""Generate MD5 checksum for metafile API authentication"""
|
||||
if sub:
|
||||
checksum_string = f"{self.api_key}{root}/{sub}/{path}"
|
||||
else:
|
||||
checksum_string = f"{self.api_key}{root}/{path}"
|
||||
return hashlib.md5(checksum_string.encode('utf-8')).hexdigest()
|
||||
|
||||
def get_file(self, root, path, keyid=1, sub=None):
|
||||
"""Make request to metafile server"""
|
||||
checksum = self._generate_checksum(root, path, sub)
|
||||
|
||||
params = {
|
||||
'root': root,
|
||||
'path': path,
|
||||
'keyid': keyid,
|
||||
'cs': checksum
|
||||
}
|
||||
|
||||
if sub:
|
||||
params['sub'] = sub
|
||||
|
||||
try:
|
||||
response = requests.get(self.base_url, params=params, timeout=30, verify=False)
|
||||
response.raise_for_status()
|
||||
return {
|
||||
'success': True,
|
||||
'data': response.text,
|
||||
'status_code': response.status_code
|
||||
}
|
||||
except requests.exceptions.RequestException as e:
|
||||
return {
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'status_code': getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None
|
||||
}
|
||||
|
||||
def get_client_from_xml(self, deliverable_number):
|
||||
"""Get client information from XML file"""
|
||||
xml_filename = f"{deliverable_number}.xml"
|
||||
result = self.get_file(root="XML", path=xml_filename)
|
||||
|
||||
if not result['success']:
|
||||
return result
|
||||
|
||||
try:
|
||||
# Parse XML
|
||||
root_elem = ET.fromstring(result['data'])
|
||||
|
||||
# Look for Set-Property elements with Property="jobpath"
|
||||
jobpath_elements = root_elem.findall(".//Set-Property[@Property='jobpath']")
|
||||
|
||||
for prop in jobpath_elements:
|
||||
jobpath_value = prop.get('Value', '')
|
||||
if jobpath_value and '/CAMPAIGNS/' in jobpath_value:
|
||||
# Extract client name from jobpath like "/ADIDAS_CAM_OLIVER_KUALA_LUMPUR/ADIDAS/CAMPAIGNS/..."
|
||||
# Use regex to extract client name before /CAMPAIGNS
|
||||
client_pattern = r'([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)'
|
||||
match = re.search(client_pattern, jobpath_value)
|
||||
if match:
|
||||
return {
|
||||
'success': True,
|
||||
'client': match.group(1)
|
||||
}
|
||||
|
||||
# Fallback: look for any Value containing /CAMPAIGNS/
|
||||
set_properties = root_elem.findall(".//Set-Property[@Value]")
|
||||
for prop in set_properties:
|
||||
value = prop.get('Value', '')
|
||||
if '/CAMPAIGNS/' in value:
|
||||
client_pattern = r'([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)'
|
||||
match = re.search(client_pattern, value)
|
||||
if match:
|
||||
return {
|
||||
'success': True,
|
||||
'client': match.group(1)
|
||||
}
|
||||
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'Could not extract client information from XML'
|
||||
}
|
||||
|
||||
except ET.ParseError as e:
|
||||
return {
|
||||
'success': False,
|
||||
'error': f'XML parsing error: {str(e)}'
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
'success': False,
|
||||
'error': f'Error processing XML: {str(e)}'
|
||||
}
|
||||
|
||||
def get_job_data(self, deliverable_number):
|
||||
"""Get complete job data (XML + JSON workflow)"""
|
||||
# Step 1: Get client from XML
|
||||
client_result = self.get_client_from_xml(deliverable_number)
|
||||
|
||||
if not client_result['success']:
|
||||
return client_result
|
||||
|
||||
client = client_result['client']
|
||||
|
||||
# Step 2: Get JSON data using client info
|
||||
json_filename = f"{deliverable_number}.json"
|
||||
json_result = self.get_file(root="JSON_STORE", path=json_filename, sub=client)
|
||||
|
||||
if not json_result['success']:
|
||||
return json_result
|
||||
|
||||
# Check if response is 200 OK
|
||||
if json_result['status_code'] != 200:
|
||||
return {
|
||||
'success': False,
|
||||
'error': f'JSON file not found or inaccessible (HTTP {json_result["status_code"]})'
|
||||
}
|
||||
|
||||
try:
|
||||
import json
|
||||
json_data = json.loads(json_result['data'])
|
||||
return {
|
||||
'success': True,
|
||||
'client': client,
|
||||
'data': json_data
|
||||
}
|
||||
except json.JSONDecodeError as e:
|
||||
return {
|
||||
'success': False,
|
||||
'error': f'JSON parsing error: {str(e)}'
|
||||
}
|
||||
|
||||
# Initialize metafile client
|
||||
metafile_client = MetafileClient(METAFILE_BASE_URL, METAFILE_KEY)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""Serve the main HTML page"""
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/job/<job_number>')
|
||||
def get_job_info(job_number):
|
||||
"""API endpoint to get job information"""
|
||||
if not job_number:
|
||||
return jsonify({'error': 'Job number is required'}), 400
|
||||
|
||||
# Sanitize job number (remove any non-alphanumeric characters except hyphens and underscores)
|
||||
job_number = re.sub(r'[^a-zA-Z0-9\-_]', '', job_number)
|
||||
|
||||
if not job_number:
|
||||
return jsonify({'error': 'Invalid job number format'}), 400
|
||||
|
||||
result = metafile_client.get_job_data(job_number)
|
||||
|
||||
if result['success']:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'job_number': job_number,
|
||||
'client': result['client'],
|
||||
'data': result['data']
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'job_number': job_number,
|
||||
'error': result['error']
|
||||
}), 400
|
||||
|
||||
@app.route('/api/health')
|
||||
def health_check():
|
||||
"""Health check endpoint"""
|
||||
return jsonify({'status': 'healthy', 'service': 'Meta File Server Query'})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
13
requirements.txt
Normal file
13
requirements.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
blinker==1.9.0
|
||||
certifi==2025.8.3
|
||||
charset-normalizer==3.4.3
|
||||
click==8.2.1
|
||||
Flask==3.1.2
|
||||
idna==3.10
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
lxml==6.0.1
|
||||
MarkupSafe==3.0.2
|
||||
requests==2.32.5
|
||||
urllib3==2.5.0
|
||||
Werkzeug==3.1.3
|
||||
34
run.py
Normal file
34
run.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Meta File Server Query Application
|
||||
|
||||
A simple Flask application that replicates the Make.com workflow for querying
|
||||
the Oliver metafile server to retrieve job information based on deliverable numbers.
|
||||
|
||||
Usage:
|
||||
python run.py
|
||||
|
||||
The server will start on http://localhost:5000
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the current directory to Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from app import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("=" * 60)
|
||||
print("Meta File Server Query Application")
|
||||
print("=" * 60)
|
||||
print("Starting server on http://localhost:5000")
|
||||
print("Press Ctrl+C to stop the server")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nServer stopped by user.")
|
||||
sys.exit(0)
|
||||
19
start.sh
Executable file
19
start.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Meta File Server Query - Start Script
|
||||
echo "Starting Meta File Server Query Application..."
|
||||
|
||||
# Check if virtual environment exists
|
||||
if [ ! -d "meta-server-venv" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv meta-server-venv
|
||||
echo "Installing dependencies..."
|
||||
source meta-server-venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
else
|
||||
echo "Activating virtual environment..."
|
||||
source meta-server-venv/bin/activate
|
||||
fi
|
||||
|
||||
echo "Starting Flask application..."
|
||||
python run.py
|
||||
272
templates/index.html
Normal file
272
templates/index.html
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Meta File Server Query</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 3px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result.success {
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.result.error {
|
||||
background-color: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.json-data {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin-top: 10px;
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.client-info {
|
||||
background-color: #e7f3ff;
|
||||
border: 1px solid #b8daff;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Meta File Server Query</h1>
|
||||
|
||||
<form id="queryForm">
|
||||
<div class="form-group">
|
||||
<label for="jobNumber">Job Number / Deliverable Number:</label>
|
||||
<input type="text" id="jobNumber" name="jobNumber" placeholder="Enter job number (e.g., 12345)" required>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit">Query Job Information</button>
|
||||
<button type="button" onclick="clearResults()">Clear</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="loading" id="loading">
|
||||
<p>🔍 Querying meta file server...</p>
|
||||
</div>
|
||||
|
||||
<div class="result" id="result">
|
||||
<div id="resultContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById('queryForm');
|
||||
const loading = document.getElementById('loading');
|
||||
const result = document.getElementById('result');
|
||||
const resultContent = document.getElementById('resultContent');
|
||||
const jobNumberInput = document.getElementById('jobNumber');
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const jobNumber = jobNumberInput.value.trim();
|
||||
|
||||
if (!jobNumber) {
|
||||
showError('Please enter a job number');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
loading.style.display = 'block';
|
||||
result.style.display = 'none';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/job/${encodeURIComponent(jobNumber)}`);
|
||||
const data = await response.json();
|
||||
|
||||
loading.style.display = 'none';
|
||||
|
||||
if (data.success) {
|
||||
showSuccess(data);
|
||||
} else {
|
||||
showError(data.error || 'Unknown error occurred');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
loading.style.display = 'none';
|
||||
showError('Network error: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
function showSuccess(data) {
|
||||
result.className = 'result success';
|
||||
result.style.display = 'block';
|
||||
|
||||
let html = `
|
||||
<h3>✅ Job Information Retrieved</h3>
|
||||
<p><strong>Job Number:</strong> ${escapeHtml(data.job_number)}</p>
|
||||
`;
|
||||
|
||||
if (data.client) {
|
||||
html += `
|
||||
<div class="client-info">
|
||||
<strong>Client:</strong> ${escapeHtml(data.client)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (data.data) {
|
||||
html += `
|
||||
<p><strong>Data:</strong></p>
|
||||
<div class="json-data">${escapeHtml(JSON.stringify(data.data, null, 2))}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
resultContent.innerHTML = html;
|
||||
}
|
||||
|
||||
function showError(error) {
|
||||
result.className = 'result error';
|
||||
result.style.display = 'block';
|
||||
resultContent.innerHTML = `
|
||||
<h3>❌ Error</h3>
|
||||
<p>${escapeHtml(error)}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
function clearResults() {
|
||||
jobNumberInput.value = '';
|
||||
result.style.display = 'none';
|
||||
loading.style.display = 'none';
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Auto-focus on job number input
|
||||
jobNumberInput.focus();
|
||||
|
||||
// Allow Enter key to submit
|
||||
jobNumberInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
form.dispatchEvent(new Event('submit'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Reference in a new issue