Example #1
0
def do_login():
    next = quorum.get_field("next", None)
    url = util.ensure_api(state = next)
    if url: return flask.redirect(url)
    return flask.redirect(
        next or flask.url_for("index")
    )
Example #2
0
def create_media_browser(id):
    api = util.get_api()
    engine = quorum.get_field("engine", None) or None
    position = quorum.get_field("position", None, cast = int)
    label = quorum.get_field("label", None) or None
    visibility = quorum.get_field("visibility", None, cast = int)
    description = quorum.get_field("description", None) or None
    thumbnails = quorum.get_field("thumbnails", False, cast = bool)
    media_file = quorum.get_field("media_file", None)
    image_type = mimetypes.guess_type(media_file.filename)[0]
    mime_type = image_type if image_type else "image/unknown"
    try: data = media_file.stream.read()
    finally: media_file.close()
    api.set_media_entity(
        id,
        data,
        engine = engine,
        position = position,
        label = label,
        mime_type = mime_type,
        visibility = visibility,
        description = description,
        thumbnails = thumbnails
    )
    return flask.redirect(
        flask.url_for("browser_extras", id = id)
    )
Example #3
0
def do_mask_extras():
    object = quorum.get_object()
    mask_name = object.get("mask_name", None)
    mask_file = object.get("mask_file", None)

    mask_name = "mask_" + mask_name if mask_name else "mask"
    mask_name = mask_name.lower()
    mask_name = mask_name.replace(" ", "_")

    api = util.get_api()
    system_company = api.self_system_company()

    data = mask_file.read()
    mime_type = mask_file.content_type
    api.set_media_entity(
        system_company["object_id"],
        mask_name,
        data = data,
        mime_type = mime_type,
        visibility = 2
    )

    return flask.redirect(
        flask.url_for(
            "template_extras",
            message = "Mask file uploaded with success"
        )
    )
Example #4
0
def oauth():
    # retrieves the reference to the current API object, so that
    # it may be used for the retrieval of the access token from
    # the currently received code value
    api = util.get_api()

    # retrieves the code value provided that is going to be used
    # to redeem the access token and the state value that is going
    # to be used as the next value for redirection (if defined)
    code = quorum.get_field("code", None)
    state = quorum.get_field("state", None)

    # tries to retrieve the error field an in case it exists raises
    # an error indicating the OAuth based problem
    error = quorum.get_field("error", None)
    error_description = quorum.get_field("error_description", None)
    if error: raise RuntimeError("%s - %s" % (error, error_description))

    # creates the access token URL for the API usage and sends the
    # appropriate attributes for the retrieval of the access token,
    # then stores it in the current session
    access_token = api.oauth_access(code)
    flask.session["omnix.access_token"] = access_token

    # ensures that a correct session value exists in session, creating
    # a new session in case that's required, this ensures that the acl
    # exists for the current user that is logging in
    api.oauth_session()

    return flask.redirect(state or flask.url_for("index"))
Example #5
0
def update_media(id):
    api = util.get_api()
    position = quorum.get_field("position", None, cast = int)
    label = quorum.get_field("label", None) or None
    visibility = quorum.get_field("visibility", None, cast = int)
    description = quorum.get_field("description", None) or None
    media_file = quorum.get_field("media_file", None)
    image_type = mimetypes.guess_type(media_file.filename)[0]
    mime_type = image_type if image_type else "image/unknown"
    media = dict(
        position = position,
        label = label,
        visibility = visibility,
        description = description
    )
    payload = dict(media = media)
    if media_file:
        try: data = media_file.stream.read()
        finally: media_file.close()
        media["mime_type"] = mime_type
        payload["data"] = data
    media = api.update_media(id, payload)
    return flask.redirect(
        flask.url_for("show_media", id = media["object_id"])
    )
Example #6
0
def oauth():
    # retrieves the reference to the current api object, so that
    # it may be used for the retrieval of the access token from
    # the currently received code value
    api = util.get_api()

    # retrieves the code value provided that is going to be used
    # to redeem the access token and the state value that is going
    # to be used as the next value for redirection (if defined)
    code = quorum.get_field("code", None)
    state = quorum.get_field("state", None)

    # tries to retrieve the error field an in case it exists raises
    # an error indicating the oauth based problem
    error = quorum.get_field("error", None)
    error_description = quorum.get_field("error_description", None)
    if error: raise RuntimeError("%s - %s" % (error, error_description))

    # creates the access token url for the api usage and sends the
    # appropriate attributes for the retrieval of the access token,
    # then stores it in the current session
    access_token = api.oauth_access(code)
    flask.session["omnix.access_token"] = access_token

    # ensures that a correct session value exists in session, creating
    # a new session in case that's required, this ensures that the acl
    # exists for the current user that is logging in
    api.oauth_session()

    return flask.redirect(
        state or flask.url_for("index")
    )
Example #7
0
def do_mask_extras():
    object = quorum.get_object()
    mask_name = object.get("mask_name", None)
    mask_file = object.get("mask_file", None)

    mask_name = "mask_" + mask_name if mask_name else "mask"
    mask_name = mask_name.lower()
    mask_name = mask_name.replace(" ", "_")

    api = util.get_api()
    system_company = api.self_system_company()

    data = mask_file.read()
    mime_type = mask_file.content_type
    api.set_media_entity(
        system_company["object_id"],
        mask_name,
        data = data,
        mime_type = mime_type,
        visibility = 2
    )

    return flask.redirect(
        flask.url_for(
            "template_extras",
            message = "Mask file uploaded with success"
        )
    )
Example #8
0
def create_media_browser(id):
    api = util.get_api()
    engine = quorum.get_field("engine", None) or None
    position = quorum.get_field("position", None, cast = int)
    label = quorum.get_field("label", None) or None
    visibility = quorum.get_field("visibility", None, cast = int)
    description = quorum.get_field("description", None) or None
    thumbnails = quorum.get_field("thumbnails", False, cast = bool)
    media_file = quorum.get_field("media_file", None)
    image_type = mimetypes.guess_type(media_file.filename)[0]
    mime_type = image_type if image_type else "image/unknown"
    try: data = media_file.stream.read()
    finally: media_file.close()
    api.set_media_entity(
        id,
        data,
        engine = engine,
        position = position,
        label = label,
        mime_type = mime_type,
        visibility = visibility,
        description = description,
        thumbnails = thumbnails
    )
    return flask.redirect(
        flask.url_for("browser_extras", id = id)
    )
Example #9
0
def flush_birthday():
    month = quorum.get_field("month", None, cast=int)
    day = quorum.get_field("day", None, cast=int)

    util.mail_birthday_all(month=month, day=day, links=False)

    return flask.redirect(
        flask.url_for("index", message="Birthday emails have been sent"))
Example #10
0
def flush_slack_previous():
    channel = quorum.get_field("channel", None)
    offset = quorum.get_field("offset", 0, cast=int)

    util.slack_previous(channel=channel, offset=offset)

    return flask.redirect(
        flask.url_for("index", message="Slack events have been sent"))
Example #11
0
def update_entities(id):
    models = util.get_models()
    api = util.get_api()
    object = quorum.get_object()
    for name, value in object.items():
        object[name] = None if value == "" else value
    entity = models.Entity.new(model=object, build=False, fill=False)
    api.update_entity(id, payload=dict(root_entity=entity.model))
    return flask.redirect(flask.url_for("show_entities", id=id))
Example #12
0
def flush_slack_week():
    channel = quorum.get_field("channel", None)
    offset = quorum.get_field("offset", 0, cast=int)
    span = quorum.get_field("span", 7, cast=int)

    util.slack_week(channel=channel, offset=offset, span=span)

    return flask.redirect(
        flask.url_for("index", message="Slack events have been sent"))
Example #13
0
def update_entities(id):
    models = util.get_models()
    api = util.get_api()
    object = quorum.get_object()
    for name, value in object.items(): object[name] = None if value == "" else value
    entity = models.Entity.new(model = object, build = False, fill = False)
    api.update_entity(id, payload = dict(root_entity = entity.model))
    return flask.redirect(
        flask.url_for("show_entities", id = id)
    )
Example #14
0
def oauth_slack():
    code = quorum.get_field("code")
    state = quorum.get_field("state")
    next = state
    api = _get_slack_api()
    access_token = api.oauth_access(code)
    settings = models.Settings.get_settings()
    settings.slack_token = access_token
    settings.slack_channel = api.channel
    settings.save()
    return flask.redirect(next or flask.url_for("index"))
Example #15
0
def flush_slack_previous():
    channel = quorum.get_field("channel", None)
    offset = quorum.get_field("offset", 0, cast = int)

    util.slack_previous(channel = channel, offset = offset)

    return flask.redirect(
        flask.url_for(
            "index",
             message = "Slack events have been sent"
        )
    )
Example #16
0
def flush_activity():
    util.mail_activity_all(
        validate = True,
        links = False
    )

    return flask.redirect(
        flask.url_for(
            "index",
            message = "Activity emails have been sent"
        )
    )
Example #17
0
def flush_slack_week():
    channel = quorum.get_field("channel", None)
    offset = quorum.get_field("offset", 0, cast = int)
    span = quorum.get_field("span", 7, cast = int)

    util.slack_week(channel = channel, offset = offset, span = span)

    return flask.redirect(
        flask.url_for(
            "index",
             message = "Slack events have been sent"
        )
    )
Example #18
0
def oauth_slack():
    code = quorum.get_field("code")
    state = quorum.get_field("state")
    next = state
    api = _get_slack_api()
    access_token = api.oauth_access(code)
    settings = models.Settings.get_settings()
    settings.slack_token = access_token
    settings.slack_channel = api.channel
    settings.save()
    return flask.redirect(
       next or flask.url_for("index")
    )
Example #19
0
def flush_birthday():
    month = quorum.get_field("month", None, cast = int)
    day = quorum.get_field("day", None, cast = int)

    util.mail_birthday_all(
        month = month,
        day = day,
        links = False
    )

    return flask.redirect(
        flask.url_for(
            "index",
            message = "Birthday emails have been sent"
        )
    )
Example #20
0
def do_costs_extras():
    # retrieves the reference to the API object that is going
    # to be used for the updating of costs operation
    api = util.get_api()

    # tries to retrieve the costs file from the current
    # form in case it's not available renders the current
    # template with an error message
    costs_file = quorum.get_field("costs_file", None)
    if costs_file == None or not costs_file.filename:
        return flask.render_template(
            "extra/costs.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    costs_file.save(file_path)

    try:
        # parses the temporary file containing the spreadsheet according
        # to the provided set of keys (to create the correct structures)
        items = quorum.xlsx_to_map(
            file_path,
            keys = ("company_product_code", "cost"),
            types = (quorum.legacy.UNICODE, None)
        )
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # uses the "resolved" items structure in the put operation to
    # the Omni API so that the costs for them get updated
    api.costs_merchandise(items)

    # redirects the user back to the costs list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "costs_extras",
            message = "Costs file processed with success"
        )
    )
Example #21
0
def do_costs_extras():
    # retrieves the reference to the api object that is going
    # to be used for the updating of costs operation
    api = util.get_api()

    # tries to retrieve the costs file from the current
    # form in case it's not available renders the current
    # template with an error message
    costs_file = quorum.get_field("costs_file", None)
    if costs_file == None or not costs_file.filename:
        return flask.render_template(
            "extra/costs.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    costs_file.save(file_path)

    try:
        # parses the temporary file containing the spreadsheet according
        # to the provided set of keys (to create the correct structures)
        items = quorum.xlsx_to_map(
            file_path,
            keys = ("company_product_code", "cost"),
            types = (quorum.legacy.UNICODE, None)
        )
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # uses the "resolved" items structure in the put operation to
    # the omni api so that the costs for them get updated
    api.costs_merchandise(items)

    # redirects the user back to the costs list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "costs_extras",
            message = "Costs file processed with success"
        )
    )
Example #22
0
def update_media(id):
    api = util.get_api()
    position = quorum.get_field("position", None, cast=int)
    label = quorum.get_field("label", None) or None
    visibility = quorum.get_field("visibility", None, cast=int)
    description = quorum.get_field("description", None) or None
    media_file = quorum.get_field("media_file", None)
    image_type = mimetypes.guess_type(media_file.filename)[0]
    mime_type = image_type if image_type else "image/unknown"
    media = dict(position=position,
                 label=label,
                 visibility=visibility,
                 description=description)
    payload = dict(media=media)
    if media_file:
        try:
            data = media_file.stream.read()
        finally:
            media_file.close()
        media["mime_type"] = mime_type
        payload["data"] = data
    media = api.update_media(id, payload)
    return flask.redirect(flask.url_for("show_media", id=media["object_id"]))
Example #23
0
def do_transfers_extras():
    # retrieves the reference to the api object that is going
    # to be used for the updating of prices operation
    api = util.get_api()

    # tries to retrieve the origin value from the provided set
    # of fields and in case it's not defined re-renders the template
    origin = quorum.get_field("origin", None, cast = int)
    if not origin:
        return flask.render_template(
            "extra/transfers.html.tpl",
            link = "extras",
            error = "No origin defined"
        )

    # tries to retrieve the transfers file from the current
    # form in case it's not available renders the current
    # template with an error message
    transfers_file = quorum.get_field("transfers_file", None)
    if transfers_file == None or not transfers_file.filename:
        return flask.render_template(
            "extra/transfers.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    transfers_file.save(file_path)

    # creates the file object that is going to be used in the
    # reading of the csv file (underlying object)
    file = open(file_path, "rb")
    try: data = file.read()
    finally: file.close()

    # constructs the bytes based buffer object from the data that
    # has just been loaded from the file
    buffer = quorum.legacy.BytesIO(data)

    # creates the maps that are going to be used to cache the
    # resolution processes for both the stores and the merchandise
    stores_map = dict()
    merchandise_map = dict()

    # creates the map that is going to hold the complete state
    # to be used in the process of the various transfers (context)
    state = dict()

    def get_transfer():
        return state.get("transfer", None)

    def new_transfer(target_id, workflow_state = 6):
        flush_transfer()
        transfer = dict(
            origin = dict(
                object_id = origin
            ),
            destination = dict(
                object_id = target_id
            ),
            transfer_lines = [],
            _parameters = dict(
                target_workflow_state = workflow_state
            )
        )
        state["transfer"] = transfer
        return transfer

    def flush_transfer():
        transfer = get_transfer()
        state["transfer"] = None
        if not transfer: return
        payload = dict(transfer = transfer)
        transfer = api.create_transfer(payload)
        transfer_id = transfer["object_id"]
        quorum.debug(
            "Created stock transfer '%d'" % transfer_id
        )
        return transfer

    def add_transfer_line(merchandise_id, quantity = 1):
        transfer = get_transfer()
        if not transfer: raise quorum.OperationalError(
            "No transfer in context"
        )
        lines = transfer["transfer_lines"]
        line = dict(
            quantity = quantity,
            merchandise = dict(
                object_id = merchandise_id
            )
        )
        lines.append(line)

    def get_store_id(store_code):
        object_id = stores_map.get(store_code, None)
        if object_id: return object_id

        kwargs = {
            "start_record" : 0,
            "number_records" : 1,
            "filters[]" : [
                "store_code:equals:%s" % store_code
            ]
        }

        try: stores = api.list_stores(**kwargs)
        except: stores = []
        if stores: object_id = stores[0]["object_id"]

        stores_map[store_code] = object_id
        return object_id

    def get_merchandise_id(company_product_code):
        # tries to retrieve the object id of the merchandise from the
        # cache and in case it succeeds returns it immediately
        object_id = merchandise_map.get(company_product_code, None)
        if object_id: return object_id

        # creates the map containing the (filter) keyword arguments that
        # are going to be send to the list merchandise operation
        kwargs = {
            "start_record" : 0,
            "number_records" : 1,
            "filters[]" : [
                "company_product_code:equals:%s" % company_product_code
            ]
        }

        # runs the list merchandise operation in order to try to find a
        # merchandise entity for the requested (unique) product code in
        # case there's at least one merchandise its object id is used
        try: merchandise = api.list_merchandise(**kwargs)
        except: merchandise = []
        if merchandise: object_id = merchandise[0]["object_id"]

        # updates the (cache) map for the merchandise with the reference
        # new object id to company product code reference and then returns
        # the object id of the merchandise to the caller method
        merchandise_map[company_product_code] = object_id
        return object_id

    def callback(line, header = None):
        code, quantity, _date, _time = line[:4]

        code = code.strip()
        quantity = quantity.strip()
        quantity = int(quantity)

        is_store = len(code) < 4
        if is_store: store_id = get_store_id(code)
        else: merchandise_id = get_merchandise_id(code)

        if is_store:
            if store_id: new_transfer(store_id)
            else: flush_transfer()
        elif merchandise_id:
            try: add_transfer_line(
                merchandise_id,
                quantity = quantity
            )
            except: pass

    try:
        # start the csv import operation that is going to import the
        # various lines of the csv in the buffer and for each of them
        # call the function passed as callback
        util.csv_import(buffer, callback, delimiter = ";")
        flush_transfer()
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the transfers list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "transfers_extras",
            message = "Transfers file processed with success"
        )
    )
Example #24
0
def do_images_extras():
    # retrieves the reference to the (Omni) API object that
    # is going to be used for the operations of updating of
    # the merchandise in bulk (multiple operations at a time)
    api = util.get_api()

    # tries to retrieve the images file from the current
    # form in case it's not available renders the current
    # template with an error message
    images_file = quorum.get_field("images_file", None)
    if images_file == None or not images_file.filename:
        return flask.render_template(
            "extra/images.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory, closing the same
    # file afterwards, as it has been properly saved
    fd, file_path = tempfile.mkstemp()
    try: images_file.save(file_path)
    finally: images_file.close()

    try:
        # creates a new temporary directory that is going to be used
        # in the extraction of the images zip file
        temp_path = tempfile.mkdtemp()
        try:
            # creates the zip file reference with the current file path
            # and then extracts the complete set of contents to the "target"
            # temporary path closing the zip file afterwards
            zip = zipfile.ZipFile(file_path)
            try: zip.extractall(temp_path)
            finally: zip.close()

            # iterates over the complete set of names in the temporary path
            # to try to upload the image to the target data source, note that
            # only the image files are considered and the base name of them
            # are going to be validation for existence in the data source
            for name in os.listdir(temp_path):
                # splits the file name into base name and extension and validates
                # the extension, so that only image files are considered
                base, extension = os.path.splitext(name)
                if not extension.lower() in (".png", ".jpg", ".jpeg"):
                    quorum.info("Skipping, '%s' not a valid image file" % name)
                    continue

                # creates the keyword arguments map so that the the merchandise
                # with the provided company product code is retrieved
                kwargs = {
                    "start_record" : 0,
                    "number_records" : 1,
                    "filters[]" : [
                        "company_product_code:equals:%s" % base
                    ]
                }

                # creates the URL for the merchandise retrieval and runs the get
                # operation with the provided filter so that the target merchandise
                # is retrieved for object id validation
                merchandise = api.list_merchandise(**kwargs)

                # verifies that at least one entity was retrieved in case nothing
                # is found skips the current loop with a not found error
                if not merchandise:
                    quorum.info("Skipping, '%s' not found in data source" % base)
                    continue

                # prints a logging message about the upload of image file that
                # is going to be performed for the current merchandise
                quorum.debug("Changing image file for merchandise '%s'" % base)

                # retrieves the first entity from the resulting list and then retrieves
                # the object identifier from it to be used in the update operation
                entity = merchandise[0]
                object_id = entity["object_id"]

                # creates the target temporary image path from the temporary directory
                # path and then "read" the complete set of contents from it closing the
                # file afterwards (no more reading allowed)
                image_path = os.path.join(temp_path, name)
                image_file = open(image_path, "rb")
                try: contents = image_file.read()
                finally: image_file.close()

                # creates the image (file) tuple with both the name of the file and the
                # contents if it (multipart standard)
                image_tuple = (name, contents)

                # creates the multipart data map with both the object id and the image
                # file parameters that are going to be used in the encoding
                data_m = {
                    "object_id" : object_id,
                    "transactional_merchandise[_parameters][image_file]" : image_tuple
                }

                # uses the "resolved" items structure in the operation to
                # the Omni API so that the images for them get updated
                api.update_merchandise(object_id, data_m)
        finally:
            # removes the temporary path as it's no longer going to be
            # required for the operation (errors are ignored)
            shutil.rmtree(temp_path, ignore_errors = True)
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the images list page with a success
    # message indicating that everything went as expected
    return flask.redirect(
        flask.url_for(
            "images_extras",
            message = "Images file processed with success"
        )
    )
Example #25
0
def do_metadata_extras():
    # retrieves the reference to the API object that is going
    # to be used for the updating of prices operation
    api = util.get_api()

    # tries to retrieve the metadata file from the current
    # form in case it's not available renders the current
    # template with an error message
    metadata_file = quorum.get_field("metadata_file", None)
    if metadata_file == None or not metadata_file.filename:
        return flask.render_template(
            "extra/metadata.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # retrieves the value of the custom field that control if
    # the importing will be performed using a dynamic approach
    # meaning that no static values will be retrieved and instead
    # the header will be used for dynamic retrieval
    custom = quorum.get_field("custom", False, cast = bool)

    # check if the CSV file to uploaded is separated by the comma
    # character or if instead it used the semicolon
    comma = quorum.get_field("comma", False, cast = bool)

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    metadata_file.save(file_path)

    # creates the file object that is going to be used in the
    # reading of the CSV file (underlying object)
    file = open(file_path, "rb")
    try: data = file.read()
    finally: file.close()

    # constructs the bytes based buffer object from the data that
    # has just been loaded from the file
    buffer = quorum.legacy.BytesIO(data)

    def callback(line, header = None):
        # in case the custom metadata mode is enabled then a special work
        # model is set where all of the columns are going to be used dynamically
        # for the update of the metadata map of the object
        if custom:
            # creates a zip of tuples with the header to line value association
            # and uses them to build a proper dictionary
            zipped = zip(header, line)
            update = dict(zipped)

            # iterates over the complete set of items in the map of values
            # and updates the update map with the sanitized value
            for name, value in update.items():
                update[name] = value.strip()

            # tries to retrieve the base identifier of the entity
            # this value is going to be used as the basis for identification
            base = update.pop("code", None)
            base = update.pop("company_product_code", base)
            base = update.pop("object_id", base)
            base = update.pop("base", base)

            # tries to retrieve some of the base entity values
            # if their found they are properly popped out
            name = update.pop("name", None)
            description = update.pop("description", None)
            upc = update.pop("upc", None)
            ean = update.pop("ean", None)

        # otherwise this is a "normal" update and the "typical" metadata
        # fields are the one to be updated
        else:
            # unpacks the current "metadata" line into its components as
            # expected by the specification
            base,\
            name,\
            _retail_price,\
            compare_price,\
            discount,\
            characteristics,\
            material,\
            category,\
            collection,\
            brand,\
            season,\
            gender,\
            description,\
            order,\
            discountable,\
            sku_field,\
            upc,\
            ean = line[:18]

            # verifies if the initials part of the CSV line exists and
            # if that's the case processes it properly
            if len(line) > 20:
                initials, initials_min, initials_max = line[18:21]
            else:
                initials, initials_min, initials_max = "", "", ""

            # normalizes the various values that have been extracted from the line
            # so they are properly represented for importing
            name = name or None
            compare_price = (compare_price and compare_price.strip()) or None
            discount = (discount and discount.strip()) or None
            characteristics = [value.strip() for value in characteristics.split(";") if value.strip()]
            material = [value.strip() for value in material.split(";") if value.strip()]
            category = [value.strip() for value in category.split(";") if value.strip()]
            collection = [value.strip() for value in collection.split(";") if value.strip()]
            brand = brand or None
            season = season or None
            gender = gender or None
            description = description or None
            order = (order and order.strip()) or None
            discountable = discountable or None
            sku_field = sku_field or None
            upc = upc or None
            ean = ean or None
            initials = initials or None
            initials_min = initials_min or None
            initials_max = initials_max or None

            # verifies and strips the various possible string values so that they
            # represent a valid not trailed value
            if name: name = name.strip()
            if compare_price: compare_price = float(compare_price)
            if brand: brand = brand.strip()
            if season: season = season.strip()
            if gender: gender = gender.strip()
            if description: description = description.strip()
            if order: order = int(order)
            if discountable: discountable = discountable == "1"
            if sku_field: sku_field = sku_field.strip()
            if discount: discount = float(discount)
            if upc: upc = upc.strip()
            if ean: ean = ean.strip()
            if initials: initials = initials == "1"
            if initials_min: initials_min = int(initials_min)
            if initials_max: initials_max = int(initials_max)

            # creates the map that is going to hold the complete set of features
            # and populates the features according to their existence
            features = dict()
            if initials:
                features["initials"] = dict(min = initials_min, max = initials_max)

            # creates the update dictionary that is going to be used in the updating
            # of the "product" metadata (this is considered to be a delta dictionary)
            update = dict(
                compare_price = compare_price,
                discount = discount,
                characteristics = characteristics,
                features = features,
                material = material,
                category = category,
                collection = collection,
                brand = brand,
                season = season,
                gender = gender,
                order = order,
                discountable = discountable,
                sku_field = sku_field
            )

        # tries to "cast" the base value as an integer and in case
        # it's possible assumes that this value is the object identifier
        try: object_id = int(base)
        except Exception: object_id = None

        # in case no object id was retrieved from the base name value
        # a secondary strategy is used, so that the merchandise database
        # is searched using the base string value as the company product code
        if not object_id:
            # creates the keyword arguments map so that the the merchandise
            # with the provided company product code is retrieved
            kwargs = {
                "start_record" : 0,
                "number_records" : 1,
                "filters[]" : [
                    "company_product_code:equals:%s" % base
                ]
            }

            # runs the list merchandise operation in order to try to find a
            # merchandise entity for the requested (unique) product code in
            # case there's at least one merchandise its object id is used
            try: merchandise = api.list_merchandise(**kwargs)
            except Exception: merchandise = []
            if merchandise: object_id = merchandise[0]["object_id"]

        # in case no object id was retrieved must skip the current loop
        # with a proper information message (as expected)
        if not object_id:
            quorum.info("Skipping, could not resolve Object ID for '%s'" % base)
            return

        # prints a logging message about the updating of the metadata for
        # the entity with the current object id
        quorum.debug("Setting metadata for entity '%d'" % object_id)

        # retrieves the reference to the entity so that it's possible to
        # retrieve the currently defined metadata for it (to be updated)
        entity = api.get_entity(object_id)
        metadata = entity.get("metadata", {}) or {}

        # updates the metadata dictionary with the new values that are going
        # to be used for the updating of the entity, note that the previous
        # metadata values are leveraged and not overwritten with this strategy
        metadata.update(update)

        # creates the model structure to be updated and then runs the
        # proper execution of the metadata import
        model = dict(metadata = metadata)
        if name: model["name"] = name
        if description: model["description"] = description
        if upc: model["upc"] = upc
        if ean: model["ean"] = ean
        api.update_entity(object_id, payload = dict(root_entity = model))

    try:
        # start the CSV import operation that is going to import the
        # various lines of the CSV in the buffer and for each of them
        # call the function passed as callback
        util.csv_import(
            buffer,
            callback,
            header = True,
            delimiter = "," if comma else ";",
            quoting = True
        )
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the metadata list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "metadata_extras",
            message = "Metadata file processed with success"
        )
    )
Example #26
0
def logout():
    next = quorum.get_field("next", None)
    util.reset_session()
    return flask.redirect(
        next or flask.url_for("index")
    )
Example #27
0
def ensure_slack():
    next = quorum.get_field("next")
    api = _get_slack_api()
    return flask.redirect(
        api.oauth_authorize(state = next)
    )
Example #28
0
def do_media_extras():
    # retrieves the reference to the (Omni) API object that
    # is going to be used for the operations of updating of
    # the merchandise in bulk (multiple operations at a time)
    api = util.get_api()

    # tries to retrieve the media file from the current
    # form in case it's not available renders the current
    # template with an error message
    media_file = quorum.get_field("media_file", None)
    if media_file == None or not media_file.filename:
        return flask.render_template(
            "extra/media.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory, closing the same
    # file afterwards, as it has been properly saved
    fd, file_path = tempfile.mkstemp()
    try: media_file.save(file_path)
    finally: media_file.close()

    try:
        # creates a new temporary directory that is going to be used
        # in the extraction of the media zip file
        temp_path = tempfile.mkdtemp()
        try:
            # creates the zip file reference with the current file path
            # and then extracts the complete set of contents to the "target"
            # temporary path closing the zip file afterwards
            zip = zipfile.ZipFile(file_path)
            try: zip.extractall(temp_path)
            finally: zip.close()

            # iterates over the complete set of names in the temporary path
            # to try to upload the media to the target data source, note that
            # only the media files are considered and the base name of them
            # are going to be validation for existence in the data source
            for name in os.listdir(temp_path):
                # splits the file name into base name and extension and validates
                # the extension, so that only media files are considered
                base, extension = os.path.splitext(name)
                if not extension.lower() in (".png", ".jpg", ".jpeg"):
                    quorum.info("Skipping, '%s' not a valid media file" % name)
                    continue

                # splits the base value of the file name so that it's possible to
                # extract the proper position of the image if that's required
                base_s = base.rsplit("_", 1)
                if len(base_s) > 1: position = int(base_s[1])
                else: position = 1

                # tries to "cast" the base file name value as an integer and in case
                # it's possible assumes that this value is the object identifier
                try: object_id = int(base_s[0])
                except Exception: object_id = None

                # in case no object id was retrieved from the base file name value
                # a secondary strategy is used, so that the merchandise database
                # is searched using the base string value as the company product code
                if not object_id:
                    # creates the keyword arguments map so that the the merchandise
                    # with the provided company product code is retrieved
                    kwargs = {
                        "start_record" : 0,
                        "number_records" : 1,
                        "filters[]" : [
                            "company_product_code:equals:%s" % base_s[0]
                        ]
                    }

                    # runs the list merchandise operation in order to try to find a
                    # merchandise entity for the requested (unique) product code in
                    # case there's at least one merchandise its object id is used
                    try: merchandise = api.list_merchandise(**kwargs)
                    except Exception: merchandise = []
                    if merchandise: object_id = merchandise[0]["object_id"]

                # in case no object id was retrieved must skip the current loop
                # with a proper information message (as expected)
                if not object_id:
                    quorum.info("Skipping, could not resolve Object ID id for '%s'" % base)
                    continue

                # prints a logging message about the upload of media file that
                # is going to be performed for the current entity
                quorum.debug(
                    "Adding media file for entity '%d' in position '%d'" %\
                    (object_id, position)
                )

                # creates the target temporary media path from the temporary directory
                # path and then "read" the complete set of contents from it closing the
                # file afterwards (no more reading allowed)
                media_path = os.path.join(temp_path, name)
                media_file = open(media_path, "rb")
                try: contents = media_file.read()
                finally: media_file.close()

                # tries to guess the proper image type for the image located at the
                # provided path and the uses this value to construct the mime type
                image_type = imghdr.what(media_path)
                mime_type = "image/" + image_type if image_type else "image/unknown"

                # sets/updates the media for the associated root entity using the
                # data extracted from the file and the information in its name
                api.set_media_entity(
                    object_id,
                    contents,
                    position = position,
                    mime_type = mime_type,
                    engine = "fs",
                    thumbnails = True
                )
        finally:
            # removes the temporary path as it's no longer going to be
            # required for the operation (errors are ignored)
            shutil.rmtree(temp_path, ignore_errors = True)
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the media list page with a success
    # message indicating that everything went as expected
    return flask.redirect(
        flask.url_for(
            "media_extras",
            message = "Media file processed with success"
        )
    )
Example #29
0
def flush_at():
    # creates a values map structure to retrieve the complete
    # set of inbound documents that have not yet been submitted
    # to at for the flush operation
    kwargs = {
        "filter_string" : "",
        "start_record" : 0,
        "number_records" : 1000,
        "sort" : "issue_date:ascending",
        "filters[]" : [
            "issue_date:greater:1356998400",
            "submitted_at:equals:2",
            "document_type:equals:3"
        ]
    }
    api = util.get_api()
    documents = api.list_signed_documents(**kwargs)

    # filters the result set retrieved so that only the valid at
    # "submittable" documents are present in the sequence
    valid_documents = [value for value in documents\
        if value["_class"] in util.AT_SUBMIT_TYPES]

    # "calculates" the total set of valid documents present in the
    # valid documents and starts the index counter
    total = len(valid_documents)
    index = 1

    # iterates over the complete set of valid documents to be sent
    # (submitted) to at and processes the submission
    for document in valid_documents:
        type = document["_class"]
        object_id = document["object_id"]
        representation = document["representation"]
        issue_date = document["issue_date"]
        issue_date_d = datetime.datetime.utcfromtimestamp(issue_date)
        issue_date_s = issue_date_d.strftime("%d %b %Y %H:%M:%S")

        # retrieves the current time and uses it to print debug information
        # about the current document submission to at
        quorum.info(
            "Submitting %s - %s (%s) [%d/%d]" % (
                type,
                representation,
                issue_date_s,
                index,
                total
            )
        )

        try:
            # starts the submission process for the invoice, taking into
            # account that the document id to be submitted is the one that
            # has been extracted from the (signed) document structure
            api.submit_invoice_at(object_id)
        except Exception as exception:
            quorum.error("Exception while submitting document - %s" % quorum.legacy.UNICODE(exception))
        else:
            quorum.info("Document submitted with success")

        # increments the index counter, because one more document
        # as been processed (submitted or failed)
        index += 1

    return flask.redirect(
        flask.url_for(
            "index",
            message = "Signed documents have been sent to AT"
        )
    )
Example #30
0
def do_transfers_extras():
    # retrieves the reference to the API object that is going
    # to be used for the updating of prices operation
    api = util.get_api()

    # tries to retrieve the origin value from the provided set
    # of fields and in case it's not defined re-renders the template
    origin = quorum.get_field("origin", None, cast = int)
    if not origin:
        return flask.render_template(
            "extra/transfers.html.tpl",
            link = "extras",
            error = "No origin defined"
        )

    # tries to retrieve the transfers file from the current
    # form in case it's not available renders the current
    # template with an error message
    transfers_file = quorum.get_field("transfers_file", None)
    if transfers_file == None or not transfers_file.filename:
        return flask.render_template(
            "extra/transfers.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    transfers_file.save(file_path)

    # creates the file object that is going to be used in the
    # reading of the CSV file (underlying object)
    file = open(file_path, "rb")
    try: data = file.read()
    finally: file.close()

    # constructs the bytes based buffer object from the data that
    # has just been loaded from the file
    buffer = quorum.legacy.BytesIO(data)

    # creates the maps that are going to be used to cache the
    # resolution processes for both the stores and the merchandise
    stores_map = dict()
    merchandise_map = dict()

    # creates the map that is going to hold the complete state
    # to be used in the process of the various transfers (context)
    state = dict()

    def get_transfer():
        return state.get("transfer", None)

    def new_transfer(target_id, workflow_state = 6):
        flush_transfer()
        transfer = dict(
            origin = dict(
                object_id = origin
            ),
            destination = dict(
                object_id = target_id
            ),
            transfer_lines = [],
            _parameters = dict(
                target_workflow_state = workflow_state
            )
        )
        state["transfer"] = transfer
        return transfer

    def flush_transfer():
        transfer = get_transfer()
        state["transfer"] = None
        if not transfer: return
        payload = dict(transfer = transfer)
        transfer = api.create_transfer(payload)
        transfer_id = transfer["object_id"]
        quorum.debug(
            "Created stock transfer '%d'" % transfer_id
        )
        return transfer

    def add_transfer_line(merchandise_id, quantity = 1):
        transfer = get_transfer()
        if not transfer: raise quorum.OperationalError(
            "No transfer in context"
        )
        lines = transfer["transfer_lines"]
        line = dict(
            quantity = quantity,
            merchandise = dict(
                object_id = merchandise_id
            )
        )
        lines.append(line)

    def get_store_id(store_code):
        object_id = stores_map.get(store_code, None)
        if object_id: return object_id

        kwargs = {
            "start_record" : 0,
            "number_records" : 1,
            "filters[]" : [
                "store_code:equals:%s" % store_code
            ]
        }

        try: stores = api.list_stores(**kwargs)
        except Exception: stores = []
        if stores: object_id = stores[0]["object_id"]

        stores_map[store_code] = object_id
        return object_id

    def get_merchandise_id(company_product_code):
        # tries to retrieve the object id of the merchandise from the
        # cache and in case it succeeds returns it immediately
        object_id = merchandise_map.get(company_product_code, None)
        if object_id: return object_id

        # creates the map containing the (filter) keyword arguments that
        # are going to be send to the list merchandise operation
        kwargs = {
            "start_record" : 0,
            "number_records" : 1,
            "filters[]" : [
                "company_product_code:equals:%s" % company_product_code
            ]
        }

        # runs the list merchandise operation in order to try to find a
        # merchandise entity for the requested (unique) product code in
        # case there's at least one merchandise its object id is used
        try: merchandise = api.list_merchandise(**kwargs)
        except Exception: merchandise = []
        if merchandise: object_id = merchandise[0]["object_id"]

        # updates the (cache) map for the merchandise with the reference
        # new object id to company product code reference and then returns
        # the object id of the merchandise to the caller method
        merchandise_map[company_product_code] = object_id
        return object_id

    def callback(line, header = None):
        code, quantity, _date, _time = line[:4]

        code = code.strip()
        quantity = quantity.strip()
        quantity = int(quantity)

        is_store = len(code) < 4
        if is_store: store_id = get_store_id(code)
        else: merchandise_id = get_merchandise_id(code)

        if is_store:
            if store_id: new_transfer(store_id)
            else: flush_transfer()
        elif merchandise_id:
            try: add_transfer_line(
                merchandise_id,
                quantity = quantity
            )
            except Exception: pass

    try:
        # start the CSV import operation that is going to import the
        # various lines of the CSV in the buffer and for each of them
        # call the function passed as callback
        util.csv_import(buffer, callback, delimiter = ";")
        flush_transfer()
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the transfers list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "transfers_extras",
            message = "Transfers file processed with success"
        )
    )
Example #31
0
def ensure_slack():
    next = quorum.get_field("next")
    api = _get_slack_api()
    return flask.redirect(api.oauth_authorize(state=next))
Example #32
0
def flush_activity():
    util.mail_activity_all(validate=True, links=False)

    return flask.redirect(
        flask.url_for("index", message="Activity emails have been sent"))
Example #33
0
def reset():
    util.reset_session()

    return flask.redirect(
        flask.url_for("index")
    )
Example #34
0
def logout():
    next = quorum.get_field("next", None)
    util.reset_session()
    return flask.redirect(next or flask.url_for("index"))
Example #35
0
def clear_media_browser(id):
    api = util.get_api()
    api.clear_media_entity(id)
    return flask.redirect(
        flask.url_for("browser_extras", id = id)
    )
Example #36
0
def do_media_extras():
    # retrieves the reference to the (omni) api object that
    # is going to be used for the operations of updating of
    # the merchandise in bulk (multiple operations at a time)
    api = util.get_api()

    # tries to retrieve the media file from the current
    # form in case it's not available renders the current
    # template with an error message
    media_file = quorum.get_field("media_file", None)
    if media_file == None or not media_file.filename:
        return flask.render_template(
            "extra/media.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory, closing the same
    # file afterwards, as it has been properly saved
    fd, file_path = tempfile.mkstemp()
    try: media_file.save(file_path)
    finally: media_file.close()

    try:
        # creates a new temporary directory that is going to be used
        # in the extraction of the media zip file
        temp_path = tempfile.mkdtemp()
        try:
            # creates the zip file reference with the current file path
            # and then extracts the complete set of contents to the "target"
            # temporary path closing the zip file afterwards
            zip = zipfile.ZipFile(file_path)
            try: zip.extractall(temp_path)
            finally: zip.close()

            # iterates over the complete set of names in the temporary path
            # to try to upload the media to the target data source, note that
            # only the media files are considered and the base name of them
            # are going to be validation for existence in the data source
            for name in os.listdir(temp_path):
                # splits the file name into base name and extension and validates
                # the extension, so that only media files are considered
                base, extension = os.path.splitext(name)
                if not extension.lower() in (".png", ".jpg", ".jpeg"):
                    quorum.info("Skipping, '%s' not a valid media file" % name)
                    continue

                # splits the base value of the file name so that it's possible to
                # extract the proper position of the image if that's required
                base_s = base.rsplit("_", 1)
                if len(base_s) > 1: position = int(base_s[1])
                else: position = 1

                # tries to "cast" the base file name value as an integer and in case
                # it's possible assumes that this value is the object identifier
                try: object_id = int(base_s[0])
                except: object_id = None

                # in case no object id was retrieved from the base file name value
                # a secondary strategy is used, so that the merchandise database
                # is searched using the base string value as the company product code
                if not object_id:
                    # creates the keyword arguments map so that the the merchandise
                    # with the provided company product code is retrieved
                    kwargs = {
                        "start_record" : 0,
                        "number_records" : 1,
                        "filters[]" : [
                            "company_product_code:equals:%s" % base_s[0]
                        ]
                    }

                    # runs the list merchandise operation in order to try to find a
                    # merchandise entity for the requested (unique) product code in
                    # case there's at least one merchandise its object id is used
                    try: merchandise = api.list_merchandise(**kwargs)
                    except: merchandise = []
                    if merchandise: object_id = merchandise[0]["object_id"]

                # in case no object id was retrieved must skip the current loop
                # with a proper information message (as expected)
                if not object_id:
                    quorum.info("Skipping, could not resolve object id for '%s'" % base)
                    continue

                # prints a logging message about the upload of media file that
                # is going to be performed for the current entity
                quorum.debug(
                    "Adding media file for entity '%d' in position '%d'" %\
                    (object_id, position)
                )

                # creates the target temporary media path from the temporary directory
                # path and then "read" the complete set of contents from it closing the
                # file afterwards (no more reading allowed)
                media_path = os.path.join(temp_path, name)
                media_file = open(media_path, "rb")
                try: contents = media_file.read()
                finally: media_file.close()

                # tries to guess the proper image type for the image located at the
                # provided path and the uses this value to construct the mime type
                image_type = imghdr.what(media_path)
                mime_type = "image/" + image_type if image_type else "image/unknown"

                # sets/updates the media for the associated root entity using the
                # data extracted from the file and the information in its name
                api.set_media_entity(
                    object_id,
                    contents,
                    position = position,
                    mime_type = mime_type,
                    engine = "fs",
                    thumbnails = True
                )
        finally:
            # removes the temporary path as it's no longer going to be
            # required for the operation (errors are ignored)
            shutil.rmtree(temp_path, ignore_errors = True)
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the media list page with a success
    # message indicating that everything went as expected
    return flask.redirect(
        flask.url_for(
            "media_extras",
            message = "Media file processed with success"
        )
    )
Example #37
0
def delete_media(id):
    api = util.get_api()
    api.delete_media(id)
    return flask.redirect(flask.url_for("list_media"))
Example #38
0
def flush_at():
    # creates a values map structure to retrieve the complete
    # set of inbound documents that have not yet been submitted
    # to at for the flush operation
    kwargs = {
        "filter_string":
        "",
        "start_record":
        0,
        "number_records":
        1000,
        "sort":
        "issue_date:ascending",
        "filters[]": [
            "issue_date:greater:1356998400", "submitted_at:equals:2",
            "document_type:equals:3"
        ]
    }
    api = util.get_api()
    documents = api.list_signed_documents(**kwargs)

    # filters the result set retrieved so that only the valid at
    # "submittable" documents are present in the sequence
    valid_documents = [value for value in documents\
        if value["_class"] in util.AT_SUBMIT_TYPES]

    # "calculates" the total set of valid documents present in the
    # valid documents and starts the index counter
    total = len(valid_documents)
    index = 1

    # iterates over the complete set of valid documents to be sent
    # (submitted) to at and processes the submission
    for document in valid_documents:
        type = document["_class"]
        object_id = document["object_id"]
        representation = document["representation"]
        issue_date = document["issue_date"]
        issue_date_d = datetime.datetime.utcfromtimestamp(issue_date)
        issue_date_s = issue_date_d.strftime("%d %b %Y %H:%M:%S")

        # retrieves the current time and uses it to print debug information
        # about the current document submission to at
        quorum.info("Submitting %s - %s (%s) [%d/%d]" %
                    (type, representation, issue_date_s, index, total))

        try:
            # starts the submission process for the invoice, taking into
            # account that the document id to be submitted is the one that
            # has been extracted from the (signed) document structure
            api.submit_invoice_at(object_id)
        except Exception as exception:
            quorum.error("Exception while submitting document - %s" %
                         quorum.legacy.UNICODE(exception))
        else:
            quorum.info("Document submitted with success")

        # increments the index counter, because one more document
        # as been processed (submitted or failed)
        index += 1

    return flask.redirect(
        flask.url_for("index",
                      message="Signed documents have been sent to AT"))
Example #39
0
def do_images_extras():
    # retrieves the reference to the (omni) api object that
    # is going to be used for the operations of updating of
    # the merchandise in bulk (multiple operations at a time)
    api = util.get_api()

    # tries to retrieve the images file from the current
    # form in case it's not available renders the current
    # template with an error message
    images_file = quorum.get_field("images_file", None)
    if images_file == None or not images_file.filename:
        return flask.render_template(
            "extra/images.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory, closing the same
    # file afterwards, as it has been properly saved
    fd, file_path = tempfile.mkstemp()
    try: images_file.save(file_path)
    finally: images_file.close()

    try:
        # creates a new temporary directory that is going to be used
        # in the extraction of the images zip file
        temp_path = tempfile.mkdtemp()
        try:
            # creates the zip file reference with the current file path
            # and then extracts the complete set of contents to the "target"
            # temporary path closing the zip file afterwards
            zip = zipfile.ZipFile(file_path)
            try: zip.extractall(temp_path)
            finally: zip.close()

            # iterates over the complete set of names in the temporary path
            # to try to upload the image to the target data source, note that
            # only the image files are considered and the base name of them
            # are going to be validation for existence in the data source
            for name in os.listdir(temp_path):
                # splits the file name into base name and extension and validates
                # the extension, so that only image files are considered
                base, extension = os.path.splitext(name)
                if not extension.lower() in (".png", ".jpg", ".jpeg"):
                    quorum.info("Skipping, '%s' not a valid image file" % name)
                    continue

                # creates the keyword arguments map so that the the merchandise
                # with the provided company product code is retrieved
                kwargs = {
                    "start_record" : 0,
                    "number_records" : 1,
                    "filters[]" : [
                        "company_product_code:equals:%s" % base
                    ]
                }

                # creates the url for the merchandise retrieval and runs the get
                # operation with the provided filter so that the target merchandise
                # is retrieved for object id validation
                merchandise = api.list_merchandise(**kwargs)

                # verifies that at least one entity was retrieved in case nothing
                # is found skips the current loop with a not found error
                if not merchandise:
                    quorum.info("Skipping, '%s' not found in data source" % base)
                    continue

                # prints a logging message about the upload of image file that
                # is going to be performed for the current merchandise
                quorum.debug("Changing image file for merchandise '%s'" % base)

                # retrieves the first entity from the resulting list and then retrieves
                # the object identifier from it to be used in the update operation
                entity = merchandise[0]
                object_id = entity["object_id"]

                # creates the target temporary image path from the temporary directory
                # path and then "read" the complete set of contents from it closing the
                # file afterwards (no more reading allowed)
                image_path = os.path.join(temp_path, name)
                image_file = open(image_path, "rb")
                try: contents = image_file.read()
                finally: image_file.close()

                # creates the image (file) tuple with both the name of the file and the
                # contents if it (multipart standard)
                image_tuple = (name, contents)

                # creates the multipart data map with both the object id and the image
                # file parameters that are going to be used in the encoding
                data_m = {
                    "object_id" : object_id,
                    "transactional_merchandise[_parameters][image_file]" : image_tuple
                }

                # uses the "resolved" items structure in the operation to
                # the omni api so that the images for them get updated
                api.update_merchandise(object_id, data_m)
        finally:
            # removes the temporary path as it's no longer going to be
            # required for the operation (errors are ignored)
            shutil.rmtree(temp_path, ignore_errors = True)
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the images list page with a success
    # message indicating that everything went as expected
    return flask.redirect(
        flask.url_for(
            "images_extras",
            message = "Images file processed with success"
        )
    )
Example #40
0
def do_login():
    next = quorum.get_field("next", None)
    url = util.ensure_api(state=next)
    if url: return flask.redirect(url)
    return flask.redirect(next or flask.url_for("index"))
Example #41
0
def do_metadata_extras():
    # retrieves the reference to the api object that is going
    # to be used for the updating of prices operation
    api = util.get_api()

    # tries to retrieve the metadata file from the current
    # form in case it's not available renders the current
    # template with an error message
    metadata_file = quorum.get_field("metadata_file", None)
    if metadata_file == None or not metadata_file.filename:
        return flask.render_template(
            "extra/metadata.html.tpl",
            link = "extras",
            error = "No file defined"
        )

    # retrieves the value of the custom field that control if
    # the importing will be performed using a dynamic approach
    # meaning that no static values will be retrieved and instead
    # the header will be used for dynamic retrieval
    custom = quorum.get_field("custom", False, cast = bool)

    # check if the csv file to uploaded is separated by the comma
    # character or if instead it used the semicolon
    comma = quorum.get_field("comma", False, cast = bool)

    # creates a temporary file path for the storage of the file
    # and then saves it into that directory
    fd, file_path = tempfile.mkstemp()
    metadata_file.save(file_path)

    # creates the file object that is going to be used in the
    # reading of the csv file (underlying object)
    file = open(file_path, "rb")
    try: data = file.read()
    finally: file.close()

    # constructs the bytes based buffer object from the data that
    # has just been loaded from the file
    buffer = quorum.legacy.BytesIO(data)

    def callback(line, header = None):
        if custom:
            # creates a zip of tuples with the header to line value association
            # and uses them to build a proper dictionary
            zipped = zip(header, line)
            update = dict(zipped)

            # iterates over the complete set of items in the map of values
            # and update the update map with the sanitized value
            for name, value in update.items():
                update[name] = value.strip()

            # tries to retrieve the base identifier of the entity
            # this value is going to be used as the basis for identification
            base = update.pop("code", None)
            base = update.pop("company_product_code", base)
            base = update.pop("object_id", base)
            base = update.pop("base", base)

            # tries to retrieve some of the base entity values
            # if their found they are properly poped out
            name = update.pop("name", None)
            description = update.pop("description", None)
        else:
            # unpacks the current "metadata" line into its components as
            # expected by the specification
            base,\
            name,\
            _retail_price,\
            characteristics,\
            material,\
            category,\
            collection,\
            brand,\
            description,\
            order = line[:10]

            # normalizes the various values that have been extracted from the line
            # so they are properly represented for importing
            characteristics = [value.strip() for value in characteristics.split(";") if value.strip()]
            material = [value.strip() for value in material.split(";") if value.strip()]
            category = [value.strip() for value in category.split(";") if value.strip()]
            collection = [value.strip() for value in collection.split(";") if value.strip()]
            brand = brand or None
            order = order or None

            # verifies and strips the various possible string values so that they
            # represent a valid not trailed value
            if name: name = name.strip()
            if brand: brand = brand.strip()
            if description: description = description.strip()
            if order: order = int(order.strip())

            # creates the update dictionary that is going to be used in the updating
            # of the "product" metadata
            update = dict(
                characteristics = characteristics,
                material = material,
                category = category,
                collection = collection,
                brand = brand,
                order = order
            )

        # tries to "cast" the base value as an integer and in case
        # it's possible assumes that this value is the object identifier
        try: object_id = int(base)
        except: object_id = None

        # in case no object id was retrieved from the base name value
        # a secondary strategy is used, so that the merchandise database
        # is searched using the base string value as the company product code
        if not object_id:
            # creates the keyword arguments map so that the the merchandise
            # with the provided company product code is retrieved
            kwargs = {
                "start_record" : 0,
                "number_records" : 1,
                "filters[]" : [
                    "company_product_code:equals:%s" % base
                ]
            }

            # runs the list merchandise operation in order to try to find a
            # merchandise entity for the requested (unique) product code in
            # case there's at least one merchandise its object id is used
            try: merchandise = api.list_merchandise(**kwargs)
            except: merchandise = []
            if merchandise: object_id = merchandise[0]["object_id"]

        # in case no object id was retrieved must skip the current loop
        # with a proper information message (as expected)
        if not object_id:
            quorum.info("Skipping, could not resolve object id for '%s'" % base)
            return

        # prints a logging message about the updating of the metadata for
        # the entity with the current object id
        quorum.debug("Setting metadata for entity '%d'" % object_id)

        # retrieves the reference to the entity so that it's possible to
        # retrieve the currently defined metadata for it (to be updated)
        entity = api.get_entity(object_id)
        metadata = entity.get("metadata", {}) or {}

        # updates the metadata dictionary with the new values that are going
        # to be used for the updating of the entity, note that the previous
        # metadata values are leveraged and not overwritten with this strategy
        metadata.update(update)

        # creates the model structure to be updated and then runs the
        # proper execution of the metadata import
        model = dict(metadata = metadata)
        if name: model["name"] = name
        if description: model["description"] = description
        api.update_entity(object_id, payload = dict(root_entity = model))

    try:
        # start the csv import operation that is going to import the
        # various lines of the csv in the buffer and for each of them
        # call the function passed as callback
        util.csv_import(
            buffer,
            callback,
            header = True,
            delimiter = "," if comma else ";",
            quoting = True
        )
    finally:
        # closes the temporary file descriptor and removes the temporary
        # file (avoiding any memory leaks)
        os.close(fd)
        os.remove(file_path)

    # redirects the user back to the metadata list page with a success
    # message indicating that everything went ok
    return flask.redirect(
        flask.url_for(
            "metadata_extras",
            message = "Metadata file processed with success"
        )
    )
Example #42
0
def reset():
    util.reset_session()

    return flask.redirect(flask.url_for("index"))
Example #43
0
def delete_media(id):
    api = util.get_api()
    api.delete_media(id)
    return flask.redirect(
        flask.url_for("list_media")
    )