added basic functionality to import designers into esales cluster
This commit is contained in:
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import esales
|
||||
@@ -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)
|
||||
@@ -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
@@ -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'
|
||||
@@ -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
|
||||
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user