# $SnapHashLicense:
#
# SnapLogic - Open source data services
#
# Copyright (C) 2008 - 2009, SnapLogic, Inc. All rights reserved.
#
# See http://www.snaplogic.org for more information about
# the SnapLogic project.
#
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
#
# "SnapLogic" is a trademark of SnapLogic, Inc.
#
#
# $
# $Id: Sequence.py 10330 2009-12-24 22:13:38Z grisha $
"""
Sequence component and resource definition.
This component allows the creation of resources that add a generated sequence number column to input
records. Currently this sequence is not persistent across pipeline runs. This means that everytime
a pipeline is run, the sequence will start from the number stored in the resource at creation time. Also,
pipelines which share a sequence will not receive unique numbers between the two. For example, if /pipe1 and
/pipe2 are both pipelines that contain sequence /seq1, when both pipelines are run (either concurrently or
separately), they will each receive the same sequence 1, 2, 3, ....
These shortcomings will be addressed at a later time.
"""
__docformat__ = "epytext en"
from decimal import Decimal
from snaplogic.common.data_types import Record
from snaplogic.common import version_info
from snaplogic.cc.component_api import ComponentAPI
import snaplogic.cc.prop as prop
from snaplogic import components
from snaplogic.common.snap_exceptions import *
from snaplogic.snapi_base import keys
from snaplogic.common.data_types import SnapNumber
# Public names
__all__ = [ "Sequence" ]
class Sequence(ComponentAPI):
"""
The sequence generating component.
"""
api_version = '1.0'
component_version = '1.1'
capabilities = {
ComponentAPI.CAPABILITY_INPUT_VIEW_LOWER_LIMIT : 1,
ComponentAPI.CAPABILITY_INPUT_VIEW_UPPER_LIMIT : 1,
ComponentAPI.CAPABILITY_OUTPUT_VIEW_LOWER_LIMIT : 1,
ComponentAPI.CAPABILITY_OUTPUT_VIEW_UPPER_LIMIT : 1,
ComponentAPI.CAPABILITY_ALLOW_PASS_THROUGH : True
}
component_description = "Generates a sequence from a starting value with a specified step."
component_label = "Sequence"
component_doc_uri = "https://www.snaplogic.org/trac/wiki/Documentation/%s/ComponentRef/Sequence" % \
version_info.doc_uri_version
def create_resource_template(self):
"""
Create Sequence resource definition template. It consists of:
InitialValue: The value where the sequence starts.
StepValue: The increase per step.
"""
self.set_property_def('InitialValue',
prop.SimpleProp("InitialValue",
SnapNumber,
"The start value of the sequence.",
None,
True))
self.set_property_def('StepValue',
prop.SimpleProp("StepValue",
SnapNumber,
"The increase of the sequence output per step.",
None,
True))
# Set some defaults
self.set_property_value('InitialValue', 1)
self.set_property_value('StepValue', 1)
# Predefine the input view with no fields, pass-through = True, modifiable=False
self.add_record_input_view_def('Input1', (), 'Sequence input view', False)
# Predefine the output view with 1 field, modifiable=False and propagate the pass through fields
self.add_record_output_view_def('Output1',
(('Value', SnapNumber, "Sequence value"),),
'Sequence output view',
False)
self.set_output_view_pass_through('Output1', ["Input1",])
def validate(self, err_obj):
"""
Validate the input/output views for correctness.
Generic validation handles checking the property values for presence and correctness.
"""
# Generic constraint validation has already checked that we have
# the correct number of input and output views but here we check
# that the input view has no fields and the output view has only 1
# field of type number.
input_views = self.list_input_view_names()
output_views = self.list_output_view_names()
# Confirm that the input view has exactly only zero fields
if len(self.get_input_view_def(input_views[keys.SINGLE_VIEW])[keys.VIEW_FIELDS]) != 0:
err_obj.get_input_view_err().set_message("Input view may not contain any fields. All input fields must be pass through.")
# Confirm that the output view has exactly only one field defined
if len(self.get_output_view_def(output_views[keys.SINGLE_VIEW])[keys.VIEW_FIELDS]) != 1:
err_obj.get_output_view_err().set_message("There must be exactly one field in the output view.")
else:
# And it's type must be number
output_view_desc = self.get_output_view_def(output_views[keys.SINGLE_VIEW])
if output_view_desc['fields'][0][keys.FIELD_TYPE] != SnapNumber:
err_obj.get_output_view_err().set_message("Sequence component output field type must be '%s'." % SnapNumber)
def execute(self, input_views, output_views):
"""
Processing the input records.
"""
try:
output_view = output_views.values()[keys.SINGLE_VIEW]
except IndexError:
raise SnapComponentError("No output view connected.")
try:
input_view = input_views.values()[keys.SINGLE_VIEW]
except IndexError:
raise SnapComponentError("No input view connected.")
# Getting properties and views and doing some sanity checking.
try:
current_value = self.get_property_value('InitialValue')
step_value = self.get_property_value('StepValue')
if type(current_value) != Decimal:
current_value = Decimal(current_value)
if type(step_value) != Decimal:
step_value = Decimal(step_value)
except Exception, e:
self.elog(e)
raise SnapComponentError("Cannot get proper numerical value for one of the properties.")
# Pre-create the output record object
out_rec = output_view.create_record()
ofname = out_rec.field_names[0]
# Start the processing loop
record = input_view.read_record()
while record is not None:
out_rec[ofname] = current_value
current_value += step_value
out_rec.transfer_pass_through_fields(record)
output_view.write_record(out_rec)
record = input_view.read_record()
output_view.completed()
def upgrade_1_0_to_1_1(self):
"""
No-op upgrade only to change component doc URI during the upgrade
which will be by cc_info before calling this method.
"""
pass
|