class Book(DjangoNode): Title = StringProperty() img_url = StringProperty() user = RelationshipFrom('UserProfileInfo','FAVORITEBOOK',cardinality=ZeroOrMore) wrote = RelationshipFrom('Author','WROTE',cardinality=OneOrMore) genre = RelationshipFrom('Genre', 'GENRE', cardinality=OneOrMore) bookRating = RelationshipFrom('Book', 'RATING', model = RatingRel, cardinality=ZeroOrMore)
class Institute(DjangoNode): #print("in institute") name=StringProperty(max_length=100,required = True) email=EmailProperty(unique=True,required = True) contact=StringProperty(max_length=10, required = True) address = StringProperty(max_length=200) select = StringProperty(max_length=12, required = True) Date = StringProperty(required = True) seek = RelationshipFrom('SeekDonation', 'SEEKFROM', cardinality=ZeroOrMore) class Meta: app_label = 'core'
class Genre(DjangoNode): name = StringProperty() genre_id = IntegerProperty() bookGenre = RelationshipTo('Book', 'GENRE', cardinality=ZeroOrMore) favGenre = RelationshipFrom('UserProfileInfo', 'FAVORITEGENRE', cardinality=ZeroOrMore)
def test_illegal_array_base_prop_raises(): try: ArrayProperty(StringProperty(index=True)) except ValueError: assert True else: assert False
def test_string_property_exceeds_max_length(): """ StringProperty is defined by two properties: `max_length` and `choices` that are mutually exclusive. Furthermore, max_length must be a positive non-zero number. """ # Try to define a property that has both choices and max_length with raises(ValueError): some_string_property = StringProperty(choices={ "One": "1", "Two": "2" }, max_length=22) # Try to define a string property that has a negative zero length with raises(ValueError): another_string_property = StringProperty(max_length=-35) # Try to validate a long string a_string_property = StringProperty(required=True, max_length=5) with raises(ValueError): a_string_property.normalize( 'The quick brown fox jumps over the lazy dog') # Try to validate a "valid" string, as per the max_length setting. valid_string = "Owen" normalised_string = a_string_property.normalize(valid_string) assert valid_string == normalised_string, "StringProperty max_length test passed but values do not match."
def test_string_property_exceeds_max_length(): """ StringProperty is defined by two properties: `max_length` and `choices` that are mutually exclusive. Furthermore, max_length must be a positive non-zero number. """ # Try to define a property that has both choices and max_length with raises(ValueError): some_string_property = StringProperty(choices={"One":"1", "Two":"2"}, max_length=22) # Try to define a string property that has a negative zero length with raises(ValueError): another_string_property = StringProperty(max_length = -35) # Try to validate a long string a_string_property = StringProperty(required=True, max_length=5) with raises(ValueError): a_string_property.normalize('The quick brown fox jumps over the lazy dog') # Try to validate a "valid" string, as per the max_length setting. valid_string = "Owen" normalised_string = a_string_property.normalize(valid_string) assert valid_string == normalised_string, "StringProperty max_length test passed but values do not match."
class UserProfileInfo(DjangoNode): first_name = StringProperty(max_length=30,required = True) last_name = StringProperty(max_length=150, required = True) email = EmailProperty(unique=True,required = True) username = StringProperty(max_length=150, unique=True, required = True) password = StringProperty(max_length=10,unique=True, required = True) address = StringProperty(max_length=200) pincode = StringProperty(max_length=6) phone = StringProperty(max_length=10, required = True) latitude = FloatProperty() longitude = FloatProperty() favGenres = RelationshipTo('Genre', 'FAVORITEGENRE', cardinality=ZeroOrMore) favBooks = RelationshipTo('Book', 'FAVORITEBOOK', cardinality=ZeroOrMore) bookRating = RelationshipTo('Book', 'RATING', model = RatingRel, cardinality=ZeroOrMore) class Meta: app_label = 'core'
class Card(BaseModel): """ Models a card in database """ number = StringProperty(required=True) due_day = IntegerProperty(required=True) expiration_date = DateProperty(required=True) cvv = StringProperty(required=True) max_limit = FloatProperty(required=True) free_limit_ = FloatProperty(required=True) wallet = RelationshipFrom('.wallet.Wallet', 'CONTAINED_BY', cardinality=One) purchases = RelationshipFrom('.billing.Purchase', 'DID', model=BillingAction) payments = RelationshipFrom('.billing.Payment', 'RECEIVED') def __init__(self, date_format='%m/%d/%Y', **kwargs): # Test if all arguments are present if not all( k in kwargs for k in ['number', 'due_day', 'expiration_date', 'cvv', 'max_limit']): raise NotEnoughCardArguments # set initial free_limit equals to max_limit kwargs['free_limit_'] = kwargs[ 'free_limit_'] if 'free_limit_' in kwargs else kwargs['max_limit'] kwargs['cvv'] = str(kwargs['cvv']) # as discussed by e-mail, due_day is limited from 1 to 28 if kwargs['due_day'] < 1 or kwargs['due_day'] > 28: raise ValueError( 'due_date should be limited from 1 to 28 to avoid problems with some months' ) if type(kwargs['expiration_date']) is str: kwargs['expiration_date'] = datetime.strptime( kwargs['expiration_date'], date_format).date() fake_today = kwargs['fake_today'] if 'fake_today' in kwargs else None date_format = kwargs[ 'date_format'] if 'date_format' in kwargs else '%m/%d/%Y' super(Card, self).__init__(**kwargs) self.date_format = '%m/%d/%Y' self.fake_today = None self.set_fake_today(fake_today, date_format) @property def due_date(self): """ Calculate next due date based on actual date and due_day :return: next due date """ due_date = self.get_fake_today().replace(day=self.due_day) # due date is next month if self.due_day <= self.fake_today.day: year = due_date.year + due_date.month // 12 month = due_date.month % 12 + 1 due_date = due_date.replace(month=month, year=year) return due_date def set_fake_today(self, fake_today=None, date_format='%m/%d/%Y'): """ Assume a fake today date, set for today if None. Useful for tests and generate reference cards sortings :param fake_today: date or string representing date to be assumed for today :param date_format: format for parsing date when fake_today is string :return: """ self.fake_today = fake_today if fake_today else datetime.today().date() if type(self.fake_today) is str: self.fake_today = datetime.strptime(fake_today, date_format).date() self.date_format = date_format def get_fake_today(self): return self.fake_today if self.fake_today else datetime.today().date() @property def free_limit(self): """ Just get free_limit_ :return: free_limit """ return self.free_limit_ @free_limit.setter def free_limit(self, value): """ Just to avoid changing free_limit_ directly, it should be increased or decreased on each payment :param value: value to be changed """ raise UnchangeableCardValue() @property def active(self): """ A card should be inactive if it's set to be inactive, or if reached expiration date :return: """ if self.expiration_date < self.fake_today: return False return self.active_ @active.setter def active(self, state): """ Change active state from an user. Raise warning if state is not changed :param state: :return: """ if self.active and state: # raise warning if activating an already active card warnings.warn(CardAlreadyActive()) elif (not self.active) and (not state): # raise warning if deactivating an already inactive card warnings.warn(CardAlreadyInactive()) else: # Otherwise, process limits self.active_ = state if state: # increase wallet limits if activating the card self.wallet[0].increase_max_limit(self.max_limit) self.wallet[0].increase_free_limit(self.max_limit) else: # decrease wallet limit if deactivating the card self.wallet[0].decrease_max_limit(self.max_limit) self.wallet[0].decrease_free_limit(self.max_limit) self.save() def decrease_free_limit(self, value): """ Decrease free limit, used on purchasing. raise NotEnoughCardFreeLimit if free_limit is not enough :param value: value to reduce free_limit :return: new value for free_limit """ # if there is not enough free_limit, raise exception if self.free_limit < value: raise NotEnoughCardFreeLimit() # if there is free limit, reduce value amount from it self.free_limit_ -= value self.save() self.wallet.single().decrease_free_limit(value) self.wallet.single().refresh() return self.free_limit def increase_free_limit(self, value): """ Increase free limit, used on payment. raises PaymentExceed when payment value and free_limit exceed maximum card limit :param value: value to increase free_limit :return: new value for free_limit """ # raise exception if exceed maximum limit of card if self.free_limit + value > self.max_limit: raise PaymentExceed # if no problem, increase limit self.free_limit_ = self.free_limit_ + value self.save() self.wallet[0].increase_free_limit(value) self.wallet[0].refresh() return self.free_limit def purchase(self, value): """ Process a purchase, raise CardIsInactive if card is inactive :param value: purchase value """ if not self.active: raise CardIsInactive() self.decrease_free_limit(value) self.save() def pay(self, value): """ Release credit increasing free_limit. Raise PaymentExceed if value+free_limit exceeds max_limit :param value: payed amount :return: new free_limit """ try: self.increase_free_limit(value) payment = Payment(value=value) payment.save() # Connect payment to card payment.card.connect(self) payment.save() self.save() self.payments.connect(payment) self.save() # Connect payment to wallet self.wallet.single().save() payment.wallet.connect(self.wallet.single()) self.wallet.single().payments.connect(payment) payment.save() self.wallet.single().save() return payment except Exception as e: raise e def to_dict(self): return dict(uid=self.uid, due_date=self.due_date, expiration_date=self.expiration_date, max_limit=self.max_limit, free_limit=self.free_limit, active=self.active) def __lt__(self, other): """ Order cards, first by largest due date, then by smaller maxmimum limit. rules defined in https://slack-files.com/T06M9ENDT-F5XK4J0P2-532510c5c0 :param other: other object which is beeing compared :return: True if is lower, False if is bigger than other """ if self.due_date > other.due_date: return True elif self.due_date == other.due_date: return self.max_limit < other.max_limit else: return False def __str__(self): s = '<Card[limit: {max_limit}, due_day: {due_date}, expiration: {expiration}, cvv: {cvv}]>' return s.format(max_limit=self.max_limit, due_date=self.due_date, expiration=self.due_date, cvv=self.cvv)
class ArrayProps(StructuredNode): uid = StringProperty(unique_index=True) untyped_arr = ArrayProperty() typed_arr = ArrayProperty(IntegerProperty())
class TestNode(StructuredNode): name_ = StringProperty(db_property="name")
class DefaultTestValueTwo(StructuredNode): uid = StringProperty(default=uid_generator, index=True)
class UniqueNullableNameNode(StructuredNode): name = StringProperty(unique_index=True)
class ConstrainedTestNode(StructuredNode): required_property = StringProperty(required=True) unique_property = StringProperty(unique_index=True) unique_required_property = StringProperty(unique_index=True, required=True) unconstrained_property = StringProperty()
class TestNode(StructuredNode): uid = UniqueIdProperty() name_ = StringProperty(db_property="name", required=True)
class TestLongString(StructuredNode): name = StringProperty(required=True, max_length=5)
class User(BaseModel): """ This class models an user in database """ # Basic field names name = StringProperty(required=True) username = StringProperty(required=True, unique_index=True) address = StringProperty(default=None) mail_address = EmailProperty(default=None) # Sensible data # TODO: Would be good hide password_ on collecting object password_ = StringProperty(db_property='password', required=True) # Relationships wallets = RelationshipTo('.wallet.Wallet', 'OWN') def wallet_uid(self): if self.wallets.single(): return self.wallets.single().uid return None def to_dict(self): return dict(name=self.name, username=self.username, uid=self.uid, mail_address=self.mail_address, active=self.active, wid=self.wallet_uid()) @property def password(self): """ Return hashed password :return: hashed password """ return self.password_ if self.password_ else None @password.setter def password(self, value): """ Once password need to be hashed, this method/property is used to hash entered password and save the hash instead of clean one """ self.password_ = mixture_pwd(self.uid, value) @property def active(self): return self.active_ @active.setter def active(self, state): self.active_ = state def save(self): """ validates if an username is in use before save it :return: * Saved user object if it's ok * 'username_in_use' if username already in use * 'no_password_given' if password is not set * 'no_username_given' if username is not set """ if not hasattr(self, 'username') or self.username is None: raise UsernameNotGiven() if not hasattr(self, 'password_') or self.password_ is None: raise UserPasswordNotGiven() # validate username if len(User.nodes.filter(username=self.username, uid__ne=self.uid)) == 0: return super(User, self).save() else: # Found user with same username and different uid, so, username is in use raise UsernameInUse() def create_wallet(self, label): """ Create Wallet associated with user :param label: name to identify this wallet :return: generated wallet """ wallet = Wallet(label=label) wallet.save() wallet.owner.connect(self) self.wallets.connect(wallet) self.save() wallet.save() return wallet @staticmethod def login(username, passwd): """ Try to login an user :param username: username to login :param passwd: user password before hash :return: * True if login is ok, * False if wrong password * 'inexistent' if username not found * 'inactive' if user.active is False """ # find user by username user = User.nodes.get_or_none(username=username) if user is None: raise UsernameNotFound() # Test if user is active if user.active: # If user is active, compare password hash_passwd = mixture_pwd(user.uid, passwd) if user.password_ == hash_passwd: return user else: # If password is wrong, raise an exception raise UserPasswordIncorrect() else: # If user is inactive, raise an exception raise UserInactive()
class DefaultTestValue(StructuredNode): name_xx = StringProperty(default='jim', index=True)
class TestChoices(StructuredNode): SEXES = {'F': 'Female', 'M': 'Male', 'O': 'Other'} sex = StringProperty(required=True, choices=SEXES)
class DefaultTestValueThree(StructuredNode): uid = StringProperty(default=factory, index=True)
class Wallet(BaseModel): """ Model a wallet in database """ label = StringProperty(required=True) max_limit_ = FloatProperty(db_property='max_limit', default=0) real_limit_ = FloatProperty(db_property='real_limit', default=0) free_limit_ = FloatProperty(db_property='free_limit', default=0) owner = RelationshipFrom('.user.User', 'OWNED', cardinality=One) cards = RelationshipTo('.card.Card', 'CONTAINS') purchases = RelationshipTo('.billing.Purchase', 'DID') payments = RelationshipFrom('.billing.Payment', 'RECEIVED') # === real_limit === @property def real_limit(self): """ real_limit is set to max_limit by default """ return self.real_limit_ if self.real_limit_ else self.max_limit @real_limit.setter def real_limit(self, value): """ Wallet limit set by user. Should be less than max_limit and non-negative :param value: new real limit :return: """ if value > self.max_limit: raise WalletLimitExceed() elif value < 0: raise WalletLimitNotAllowed() else: self.real_limit_ = value self.save() # === max_limit === @property def max_limit(self): return self.max_limit_ @max_limit.setter def max_limit(self, value): raise UnchangeableWalletValue() # === free_limit === @property def free_limit(self): """ usable remaining limit related to max_limit """ return self.free_limit_ @free_limit.setter def free_limit(self, value): raise UnchangeableWalletValue() # === real_free_limit === @property def real_free_limit(self): """ Analogous to free limit, but related to real_limit instead of max_limit :return: """ return self.real_limit - self.total_used # === total_used === @property def total_used(self): return self.max_limit - self.free_limit # === Limits Manipulators === def increase_free_limit(self, value=1.0): """ Increase free_limit of wallet, usually in card bill payments. :param value: amount to be increased :return: new free limit """ self.free_limit_ += value self.save() return self.free_limit def decrease_free_limit(self, value=1.0): """ Decrease free_limit of wallet, usually in purchases Raises an exception if limit become negative :param value: amount to reduce :return: new limit """ if self.free_limit < value: raise WalletLimitNotAllowed() else: return self.increase_free_limit(-value) def increase_max_limit(self, amount=1.0): self.max_limit_ += amount self.save() return self.max_limit def decrease_max_limit(self, amount=1.0): # Raise exception if limit become negative if amount > self.max_limit: raise WalletLimitNotAllowed() self.max_limit_ -= amount self.save() if self.real_limit > self.max_limit: self.real_limit = self.max_limit return self.max_limit # === Card manipulators === def create_card(self, **kwargs): card = Card(**kwargs) card.save() card.wallet.connect(self) self.cards.connect(card) self.increase_max_limit(card.max_limit) self.increase_free_limit(card.free_limit) self.save() card.save() return card def sorted_cards(self, fake_today=None, date_format='%m/%d/%Y'): """ Sort cards by distance to due date, than to lower limit considering today as fake_today :param fake_today: a pseudo date (or None for today) :param date_format: (format of used date) :return: active cards sorted according to rules in (enuntiate)[https://slack-files.com/T06M9ENDT-F5XK4J0P2-532510c5c0] """ cards = [] for card in self.cards: if card.active: card.set_fake_today(fake_today, date_format) cards.append(card) cards.sort() return cards # === object dictionarization === def to_dict(self): return dict(real_limit=self.real_limit, max_limit=self.max_limit, free_limit=self.free_limit, real_free_limit=self.real_free_limit, total_used=self.total_used, total_cards=len(self.cards)) # === purchase === def purchase(self, value): """ Purchase following rules in (enuntiate)[https://slack-files.com/T06M9ENDT-F5XK4J0P2-532510c5c0] :param value: :return: Purchase object generated on purchase """ # Raise RealLimitExceeded if purchase exceeds real_free_limit if self.real_free_limit < value: raise RealLimitExceeded() purchase = Purchase() purchase.total = value purchase = purchase.set_wallet(self) # If possible, purchase with only one card for card in self.sorted_cards(): if card.free_limit >= value: purchase = purchase.use_card(card, value) return purchase # Else, purchase with multiple cards for card in self.sorted_cards(): value_in_card = value if card.free_limit > value else card.free_limit purchase = purchase.use_card(card, value_in_card) value -= value_in_card if value <= 0: break return purchase
class TestChoices(StructuredNode): SEXES = (('M', 'Male'), ('F', 'Female')) sex = StringProperty(required=True, choices=SEXES)
class Author(DjangoNode): name = StringProperty() wrote = RelationshipTo('Book','WROTE',cardinality=ZeroOrMore)
def test_illegal_array_base_prop_raises(): with raises(ValueError): ArrayProperty(StringProperty(index=True))
class TestChoices(StructuredNode): SEXES = {"F": "Female", "M": "Male", "O": "Other"} sex = StringProperty(required=True, choices=SEXES)