class Guild(BaseModel): WhitelistFlags = Enum('MUSIC', 'MODLOG_CUSTOM_FORMAT', bitmask=False) guild_id = BigIntegerField(primary_key=True) owner_id = BigIntegerField(null=True) name = TextField(null=True) icon = TextField(null=True) splash = TextField(null=True) region = TextField(null=True) last_ban_sync = DateTimeField(null=True) # Rowboat specific data config = BinaryJSONField(null=True) config_raw = BlobField(null=True) enabled = BooleanField(default=True) whitelist = BinaryJSONField(default=[]) added_at = DateTimeField(default=datetime.utcnow) # SQL = ''' # CREATE OR REPLACE FUNCTION shard (int, bigint) # RETURNS bigint AS $$ # SELECT ($2 >> 22) % $1 # $$ LANGUAGE SQL; # ''' class Meta: db_table = 'guilds' @classmethod def with_id(cls, guild_id): return cls.get(guild_id=guild_id) @classmethod def setup(cls, guild): return cls.create(guild_id=guild.id, owner_id=guild.owner_id, name=guild.name, icon=guild.icon, splash=guild.splash, region=guild.region, config={'web': { str(guild.owner_id): 'admin' }}, config_raw='') def is_whitelisted(self, flag): return int(flag) in self.whitelist def update_config(self, actor_id, raw): from rowboat.types.guild import GuildConfig parsed = yaml.load(raw) GuildConfig(parsed).validate() GuildConfigChange.create(user_id=actor_id, guild_id=self.guild_id, before_raw=self.config_raw, after_raw=raw) self.update( config=parsed, config_raw=raw).where(Guild.guild_id == self.guild_id).execute() self.emit_update() def emit_update(self): rdb.publish( 'actions', json.dumps({ 'type': 'GUILD_UPDATE', 'id': self.guild_id, })) def sync(self, guild): updates = {} for key in ['owner_id', 'name', 'icon', 'splash', 'region']: if getattr(guild, key) != getattr(self, key): updates[key] = getattr(guild, key) if updates: Guild.update(**updates).where( Guild.guild_id == self.guild_id).execute() def get_config(self, refresh=False): from rowboat.types.guild import GuildConfig if refresh: self.config = Guild.select(Guild.config).where( Guild.guild_id == self.guild_id).get().config if refresh or not hasattr(self, '_cached_config'): self._cached_config = GuildConfig(self.config) return self._cached_config def sync_bans(self, guild): # Update last synced time Guild.update(last_ban_sync=datetime.utcnow()).where( Guild.guild_id == self.guild_id).execute() try: bans = guild.get_bans() except: log.exception('sync_bans failed:') return log.info('Syncing %s bans for guild %s', len(bans), guild.id) GuildBan.delete().where((~(GuildBan.user_id << list(bans.keys()))) & (GuildBan.guild_id == guild.id)).execute() for ban in bans.values(): GuildBan.ensure(guild, ban.user, ban.reason)
class BaseModel(Model): uid = UUIDField(null=False, default=uuid.uuid4, unique=True, help_text="uid 不是主键,但是要当做主键来用") is_deleted = BooleanField(default=False, null=False, help_text="1 delete, 0 normal") create_time = DateTimeField(default=datetime.now) class Meta: database = db @classmethod def get_with_uid(cls, uid): if not uid: return None return cls.get_or_none(cls.uid == uid, cls.is_deleted == False) @classmethod def remove(cls, uid): r = cls.get_with_uid(uid) if r: r.is_deleted = True r.save() return r @classmethod def _get_field_names(cls): return cls._meta.sorted_field_names @classmethod def _get_fields(cls): fields = [] for field_name in cls.get_field_names(): field = cls._meta.fields[field_name] fields.append(field) return fields @classmethod def verify_params(cls, **params): # 验证 params 中的参数,主要验证非 None 字段是否有值 fields = cls._get_fields() for field in fields: if field.auto_increment or field.default is not None or field.null: continue if params.get(field.name) is None: print("{} is None".format(field.name)) return False # if field.unique: # r = cls.get_or_none(field==params.get(field.name)) # if r: # print("Duplicate data, field = {}, value = {}".format(field.name, params.get(field.name))) # return False return True @classmethod def validate(cls, **params): # 继承该方法,验证参数 pass @classmethod def params_handler(cls, params): # Model 继承该方法,处理参数 pass @classmethod def create_data(cls, **params): return cls.create(**params) @classmethod def list(cls): return cls.select().where(cls.is_deleted == False) def to_json(self): r = model_to_dict(self) # id 对使用者不可见 r.pop("id", None) for k, v in r.items(): r[k] = field_to_json(v) return r
class DbBarData(Model): """ Candlestick bar data for database storage. Index is defined unique with vt_symbol, interval and datetime. """ symbol = CharField() exchange = CharField() datetime = DateTimeField() interval = CharField() volume = FloatField() open_price = FloatField() high_price = FloatField() low_price = FloatField() close_price = FloatField() vt_symbol = CharField() gateway_name = CharField() class Meta: database = DB indexes = ((("vt_symbol", "interval", "datetime"), True), ) @staticmethod def from_bar(bar: BarData): """ Generate DbBarData object from BarData. """ db_bar = DbBarData() db_bar.symbol = bar.symbol db_bar.exchange = bar.exchange.value db_bar.datetime = bar.datetime db_bar.interval = bar.interval.value db_bar.volume = bar.volume db_bar.open_price = bar.open_price db_bar.high_price = bar.high_price db_bar.low_price = bar.low_price db_bar.close_price = bar.close_price db_bar.vt_symbol = bar.vt_symbol db_bar.gateway_name = "DB" return db_bar def to_bar(self): """ Generate BarData object from DbBarData. """ bar = BarData( symbol=self.symbol, exchange=Exchange(self.exchange), datetime=self.datetime, interval=Interval(self.interval), volume=self.volume, open_price=self.open_price, high_price=self.high_price, low_price=self.low_price, close_price=self.close_price, gateway_name=self.gateway_name, ) return bar
class User(BaseModel): user_id = BigIntegerField(primary_key=True) username = TextField() discriminator = SmallIntegerField() avatar = TextField(null=True) bot = BooleanField() created_at = DateTimeField(default=datetime.utcnow) admin = BooleanField(default=False) SQL = ''' CREATE INDEX IF NOT EXISTS users_username_trgm ON users USING gin(username gin_trgm_ops); ''' class Meta: table_name = 'users' indexes = ((('user_id', 'username', 'discriminator'), True), ) def serialize(self, us=False): base = { 'id': str(self.user_id), 'username': self.username, 'discriminator': self.discriminator, 'avatar': self.avatar, 'bot': self.bot, } if us: base['admin'] = self.admin return base @property def id(self): return self.user_id @classmethod def ensure(cls, user, should_update=True): return cls.from_disco_user(user) @classmethod def with_id(cls, uid): try: return User.get(user_id=uid) except User.DoesNotExist: return @classmethod def from_disco_user(cls, user, should_update=True): obj, _ = cls.get_or_create(user_id=user.id, defaults={ 'username': user.username, 'discriminator': user.discriminator, 'avatar': user.avatar, 'bot': user.bot }) if should_update: updates = {} if obj.username != user.username: updates['username'] = user.username if obj.discriminator != user.discriminator: updates['discriminator'] = user.discriminator if obj.avatar != user.avatar: updates['avatar'] = user.avatar if updates: cls.update(**updates).where(User.user_id == user.id).execute() return obj def __str__(self): return '{}#{}'.format(self.username, str(self.discriminator).zfill(4))
class User(BaseModel): uid = IntegerField(primary_key=True) name = CharField(max_length=250) path = IntegerField() step = IntegerField(default=1) updated = DateTimeField(default=datetime.datetime.now)
class Subscribe(BaseModel): user = ForeignKeyField(User) name = CharField(verbose_name="订阅名称", max_length=20, default="") keyword = CharField(verbose_name="搜索条件(json)", max_length=200, default="") create_at = DateTimeField(verbose_name="创建时间", null=False, default=utils.now)
class DbBarData(ModelBase): """ Candlestick bar data for database storage. Index is defined unique with datetime, interval, symbol """ id = AutoField() symbol: str = CharField() exchange: str = CharField() datetime: datetime = DateTimeField() interval: str = CharField() volume: float = FloatField() open_interest: float = FloatField() open_price: float = FloatField() high_price: float = FloatField() low_price: float = FloatField() close_price: float = FloatField() class Meta: database = db indexes = ((("symbol", "exchange", "interval", "datetime"), True), ) @staticmethod def from_bar(bar: BarData): """ Generate DbBarData object from BarData. """ db_bar = DbBarData() db_bar.symbol = bar.symbol db_bar.exchange = bar.exchange.value db_bar.datetime = bar.datetime db_bar.interval = bar.interval.value db_bar.volume = bar.volume db_bar.open_interest = bar.open_interest db_bar.open_price = bar.open_price db_bar.high_price = bar.high_price db_bar.low_price = bar.low_price db_bar.close_price = bar.close_price return db_bar def to_bar(self): """ Generate BarData object from DbBarData. """ bar = BarData( symbol=self.symbol, exchange=Exchange(self.exchange), datetime=self.datetime, interval=Interval(self.interval), volume=self.volume, open_price=self.open_price, high_price=self.high_price, open_interest=self.open_interest, low_price=self.low_price, close_price=self.close_price, gateway_name="DB", ) return bar @staticmethod def save_all(objs: List["DbBarData"], progress_bar_dict=None): """ save a list of objects, update if exists. """ dicts = [i.to_dict() for i in objs] with db.atomic(): if driver is Driver.POSTGRESQL: for bar in dicts: DbBarData.insert(bar).on_conflict( update=bar, conflict_target=( DbBarData.symbol, DbBarData.exchange, DbBarData.interval, DbBarData.datetime, ), ).execute() else: total_sz = len(dicts) loaded = 0 for c in chunked(dicts, 50): DbBarData.insert_many( c).on_conflict_replace().execute() if 'save_progress_bar' in progress_bar_dict: loaded += 50 percent_saved = min( round(100 * loaded / total_sz, 2), 100) QApplication.processEvents() progress_bar_dict['save_progress_bar'].setValue( percent_saved)
class Tags(BaseModel): name = CharField() status = SmallIntegerField(default=0) createTime = DateTimeField(default=datetime.now) updateTime = DateTimeField(default=datetime.now)
#!/usr/bin/env python import os from datetime import datetime from peewee import DateTimeField, IntegerField, SqliteDatabase from playhouse.migrate import SqliteMigrator, migrate db = SqliteDatabase(os.getenv("DATABASE_FILE", "omegabot.db")) migrator = SqliteMigrator(db) with db.atomic(): migrate( migrator.add_column("user", "xp", IntegerField(default=0)), migrator.add_column("user", "xp_last_update_time", DateTimeField(default=datetime.now)), )
class GymDetails(BaseModel): gym_id = CharField(primary_key=True) name = CharField() description = CharField(null=True) url = CharField() last_scanned = DateTimeField(default=datetime.utcnow)
class Pokemon(BaseModel): # We are base64 encoding the ids delivered by the api # because they are too big for sqlite to handle encounter_id = CharField(primary_key=True, max_length=50) spawnpoint_id = CharField(index=True) pokemon_id = IntegerField(index=True) latitude = DoubleField() longitude = DoubleField() disappear_time = DateTimeField(index=True) class Meta: indexes = ((('latitude', 'longitude'), False), ) @staticmethod def get_active(swLat, swLng, neLat, neLng): if swLat is None or swLng is None or neLat is None or neLng is None: query = (Pokemon.select().where( Pokemon.disappear_time > datetime.utcnow()).dicts()) else: query = (Pokemon.select().where( (Pokemon.disappear_time > datetime.utcnow()) & (((Pokemon.latitude >= swLat) & (Pokemon.longitude >= swLng) & (Pokemon.latitude <= neLat) & (Pokemon.longitude <= neLng)))).dicts()) # Performance: Disable the garbage collector prior to creating a (potentially) large dict with append() gc.disable() pokemons = [] for p in query: p['pokemon_name'] = get_pokemon_name(p['pokemon_id']) p['pokemon_rarity'] = get_pokemon_rarity(p['pokemon_id']) p['pokemon_types'] = get_pokemon_types(p['pokemon_id']) if args.china: p['latitude'], p['longitude'] = \ transform_from_wgs_to_gcj(p['latitude'], p['longitude']) pokemons.append(p) # Re-enable the GC. gc.enable() return pokemons @staticmethod def get_active_by_id(ids, swLat, swLng, neLat, neLng): if swLat is None or swLng is None or neLat is None or neLng is None: query = (Pokemon.select().where((Pokemon.pokemon_id << ids) & ( Pokemon.disappear_time > datetime.utcnow())).dicts()) else: query = (Pokemon.select().where( (Pokemon.pokemon_id << ids) & (Pokemon.disappear_time > datetime.utcnow()) & (Pokemon.latitude >= swLat) & (Pokemon.longitude >= swLng) & (Pokemon.latitude <= neLat) & (Pokemon.longitude <= neLng)).dicts()) # Performance: Disable the garbage collector prior to creating a (potentially) large dict with append() gc.disable() pokemons = [] for p in query: p['pokemon_name'] = get_pokemon_name(p['pokemon_id']) p['pokemon_rarity'] = get_pokemon_rarity(p['pokemon_id']) p['pokemon_types'] = get_pokemon_types(p['pokemon_id']) if args.china: p['latitude'], p['longitude'] = \ transform_from_wgs_to_gcj(p['latitude'], p['longitude']) pokemons.append(p) # Re-enable the GC. gc.enable() return pokemons @classmethod def get_seen(cls, timediff): if timediff: timediff = datetime.utcnow() - timediff pokemon_count_query = (Pokemon.select( Pokemon.pokemon_id, fn.COUNT(Pokemon.pokemon_id).alias('count'), fn.MAX(Pokemon.disappear_time).alias('lastappeared')).where( Pokemon.disappear_time > timediff).group_by( Pokemon.pokemon_id).alias('counttable')) query = (Pokemon.select( Pokemon.pokemon_id, Pokemon.disappear_time, Pokemon.latitude, Pokemon.longitude, pokemon_count_query.c.count).join( pokemon_count_query, on=(Pokemon.pokemon_id == pokemon_count_query.c.pokemon_id )).distinct().where( Pokemon.disappear_time == pokemon_count_query.c.lastappeared).dicts()) # Performance: Disable the garbage collector prior to creating a (potentially) large dict with append() gc.disable() pokemons = [] total = 0 for p in query: p['pokemon_name'] = get_pokemon_name(p['pokemon_id']) pokemons.append(p) total += p['count'] # Re-enable the GC. gc.enable() return {'pokemon': pokemons, 'total': total} @classmethod def get_appearances(cls, pokemon_id, last_appearance, timediff): ''' :param pokemon_id: id of pokemon that we need appearances for :param last_appearance: time of last appearance of pokemon after which we are getting appearances :param timediff: limiting period of the selection :return: list of pokemon appearances over a selected period ''' if timediff: timediff = datetime.utcnow() - timediff query = (Pokemon.select().where( (Pokemon.pokemon_id == pokemon_id) & (Pokemon.disappear_time > datetime.utcfromtimestamp( last_appearance / 1000.0)) & (Pokemon.disappear_time > timediff)).order_by( Pokemon.disappear_time.asc()).dicts()) return list(query) @classmethod def get_spawnpoints(cls, southBoundary, westBoundary, northBoundary, eastBoundary): query = Pokemon.select(Pokemon.latitude, Pokemon.longitude, Pokemon.spawnpoint_id) if None not in (northBoundary, southBoundary, westBoundary, eastBoundary): query = (query.where((Pokemon.latitude <= northBoundary) & (Pokemon.latitude >= southBoundary) & (Pokemon.longitude >= westBoundary) & (Pokemon.longitude <= eastBoundary))) # Sqlite doesn't support distinct on columns if args.db_type == 'mysql': query = query.distinct(Pokemon.spawnpoint_id) else: query = query.group_by(Pokemon.spawnpoint_id) return list(query.dicts()) @classmethod def get_spawnpoints_in_hex(cls, center, steps): log.info('Finding spawn points {} steps away'.format(steps)) n, e, s, w = hex_bounds(center, steps) query = (Pokemon.select(Pokemon.latitude.alias('lat'), Pokemon.longitude.alias('lng'), ((Pokemon.disappear_time.minute * 60) + Pokemon.disappear_time.second).alias('time'), Pokemon.spawnpoint_id)) query = (query.where((Pokemon.latitude <= n) & (Pokemon.latitude >= s) & (Pokemon.longitude >= w) & (Pokemon.longitude <= e))) # Sqlite doesn't support distinct on columns if args.db_type == 'mysql': query = query.distinct(Pokemon.spawnpoint_id) else: query = query.group_by(Pokemon.spawnpoint_id) s = list(query.dicts()) # Filter to spawns which actually fall in the hex locations # This loop is about as non-pythonic as you can get, I bet. # Oh well. filtered = [] hex_locations = list(generate_location_steps(center, steps, 0.07)) for hl in hex_locations: for idx, sp in enumerate(s): if geopy.distance.distance( hl, (sp['lat'], sp['lng'])).meters <= 70: filtered.append(s.pop(idx)) # at this point, 'time' is DISAPPEARANCE time, we're going to morph it to APPEARANCE time for location in filtered: # examples: time shifted # 0 ( 0 + 2700) = 2700 % 3600 = 2700 (0th minute to 45th minute, 15 minutes prior to appearance as time wraps around the hour) # 1800 (1800 + 2700) = 4500 % 3600 = 900 (30th minute, moved to arrive at 15th minute) # todo: this DOES NOT ACCOUNT for pokemons that appear sooner and live longer, but you'll _always_ have at least 15 minutes, so it works well enough location['time'] = (location['time'] + 2700) % 3600 return filtered
class Trainer(BaseModel): name = CharField(primary_key=True) team = IntegerField() level = IntegerField() last_seen = DateTimeField(default=datetime.utcnow)
class Gym(BaseModel): UNCONTESTED = 0 TEAM_MYSTIC = 1 TEAM_VALOR = 2 TEAM_INSTINCT = 3 gym_id = CharField(primary_key=True, max_length=50) team_id = IntegerField() guard_pokemon_id = IntegerField() gym_points = IntegerField() enabled = BooleanField() latitude = DoubleField() longitude = DoubleField() last_modified = DateTimeField(index=True) last_scanned = DateTimeField(default=datetime.utcnow) class Meta: indexes = ((('latitude', 'longitude'), False), ) @staticmethod def get_gyms(swLat, swLng, neLat, neLng): if swLat is None or swLng is None or neLat is None or neLng is None: results = (Gym.select().dicts()) else: results = (Gym.select().where((Gym.latitude >= swLat) & (Gym.longitude >= swLng) & (Gym.latitude <= neLat) & (Gym.longitude <= neLng)).dicts()) # Performance: Disable the garbage collector prior to creating a (potentially) large dict with append() gc.disable() gyms = {} gym_ids = [] for g in results: g['name'] = None g['pokemon'] = [] gyms[g['gym_id']] = g gym_ids.append(g['gym_id']) if len(gym_ids) > 0: pokemon = ( GymMember.select(GymMember.gym_id, GymPokemon.cp.alias('pokemon_cp'), GymPokemon.pokemon_id, Trainer.name.alias('trainer_name'), Trainer.level.alias('trainer_level')). join(Gym, on=(GymMember.gym_id == Gym.gym_id)).join( GymPokemon, on=(GymMember.pokemon_uid == GymPokemon.pokemon_uid)).join( Trainer, on=(GymPokemon.trainer_name == Trainer.name)) .where(GymMember.gym_id << gym_ids).where( GymMember.last_scanned > Gym.last_modified).order_by( GymMember.gym_id, GymPokemon.cp).dicts()) for p in pokemon: p['pokemon_name'] = get_pokemon_name(p['pokemon_id']) gyms[p['gym_id']]['pokemon'].append(p) details = (GymDetails.select( GymDetails.gym_id, GymDetails.name).where(GymDetails.gym_id << gym_ids).dicts()) for d in details: gyms[d['gym_id']]['name'] = d['name'] # Re-enable the GC. gc.enable() return gyms
class MainWorker(BaseModel): worker_name = CharField(primary_key=True, max_length=50) message = CharField() method = CharField(max_length=50) last_modified = DateTimeField(index=True)
class BaseModel(Model): created_at = DateTimeField(default=datetime.datetime.now) class Meta: database = db
class BaseModel(Model): timestamp = DateTimeField(default=datetime.datetime.utcnow, index=True) class Meta: database = db
class Person(BaseModel): name = CharField() email = CharField() create_datetime = DateTimeField(default=datetime.datetime.now, null=True)
class Plant(BaseModel): """ A plant, i.e. the whole purpose of this application. """ user = ForeignKeyField(User, backref="plants") user_active = ForeignKeyField(User, null=True, unique=True, backref="active_plants") created_at = DateTimeField(default=datetime.now) updated_at = DateTimeField(default=datetime.now) watered_at = DateTimeField( default=lambda: datetime.now() - timedelta(days=1)) watered_by = ForeignKeyField(User, null=True) generation = IntegerField(default=1) score = IntegerField(default=0) stage = IntegerField(default=0) species = IntegerField( default=lambda: random.randrange(len(constants.SPECIES))) rarity = IntegerField(default=_default_rarity) color = IntegerField( default=lambda: random.randrange(len(constants.COLORS))) mutation = IntegerField(null=True) dead = BooleanField(default=False) name = TextField(default=fake.first_name) fertilized_at = DateTimeField( default=lambda: datetime.now() - timedelta(days=4)) shaken_at = IntegerField(default=0) @classmethod def all_active(cls): return cls.filter(cls.user_active.is_null(False)).join(User) @property def color_str(self) -> str: return constants.COLORS[self.color] @property def stage_str(self) -> str: return constants.STAGES[self.stage] @property def species_str(self) -> str: return constants.SPECIES[self.species] @property def rarity_str(self) -> str: return constants.RARITIES[self.rarity] @property def mutation_str(self) -> Optional[str]: if self.mutation is not None: return constants.MUTATIONS[self.mutation] else: return None @property def description(self) -> str: """ A single-line description of the plant and all of its attributes. """ words: List[str] = [] if self.stage > 2: words.append(self.rarity_str) if self.mutation_str is not None: words.append(self.mutation_str) if self.stage > 3: words.append(self.color_str) words.append(self.stage_str) if self.stage > 1: words.append(self.species_str) if self.dead: words.append("(deceased)") return " ".join(words) @property def age(self) -> int: """ The plant's age in days. """ return (datetime.now() - self.created_at).days @property def growth_rate(self) -> float: """ A growth multiplier based on the plant's generation. """ return 1 + 0.2 * (self.generation - 1) @property def is_wilted(self) -> bool: """ Is the plant close to death? """ if self.dead: return False else: return self.watered_at < datetime.now() - timedelta(days=3) @property def water_supply_percent(self) -> int: """ The percentage of water supply remaining, as an integer from 0 to 100. """ seconds_per_day = 24 * 60 * 60 elapsed_seconds = (datetime.now() - self.watered_at).total_seconds() remaining_water = max(0.0, 1 - (elapsed_seconds / seconds_per_day)) return math.ceil(remaining_water * 100) @property def fertilizer_percent(self) -> int: """ The percentage of fertilizer remaining, as an integer from 0 to 100. """ seconds_per_day = 3 * 24 * 60 * 60 elapsed_seconds = (datetime.now() - self.fertilized_at).total_seconds() remaining_fertilizer = max(0.0, 1 - (elapsed_seconds / seconds_per_day)) return math.ceil(remaining_fertilizer * 100) def can_fertilize(self) -> bool: """ Return if the user can apply fertilizer to the plant. """ if self.dead: return False elif self.fertilizer_percent: return False elif not self.user_active.get_item_quantity(items.fertilizer): return False else: return True def get_water_gauge(self, ansi_enabled: bool = False) -> str: """ Build an ascii graph that displays the plant's remaining water supply. """ if self.dead: return "N/A" percent = self.water_supply_percent bar = ("█" * (percent // 10)).ljust(10) if ansi_enabled: # Make the water blue bar = colorize(bar, fg=12) return f"|{bar}| {percent}%" def get_fertilizer_gauge(self, ansi_enabled: bool = False) -> str: """ Build an ascii graph that displays the plant's remaining fertilizer. """ if self.dead: return "N/A" percent = self.fertilizer_percent bar = ("▞" * (percent // 10)).ljust(10) if ansi_enabled: # Make the fertilizer purple bar = colorize(bar, fg=40) return f"|{bar}| {percent}%" def get_ascii_art(self, ansi_enabled: bool = False) -> str: """ Build an ascii-art picture based on the plant's generation and species. """ today = date.today() if self.dead: filename = "rip.psci" elif (today.month, today.day) == (10, 31): filename = "jackolantern.psci" elif self.stage == 0: filename = "seed.psci" elif self.stage == 1: filename = "seedling.psci" elif self.stage == 2: filename = f"{self.species_str.replace(' ', '')}1.psci" elif self.stage in (3, 5): filename = f"{self.species_str.replace(' ', '')}2.psci" elif self.stage == 4: filename = f"{self.species_str.replace(' ', '')}3.psci" else: raise ValueError("Unknown stage") return render_art(filename, self.color_str, ansi_enabled) def get_observation(self, ansi_enabled: bool = False) -> str: """ A long-form description of the plant. This includes a random observations and other specific details about the plant's current stage. """ observation = [] stage = 99 if self.dead else self.stage desc = random.choice(constants.STAGE_DESCRIPTIONS[stage]) desc = desc.format(color=self.color_str, species=self.species_str) observation.append(desc) if stage < len(constants.STAGES) - 2: last_cutoff = constants.STAGE_CUTOFFS[stage] next_cutoff = constants.STAGE_CUTOFFS[stage + 1] if (self.score - last_cutoff) > 0.8 * (next_cutoff - last_cutoff): observation.append("You notice your plant looks different.") if stage == 1: choices = [ constants.SPECIES[self.species], constants.SPECIES[(self.species - 3) % len(constants.SPECIES)], constants.SPECIES[(self.species + 3) % len(constants.SPECIES)], ] random.shuffle(choices) hint = f"It could be a(n) {choices[0]}, {choices[1]}, or {choices[2]}." observation.append(hint) elif stage == 2: if self.rarity > 0: observation.append("You feel like your plant is special.") elif stage == 3: choices = [ constants.COLORS[self.color], constants.COLORS[(self.color - 3) % len(constants.COLORS)], constants.COLORS[(self.color + 3) % len(constants.COLORS)], ] random.shuffle(choices) hint = f"You can see the first hints of {choices[0]}, {choices[1]}, or {choices[2]}." observation.append(hint) return "\n".join(observation) def refresh(self) -> None: """ Update the internal state of the plant. This will recompute the plant's score, remaining water supply, mutations, any evolutions that should be happening, etc... """ last_updated = self.updated_at self.updated_at = datetime.now() # If it has been >5 days since watering, sorry plant is dead :( if self.updated_at - self.watered_at >= timedelta(days=5): self.dead = True return # Add a tick for every second since we last updated, up to 24 hours # after the last time the plant was watered min_time = max((self.watered_at, last_updated)) max_time = min((self.watered_at + timedelta(days=1), self.updated_at)) ticks = max((0, (max_time - min_time).total_seconds())) # Add a multiplier for fertilizer, up to 3 days after the last time # that the plant was fertilized min_time = max((self.fertilized_at, last_updated, self.watered_at)) max_time = min(( self.fertilized_at + timedelta(days=3), self.watered_at + timedelta(days=1), self.updated_at, )) bonus_ticks = max((0, (max_time - min_time).total_seconds())) ticks += bonus_ticks * 0.5 ticks *= self.growth_rate ticks = int(ticks) self.score += ticks # Roll for a new mutation if self.mutation is None: coefficient = 200_000 if random.random() > ((coefficient - 1) / coefficient)**ticks: self.mutation = random.randrange(len(constants.MUTATIONS)) # Evolutions while self.stage < len(constants.STAGES) - 1: if self.score >= constants.STAGE_CUTOFFS[self.stage + 1]: self.stage += 1 else: break def water(self, user: User = None) -> str: """ Attempt to water the plant. Args: user: The user watering the plant, if not the plant's owner. Returns: A string with a description of the resulting action. """ if self.dead: return "There's no point in watering a dead plant." elif self.water_supply_percent == 100: return "The soil is already damp." if user is None: self.watered_at = datetime.now() self.watered_by = None return "You sprinkle some water over your plant." query = Plant.select().where( Plant.watered_by == user, Plant.watered_at >= datetime.now() - timedelta(hours=0.5), ) if query.exists(): return "Your watering can is empty, try again later!" self.watered_at = datetime.now() self.watered_by = user info = f"You water {self.user.username}'s plant for them." return info def pick_petal(self, user: User = None) -> str: """ Pick a petal from a flowering plant. Args: user: The user picking the petal, if not the plant's owner. Returns: A string with a description of the resulting action. """ if self.dead: return "You shouldn't be here!" elif self.stage_str != "flowering": return "You shouldn't be here!" if user is None: user = self.user target = f"plant_{self.id}" last_event = Event.select().where( Event.user == user, Event.created_at >= datetime.now() - timedelta(days=1), Event.event_type == Event.PICK_PETAL, Event.target == target, ) if last_event.exists(): return "The ground around this plant is bare, come back tomorrow!" Event.create(user=user, event_type=Event.PICK_PETAL, target=target) if self.color_str == "rainbow": petal_color = random.choice(constants.COLORS_PLAIN) else: petal_color = self.color_str user.add_item(items.petals[petal_color]) lines = ( f"You spot a [{petal_color} petal] lying on the ground nearby.", "You pick it up and stick it in your backpack.", ) return "\n".join(lines) def shake(self) -> str: """ Shake the user's own plant to get money. Coins are accumulated at a rate of 1 coin per 3600 points, which is equal to 1 un-adjusted hour of watered plant time. """ multiplier = 3600 coins = (self.score - self.shaken_at) // multiplier # Leave fractional coins unclaimed self.shaken_at += coins * multiplier # Hard-cap to encourage users to shake every once and a while coins = min(coins, 100) if coins: self.user.add_item(items.coin, quantity=coins) if coins < 1: msg = "but nothing happens." elif coins < 2: msg = "and you hear the plink of a single coin." elif coins < 5: msg = "and a few coins come loose from the leaves." elif coins < 25: msg = "and a handful of coins sprinkle down." elif coins < 99: msg = "and coins shower down all around." else: msg = "and a golden nugget clonks you on the head." return f"You shake your plant, {msg}\n(+{coins} coins)" def fertilize(self) -> str: """ Attempt to fertilize the plant. Returns: A string with a description of the resulting action. """ if self.dead: return "It's time to let go." elif self.fertilizer_percent: return "The soil is still rich with nutrients." if not self.user.remove_item(items.fertilizer): return "You don't have any fertilizer to apply." self.fertilized_at = datetime.now() return "You apply a bottle of fertilizer to your plant." def harvest(self) -> Plant: """ Harvest the plant and generate a new active plant for the user. """ if self.stage == 5: new_generation = self.generation + 1 else: new_generation = 1 self.dead = True self.user_active = None self.save() return self.__class__.create(user=self.user, user_active=self.user, generation=new_generation)
class DbTickData(ModelBase): """ Tick data for database storage. Index is defined unique with (datetime, symbol) """ id = AutoField() symbol: str = CharField() exchange: str = CharField() datetime: datetime = DateTimeField() name: str = CharField() volume: float = FloatField() open_interest: float = FloatField() last_price: float = FloatField() last_volume: float = FloatField() limit_up: float = FloatField() limit_down: float = FloatField() open_price: float = FloatField() high_price: float = FloatField() low_price: float = FloatField() pre_close: float = FloatField() bid_price_1: float = FloatField() bid_price_2: float = FloatField(null=True) bid_price_3: float = FloatField(null=True) bid_price_4: float = FloatField(null=True) bid_price_5: float = FloatField(null=True) ask_price_1: float = FloatField() ask_price_2: float = FloatField(null=True) ask_price_3: float = FloatField(null=True) ask_price_4: float = FloatField(null=True) ask_price_5: float = FloatField(null=True) bid_volume_1: float = FloatField() bid_volume_2: float = FloatField(null=True) bid_volume_3: float = FloatField(null=True) bid_volume_4: float = FloatField(null=True) bid_volume_5: float = FloatField(null=True) ask_volume_1: float = FloatField() ask_volume_2: float = FloatField(null=True) ask_volume_3: float = FloatField(null=True) ask_volume_4: float = FloatField(null=True) ask_volume_5: float = FloatField(null=True) class Meta: database = db indexes = ((("symbol", "exchange", "datetime"), True), ) @staticmethod def from_tick(tick: TickData): """ Generate DbTickData object from TickData. """ db_tick = DbTickData() db_tick.symbol = tick.symbol db_tick.exchange = tick.exchange.value db_tick.datetime = tick.datetime db_tick.name = tick.name db_tick.volume = tick.volume db_tick.open_interest = tick.open_interest db_tick.last_price = tick.last_price db_tick.last_volume = tick.last_volume db_tick.limit_up = tick.limit_up db_tick.limit_down = tick.limit_down db_tick.open_price = tick.open_price db_tick.high_price = tick.high_price db_tick.low_price = tick.low_price db_tick.pre_close = tick.pre_close db_tick.bid_price_1 = tick.bid_price_1 db_tick.ask_price_1 = tick.ask_price_1 db_tick.bid_volume_1 = tick.bid_volume_1 db_tick.ask_volume_1 = tick.ask_volume_1 if tick.bid_price_2: db_tick.bid_price_2 = tick.bid_price_2 db_tick.bid_price_3 = tick.bid_price_3 db_tick.bid_price_4 = tick.bid_price_4 db_tick.bid_price_5 = tick.bid_price_5 db_tick.ask_price_2 = tick.ask_price_2 db_tick.ask_price_3 = tick.ask_price_3 db_tick.ask_price_4 = tick.ask_price_4 db_tick.ask_price_5 = tick.ask_price_5 db_tick.bid_volume_2 = tick.bid_volume_2 db_tick.bid_volume_3 = tick.bid_volume_3 db_tick.bid_volume_4 = tick.bid_volume_4 db_tick.bid_volume_5 = tick.bid_volume_5 db_tick.ask_volume_2 = tick.ask_volume_2 db_tick.ask_volume_3 = tick.ask_volume_3 db_tick.ask_volume_4 = tick.ask_volume_4 db_tick.ask_volume_5 = tick.ask_volume_5 return db_tick def to_tick(self): """ Generate TickData object from DbTickData. """ tick = TickData( symbol=self.symbol, exchange=Exchange(self.exchange), datetime=self.datetime, name=self.name, volume=self.volume, open_interest=self.open_interest, last_price=self.last_price, last_volume=self.last_volume, limit_up=self.limit_up, limit_down=self.limit_down, open_price=self.open_price, high_price=self.high_price, low_price=self.low_price, pre_close=self.pre_close, bid_price_1=self.bid_price_1, ask_price_1=self.ask_price_1, bid_volume_1=self.bid_volume_1, ask_volume_1=self.ask_volume_1, gateway_name="DB", ) if self.bid_price_2: tick.bid_price_2 = self.bid_price_2 tick.bid_price_3 = self.bid_price_3 tick.bid_price_4 = self.bid_price_4 tick.bid_price_5 = self.bid_price_5 tick.ask_price_2 = self.ask_price_2 tick.ask_price_3 = self.ask_price_3 tick.ask_price_4 = self.ask_price_4 tick.ask_price_5 = self.ask_price_5 tick.bid_volume_2 = self.bid_volume_2 tick.bid_volume_3 = self.bid_volume_3 tick.bid_volume_4 = self.bid_volume_4 tick.bid_volume_5 = self.bid_volume_5 tick.ask_volume_2 = self.ask_volume_2 tick.ask_volume_3 = self.ask_volume_3 tick.ask_volume_4 = self.ask_volume_4 tick.ask_volume_5 = self.ask_volume_5 return tick @staticmethod def save_all(objs: List["DbTickData"]): dicts = [i.to_dict() for i in objs] with db.atomic(): if driver is Driver.POSTGRESQL: for tick in dicts: DbTickData.insert(tick).on_conflict( update=tick, conflict_target=( DbTickData.symbol, DbTickData.exchange, DbTickData.datetime, ), ).execute() else: for c in chunked(dicts, 50): DbTickData.insert_many( c).on_conflict_replace().execute()
class User(BaseModel): """ A user account corresponding to a TLS client certificate. """ user_id = TextField(unique=True, index=True, default=gen_user_id) username = TextField() created_at = DateTimeField(default=datetime.now) ansi_enabled = BooleanField(default=False) # TODO: Delete this field password = BlobField(null=True) @classmethod def admin(cls) -> User: user, _ = cls.get_or_create(user_id="0" * 32, username="******") return user @classmethod def initialize(cls, username: str) -> User: """ Register a new player. """ user = cls.create(username=username) user.add_item(items.paperclip) user.add_item(items.fertilizer, quantity=1) subject, body = Inbox.load_mail_file("welcome.txt") body = body.format(user=user) Inbox.create( user_from=User.admin(), user_to=user, subject=subject, body=body, ) return user @classmethod def login(cls, fingerprint: str) -> Optional[Certificate]: """ Load a user from their certificate fingerprint. Join on the active_plant to avoid making an extra query, since we will almost always access the user's plant later. """ query = (Certificate.select().join( User, on=Certificate.user == User.id).join( Plant, JOIN.LEFT_OUTER, on=Plant.user_active == User.id).where( Certificate.fingerprint == fingerprint)) try: cert = query.get() cert.update(last_seen=datetime.now()) cert.save() except Certificate.DoesNotExist: cert = None return cert @property def plant(self) -> Plant: """ Return the user's current "active" plant, or generate a new one. This is cached locally to avoid unnecessary DB lookups. """ if not hasattr(self, "_plant"): try: self._plant = self.active_plants.get() except Plant.DoesNotExist: self._plant = Plant.create(user=self, user_active=self) return self._plant def set_password(self, password: str) -> None: self.password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) def check_password(self, password: str) -> bool: if not self.password: return False return bcrypt.checkpw(password.encode(), self.password) def add_item(self, item: items.Item, quantity: int = 1) -> ItemSlot: """ Add an item to the user's inventory. """ item_slot, _ = ItemSlot.get_or_create(user=self, item_id=item.item_id) item_slot.quantity += quantity item_slot.save() return item_slot def remove_item(self, item: items.Item, quantity: int = 1) -> bool: """ Remove an item from the user's inventory. Returns True if the item was successfully removed. """ item_slot = ItemSlot.get_or_none(user=self, item_id=item.item_id) if not item_slot: return False if item_slot.quantity < quantity: return False if item_slot.quantity == quantity: item_slot.delete_instance() return True item_slot.quantity -= quantity item_slot.save() return True def get_item_quantity(self, item: items.Item) -> int: """ Return the number of items in the user's inventory. """ item_slot = ItemSlot.get_or_none(user=self, item_id=item.item_id) if item_slot: return item_slot.quantity return 0
class Game(BaseModel): name = TextField() platform = TextField() category = TextField() append_datetime = DateTimeField() finish_datetime = DateTimeField(null=True) ignored = BooleanField(default=False) root_alias = ForeignKeyField('self', null=True) class Meta: indexes = ((("name", "platform", "category"), True), ) @classmethod def get_all_by(cls, **filters) -> List['Game']: return list(cls.select().filter(**filters).order_by(cls.id)) def get_first_root(self) -> 'Game': # Идем вверх пока не найдем самую первую игру root_alias = self.root_alias while root_alias: # Если у текущей игры нет псевдонима if not root_alias.root_alias: break root_alias = root_alias.root_alias return root_alias @property def append_datetime_dt(self) -> Union[DT.datetime, DateTimeField]: append_datetime = self.append_datetime root_alias = self.get_first_root() if root_alias and root_alias.append_datetime is not None: append_datetime = root_alias.append_datetime if isinstance(append_datetime, str): return DT.datetime.fromisoformat(append_datetime) return append_datetime @property def finish_datetime_dt(self) -> Union[DT.datetime, DateTimeField]: finish_datetime = self.finish_datetime root_alias = self.get_first_root() if root_alias and root_alias.finish_datetime is not None: finish_datetime = root_alias.finish_datetime if isinstance(finish_datetime, str): return DT.datetime.fromisoformat(finish_datetime) return finish_datetime @classmethod def get_all_finished_by_year(cls, year: int) -> Iterator['Game']: query = (cls.select().where(cls.finish_datetime.is_null(False), cls.ignored == 0).order_by( cls.finish_datetime.desc())) return [game for game in query if game.finish_datetime_dt.year == year] @classmethod def get_year_by_number(cls) -> List[Tuple[int, int]]: fn_year = fn.strftime('%Y', cls.finish_datetime).cast('INTEGER') year_by_number = [] for game in (cls.select( fn_year.alias('year'), fn.count(cls.id).alias('count')).where( cls.finish_datetime.is_null(False), cls.ignored == 0).group_by(fn_year).order_by( fn_year.desc())): year_by_number.append((game.year, game.count)) return year_by_number @classmethod def get_day_by_games(cls, year: int) -> Dict[str, List['Game']]: day_by_games = defaultdict(list) for game in Game.get_all_finished_by_year(year): day = game.finish_datetime_dt.strftime('%d/%m/%Y') day_by_games[day].append(game) return day_by_games
def add_plant_fertilized_at(migrator): dt = datetime.now() - timedelta(days=4) migrate.migrate( migrator.add_column("plant", "fertilized_at", DateTimeField(default=dt)))
class Infraction(BaseModel): Types = Enum( 'MUTE', 'KICK', 'TEMPBAN', 'SOFTBAN', 'BAN', 'TEMPMUTE', 'UNBAN', 'TEMPROLE', 'WARNING', bitmask=False, ) guild_id = BigIntegerField() user_id = BigIntegerField() actor_id = BigIntegerField(null=True) type_ = IntegerField(column_name='type') reason = TextField(null=True) metadata = BinaryJSONField(default={}) expires_at = DateTimeField(null=True) created_at = DateTimeField(default=datetime.utcnow) active = BooleanField(default=True) class Meta: table_name = 'infractions' indexes = ((('guild_id', 'user_id'), False), ) def serialize(self, guild=None, user=None, actor=None, include_metadata=False): base = { 'id': str(self.id), 'guild': (guild and guild.serialize()) or { 'id': str(self.guild_id) }, 'user': (user and user.serialize()) or { 'id': str(self.user_id) }, 'actor': (actor and actor.serialize()) or { 'id': str(self.actor_id) }, 'reason': self.reason, 'expires_at': self.expires_at, 'created_at': self.created_at, 'active': self.active, 'type': { 'id': self.type_, 'name': next(i.name for i in Infraction.Types.attrs if i.index == self.type_) } } if include_metadata: base['metadata'] = self.metadata return base @staticmethod def admin_config(event): return getattr(event.base_config.plugins, 'admin', None) @classmethod def temprole(cls, plugin, event, member, role_id, reason, expires_at): User.from_disco_user(member.user) # TODO: modlog member.add_role(role_id, reason=reason) cls.create(guild_id=event.guild.id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.TEMPROLE, reason=reason, expires_at=expires_at, metadata={'role': role_id}) @classmethod def kick(cls, plugin, event, member, reason): from rowboat.plugins.modlog import Actions User.from_disco_user(member.user) # Prevent the GuildMemberRemove log event from triggering plugin.call('ModLogPlugin.create_debounce', event, ['GuildMemberRemove'], user_id=member.user.id) member.kick(reason=reason) # Create a kick modlog event plugin.call('ModLogPlugin.log_action_ext', Actions.MEMBER_KICK, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason') cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.KICK, reason=reason) @classmethod def tempban(cls, plugin, event, member, reason, expires_at): from rowboat.plugins.modlog import Actions User.from_disco_user(member.user) plugin.call('ModLogPlugin.create_debounce', event, ['GuildMemberRemove', 'GuildBanAdd'], user_id=member.user.id) member.ban(reason=reason) plugin.call( 'ModLogPlugin.log_action_ext', Actions.MEMBER_TEMPBAN, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason', expires=expires_at, ) cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.TEMPBAN, reason=reason, expires_at=expires_at) @classmethod def softban(cls, plugin, event, member, reason): from rowboat.plugins.modlog import Actions User.from_disco_user(member.user) plugin.call('ModLogPlugin.create_debounce', event, ['GuildMemberRemove', 'GuildBanAdd', 'GuildBanRemove'], user_id=member.user.id) member.ban(delete_message_days=7, reason=reason) member.unban(reason=reason) plugin.call('ModLogPlugin.log_action_ext', Actions.MEMBER_SOFTBAN, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason') cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.SOFTBAN, reason=reason) @classmethod def ban(cls, plugin, event, member, reason, guild, delete_message_days=0): from rowboat.plugins.modlog import Actions if isinstance(member, int): user_id = member else: User.from_disco_user(member.user) user_id = member.user.id plugin.call( 'ModLogPlugin.create_debounce', event, ['GuildMemberRemove', 'GuildBanAdd'], user_id=user_id, ) guild.create_ban( user_id, delete_message_days if delete_message_days <= 7 else 7, reason=reason) plugin.call( 'ModLogPlugin.log_action_ext', Actions.MEMBER_BAN, guild.id, user=member, user_id=user_id, actor=event.author if event.author.id != user_id else 'Automatic', reason=reason or 'no reason') cls.create(guild_id=guild.id, user_id=user_id, actor_id=event.author.id, type_=cls.Types.BAN, reason=reason) @classmethod def warn(cls, plugin, event, member, reason, guild): from rowboat.plugins.modlog import Actions User.from_disco_user(member.user) user_id = member.user.id cls.create(guild_id=guild.id, user_id=user_id, actor_id=event.author.id, type_=cls.Types.WARNING, reason=reason) plugin.call('ModLogPlugin.log_action_ext', Actions.MEMBER_WARNED, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason') @classmethod def mute(cls, plugin, event, member, reason): from rowboat.plugins.modlog import Actions admin_config = cls.admin_config(event) if not admin_config.mute_role: plugin.log.warning('Cannot mute member {}, no mute role'.format( member.id)) return plugin.call( 'ModLogPlugin.create_debounce', event, ['GuildMemberUpdate'], user_id=member.user.id, role_id=admin_config.mute_role, ) member.add_role(admin_config.mute_role, reason=reason) plugin.call('ModLogPlugin.log_action_ext', Actions.MEMBER_MUTED, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason') cls.create(guild_id=event.guild.id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.MUTE, reason=reason, metadata={'role': admin_config.mute_role}) @classmethod def tempmute(cls, plugin, event, member, reason, expires_at): from rowboat.plugins.modlog import Actions admin_config = cls.admin_config(event) if not admin_config.mute_role: plugin.log.warning( 'Cannot tempmute member {}, no mute role'.format(member.id)) return plugin.call( 'ModLogPlugin.create_debounce', event, ['GuildMemberUpdate'], user_id=member.user.id, role_id=admin_config.mute_role, ) member.add_role(admin_config.mute_role, reason=reason) plugin.call( 'ModLogPlugin.log_action_ext', Actions.MEMBER_TEMP_MUTED, event.guild.id, member=member, actor=event.author if event.author.id != member.id else 'Automatic', reason=reason or 'no reason', expires=expires_at, ) cls.create(guild_id=event.guild.id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.TEMPMUTE, reason=reason, expires_at=expires_at, metadata={'role': admin_config.mute_role}) @classmethod def clear_active(cls, event, user_id, types): """ Marks a previously active tempmute as inactive for the given event/user. This should be used in all locations where we either think this is no longer active (e.g. the mute role was removed) _or_ when we don't want to unmute the user any longer, e.g. they've been remuted by another command. """ return cls.update(active=False).where( (cls.guild_id == event.guild.id) & (cls.user_id == user_id) & (cls.type_ << types) & (cls.active == 1)).execute() >= 1
class Msg(BaseModel): tg_id = IntegerField() text = TextField() date = DateTimeField(default=datetime.now)
class JoinTable(BaseModel): id = AutoField() code = CharField(unique=True, max_length=50) creation_time = DateTimeField(default=datetime.datetime.now) simple = ForeignKeyField(SimpleTable, backref='joins')
class Message(BaseModel): content = TextField() channel_id = TextField() member = ForeignKeyField(Member, backref="messages") date = DateTimeField(default=datetime.datetime.now)
class DbTickData(Model): """ Tick data for database storage. Index is defined unique with vt_symbol, interval and datetime. """ symbol = CharField() exchange = CharField() datetime = DateTimeField() name = CharField() volume = FloatField() last_price = FloatField() last_volume = FloatField() limit_up = FloatField() limit_down = FloatField() open_price = FloatField() high_price = FloatField() low_price = FloatField() close_price = FloatField() bid_price_1 = FloatField() bid_price_2 = FloatField() bid_price_3 = FloatField() bid_price_4 = FloatField() bid_price_5 = FloatField() ask_price_1 = FloatField() ask_price_2 = FloatField() ask_price_3 = FloatField() ask_price_4 = FloatField() ask_price_5 = FloatField() bid_volume_1 = FloatField() bid_volume_2 = FloatField() bid_volume_3 = FloatField() bid_volume_4 = FloatField() bid_volume_5 = FloatField() ask_volume_1 = FloatField() ask_volume_2 = FloatField() ask_volume_3 = FloatField() ask_volume_4 = FloatField() ask_volume_5 = FloatField() vt_symbol = CharField() gateway_name = CharField() class Meta: database = DB indexes = ((("vt_symbol", "datetime"), True), ) @staticmethod def from_tick(tick: TickData): """ Generate DbTickData object from TickData. """ db_tick = DbTickData() db_tick.symbol = tick.symbol db_tick.exchange = tick.exchange.value db_tick.datetime = tick.datetime db_tick.name = tick.name db_tick.volume = tick.volume db_tick.last_price = tick.last_price db_tick.last_volume = tick.last_volume db_tick.limit_up = tick.limit_up db_tick.limit_down = tick.limit_down db_tick.open_price = tick.open_price db_tick.high_price = tick.high_price db_tick.low_price = tick.low_price db_tick.pre_close = tick.pre_close db_tick.bid_price_1 = tick.bid_price_1 db_tick.ask_price_1 = tick.ask_price_1 db_tick.bid_volume_1 = tick.bid_volume_1 db_tick.ask_volume_1 = tick.ask_volume_1 if tick.bid_price_2: db_tick.bid_price_2 = tick.bid_price_2 db_tick.bid_price_3 = tick.bid_price_3 db_tick.bid_price_4 = tick.bid_price_4 db_tick.bid_price_5 = tick.bid_price_5 db_tick.ask_price_2 = tick.ask_price_2 db_tick.ask_price_3 = tick.ask_price_3 db_tick.ask_price_4 = tick.ask_price_4 db_tick.ask_price_5 = tick.ask_price_5 db_tick.bid_volume_2 = tick.bid_volume_2 db_tick.bid_volume_3 = tick.bid_volume_3 db_tick.bid_volume_4 = tick.bid_volume_4 db_tick.bid_volume_5 = tick.bid_volume_5 db_tick.ask_volume_2 = tick.ask_volume_2 db_tick.ask_volume_3 = tick.ask_volume_3 db_tick.ask_volume_4 = tick.ask_volume_4 db_tick.ask_volume_5 = tick.ask_volume_5 db_tick.vt_symbol = tick.vt_symbol db_tick.gateway_name = "DB" return tick def to_tick(self): """ Generate TickData object from DbTickData. """ tick = TickData( symbol=self.symbol, exchange=Exchange(self.exchange), datetime=self.datetime, name=self.name, volume=self.volume, last_price=self.last_price, last_volume=self.last_volume, limit_up=self.limit_up, limit_down=self.limit_down, open_price=self.open_price, high_price=self.high_price, low_price=self.low_price, pre_close=self.pre_close, bid_price_1=self.bid_price_1, ask_price_1=self.ask_price_1, bid_volume_1=self.bid_volume_1, ask_volume_1=self.ask_volume_1, gateway_name=self.gateway_name, ) if self.bid_price_2: tick.bid_price_2 = self.bid_price_2 tick.bid_price_3 = self.bid_price_3 tick.bid_price_4 = self.bid_price_4 tick.bid_price_5 = self.bid_price_5 tick.ask_price_2 = self.ask_price_2 tick.ask_price_3 = self.ask_price_3 tick.ask_price_4 = self.ask_price_4 tick.ask_price_5 = self.ask_price_5 tick.bid_volume_2 = self.bid_volume_2 tick.bid_volume_3 = self.bid_volume_3 tick.bid_volume_4 = self.bid_volume_4 tick.bid_volume_5 = self.bid_volume_5 tick.ask_volume_2 = self.ask_volume_2 tick.ask_volume_3 = self.ask_volume_3 tick.ask_volume_4 = self.ask_volume_4 tick.ask_volume_5 = self.ask_volume_5 return tick
class Post(BaseModel): class Meta: table_name = 'post' languages = ( ('ukr', u'Українська'), ('eng', 'English'), ('rus', u'Русский'), ) post_id = AutoField(column_name='post_id') category = ForeignKeyField(Category, field='category_id') user = ForeignKeyField(User, field='user_id') date_posted = DateTimeField(default=datetime.now) date_updated = DateTimeField(default=datetime.now) post_text = TextField() title = CharField() slug = CharField(default='', unique=True) language = CharField(default=languages[0][0], choices=languages) likes = IntegerField(default=0) views = IntegerField(default=0) show_on_index = BooleanField(default=True) draft = BooleanField() deleted = BooleanField() def serialize(self): return { 'id': self.post_id, 'title': self.title, 'date': self.date_posted.strftime(config.DEFAULT_DATE_FORMAT), 'short_text': shorten_text(self.post_text), 'url_id': self.url_id, } @property def url_id(self): if self.slug: return self.slug return self.post_id @property def actuality(self): """ Return sophisticated value of post actuality """ cv = lambda x, y: float(x) / y if x < y else 1.0 # ceil part-value now_time = datetime.now() int_posted = (now_time - self.date_posted).total_seconds() int_updt = (now_time - self.date_updated).total_seconds() int_years = timedelta(days=365 * 5).total_seconds() comments_weight = cv(self.comments, 10) * 15 likes_weight = cv(self.likes, 30) * 15 views_weight = cv(self.views, 100) * 10 # the less time passed the higher value posted_weight = (1 - cv(int_posted, int_years)) * 40 updated_weight = (1 - cv(int_updt, int_years)) * 20 act = (comments_weight + likes_weight + views_weight + posted_weight + updated_weight) assert act < 100 if act < 10: app.log('Post %d is not relevant' % self.post_id, 'warning') return act @property def comments(self): """ Get comments count """ return 10 @property def tags(self): return Tag.select().join(Tag_to_Post).\ where(Tag_to_Post.post_id == self.post_id) def save(self, *args, **kwargs): if not self.slug: slug = create_slug(self.title) self.slug = self.ensure_unique_slug(slug, 0) # Ensure lowercase self.slug = self.slug.lower() return super(Model, self).save(*args, **kwargs) @classmethod def ensure_unique_slug(cls, slug: str, post_id: int) -> str: new_slug = slug post_with_a_slug = cls.select() \ .where(Post.slug == slug.lower()).first() if post_with_a_slug is None: return new_slug try: post_id = int(post_id) except: post_id = 0 # Some other post have the same slug if post_with_a_slug.post_id != post_id: date_spec = datetime.now().strftime(config.SLUG_DATE_FORMAT) if not slug: slug = 'article' new_slug = '{}-{}'.format(date_spec, slug) return new_slug @classmethod def create(cls, **query): return super(Post, cls).create(**query) @classmethod def search(cls, query): return cls.get_posts().where(Post.title.contains(query)) @classmethod def get_drafts(cls): """ Return only draft posts """ return cls.select().where(Post.deleted == False, Post.draft == True) @classmethod def get_deleted(cls): """ Return only deleted """ return cls.select().where(Post.deleted == True) @classmethod def get_posts(cls, index_only=False): """ Get not deleted and not drafts to display in post list """ queryset = cls.select().where( Post.deleted == False, Post.draft == False, ) if index_only: queryset = queryset.where(Post.show_on_index == True) return queryset @classmethod def get_for_user(cls, user_id): """ Get published posts for this specific user """ return cls.select().where(Post.deleted == False, Post.draft == False, Post.user == user_id) def get_all(self): raise NotImplementedError() def __str__(self): return '#{post_id}. {post_title}'.format( post_id=self.post_id, post_title=self.title.encode('utf-8'))
class AhvSubnetsCache(CacheTableBase): __cache_type__ = CACHE.ENTITY.AHV_SUBNET name = CharField() uuid = CharField() cluster = CharField() account_uuid = CharField(default="") cluster_uuid = CharField( default="") # TODO separate out uuid and create separate table for it last_update_time = DateTimeField(default=datetime.datetime.now()) def get_detail_dict(self, *args, **kwargs): return { "name": self.name, "uuid": self.uuid, "cluster": self.cluster, "cluster_uuid": self.cluster_uuid, "account_uuid": self.account_uuid, "last_update_time": self.last_update_time, } @classmethod def clear(cls): """removes entire data from table""" for db_entity in cls.select(): db_entity.delete_instance() @classmethod def show_data(cls): """display stored data in table""" if not len(cls.select()): click.echo(highlight_text("No entry found !!!")) return table = PrettyTable() table.field_names = [ "NAME", "UUID", "CLUSTER_NAME", "ACCOUNT_UUID", "LAST UPDATED", ] for entity in cls.select(): entity_data = entity.get_detail_dict() last_update_time = arrow.get( entity_data["last_update_time"].astimezone( datetime.timezone.utc)).humanize() table.add_row([ highlight_text(entity_data["name"]), highlight_text(entity_data["uuid"]), highlight_text(entity_data["cluster"]), highlight_text(entity_data["account_uuid"]), highlight_text(last_update_time), ]) click.echo(table) @classmethod def sync(cls): """sync the table from server""" # clear old data cls.clear() client = get_api_client() payload = {"length": 250, "filter": "state==VERIFIED;type==nutanix_pc"} account_name_uuid_map = client.account.get_name_uuid_map(payload) AhvVmProvider = get_provider("AHV_VM") AhvObj = AhvVmProvider.get_api_obj() for e_name, e_uuid in account_name_uuid_map.items(): try: res = AhvObj.subnets(account_uuid=e_uuid) except Exception: LOG.warning( "Unable to fetch subnets for Nutanix_PC Account(uuid={})". format(e_uuid)) continue for entity in res["entities"]: name = entity["status"]["name"] uuid = entity["metadata"]["uuid"] cluster_ref = entity["status"]["cluster_reference"] cluster_name = cluster_ref.get("name", "") cluster_uuid = cluster_ref.get("uuid", "") cls.create_entry( name=name, uuid=uuid, cluster=cluster_name, account_uuid=e_uuid, cluster_uuid=cluster_uuid, ) # For older version < 2.9.0 # Add working for older versions too @classmethod def create_entry(cls, name, uuid, **kwargs): account_uuid = kwargs.get("account_uuid", "") if not account_uuid: LOG.error("Account UUID not supplied for subnet {}".format(name)) sys.exit(-1) cluster_name = kwargs.get("cluster", None) if not cluster_name: LOG.error("cluster not supplied for subnet {}".format(name)) sys.exit(-1) cluster_uuid = kwargs.get("cluster_uuid", "") # store data in table super().create( name=name, uuid=uuid, cluster=cluster_name, account_uuid=account_uuid, cluster_uuid=cluster_uuid, ) @classmethod def get_entity_data(cls, name, **kwargs): query_obj = {"name": name} account_uuid = kwargs.get("account_uuid", "") if account_uuid: query_obj["account_uuid"] = account_uuid cluster_name = kwargs.get("cluster", "") if cluster_name: query_obj["cluster"] = cluster_name try: # The get() method is shorthand for selecting with a limit of 1 # If more than one row is found, the first row returned by the database cursor entity = super().get(**query_obj) return entity.get_detail_dict() except DoesNotExist: return None @classmethod def get_entity_data_using_uuid(cls, uuid, **kwargs): account_uuid = kwargs.get("account_uuid", "") try: if account_uuid: entity = super().get(cls.uuid == uuid, cls.account_uuid == account_uuid) else: entity = super().get(cls.uuid == uuid) return entity.get_detail_dict() except DoesNotExist: return None class Meta: database = dsl_database primary_key = CompositeKey("name", "uuid", "account_uuid")
class SystemPlatform(BaseModel): """system_platform table""" id = AutoField() inventory_id = UUIDStrField(null=False, unique=True) display_name = TextField(null=True) rh_account_id = ForeignKeyField(column_name="rh_account_id", model=RHAccount, field="id") first_reported = DateTimeField(null=False) s3_url = TextField(null=True) vmaas_json = TextField(null=True) json_checksum = TextField(null=True) last_updated = DateTimeField(null=False) unchanged_since = DateTimeField(null=False) last_evaluation = DateTimeField(null=True) advisor_evaluated = DateTimeField(null=True) opt_out = BooleanField(null=False) last_upload = DateTimeField(null=False) stale_timestamp = DateTimeField(null=True) stale_warning_timestamp = DateTimeField(null=True) culled_timestamp = DateTimeField(null=True) stale = BooleanField(null=False) when_deleted = DateTimeField(null=True) advisor_checksum = TextField(null=True) advisor_unchanged_since = DateTimeField(null=False) cve_count_cache = IntegerField(null=False) host_type = TextField(null=True) class Meta: """system_platform table metadata""" table_name = "system_platform"