class OrderItem(db.Entity): """Продукт в заказе""" products = Set('Product') amount = Optional(int, default=1, min=1) order = Required('Order')
class Role(db.Entity): name = Required(str, unique=True) description = Optional(str, nullable=True) users = Set(lambda: User)
class Track(PathMixin, db.Entity): _table_ = "track" id = PrimaryKey(UUID, default=uuid4) disc = Required(int) number = Required(int) title = Required(str) year = Optional(int) genre = Optional(str, nullable=True) duration = Required(int) has_art = Required(bool, default=False) album = Required(Album, column="album_id") artist = Required(Artist, column="artist_id") bitrate = Required(int) path = Required(str, 4096, autostrip=False) # unique _path_hash = Required(buffer, column="path_hash") created = Required(datetime, precision=0, default=now) last_modification = Required(int) play_count = Required(int, default=0) last_play = Optional(datetime, precision=0) root_folder = Required(Folder, column="root_folder_id") folder = Required(Folder, column="folder_id") __lastly_played_by = Set(lambda: User) # Never used, hide it stars = Set(lambda: StarredTrack) ratings = Set(lambda: RatingTrack) def as_subsonic_child(self, user, prefs): info = { "id": str(self.id), "parent": str(self.folder.id), "isDir": False, "title": self.title, "album": self.album.name, "artist": self.artist.name, "track": self.number, "size": os.path.getsize(self.path) if os.path.isfile(self.path) else -1, "contentType": self.mimetype, "suffix": self.suffix(), "duration": self.duration, "bitRate": self.bitrate, "path": self.path[len(self.root_folder.path) + 1:], "isVideo": False, "discNumber": self.disc, "created": self.created.isoformat(), "albumId": str(self.album.id), "artistId": str(self.artist.id), "type": "music", } if self.year: info["year"] = self.year if self.genre: info["genre"] = self.genre if self.has_art: info["coverArt"] = str(self.id) elif self.folder.cover_art: info["coverArt"] = str(self.folder.id) try: starred = StarredTrack[user.id, self.id] info["starred"] = starred.date.isoformat() except ObjectNotFound: pass try: rating = RatingTrack[user.id, self.id] info["userRating"] = rating.rating except ObjectNotFound: pass avgRating = avg(self.ratings.rating) if avgRating: info["averageRating"] = avgRating if (prefs is not None and prefs.format is not None and prefs.format != self.suffix()): info["transcodedSuffix"] = prefs.format info["transcodedContentType"] = (mimetypes.guess_type( "dummyname." + prefs.format, False)[0] or "application/octet-stream") return info @property def mimetype(self): return mimetypes.guess_type(self.path, False)[0] or "application/octet-stream" def duration_str(self): ret = "{:02}:{:02}".format((self.duration % 3600) / 60, self.duration % 60) if self.duration >= 3600: ret = "{:02}:{}".format(self.duration / 3600, ret) return ret def suffix(self): return os.path.splitext(self.path)[1][1:].lower() def sort_key(self): return f"{self.album.artist.name}{self.album.name}{self.disc:02}{self.number:02}{self.title}".lower( )
class MoleculeMerge(db.Entity): _table_ = '%s_molecule_merge' % schema if DEBUG else (schema, 'molecule_merge') id = PrimaryKey(int, auto=True) source = Required('Molecule', reverse='merge_target') target = Required('Molecule', reverse='merge_source') mapping = Optional(Json)
class Snatched(db.Entity): date = Required(datetime.datetime) announced = Required(Announced) backend = Required(str)
class Issues(db.Entity): type = Required(str) id = PrimaryKey(int) bad = Required(bool) switcharoos = Set(Switcharoo)
class TelegaMessaga(db.Entity): id = PrimaryKey(int, auto=True) telega_id = Required(int, size=16) date = Optional(datetime, default=lambda: datetime.now())
class Comment(db.Entity): """Комментарии""" product = Required('Product')
class Tag(db.Entity): id = PrimaryKey(int, auto=True) name = Required(str) problemas = Set(Problema)
class OrderItem(db.Entity): product = Required(Product) amount = Optional(int, default=1, min=1) order = Required(Order)
class CartItem(db.Entity): """Продукт в корзине""" product = Required(Product) amount = Optional(int, default=1, min=1) cart = Required('Cart')
class User(db.Entity): login = PrimaryKey(str) password = Required(str) email = Optional(str) addresses = Set("Shortener")
class Shortener(db.Entity): shortcut = PrimaryKey(str) url = Required(str) user = Optional(User)
class Molecule(SearchMolecule, metaclass=LazyEntityMeta, database='CGRdb'): id = PrimaryKey(int, auto=True) date = Required(datetime, default=datetime.utcnow) user = DoubleLink(Required('User', reverse='molecules'), Set('Molecule')) _structures = Set('MoleculeStructure') reactions = Set('MoleculeReaction') special = Optional(Json) def __init__(self, structure, user, special=None): super().__init__(user=user) self._cached_structure = self._database_.MoleculeStructure(self, structure, user) self._cached_structures_all = (self._cached_structure,) if special: self.special = special def __str__(self): """ signature of last edition of molecule """ return str(self.structure) def __bytes__(self): """ hashed signature of last edition of molecule """ return bytes(self.structure) @property def structure(self): return self.last_edition.structure @property def structure_raw(self): return self.raw_edition.structure @property def structures_all(self): return tuple(x.structure for x in self.all_editions) @property def last_edition(self): if self._cached_structure is None: self._cached_structure = self._structures.filter(lambda x: x.last).first() return self._cached_structure @property def raw_edition(self): if self._cached_structure_raw is not None: return self._cached_structure_raw raise AttributeError('available in entities from queries results only') @property def all_editions(self): if self._cached_structures_all is None: s = tuple(self._structures.select()) self._cached_structures_all = s if self._cached_structure is None: self._cached_structure = next(x for x in s if x.last) return self._cached_structures_all _cached_structure = _cached_structure_raw = _cached_structures_all = None
class Additiveset(db.Entity): _table_ = '%s_additives' % schema if DEBUG else (schema, 'additives') id = PrimaryKey(int, auto=True) additive = Required('Additive') amount = Required(float, default=1) structure = Required('Structure')
class Metainfo(db.Entity): id = PrimaryKey(int, auto=True) info = Required(str) problema = Required(Problema)
class UserFlair(db.Entity): user = PrimaryKey(str, max_len=21) roos = Required(int, default=0) fixes = Required(int, default=0)
class Tarea(db.Entity, mixins.TareaMixin): id = PrimaryKey(str) nombre = Required(str) tipo = Required(str) creador = Required(Profesor, reverse='tareas') completada = Required(bool, default=False)
class ArtObject(db.Entity): id = PrimaryKey(str, 32) path = Required(str) f_type = Required(str, 50) ext = Required(str) preview = Optional(str)
class Asignatura(db.Entity, mixins.AsignaturaMixin): id = PrimaryKey(int, auto=True) nombre = Required(str) titulacion = Required(str) composite_index(nombre, titulacion) examenes = Set(Examen)
class Reaction(db.Entity, UserMixin, FingerprintMixin, IsomorphismMixin, Similarity, ReactionStructureSearch, ReactionSubStructureSearch): _table_ = '%s_reaction' % schema if DEBUG else (schema, 'reaction') id = PrimaryKey(int, auto=True) date = Required(datetime) user_id = Required(int, column='user') fear = Required(str, unique=True) mapless_fear = Required(str) fingerprint = Required(str) if DEBUG else Required(str, sql_type='bit(%s)' % (2 ** FP_SIZE)) children = Set('Reaction', cascade_delete=True) parent = Optional('Reaction') molecules = Set('MoleculeReaction') conditions = Set('Conditions') special = Optional(Json) # FEAR - классификаторы # словарь {"FEAR":list[fear_strings]} # На основе реакции возраващает список уникальных строк def __init__(self, structure, user, conditions=None, special=None, fingerprint=None, fear_string=None, mapless_fear_string=None, cgr=None, substrats_fears=None, products_fears=None): new_mols, batch = OrderedDict(), {} fears = dict(substrats=iter(substrats_fears if substrats_fears and len(substrats_fears) == len(structure.substrats) else []), products=iter(products_fears if products_fears and len(products_fears) == len(structure.products) else [])) refreshed = ReactionContainer() m_count = count() for i, is_p in (('substrats', False), ('products', True)): for x in structure[i]: m_fear_string = next(fears[i], Molecule.get_fear(x)) m = Molecule.get(fear=m_fear_string) if m: mapping = self.match_structures(m.structure_raw, x) batch[next(m_count)] = (m.last_edition, is_p, [(k, v) for k, v in mapping.items() if k != v] or None) refreshed[i].append(relabel_nodes(m.structure, mapping)) else: new_mols[next(m_count)] = (x, is_p, m_fear_string) refreshed[i].append(x) if new_mols: for_fp, for_x = [], [] for x, _, m_fp in new_mols.values(): if m_fp not in for_fp: for_fp.append(m_fp) for_x.append(x) fp_dict = dict(zip(for_fp, Molecule.get_fingerprints(for_x))) dups = {} for n, (x, is_p, m_fear_string) in new_mols.items(): if m_fear_string not in dups: m = Molecule(x, user, fear_string=m_fear_string, fingerprint=fp_dict[m_fear_string]) dups[m_fear_string] = m mapping = None else: m = dups[m_fear_string] mapping = [(k, v) for k, v in self.match_structures(m.structure_raw, x).items() if k != v] or None batch[n] = (m, is_p, mapping) if mapless_fear_string is None: mapless_fear_string, merged = self.get_mapless_fear(refreshed, get_merged=True) else: merged = None if fear_string is None: fear_string, cgr = (self.get_fear(structure, get_cgr=True) if merged is None else self.get_fear(merged, is_merged=True, get_cgr=True)) elif cgr is None: cgr = cgr_core.getCGR(refreshed) if merged is None else cgr_core.getCGR(merged, is_merged=True) if fingerprint is None: fingerprint = self.get_fingerprints([cgr], is_cgr=True)[0] db.Entity.__init__(self, user_id=user.id, fear=fear_string, fingerprint=fingerprint.bin, date=datetime.utcnow(), mapless_fear=mapless_fear_string) for m, is_p, mapping in (batch[x] for x in sorted(batch)): MoleculeReaction(reaction=self, molecule=m, product=is_p, mapping=mapping) if conditions: Conditions(conditions, self, user) if special: self.special = special self.__cached_cgr = cgr self.__cached_structure = structure self.__cached_bitstring = fingerprint @classmethod def refresh_reaction(cls, structure): fresh = dict(substrats=[], products=[]) for i, is_p in (('substrats', False), ('products', True)): for x in structure[i]: m = Molecule.get(fear=Molecule.get_fear(x)) if m: fresh[i].append(m) else: return False res = ReactionContainer() for k in ('products', 'substrats'): for x, y in zip(fresh[k], structure[k]): mapping = cls.match_structures(x.structure_raw, y) res[k].append(relabel_nodes(x.structure, mapping)) return res @staticmethod def get_fingerprints(structures, is_cgr=False): cgrs = structures if is_cgr else [cgr_core.getCGR(x) for x in structures] f = Fragmentor(workpath='.', version=FRAGMENTOR_VERSION, fragment_type=FRAGMENT_TYPE_CGR, min_length=FRAGMENT_MIN_CGR, max_length=FRAGMENT_MAX_CGR, cgr_dynbonds=FRAGMENT_DYNBOND_CGR, useformalcharge=True).get(cgrs)['X'] return fingerprints.get_fingerprints(f) @staticmethod def get_fear(structure, is_merged=False, get_cgr=False): cgr = cgr_core.getCGR(structure, is_merged=is_merged) fear_string = Molecule.get_fear(cgr) return (fear_string, cgr) if get_cgr else fear_string def get_fear_classes(self): atoms=fear.get_center_atoms(self.cgr) reaction_center=fear.get_environment(self.cgr, atoms) rc_list=[] for i in connected_component_subgraphs(reaction_center): rc_list.append(Molecule.get_fear(i)) return rc_list @staticmethod def get_mapless_fear(structure, is_merged=False, get_merged=False): merged = structure if is_merged else cgr_core.merge_mols(structure) fear_string = '%s>>%s' % (Molecule.get_fear(merged['substrats']), Molecule.get_fear(merged['products'])) return (fear_string, merged) if get_merged else fear_string @property def cgr(self): if self.__cached_cgr is None: self.__cached_cgr = cgr_core.getCGR(self.structure) return self.__cached_cgr @property def structure(self): if self.__cached_structure is None: r = ReactionContainer() for m in self.molecules.order_by(lambda x: x.id): r['products' if m.product else 'substrats'].append( relabel_nodes(m.molecule.structure_raw, dict(m.mapping)) if m.mapping else m.molecule.structure) self.__cached_structure = r return self.__cached_structure def refresh_fear_fingerprint(self): fear_string, cgr = self.get_fear(self.structure, get_cgr=True) fingerprint = self.get_fingerprints([cgr], is_cgr=True)[0] print(self.date) # Pony BUG. AD-HOC! self.fear = fear_string self.fingerprint = fingerprint.bin self.__cached_bitstring = fingerprint __cached_structure = None __cached_cgr = None __cached_conditions = None def flush_cache(self): self.__cached_structure = None self.__cached_cgr = None self.__cached_conditions = None FingerprintMixin.flush_cache(self)
class Problema_examen(db.Entity): posicion = Required(int) problema_id = Required(Problema) examen_id = Required(Examen) PrimaryKey(problema_id, examen_id)
class Molecule(db.Entity, UserMixin, FingerprintMixin, IsomorphismMixin, Similarity, MoleculeStructureSearch, MoleculeSubStructureSearch): _table_ = '%s_molecule' % schema if DEBUG else (schema, 'molecule') id = PrimaryKey(int, auto=True) date = Required(datetime) user_id = Required(int, column='user') data = Required(Json) fear = Required(str, unique=True) fingerprint = Required(str) if DEBUG else Required(str, sql_type='bit(%s)' % (2 ** FP_SIZE)) children = Set('Molecule', reverse='parent', cascade_delete=True) parent = Optional('Molecule', reverse='children') last = Required(bool, default=True) merge_source = Set('MoleculeMerge', reverse='target') # molecules where self is more correct merge_target = Set('MoleculeMerge', reverse='source') # links to correct molecules reactions = Set('MoleculeReaction') def __init__(self, structure, user, fingerprint=None, fear_string=None): data = node_link_data(structure) if fear_string is None: fear_string = self.get_fear(structure) if fingerprint is None: fingerprint = self.get_fingerprints([structure])[0] self.__cached_structure_raw = structure self.__cached_bitstring = fingerprint db.Entity.__init__(self, data=data, user_id=user.id, fear=fear_string, fingerprint=fingerprint.bin, date=datetime.utcnow()) def update_structure(self, structure, user): """ update structure representation. atom mapping should be equal to self. :param structure: Molecule container :param user: user entity :return: True if updated. False if conflict found. """ new_hash = {k: v['element'] for k, v in structure.nodes(data=True)} old_hash = {k: v['element'] for k, v in self.structure_raw.nodes(data=True)} if new_hash != old_hash: raise Exception('Structure or mapping not match') fear_string = self.get_fear(structure) exists = Molecule.get(fear=fear_string) if not exists: m = Molecule(structure, user, fear_string=fear_string) for mr in self.last_edition.reactions: ''' replace current last molecule edition in all reactions. ''' mr.molecule = m mr.reaction.refresh_fear_fingerprint() self.last_edition.last = False m.parent = self.parent or self self.__last_edition = m return True ''' this code not optimal. but this procedure is rare if db correctly standardized before population. ''' ex_parent = exists.parent or exists if ex_parent != (self.parent or self) and not any((x.target.parent or x.target) == ex_parent for x in self.merge_target): ''' if exists structure not already in merge list ''' mapping = self.match_structures(structure, exists.structure_raw) MoleculeMerge(target=exists, source=self, mapping=[(k, v) for k, v in mapping.items() if k != v] or None) return False def merge_molecule(self, molecule): m = Molecule[molecule] mm = MoleculeMerge.get(target=m, source=self) if not mm: return False ''' replace self in reactions to last edition of mergable molecule. ''' mmap = dict(mm.mapping or []) mapping = [(n, mmap.get(n, n)) for n in self.structure_raw.nodes()] for mr in self.last_edition.reactions: rmap = dict(mr.mapping or []) mr.mapping = [(k, v) for k, v in ((v, rmap.get(k, k)) for k, v in mapping) if k != v] or None mr.molecule = m.last_edition mr.reaction.refresh_fear_fingerprint() ''' remap self''' if self.parent: tmp = [self.parent] + list(self.parent.children) else: tmp = [self] + list(self.children) for x in tmp: x.data = node_link_data(relabel_nodes(x.structure_raw, mmap)) ''' set self.parent to molecule chain ''' if m.parent: tmp = [m.parent] + list(m.parent.children) else: tmp = [m] + list(m.children) for x in tmp: x.parent = self.parent or self self.last_edition.last = False self.__last_edition = m.last_edition mm.delete() return True @staticmethod def get_fear(structure): return fear.get_cgr_string(structure) @staticmethod def get_fingerprints(structures): f = Fragmentor(workpath='.', version=FRAGMENTOR_VERSION, fragment_type=FRAGMENT_TYPE_MOL, min_length=FRAGMENT_MIN_MOL, max_length=FRAGMENT_MAX_MOL, useformalcharge=True).get(structures)['X'] return fingerprints.get_fingerprints(f) @property def structure_raw(self): if self.__cached_structure_raw is None: g = node_link_graph(self.data) g.__class__ = MoleculeContainer self.__cached_structure_raw = g return self.__cached_structure_raw @property def structure_parent(self): if self.parent: return self.parent.structure_raw return None @property def structure(self): return self.last_edition.structure_raw @property def last_edition(self): if self.__last_edition is None: if self.last: tmp = self elif self.parent and self.parent.last: tmp = self.parent else: tmp = (self.parent or self).children.filter(lambda x: x.last).first() self.__last_edition = tmp return self.__last_edition __cached_structure_raw = None __last_edition = None def flush_cache(self): self.__cached_structure_raw = None self.__last_edition = None FingerprintMixin.flush_cache(self)
class Figura(db.Entity): id = PrimaryKey(int, auto=True) filename = Required(str) problemas = Set(Problema)
class Snatched(db.Entity): date = Required(datetime.datetime) title = Required(str) indexer = Required(str) torrent = Required(str) backend = Required(str)
class CoffinJobs(db.Entity): id = PrimaryKey(int, auto=True) context = Required(Json) origin_host = Required(int) origin_id = Required(str) assignee = Optional(QueueParticipants)
class Page(db.Entity): id = PrimaryKey(UUID, auto=True, default=uuid4) created = Required(datetime.datetime, default=datetime.datetime.utcnow) modified = Optional(datetime.datetime) titre = Optional(str, default="") activite = Required("Activite") lastPosition = Optional(int, default=0) sections = Set("Section") def before_insert(self): self.modified = self.created def before_update(self): if hasattr(self, "reasonUpdate"): del self.reasonUpdate # block page autoupdate when provient de section else: self.update_modified() def update_modified(self): self.modified = datetime.datetime.utcnow() return self.modified def to_dict(self): dico = super().to_dict() dico.update( { "id": str(self.id), "created": self.created.isoformat(), "modified": self.modified.isoformat(), "activite": str(self.activite.id), "annee": self.activite.matiere.groupe.annee.id, "lastPosition": self.lastPosition, "sections": [ str(s.id) for s in self.sections.order_by(lambda x: x.position) ], "matiere": str(self.activite.matiere.id), "matiereNom": self.activite.matiere.nom, "matiereFgColor": self.activite.matiere.fgColor, "matiereBgColor": self.activite.matiere.bgColor, } ) return dico def _query_recents(self, annee): query = ( select(p for p in Page if p.activite.matiere.groupe.annee.id == annee) .order_by(desc(Page.modified)) .limit(50) ) # pragma: no cover_all return query @classmethod def recents(cls, annee): return [p.to_dict() for p in cls._query_recents(cls, annee)] @staticmethod def new_page(activite, titre=""): return Page(titre=titre, activite=activite).to_dict() @property def content(self): return [p for p in self.sections.order_by(db.Section._position)]
class Person(db.Entity): name = Required(str) age = Required(int) cars = Set('Car')
class Meta(metadb.Entity): _table_ = "meta" key = PrimaryKey(str, 32) value = Required(str, 256)
class Score(db.Entity): _table_ = '%s_score' % schema if DEBUG else (schema, 'score') id = PrimaryKey(int, auto=True) year = Required(int) score = Required(float) issn = Required('JournalISSN')