metaserver-web-python/app.py
DJP dd77b199e0 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>
2025-09-09 15:26:40 -04:00

197 lines
No EOL
6.7 KiB
Python

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)