class Species(StructuredNode): # ---- attributi species = StringProperty(UniqueIndex=True, Required=True) # ---- relazioni forSpecies = RelationshipFrom('Experiment', 'For_Species') ofSpecies = RelationshipFrom('Genotype', 'Of_Species')
class Gene(StructuredNode): ensid = StringProperty() symbol = StringProperty() description = StringProperty() # had = RelationshipTo('Fusion', "HAD") # #fromFusion = RelationshipFrom('Fusion',"WITH", model=WITH) fromExonToGene = RelationshipFrom('Exon', "IN_GENE") fromChromosomeToGene = RelationshipFrom('Chromosome', "OF_GENE") fromFusionToGene = RelationshipFrom('Fusion', 'WITH')
class Fusion(StructuredNode): fusion_id = IntegerProperty() # with_fc_script = RelationshipTo('FusionCatcher', "WITH_FC_SCRIPT") with_eric_script = RelationshipTo('EricScript', "WITH_ERIC_SCRIPT") with_tophat_script = RelationshipTo('Tophat', "WITH_TOPHAT_SCRIPT") with_gene = RelationshipTo('Gene', "WITH") at_chromosome = RelationshipTo('Chromosome', "AT_CHROMOSOME") # fromCellLineToFusion = RelationshipFrom('CellLine', "HAPPEN") fromGeneToFusion = RelationshipFrom('Gene', "HAD")
class Couple(StructuredNode): couple = IntegerProperty() # with_other_transcript = RelationshipTo('Transcript', "WITH_OTHER_TRANSCRIPT", model=WITH_OTHER_TRANSCRIPT) with_protein = RelationshipTo('Protein', "WITH_PROTEIN") # fromTranscriptToCouple = RelationshipFrom('Transcript', "IN_COUPLE", model=IN_COUPLE) fromFusionToCouple = RelationshipFrom('FusionCatcher', "WITH_TRANS_COUPLE")
class Variant(StructuredNode): # ---- attributi variant_id = StringProperty(UniqueIndex=True, Required=True) CHROM = StringProperty(Required=True) POS = IntegerProperty() REF = StringProperty() ALT = ArrayProperty() MUTATION = StringProperty() # ---- relazioni forVariant = RelationshipFrom('Info', 'For_Variant') hasVariant = RelationshipFrom('Chromosome', 'Has_Variant') inVariant = RelationshipFrom('Gene', 'In_Variant')
class Info(StructuredNode): # ---- attributi info_id = StringProperty(UniqueIndex=True, Required=True) END = IntegerProperty() ID = StringProperty() QUAL = FloatProperty() FILTER = StringProperty() FORMAT = StringProperty() HETEROZIGOSITY = FloatProperty() dbSNP = StringProperty() DP = FloatProperty() Gene_refGene = ArrayProperty() Func_refGene = ArrayProperty() QD = FloatProperty() SIFT_score = FloatProperty() otg_all = FloatProperty() NM = IntegerProperty() LM = ArrayProperty() FS = FloatProperty() MQ0 = FloatProperty() attributes = JSONProperty() # ---- relazioni contains = RelationshipFrom('File', 'Contains') supportedBy = RelationshipTo('Genotype', 'Supported_By', model=SupportedByRel) forVariant = RelationshipTo('Variant', 'For_Variant')
class Chromosome(StructuredNode): # ---- attributi chromosome = StringProperty(UniqueIndex=True, Required=True) # ---- relazioni inChromosomome = RelationshipFrom('Gene', 'In_Chromosome') hasVariant = RelationshipTo('Variant', 'Has_Variant')
class Chromosome(StructuredNode): chromosome = StringProperty() # of_gene = RelationshipTo('Gene', "OF_GENE") # fromFusiontoChromosome = RelationshipFrom('Fusion', "AT_CHROMOSOME", model=AT_CHROMOSOME)
class Virus(StructuredNode): name = StringProperty() gi = StringProperty() ref = StringProperty() # fromCellLineToVirus = RelationshipFrom('CellLine', "WITH_VIRUSES", model=WITH_VIRUSES)
class Transcript(StructuredNode): transcript = StringProperty() # in_couple = RelationshipTo('Couple', "IN_COUPLE", model=IN_COUPLE) # fromCoupleToTranscript = RelationshipFrom('Couple', "WITH_OTHER_TRANSCRIPT", model=WITH_OTHER_TRANSCRIPT)
class Genotype(StructuredNode): # ---- attributi sample = StringProperty(UniqueIndex=True, Required=True) # ---- relazioni supportedBy = RelationshipFrom('Info', 'Supported_By', model=SupportedByRel) ofSpecies = RelationshipTo('Species', 'Of_Species')
class Experiment(StructuredNode): # ---- attributi name = StringProperty(Required=True) # ---- relazioni created = RelationshipFrom('User', 'Created') forSpecies = RelationshipTo('Species', 'For_Species') composedBy = RelationshipTo('File', 'Composed_By')
class Tophat(StructuredNode): tophat_id = IntegerProperty() left_coord = StringProperty() right_coord = StringProperty() spanning_reads = StringProperty() spanning_mate_pairs = StringProperty() spanning_mate_pairs_end = StringProperty() # fromFusionToTophat = RelationshipFrom('Fusion', 'WTIH_TOPHAT_SCRIPT')
class File(StructuredNode): # ---- attributi name = StringProperty(UniqueIndex=True, Required=True) extension = StringProperty() statistics = JSONProperty() # ---- relazioni composedBy = RelationshipFrom('Experiment', 'Composed_By') contains = RelationshipTo('Info', 'Contains')
class Payment(BaseModel): """ Payment object represents a payment for a card contained in a wallet """ value = FloatProperty() date_time = DateTimeProperty(default_now=True) card = RelationshipTo('.card.Card', 'FOR', cardinality=One) wallet = RelationshipFrom('.wallet.Wallet', 'FOR', cardinality=One) def to_dict(self): return dict(value=self.value, cid=self.card.single().uid, date_time=self.date_time, wid=self.wallet.single().uid)
class EricScript(StructuredNode): ericscript_id = IntegerProperty() breakpoint_1 = StringProperty() strand_1 = StringProperty() breakpoint_2 = StringProperty() strand_2 = StringProperty() crossing_reads = IntegerProperty() spanning_reads = IntegerProperty() mean_intersize = FloatProperty() homology = StringProperty() fusion_type = StringProperty() junction_sequence = StringProperty() gene_expr_1 = FloatProperty() gene_expr_2 = FloatProperty() gene_expr_fused = FloatProperty() es = FloatProperty() gjs = StringProperty() us = FloatProperty() eric_score = FloatProperty() # fromFusionToEricScript = RelationshipFrom('Fusion', "WITH_ERIC_SCRIPT")
class FusionCatcher(StructuredNode): fusion_id = IntegerProperty() description = ArrayProperty() common_mapping_reads = IntegerProperty() spanning_pairs = IntegerProperty() spanning_unique_reads = IntegerProperty() longest_anchor_found = IntegerProperty() fusion_finding_method = StringProperty() fusion_sequence = StringProperty() fusion_point_1 = IntegerProperty() fusion_point_2 = IntegerProperty() strand_1 = StringProperty() strand_2 = StringProperty() predicted_effect_1 = StringProperty() predicted_effect_2 = StringProperty() # at_exon = RelationshipTo('Exon', "AT_EXON") with_trans_couple = RelationshipTo('Couple', "WITH_TRANS_COUPLE") with_gene = RelationshipTo('Gene', "WITH") # #fromCellLineToFusion = RelationshipFrom('CellLine',"HAPPEN") fromFusionToFusionCatcher = RelationshipFrom('Fusion', "WITH_FC_SCRIPT")
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 Purchase(BaseModel): """ Purchase objects represents a purchase made with wallet, it is, with one or more cards """ total = FloatProperty(required=True) cards_ = RelationshipTo('.card.Card', 'WITH', model=BillingAction) wallet_ = RelationshipFrom('.wallet.Wallet', 'DONE_WITH', cardinality=One) @property def wallet(self): return self.wallet_ @wallet.setter def wallet(self, wallet): raise PurchaseUnchangeableProperty() @property def cards(self): return self.cards_ @cards.setter def cards(self, card): raise PurchaseUnchangeableProperty() def use_card(self, card, value): self.cards_.connect(card, {'value': value}) self.save() card.purchases.connect(self, {'value': value}) card.decrease_free_limit(value) card.save() return self def to_dict(self): card_relations = [] for card in self.cards: relation = dict() relation['cid'] = card.uid rel = self.cards.relationship(card) relation['value'] = rel.value relation['date_time'] = rel.date_time card_relations.append(relation) return dict(wid=self.wallet.single().uid, total=self.total, relations=relation) def set_wallet(self, wallet): self.save() self.wallet_.connect(wallet) wallet.purchases.connect(self) self.save() wallet.save() return self
class Exon(StructuredNode): exon = StringProperty() # in_gene = RelationshipTo('Gene', "IN_GENE") # fromFusionToExon = RelationshipFrom('FusionCatcher', "AT_EXON")
class Protein(StructuredNode): protein = StringProperty() # fromCoupleToProtein = RelationshipFrom('Couple', "WITH_PROTEIN")
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)