Beispiel #1
0
class UrnConfig(DocumentSchema):
    """
    A document schema representing a WFItem urn_config
    used to identify unique groups of items
    """
    field = schema.StringProperty()
    subfield = schema.StringProperty()
Beispiel #2
0
class Field(schema.DocumentSchema):
    """
    A document schema representing a WFItem field
    """
    indicator1 = schema.StringProperty()
    indicator2 = schema.StringProperty()
    subfields = schema.SchemaDictProperty(SubField())

    def __repr__(self):
        return "<Field i:[%s, %s]>" % (self.indicator1, self.indicator2)
Beispiel #3
0
class Penalty(DocumentSchema):
    """
    A document schema representing a User Penalty
    """

    id = schema.IntegerProperty()
    type = schema.StringProperty(choices=('fault', 'suspend'))
    start = schema.DateProperty()
    end = schema.DateProperty()
    cause = schema.StringProperty()
Beispiel #4
0
class BaseField(DocumentSchema):
    """
    A document schema representing a WFItem base field
    used by field and subfield
    """
    id = schema.StringProperty()
    field_name = schema.StringProperty()
    repeat = schema.BooleanProperty()
    type = schema.StringProperty()
    default_value = schema.StringProperty()
    exec_value = schema.ListProperty()
Beispiel #5
0
class CirculationLog(Document):
    _db = get_db("couchflow")

    type = schema.StringProperty(choices=['loan', 'return', 'renew'])
    loan_type = schema.StringProperty(choices=['room', 'home', 'interbiblio'])
    item_type = schema.StringProperty()
    date = schema.DateProperty(default=datetime.date.today)
    length = schema.IntegerProperty()
    item_id = schema.StringProperty()
    user_id = schema.StringProperty()
    timestamp_added = schema.DateTimeProperty(default=datetime.datetime.now)
Beispiel #6
0
class Loan(DocumentSchema):
    """
    A document schema representing a WFItem loan
    """
    user_id = schema.StringProperty()
    type = schema.StringProperty(choices=['room', 'home'])
    start = schema.DateProperty()
    end = schema.DateProperty()
    renew_count = schema.IntegerProperty()

    def __repr__(self):
        class_name = self.__class__.__name__
        start, end = self.start, self.end
        return '<%s: start:"%s" end:"%s">' % (class_name, start, end)
Beispiel #7
0
class Conector(Document):
    _db = get_db("couchflow")
    workflow_id = schema.StringProperty()
    conector_type = schema.BooleanProperty(default=True)
    name = schema.StringProperty(default="Conector")
    status = schema.StringProperty()
    step = schema.IntegerProperty(default=0)
    is_clone = schema.BooleanProperty(default=False)
    start = schema.BooleanProperty(default=True)
    end = schema.BooleanProperty(default=False)
    active = schema.BooleanProperty()
    previous_tasks = schema.ListProperty()
    next_tasks = schema.ListProperty()
    get_id = property(lambda self: self['_id'])
    wfitems_ids = schema.ListProperty()

    def put_step(self):
        if not self.previous_tasks:
            self.step = 1
            self.save()
            workflow = WorkFlow.get(self.workflow_id)
            workflow.steps = self.step
            return True
        prev_task = get_task(self.previous_tasks[0])
        self.step = prev_task.step + 1
        workflow = WorkFlow.get(self.workflow_id)
        workflow.steps = self.step
        workflow.save()
        self.save()
        return True

    def back_to_top(self):
        previous_tasks = []
        if self.previous_tasks:
            for prev_task_id in self.previous_tasks:
                prev_task = Task.get(prev_task_id)
                previous_tasks.append(prev_task)
            return previous_tasks
        return None

    def go_deep(self):
        next_tasks = []
        if self.next_tasks:
            for next_task_id in self.next_tasks:
                next_task = get_task(next_task_id)
                next_tasks.append(next_task)
            return next_tasks
        return None
Beispiel #8
0
class DecisionTask(Task):
    sentence = schema.StringProperty()
    confirmation = schema.BooleanProperty(default=None)

    def check_data(self):
        return self.confirmation

    def active_conector(self):
        if self.conector_id:
            conector = get_conector(self.conector_id)
            if conector.doc_type == "SequenceConector" and \
                   self.confirmation:
                self.finish_task("finished")
                self.save()
                conector.to_next_tasks()
                return True
            if conector.doc_type == "SequenceConector" and \
                   not self.confirmation:
                self.finish_task("cancel")
                self.save()
                return True
            elif conector.doc_type != "SequenceConector":
                self.finish_task("finished")
                self.save()
                conector.to_next_tasks()
                return True
        return True
Beispiel #9
0
class Group(Document):
    _db = get_db("couchauth")
    name = schema.StringProperty()
    users = schema.ListProperty()
    max_loan_days = schema.IntegerProperty()
    get_id = property(lambda self: self['_id'])

    def add_user(self, user_id):
        if user_id not in self.users:
            self.users.append(user_id)
            self.save()
        return True

    def del_user(self, user_id):
        if user_id in self.users:
            del (self.users[self.users.index(user_id)])
            self.save()
        return True

    def update_users(self, users):
        for user_id in self.users:
            if user_id not in users:
                user = User.get(user_id)
                user.del_group(self._id)
        for user_id in users:
            user = User.get(user_id)
            user.add_group(self._id)

    def id(self):
        return self._id
Beispiel #10
0
class UntilConector(Conector):
    back_task_id = schema.StringProperty(default=None)

    def add_next_task(self, task):
        if len(self.next_tasks) >= 2:
            return False
        elif len(self.next_tasks) == 1 and \
                 self.back_task_id == self.next_tasks[0]:
            self.next_tasks = [task._id] + self.next_tasks
            self.save()
        elif len(self.next_tasks) == 1 and \
                 not self.back_task_id:
            self.back_task_id = task._id
            self.next_tasks.append(task._id)
        elif len(self.next_tasks) == 0:
            self.next_tasks.append(task._id)
        if not task.prev_conector_id:
            task.prev_conector_id = self._id
            task.save()
        else:
            task.have_until = True
            task.save()
        self.save()
        return True

    def add_previous_task(self, task):
        if len(self.previous_tasks) > 0:
            return False

        self.previous_tasks.append(task._id)
        return True

    def check_previous_tasks(self):
        """
        """

    def to_next_tasks(self):
        if len(self.next_tasks) > 1 and self.previous_tasks:
            previous_tasks = get_task(self.previous_tasks[0])
            if previous_tasks.check_data():
                task = get_task(self.next_tasks[0])
            else:
                task = get_task(self.next_tasks[1])
                task.status = None
            self.active = True
            task.active = True
            task.enabled = True
            task.exec_process()
            task.save()
            previous_tasks.active = False
            previous_tasks.save()
            return True
        return False
Beispiel #11
0
class WorkFlow(Document):
    _db = get_db("couchflow")
    name = schema.StringProperty()
    workflow_type = schema.StringProperty()
    item_type = schema.StringProperty()
    user_id = schema.StringProperty()
    conectors = schema.DictProperty()
    nro_pedido = schema.IntegerProperty(default=0)
    tasks = schema.DictProperty()
    merge_conectors = schema.DictProperty()
    original_task_ids = schema.DictProperty()
    enabled = schema.BooleanProperty(default=False)
    steps = schema.IntegerProperty(default=0)
    is_clone = schema.BooleanProperty(default=False)
    get_id = property(lambda self: self['_id'])
    # text shown in the task
    description = schema.StringProperty()
    path = schema.ListProperty(schema.StringProperty())
    # keep track of starting point of workflow
    # usefull to get all workflows that starts
    # with X task type, for example FilterItems
    first_task_type = schema.StringProperty()
    first_task_id = schema.StringProperty()
    visible = schema.BooleanProperty(default=True)

    def get_item(self):
        if not self.item_type:
            return False
        items = WFItem.view("couchflow/item_names", limit=1, include_docs=True)
        item = items[self.item_type]
        item = item.one()
        return item

    def get_items(self):
        item_query = WFItem.view("couchflow/items", include_docs=True)
        items = {}
        for item in item_query.all():
            items[item.item_type] = (item.name, False)

        if self.item_type in items:
            items[self.item_type] = (items[self.item_type][0], True)

        items = [(key, items[key][0], items[key][1]) for key in items]
        return items

    def remove_relations(self):
        conectors = WorkFlow.view("couchflow/flowconector", include_docs=True)
        conectors = conectors[self._id]
        # TODO: bulk delete
        for conector in conectors:
            conector.delete()
        tasks = Task.view("couchflow/flowtask", include_docs=True)
        tasks = tasks[self._id]
        for task in tasks:
            task.delete()
        return True

    def set_all_inactive(self):
        conectors = WorkFlow.view("couchflow/flowconector", include_docs=True)
        conectors = conectors[self._id]
        # TODO: bulk save
        for conector in conectors:
            conector.active = False
            conector.save()
        tasks = Task.view("couchflow/flowtask", include_docs=True)
        tasks = tasks[self._id]
        for task in tasks:
            task.finish_task(None)
            task.save()
        return True

    def get_docs(self):
        documents = WorkFlow.view("couchflow/flowdocs", include_docs=True)
        documents = documents[self._id]
        documents = documents.all()
        documents.reverse()
        return documents

    def set_enabled(self):
        tasks = Task.view("couchflow/flowtask", include_docs=True)
        tasks = tasks[self._id]
        flag = True
        for task in tasks:
            task.enabled = flag
            task.save()
        return True

    def set_disabled(self):
        tasks = Task.view("couchflow/flowtask", include_docs=True)
        tasks = tasks[self._id]
        flag = True
        for task in tasks:
            task.enabled = flag
            task.save()
        return True

    def get_first_conector(self):
        conectors = WorkFlow.view("couchflow/firstconector", include_docs=True)
        conectors = conectors[self._id]
        return conectors.one()

    def get_active_tasks(self):
        tasks = Task.view("couchflow/activetask", include_docs=True)
        tasks = tasks[self._id]
        return tasks

    def conector_tasks(self, conector):
        if len(conector.next_tasks) > 0:
            tasks = []
            # TODO: use bulk api to get tasks
            if not conector.doc_type == "UntilConector":
                for task_id in conector.next_tasks:
                    task = Task.get(task_id)
                    tasks.append(task)
            else:
                task = Task.get(conector.next_tasks[0])
                tasks.append(task)
            return tasks
        return False

    def tasks_conectors(self, tasks):
        conectors = []
        for task in tasks:
            if task.conector_id:
                conector = get_conector(task.conector_id)
                #if conector.doc_type == "SequenceConector" and
                #conector.active:
                #    sequence_conector = SequenceConector.get(conector._id)
                conectors.append(conector)
        if len(conectors) > 0:
            return conectors
        return False

    def tasks_tree(self):
        first_conector = self.get_first_conector()
        tasks_tree = [
            [first_conector],
        ]

        tasks = self.conector_tasks(first_conector)

        while tasks:
            tasks_tree.append(tasks)
            conectors = self.tasks_conectors(tasks)
            if conectors:
                tasks_tree.append(conectors)
                tasks = []
                for conector in conectors:
                    try:
                        tasks += self.conector_tasks(conector)
                    except:
                        pass
            else:
                tasks = False
        if len(tasks_tree) > 1:
            return tasks_tree
        return False

    def add_conector(self, conector, active=False):
        self.conectors[conector._id] = active
        self.save()
        return True

    def add_task(self, task, active=False):
        self.tasks[task._id] = active
        self.save()
        return True

    def remove_tree(self, task):
        return True
Beispiel #12
0
class WFItem(Document):
    """
    WFItem could be any material of the system, like a book or a movie
    """
    _db = get_db("couchflow")
    name = schema.StringProperty()
    item_type = schema.StringProperty()
    is_clone = schema.BooleanProperty(default=False)

    fields_properties = schema.SchemaDictProperty(Fields)

    # circulation
    loan = schema.SchemaProperty(Loan)
    reserves = schema.SchemaListProperty(Reserve)

    # Unified Resource Name
    # We use a sum of fields to generate urn field
    urn_config = schema.SchemaListProperty(UrnConfig)

    # Not every item in the system can be loanable
    loanable = schema.BooleanProperty(default=True)

    # Order Number to track orders between workflows
    order_nbr = schema.IntegerProperty()

    # Reference Item
    # if it's a reference, it is used to complete fields from other items
    reference = schema.BooleanProperty(default=False)

    comments = schema.StringProperty()

    @property
    def urn(self):
        """
        Returns the urn based on urn_config property
        or None if it have not a valid urn
        """

        return get_urn(self)

    @property
    def inventory_nbr(self):
        """
        Returns inventory number (876_a) or None
        """

        field = self.get_field("876", "a")
        if field:
            return field[0]

    @property
    def title(self):
        """
        Returns item title, here for consistency
        """

        title = self.get_field("245", "a")
        if not title:
            title = "Unknown title"
        else:
            title = title[0]
        return title

    def get_field(self, field, subfield=None, first=True):
        """
        Helper that returns field or subfield, or None if can't find it
        """
        return get_field(self, field, subfield, first)

    def get_info(self):
        fields = []
        for k in self.fields_properties:
            field_prop = self.fields_properties[k]
            # TODO: support to more fields
            first_field_prop = field_prop.first
            if first_field_prop['exec_value']:
                value = first_field_prop['exec_value']
                k = k + ' - ' + field_prop['field_name']
                field = (k, value)
                fields.append(field)

        return fields

    def show_fields_properties(self):
        """
        returns [(sequence number, Fields), ...]
        """
        return enumerate(
            sorted(self.fields_properties.values(),
                   key=lambda x: x.id and int(x.id)))

    def fields_properties_items_sorted(self):
        return sorted(self.fields_properties.items())

    def check_form(self, post, task=None):
        errors = []

        def validate_field(number, value, field_type):
            """
            spec is the DocumentSchema for either Field or SubField
            """

            if field_type == 'int':
                if not value.isdigit():
                    errors.append([number, field_type])
            elif field_type == 'float':
                try:
                    float(value)
                except ValueError:
                    errors.append((number, field_type))

        for field_id, fields in self.fields_properties.iteritems():
            # forms don't support repeated fields yet (TODO elsewhere)
            field = fields.first
            field_name = fields.field_name
            if field_id in post:
                validate_field(field_name, post[field_id], fields.type)
                for subfield_id, subfield in field.subfields.iteritems():
                    sf_full_id = "%s_%s" % (field_id, subfield_id)
                    # TODO: check
                    if "sf_input" in post:
                        validate_field(sf_full_id, post["sf_" + sf_full_id],
                                       subfield)

        if task:
            for field in task.item_required_fields:
                if "_" in field:
                    value = post.get("sf_" + field, '')
                else:
                    value = post.get(field, '')

                if not value:
                    errors.append([field, 'required'])

        return errors

    def marc_record(self):
        """
        Returns the item as a pymarc Record
        """
        record = pymarc.Record()

        for tag, fprop in self.fields_properties.iteritems():
            try:
                tag = int(tag)
            except Exception, error:
                continue

            # only marc fields
            if tag > 999:
                continue

            if fprop.first.subfields:
                sfields = []
                indicators = [
                    fprop.first.indicator1 or "#", fprop.first.indicator2
                    or "#"
                ]
                for sf in fprop.first.subfields.values():
                    for val in sf.exec_value:
                        sfields.append(sf.field_name)
                        sfields.append(val)
                field = pymarc.Field(tag, indicators, sfields)
                record.add_field(field)
            else:
                try:
                    exec_value = fprop.first.exec_value
                except Exception:
                    exec_value = []
                indicators = [
                    fprop.first.indicator1 or "#", fprop.first.indicator2
                    or "#"
                ]
                for val in exec_value:
                    record.add_field(
                        pymarc.Field(tag, indicators, data=str(val)))
        return record
Beispiel #13
0
class SubField(BaseField):
    """
    A document schema representing a WFItem subfield
    """
    description = schema.StringProperty()
Beispiel #14
0
class MultipleInstances(Conector):
    iterator_field = schema.StringProperty()

    def add_next_task(self, task):
        if len(self.next_tasks) >= 1:
            return False
        self.next_tasks.append(task._id)
        task.prev_conector_id = self._id
        task.save()
        self.save()
        return True

    def add_previous_task(self, task):
        if len(self.previous_tasks) > 0:
            return False
        self.previous_tasks.append(task._id)
        return True

    def check_previous_tasks(self):
        """
        """

    def clone_conector(self, conector_id, wfitem_id=False):
        conector = get_conector(conector_id)
        try:
            del (conector._id)
        except AttributeError:
            pass

        try:
            del (conector._rev)
        except AttributeError:
            pass

        conector.previous_id = conector_id
        conector.is_clone = True

        if wfitem_id:
            conector.wfitems_ids.append(wfitem_id)
        conector.save()
        return conector._id

    def clone_task(self, task_id, wfitem_id=False):
        task = get_task(task_id)
        task = task.really_clone()

        task.previous_id = task_id
        task.is_clone = True
        if wfitem_id:
            task.wfitems_ids = [wfitem_id]
        task.save()
        if task.conector_id:
            task.conector_id = self.clone_conector(task.conector_id, wfitem_id)
            task.save()
            conector = get_conector(task.conector_id)
            conector.previous_tasks[conector.previous_tasks.index(
                task_id)] = task._id
            conector.save()
            next_tasks = []

            if conector.doc_type == "UntilConector":
                next_tasks.append(
                    self.clone_task(conector.next_tasks[0], wfitem_id))
                t_id = conector.next_tasks[1]
                t_task = Task.view("couchflow/wf_oldid", include_docs=True)
                t_task = t_task[conector.workflow_id]
                t_task = t_task.all()[0]
                next_tasks.append(t_task._id)
            else:
                for con_task_id in conector.next_tasks:
                    next_tasks.append(self.clone_task(con_task_id, wfitem_id))

            conector.next_tasks = next_tasks
            conector.save()
        return task._id

    def create_multiple(self, number):
        task = get_task(self.next_tasks[0])
        number = int(number)
        for i in range(1, number):
            new_clone_id = self.clone_task(task._id)
            new_task = get_task(new_clone_id)
            new_task.active = True
            new_task.save()
            self.next_tasks.append(new_clone_id)
        self.save()
        return True

    def create_multiple_wfitems(self, wfitems):
        task = get_task(self.next_tasks[0])
        for wfitem_id in wfitems:
            new_clone_id = self.clone_task(task._id, wfitem_id)
            new_task = get_task(new_clone_id)
            new_task.active = True
            new_task.save()
            self.next_tasks.append(new_clone_id)
        self.next_tasks.remove(self.next_tasks[0])
        self.save()
        return True

    def to_next_tasks(self):
        if len(self.next_tasks) >= 1 and self.previous_tasks:
            previous_tasks = get_task(self.previous_tasks[0])
            if previous_tasks.doc_type == "DynamicDataTask":
                number = previous_tasks.extra_fields[
                    self.iterator_field]['exec_value'][0]
                try:
                    number = int(number)
                except ValueError:
                    return False
                self.create_multiple(number)
                task = Task.get(self.next_tasks[0])
                task.active = True
                task.save()
                self.save()
            elif previous_tasks.doc_type == "WFItemsLoader" or \
                     previous_tasks.doc_type == "FilterWFItems" or\
                     previous_tasks.doc_type == "CloneItems":
                self.create_multiple_wfitems(previous_tasks.wfitems_ids)
            for i in self.next_tasks:
                task = get_task(i)
                task.exec_process()
        return False
Beispiel #15
0
class Task(Document):
    _db = get_db("couchflow")
    name = schema.StringProperty()
    task_type = schema.BooleanProperty(default=True)
    item_type = schema.StringProperty()
    node_type = schema.StringProperty(default="default")
    workflow_id = schema.StringProperty()
    workflow_type = schema.StringProperty()
    workflow_nro = schema.IntegerProperty(default=0)
    comments = schema.StringProperty()
    wfitems_ids = schema.ListProperty()
    conector_id = schema.StringProperty(default=None)
    user_id = schema.StringProperty(default=None)
    saved_by = schema.StringProperty(default=None)
    group_id = schema.StringProperty(default=None)
    prev_conector_id = schema.StringProperty(default=None)
    is_clone = schema.BooleanProperty(default=False)
    item_fields = schema.DictProperty()
    item_required_fields = schema.DictProperty()
    item_tema3_fields = schema.DictProperty()
    end = schema.BooleanProperty(default=False)
    step = schema.IntegerProperty(default=0)
    active = schema.BooleanProperty()
    have_until = schema.BooleanProperty(default=False)
    enabled = schema.BooleanProperty(default=False)
    status = schema.StringProperty()
    position = schema.DictProperty(default={'x': 50, 'y': 50})
    get_id = property(lambda self: self['_id'])

    # text shown in the task
    description = schema.StringProperty()
    group_by_urn = schema.BooleanProperty(default=False)
    visible = schema.BooleanProperty(default=True)

    # template used to display tasks, if exists
    html_tpl = schema.StringProperty(default="")

    # Used in loader
    base_item = schema.StringProperty()

    def is_bulkable(self):
        for i in self.item_fields:
            if self.item_fields[i] == 'write':
                return False
        return True

    def exec_process(self):
        return True

    @property
    def item_name(self):
        name = ''
        try:
            item = self.get_item(json=True)
            field = item['fields_properties']['245']['list'][0]
            name = field['subfields']['a']['exec_value'][0]
        except Exception, error:
            print 'Cant get item_name', error
            try:
                item = self.get_item(json=True)
                field = item['fields_properties']['700']['list'][0]
                name = field['subfields']['a']['exec_value'][0]
            except Exception, error:
                print '  Cant get item_name', error
Beispiel #16
0
class User(Document):
    _db = get_db("couchauth")
    get_id = property(lambda self: self['_id'])

    first_name = schema.StringProperty(required=False)
    last_name = schema.StringProperty(required=False)
    email = schema.StringProperty(required=False)

    document_type = schema.StringProperty(required=False)
    document_number = schema.IntegerProperty(required=False)
    address = schema.StringProperty(required=False)
    phone_number = schema.StringProperty(required=False)
    cellphone_number = schema.StringProperty(required=False)
    comment = schema.StringProperty(required=False)

    username = schema.StringProperty()
    password = schema.StringProperty()

    groups = schema.ListProperty()

    is_staff = schema.BooleanProperty()
    is_superuser = schema.BooleanProperty(default=False)
    is_active = schema.BooleanProperty(default=True)

    last_login = schema.DateTimeProperty(required=False)
    date_joined = schema.DateTimeProperty(default=datetime.utcnow)

    penalties = schema.SchemaListProperty(Penalty)

    tematres_host = schema.StringProperty(required=False)

    def del_group(self, group_id):
        group = Group.get(group_id)
        group.del_user(self._id)
        if group_id in self.groups:
            del (self.groups[self.groups.index(group_id)])
            self.save()

    def add_group(self, group_id):
        group = Group.get(group_id)
        group.add_user(self._id)
        if group_id not in self.groups:
            self.groups.append(group_id)
            self.save()

    def update_groups(self, groups):
        for group_id in self.groups:
            if group_id not in groups:
                self.del_group(group_id)
        for group_id in groups:
            self.add_group(group_id)

    def id(self):
        return self._id

    def set_password(self, password_string):
        self.password = encrypt_value(password_string)
        return True

    def check_password(self, password_string):
        if self.password == encrypt_value(password_string):
            return True
        return False

    @classmethod
    def get_user(cls, username, is_active=True):
        param = {"key": username}

        user = cls.view('couchauth/username', include_docs=True,
                        **param).first()
        if user and user.is_active:
            return user
        return None

    @classmethod
    def get_user_with_groups(cls, username):
        db = cls.get_db()
        rows = list(
            db.view('couchauth/user_and_groups',
                    include_docs=True,
                    startkey=[username, 0],
                    endkey=[username, 1]).all())

        if rows:
            user = cls.wrap(rows[0]['doc'])
            user.group_names = [x['doc']['name'] for x in rows[1:]]
            return user

    @classmethod
    def get_user_by_email(cls, email, is_active=True):
        param = {"key": email}

        user = cls.view('couchauth/get_by_email', include_docs=True,
                        **param).first()
        if user and user.is_active:
            return user
        return None

    def __unicode__(self):
        return self.username

    def __repr__(self):
        return "<User: %s>" % self.username

    def is_anonymous(self):
        return False

    def save(self):
        if not self.check_username():
            raise Exception('This username is already in use.')
        if not self.check_email():
            raise Exception('This email address is already in use.')
        return super(self.__class__, self).save()

    def check_username(self):
        user = User.get_user(self.username, is_active=None)
        if user is None:
            return True
        return user._id == self._id

    def check_email(self):
        if not self.email:
            return True

        user = User.get_user_by_email(self.email, is_active=None)
        if user is None:
            return True

        return user._id == self._id

    def _get_id(self):
        return self.username

    id = property(_get_id)

    def is_authenticated(self):
        return True