#-*- coding: utf-8 -*-
import datetime
import re
from stalker.ext.validatedList import ValidatedList
########################################################################
class EntityMeta(type):
"""the metaclass for the very basic entity, just adds the name of the class
as the entity_type class attribute
"""
#----------------------------------------------------------------------
def __init__(cls, classname, bases, dict_):
setattr(cls, "entity_type", unicode(classname))
return type.__init__(cls, classname, bases, dict_)
########################################################################
[docs]class SimpleEntity(object):
"""The base class of all the others
This class has the basic information about an entity which are the name,
the description, tags and the audit information like created_by,
updated_by, date_created and date_updated about this entity.
Two SimpleEntities considered equal if they have the same name, the other
attributes doesn't matter.
:param string name: A string or unicode value that holds the name of this
entity. It can not be empty, the first letter should be an alphabetic
([a-zA-z]) (not alphanumeric [a-zA-Z0-9]) letter and it should not
contain any white space at the beggining and at the end of the string,
giving an object the object will be converted to string and then the
resulting string will be conditioned.
:param str description: A string or unicode attribute that holds the
description of this entity object, it could be an empty string, and it
could not again have white spaces at the beggining and at the end of the
string, again any given objects will be converted to strings
:param created_by: The :class:`~stalker.core.models.user.User` who has
created this object
:type created_by: :class:`~stalker.core.models.user.User`
:param updated_by: The :class:`~stalker.core.models.user.User` who has
updated this object lastly. The created_by and updated_by attributes
point the same object if this object is just created.
:param date_created: The date that this object is created.
:type date_created: :class:`datetime.datetime`
:param date_updated: The date that this object is updated lastly. For newly
created entities this is equal to date_created and thedate_updated cannot
point a date which is before date_created.
:type date_updated: :class:`datetime.datetime`
:param str code: The code name of this object. It accepts string or unicode
values and any other kind of objects will be converted to string. Can be
omitted and it will be set to the uppercase version of the nice_name
attribute. If both the name and code arguments are given the code
attribute will be set to code, but in any update to name attribute the
code also will be updated to the uppercase form of the nice_name
attribute. The default value is the upper case form of the nice_name
"""
__metaclass__ = EntityMeta
#----------------------------------------------------------------------
[docs] def __init__(self,
name=None,
description="",
created_by=None,
updated_by=None,
date_created=datetime.datetime.now(),
date_updated=datetime.datetime.now(),
code=None,
**kwargs
):
# code attribute
# just set it to None for now
self._code = ""
# name and nice_name
self._nice_name = ""
self._name = ""
self.name = name
# code
# if the given code argument is not None
# use it to set the code
if code is not None and code is not "":
self._code = self._validate_code(code)
self._description = self._validate_description(description)
self._created_by = self._validate_created_by(created_by)
self._updated_by = self._validate_updated_by(updated_by)
self._date_created = self._validate_date_created(date_created)
self._date_updated = self._validate_date_updated(date_updated)
#----------------------------------------------------------------------
def __repr__(self):
"""the representation of the SimpleEntity
"""
return "<%s (%s, %s)>" % (self.entity_type, self.name, self.code)
#----------------------------------------------------------------------
def _validate_description(self, description_in):
"""validates the given description_in value
"""
if description_in is None:
description_in = ""
return str(description_in)
#----------------------------------------------------------------------
def _validate_name(self, name_in):
"""validates the given name_in value
"""
# it is None
if name_in is None:
raise ValueError("the name couldn't be set to None")
name_in = self._condition_name(str(name_in))
# it is empty
if name_in == "":
raise ValueError("the name couldn't be an empty string")
return name_in
def _condition_code(self, code_in):
"""formats the given code_in value
"""
# just set it to the uppercase of what nice_name gives
return self._condition_nice_name(code_in).upper()
#----------------------------------------------------------------------
def _condition_name(self, name_in):
"""conditions the name_in value
"""
# remove unnecesary characters from the string
name_in = re.sub("([^a-zA-Z0-9\s_\-]+)", r"", name_in).strip()
# remove all the characters which are not alpabetic
name_in = re.sub("(^[^a-zA-Z]+)", r"", name_in)
return name_in
#----------------------------------------------------------------------
def _condition_nice_name(self, nice_name_in):
"""conditions the given nice name
"""
## remove unnecesary characters from the beginning
#nice_name_in = re.sub("(^[^A-Za-z]+)", r"", nice_name_in)
# remove unnecesary characters from the string
nice_name_in = self._validate_name(nice_name_in)
# replace camel case letters
nice_name_in = re.sub(r"(.+?[a-z]+)([A-Z])", r"\1_\2", nice_name_in)
# replace white spaces with under score
nice_name_in = re.sub("([\s\-])+", r"_", nice_name_in)
# remove multiple underscores
nice_name_in = re.sub(r"([_]+)", r"_", nice_name_in)
# turn it to lower case
nice_name_in = nice_name_in.lower()
return nice_name_in
#----------------------------------------------------------------------
[docs] def description():
def fget(self):
return self._description
def fset(self, description_in):
self._description = self._validate_description(description_in)
doc = """Description of this object."""
return locals()
description = property(**description())
#----------------------------------------------------------------------
[docs] def name():
def fget(self):
return self._name
def fset(self, name_in):
self._name = self._validate_name(name_in)
# also set the nice_name
self._nice_name = self._condition_nice_name(self._name)
# set the code
self.code = self._name
doc = """name of this object"""
return locals()
name = property(**name())
#----------------------------------------------------------------------
[docs] def nice_name():
def fget(self):
return self._nice_name
doc = """The ``nice name`` of this object.
It has the same value with the name (contextually) but with a different
format like, all the white spaces replaced by underscores ("\_"), all
the CamelCase form will be expanded by underscore (\_) characters and
it is always lower case.
There is also the ``code`` attribute which is simply the upper case
form of ``nice_name`` if it is not defined differently (i.e set to
another value)."""
return locals()
nice_name = property(**nice_name())
#----------------------------------------------------------------------
def _validate_code(self, code_in):
"""validates the given code value
"""
# check if the code_in is None or empty string
if code_in is None or code_in=="":
# restore the value from nice_name and let it be reformatted
code_in = self.nice_name
return self._condition_code(str(code_in))
#----------------------------------------------------------------------
def _validate_created_by(self, created_by_in):
"""validates the given created_by_in attribute
"""
#-------------------------------------------------------------------
# Python documentation says one of the nice ways to over come circular
# imports is late imports and it is perfectly ok to use it like that
#
# just try to import the module as late as possible
#
# ref:
# http://docs.python.org/faq/programming.html#
# what-are-the-best-practices-for-using-import-in-a-module
#-------------------------------------------------------------------
from stalker.core.models import user
## raise ValueError when:
## it is None
#if created_by_in is None:
#raise ValueError("the created_by attribute can not be set to None")
if created_by_in is not None:
if not isinstance(created_by_in, user.User):
raise ValueError("the created_by attribute should be an "
"instance of stalker.core.models.user.User")
return created_by_in
#----------------------------------------------------------------------
def _validate_updated_by(self, updated_by_in):
"""validates the given updated_by_in attribute
"""
from stalker.core.models import user
if updated_by_in is None:
# set it to what created_by attribute has
updated_by_in = self._created_by
if updated_by_in is not None:
if not isinstance(updated_by_in, user.User):
raise ValueError("the updated_by attribute should be an "
"instance of stalker.core.models.user.User")
return updated_by_in
#----------------------------------------------------------------------
def _validate_date_created(self, date_created_in):
"""validates the given date_creaetd_in
"""
# raise ValueError when:
# it is None
if date_created_in is None:
raise ValueError("the date_created could not be None")
if not isinstance(date_created_in, datetime.datetime):
raise ValueError("the date_created should be an instance of "
"datetime.datetime")
return date_created_in
#----------------------------------------------------------------------
def _validate_date_updated(self, date_updated_in):
"""validates the given date_updated_in
"""
# raise ValueError when:
# it is None
if date_updated_in is None:
raise ValueError("the date_updated could not be None")
# it is not an instance of datetime.datetime
if not isinstance(date_updated_in, datetime.datetime):
raise ValueError("the date_updated should be an instance of "
"datetime.datetime")
# lower than date_created
if date_updated_in < self.date_created:
raise ValueError("the date_updated could not be set to a date "
"before date_created, try setting the "
"date_created before")
return date_updated_in
#----------------------------------------------------------------------
[docs] def code():
def fget(self):
return self._code
def fset(self, code_in):
self._code = self._validate_code(code_in)
doc = """The code name of this object.
It accepts string or unicode values and any other kind of objects will
be converted to string. In any update to the name attribute the code
also will be updated to the uppercase form of the nice_name attribute.
If the not initialized or given as None, it will be set to the
uppercase version of the nice_name attribute. Setting the code
attribute to None will reset it to the default value. The default value
is the upper case form of the nice_name. """
return locals()
code = property(**code())
#----------------------------------------------------------------------
[docs] def created_by():
def fget(self):
return self._created_by
def fset(self, created_by_in):
self._created_by = self._validate_created_by(created_by_in)
doc = """The :class:`~stalker.core.models.user.User` who has created
this object."""
return locals()
created_by = property(**created_by())
#----------------------------------------------------------------------
[docs] def updated_by():
def fget(self):
return self._updated_by
def fset(self, updated_by_in):
self._updated_by = self._validate_updated_by(updated_by_in)
doc = """The :class:`~stalker.core.models.user.User` who has updated
this object."""
return locals()
updated_by = property(**updated_by())
#----------------------------------------------------------------------
[docs] def date_created():
def fget(self):
return self._date_created
def fset(self, date_created_in):
self._date_created = self._validate_date_created(date_created_in)
doc = """A :class:`datetime.datetime` instance showing the creation
date and time of this object."""
return locals()
date_created = property(**date_created())
#----------------------------------------------------------------------
[docs] def date_updated():
def fget(self):
return self._date_updated
def fset(self, date_updated_in):
self._date_updated = self._validate_date_updated(date_updated_in)
doc = """A :class:`datetime.datetime` instance showing the update
date and time of this object."""
return locals()
date_updated = property(**date_updated())
#----------------------------------------------------------------------
def __eq__(self, other):
"""the equality operator
"""
return isinstance(other, SimpleEntity) and \
self.name == other.name #and \
#self.description == other.description
#----------------------------------------------------------------------
def __ne__(self, other):
"""the inequality operator
"""
return not self.__eq__(other)
########################################################################
[docs]class Entity(SimpleEntity):
"""Another base data class that adds tags and notes to the attributes list.
This is the entity class which is derived from the SimpleEntity and adds
only tags to the list of parameters.
Two Entities considered equal if they have the same name. It doesn't matter
if they have different tags or notes.
:param tags: a list of tag objects related to this entity. tags could be an
empty list, or when omitted it will be set to an empty list
:param notes: a list of note objects. notes can be an empty list, or when
omitted it will be set to an empty list
"""
#----------------------------------------------------------------------
[docs] def __init__(self,
tags=[],
notes=[],
**kwargs
):
super(Entity, self).__init__(**kwargs)
self._tags = self._validate_tags(tags)
self._notes = self._validate_notes(notes)
#----------------------------------------------------------------------
def _validate_notes(self, notes_in):
"""validates the given notes value
"""
if not isinstance(notes_in, list):
raise ValueError("notes should be an instance of list")
from stalker.core.models import note
for element in notes_in:
if not isinstance(element, note.Note):
raise ValueError("every element in notes should be an "
"instance of stalker.core.models.note.Note "
"class")
return ValidatedList(notes_in)
#----------------------------------------------------------------------
def _validate_tags(self, tags_in):
"""validates the given tags_in value
"""
# it is not an instance of list
if not isinstance(tags_in, list):
raise ValueError("the tags attribute should be set to a list")
return ValidatedList(tags_in)
#----------------------------------------------------------------------
[docs] def notes():
def fget(self):
return self._notes
def fset(self, notes_in):
self._notes = self._validate_notes(notes_in)
doc = """all the notes about this entity, it should be a list of Notes
objects or an empty list, None is not accepted
"""
return locals()
notes = property(**notes())
#----------------------------------------------------------------------
tags = property(**tags())
#----------------------------------------------------------------------
def __eq__(self, other):
"""the equality operator
"""
return super(Entity, self).__eq__(other) and \
isinstance(other, Entity)
########################################################################
[docs]class TypeEntity(Entity):
"""The entry point for types.
It is created to group the `Type` objects, so any other classes accepting a
``TypeEntity`` object can have one of the derived classes, this is done in
that way mainly to ease the of creation of only one
:class:`~stalker.core.models.types.TypeTemplate` class and let the
others to use this one TypeTemplate class.
It doesn't add any new parameters to it's super.
"""
#----------------------------------------------------------------------
[docs] def __init__(self, **kwargs):
super(TypeEntity, self).__init__(**kwargs)
##----------------------------------------------------------------------
#def __repr__(self):
#"""the representation
#"""
#return "<TypeEntity (%s, %s)>" % (self.name, self.code)
#----------------------------------------------------------------------
def __eq__(self, other):
"""the equality operator
"""
return super(TypeEntity, self).__eq__(other) and \
isinstance(other, TypeEntity)
#----------------------------------------------------------------------
def __ne__(self, other):
"""the inequality operator
"""
return not self.__eq__(other)