- 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>
197 lines
No EOL
6.7 KiB
Python
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) |