added basic functionality to import designers into esales cluster

This commit is contained in:
Martin
2016-05-11 16:17:54 +02:00
parent 9ced739ef6
commit af864c3438
14 changed files with 186 additions and 5 deletions
+8 -2
View File
@@ -29,7 +29,9 @@ List of available configuration options:
| ---- | ----------- |
| `SQLALCHEMY_DATABASE_URI` | Database connection string. e.g: `postgresql://user:password@localhost/photowall` |
| `DEBUG` | Enable/disable debug mode |
| `API_KEYS` | A list of api keys that are used to authenticate to the API
| `API_KEYS` | A list of api keys that are used to authenticate to the API |
| `ESALES_URL` | URL to the eSales server. If its a cloud cluster it has the format `username:password` |
| `ESALES_IMPORT_DIR` | XML-files that should be imported to eSales are temporarily stored in this directory. e.g: `/tmp/esales`
## Authentication
Authentication to the API is performed via HTTP Basic Auth. The user should provide her API key as the basic auth username parameter. An example request in cURL looks like this (adding a colon after the username prevents cURL from asking for a password):
@@ -42,4 +44,8 @@ API keys should be added to the `API_KEYS` configuration option
Starting the application:
`$ python manage.py runserver`
`$ python manage.py runserver`
Running the eSales import
`$ python manage.py run_esales_import`
+4
View File
@@ -1,5 +1,6 @@
from flask import Flask, jsonify, Response, request
from flask_sqlalchemy import SQLAlchemy
from esales.cluster import Cluster
app = Flask(__name__)
app.config.from_object('config')
@@ -13,6 +14,9 @@ app.errorhandler(404)(on_404)
# SQLAlchemy
db = SQLAlchemy(app)
# eSales
esales = Cluster(app.config.get("ESALES_URL"))
from api.views import designers
app.register_blueprint(designers.mod)
+1
View File
@@ -0,0 +1 @@
from . import esales
+27
View File
@@ -0,0 +1,27 @@
# coding=utf-8
from api import app
from api.models.locale import Locale
from esales.models import Category, CategoryTree
from esales.utils import ImportFile
def update_designer(designer):
locales = Locale.query.all()
trees = []
category = Category(
"root/designer/{}".format(designer.path),
designer.name,
hidden=1,
path="/designers/{}".format(designer.path)
)
importfile = ImportFile()
for locale in locales:
tree = CategoryTree("categories_{}".format(locale.value))
tree.add_category(category)
importfile.add_operation("update", tree)
directory = app.config.get("ESALES_IMPORT_DIR")
importfile.write(directory)
+2 -1
View File
@@ -1,5 +1,5 @@
from flask import Blueprint, request, jsonify
from api import db
from api import db, tasks
from api.models import Designer
mod = Blueprint('designers', __name__, url_prefix='/designers')
@@ -34,6 +34,7 @@ def update(id):
setattr(designer, k, v)
db.session.add(designer)
db.session.commit()
tasks.esales.update_designer(designer)
return jsonify(designer.to_json())
+3 -1
View File
@@ -1,4 +1,6 @@
SQLALCHEMY_DATABASE_URI = 'postgresql://user:password@host/db_name'
SQLALCHEMY_TRACK_MODIFICATIONS = True
DEBUG = True
API_KEYS = ['secretkey']
API_KEYS = ['secretkey']
ESALES_URL = 'username:password'
ESALES_IMPORT_DIR = '/tmp/esales'
+52
View File
@@ -0,0 +1,52 @@
# coding=utf-8
import glob
import os
import sys
import subprocess
import logging
logger = logging.getLogger("esales")
class CommandException(Exception):
pass
class Cluster(object):
def __init__(self, url):
self.url = url
def run_command(self, command):
""" execute a command against the esales cluster """
try:
# find the location of the jar file (provided by apptus) that is
# used to execute the commands
jar = os.path.abspath(os.path.dirname(
__file__) + "/command/command.jar")
subprocess.check_output(
"java -jar {0} {1}".format(jar, command).split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
message = "Could not execute command {0}. Error: {1}".format(
command, e.output)
logger.error(message)
raise CommandException(message)
except OSError:
sys.exit("Java not found on this system")
def import_products_and_categories(self, pattern):
""" imports products and categories into the eSales cluster """
i = 0
for f in sorted(glob.iglob(pattern)):
if os.path.isfile(f):
logger.info("Importing {}".format(f))
try:
self.run_command("import -p {0} {1}".format(self.url, f))
i += 1
except CommandException as e:
logger.error("Failed to import {}".format(f))
finally:
os.remove(f)
logger.info("Imported {} file(s)".format(i))
return i
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+47
View File
@@ -0,0 +1,47 @@
# coding=utf-8
from xml.etree.cElementTree import Element, SubElement
class Category(object):
""" represents an eSales category """
def __init__(self, key, display_name=None, **kwargs):
self.key = key
self.display_name = display_name
self.attributes = {}
for k, v in kwargs.items():
self.attributes[k] = v
@property
def etree(self):
root = Element("category", key=self.key)
if self.display_name:
SubElement(root, "display_name").text = self.display_name
if self.attributes:
attributes = SubElement(root, "attributes")
for k, v in self.attributes.items():
s = str(v)
if s:
SubElement(attributes, k).text = s
return root
class CategoryTree(object):
"""represents an eSales CategoryTree"""
def __init__(self, product_attribute):
self.categories = []
self.product_attribute = product_attribute
def add_category(self, category):
self.categories.append(category)
@property
def etree(self):
root = Element("category_tree",
product_attribute=self.product_attribute)
for category in self.categories:
root.append(category.etree)
return root
+35
View File
@@ -0,0 +1,35 @@
# coding=utf-8
import os
import uuid
import time
from xml.etree.cElementTree import ElementTree, Element, SubElement, dump
class ImportFile(object):
def __init__(self):
self.operations = []
def add_operation(self, operation, model):
assert operation in ["create", "update", "delete"]
self.operations.append((operation, model))
@property
def etree(self):
root = Element("operations")
for name, model in self.operations:
operation = SubElement(root, name)
operation.append(model.etree)
return root
def dump_xml(self):
return dump(self.etree)
def write(self, directory, filename=None):
if not os.path.exists(directory):
os.makedirs(directory)
if not filename:
filename = "{}.xml".format(time.time())
xml = ElementTree(self.etree)
xml.write("{}/{}".format(directory, filename),
encoding="UTF-8", xml_declaration=True)
+7 -1
View File
@@ -1,5 +1,5 @@
from flask.ext.script import Manager, prompt_bool
from api import app, db
from api import app, db, esales
from api.models import Designer
manager = Manager(app)
@@ -26,5 +26,11 @@ def seed_db():
db.session.commit()
@manager.command
def run_esales_import():
print("Importing products and categories into eSales")
pattern = "{}/*.xml".format(app.config.get("ESALES_IMPORT_DIR"))
esales.import_products_and_categories(pattern)
if __name__ == '__main__':
manager.run()