def __import_interactions_from_csv(self, ifn, pdb_file): """Reads the csv file, imports all interactions, deletes the file when done to avoid stale data and free up disk space""" logging.info('Importing interactions') commit_every = 1000 reader = csv.reader(open(ifn, 'rb'), delimiter=',', quotechar='"') for i,row in enumerate(reader): I = PairwiseInteractions(iPdbSig=row[0], jPdbSig=row[1]) I.pdb_id = pdb_file I.f_crossing = int(row[3]) interaction = row[2].strip() if interaction[0] == 's' or interaction[0:2] == 'ns': I.f_stacks = interaction elif interaction[-2:] == 'BR': I.f_brbs = interaction elif interaction[-3:] == 'BPh': I.f_bphs = interaction else: I.f_lwbp = interaction session.merge(I) """Since the files can be huge, it's unfeasible to store all objects in memory, have to commit regularly""" if i % commit_every == 0: session.commit() session.commit() os.remove(ifn) logging.info('Csv file successfully imported')
def html_stats(): sitemaps = Sitemap.get_by_status(SitemapTaskStatus.before_html_stats) for sitemap in sitemaps: headers = {'User-Agent': BaseConfig.UA} r = requests.get(urllib.parse.quote(sitemap.loc, safe=':/'), headers=headers) soup = BeautifulSoup(r.text, 'html.parser') text = ''.join(soup.findAll(text=True)) html_stats = HtmlStats() html_stats.loc = sitemap.loc html_stats.domain = sitemap.domain html_stats.path = sitemap.path html_stats.word_count = len(text) html_stats.number_of_punctuation_marks = (text.count("、") + text.count("。")) html_stats.number_of_h1 = len(soup.find_all('h1')) html_stats.number_of_h2 = len(soup.find_all('h2')) html_stats.number_of_h3 = len(soup.find_all('h3')) html_stats.number_of_h4 = len(soup.find_all('h4')) html_stats.number_of_h5 = len(soup.find_all('h5')) html_stats.number_of_h6 = len(soup.find_all('h6')) html_stats.number_of_table = len(soup.find_all('table')) html_stats.number_of_li = len(soup.find_all('li')) html_stats.number_of_dl = len(soup.find_all('dl')) html_stats.number_of_image = len(soup.find_all('img')) html_stats.number_of_a = len(soup.find_all('a')) html_stats.number_of_iframe = len(soup.find_all('iframe')) html_stats.update_at = datetime.datetime.now() sitemap.status = SitemapTaskStatus.task_done session.merge(html_stats) session.flush() session.commit()
def import_loops(self, Loops, l, pdb_id, loop_type): """ """ try: if Loops == 0: self.mark_pdb_as_analyzed(pdb_id, loop_type) return for i in xrange(l): loop_id = self._get_loop_id(Loops[i].AllLoops_table.full_id, pdb_id, loop_type) Loops[i].Filename = loop_id session.merge( AllLoops(id = loop_id, type = loop_type, pdb = pdb_id, sequential_id = loop_id[-3:], length = int(Loops[i].NumNT[0][0]), seq = Loops[i].AllLoops_table.seq, r_seq = Loops[i].AllLoops_table.r_seq, nwc_seq = Loops[i].AllLoops_table.nwc, r_nwc_seq = Loops[i].AllLoops_table.r_nwc, pdb_file = Loops[i].PDBFilename, nt_ids = Loops[i].AllLoops_table.full_id, loop_name = Loops[i].AllLoops_table.loop_name)) self.save_mat_files(Loops) self.mark_pdb_as_analyzed(pdb_id, loop_type) logging.info('%s from %s successfully imported', loop_type, pdb_id) except: e = sys.exc_info()[1] MotifAtlasBaseClass._crash(self,e)
def stats(bot, update): if "Haro" in update.message.text and "?" in update.message.text: update.message.reply_text('Haro figyel! Haro figyel!') user = User( update.message.from_user.id, update.message.from_user.first_name, update.message.from_user.last_name, update.message.from_user.username ) user = session.merge(user) session.add(user) chat = Chat( update.message.chat_id, update.message.chat.title if update.message.chat.title else update.message.chat.type, ) chat = session.merge(chat) session.add(chat) user_activity = UserActivity( user, chat, update.message.date ) session.add(user_activity) session.commit()
def save_sitemap(url_object): url = url_object.get("value") headers = {'User-Agent': BaseConfig.UA} r = requests.get(url, headers=headers) soup = BeautifulSoup(r.text, 'html.parser') urls = soup.find_all("url") for url in urls: if not url.loc: raise ValueError("url.loc is None...") parse_result = urllib.parse.urlparse(url.loc.text) sitemap = session.query(Sitemap).filter(Sitemap.loc == urllib.parse.unquote(url.loc.text)).first() if sitemap is None: sitemap = Sitemap() sitemap.loc = urllib.parse.unquote(url.loc.text) sitemap.lastmod = strptime(url.lastmod.text) if url.lastmod else None sitemap.change_freq = url.changefreq.text if url.changefreq else None sitemap.priority = Decimal(url.priority.text) if url.priority else None sitemap.domain = parse_result.netloc sitemap.path = urllib.parse.unquote(parse_result.path) sitemap.status = SitemapTaskStatus.before_html_stats session.add(sitemap) elif sitemap.lastmod is None or sitemap.lastmod > strptime(url.lastmod.text) if url.lastmod else None : sitemap.lastmod = strptime(url.lastmod.text) if url.lastmod else None sitemap.status = SitemapTaskStatus.before_html_stats session.merge(sitemap) session.flush() session.commit()
def redo_compare_all_releases(self): """ When problems in release comparisons are discovered, use this function in manual mode to recompute the release difference table. """ all_releases = self.list_all_releases(self.motif_type) for i in xrange(len(all_releases)): release = all_releases[i] c1 = MotifCollection(release=release,type=self.motif_type) for j in xrange((i+1),len(all_releases)): print '%s vs %s' % (all_releases[i], all_releases[j]) c2 = MotifCollection(release=all_releases[j],type=self.motif_type) logging.info('Comparing %s and %s' % (c1.release, c2.release)) A = Uploader(ensembles=MotifCollectionMerger(c1,c2), upload_mode='release_diff', motif_type=self.motif_type) A.import_release() # set direct parent flag if j == i+1: a = session.query(Release_diff).filter(Release_diff.release_id1==release)\ .filter(Release_diff.release_id2==all_releases[j])\ .first() a.direct_parent = 1; session.merge(a) session.commit()
def delete(self, **kwargs): booking_id = int(kwargs.get('booking_id')) booking_obj = session.query(Booking).filter( Booking.id == booking_id).all() if len(booking_obj) == 0: return Response(json.dumps({'message': 'please check booking id'}), mimetype='application/json') parking_spot_id = booking_obj[0].to_dict()['parking_spot_id'] booking_obj_id = booking_obj[0].to_dict()['id'] parking_obj = session.query(ParkingSpot).filter( ParkingSpot.id == parking_spot_id).all() parking_obj = parking_obj[0].to_dict() if booking_id == booking_obj_id and parking_obj['reserved'] == 1: obj = Booking.query.filter_by(id=booking_id).one() session.delete(obj) session.commit() parking_obj['reserved'] = 0 parking_data = parking_obj parking = ParkingSpot(**parking_data) session.merge(parking) session.commit() return Response(json.dumps({ 'message': 'booking deleted successfully ', 'id': booking_id }), mimetype='application/json') else: return Response(json.dumps({'message': 'please check booking id'}), mimetype='application/json')
def post(self): parking_spot_id = int(request.json.get('parking_spot_id')) user_id = int(request.json.get('user_id')) data = {} user_obj = session.query(User).filter(User.id == user_id).all() parking_obj = session.query(ParkingSpot).filter( ParkingSpot.id == parking_spot_id).all() user_obj_id = user_obj[0].to_dict()['id'] parking_obj_id = parking_obj[0].to_dict()['id'] parking_status = parking_obj[0].to_dict()['reserved'] parking_obj = parking_obj[0].to_dict() if user_obj_id == user_id and parking_obj_id == parking_spot_id and parking_obj[ 'reserved'] == 0: data.update({'user_id': user_id}) data.update({'parking_spot_id': parking_spot_id}) data_obj = Booking(**data) session.add(data_obj) session.commit() parking_obj['reserved'] = 1 parking_data = parking_obj parking = ParkingSpot(**parking_data) session.merge(parking) session.commit() return Response(json.dumps( {'message': 'booking successfully done'}), status=201, mimetype='application/json') else: return Response(json.dumps({ 'message': 'please check your parking area existence,sorry not booked' }), mimetype='application/json')
def add(name, monster_type): monster_type = (session.query(Monster).filter( Monster.name == monster_type).one()) session.merge( MonsterInstance( name=name, monster_type=monster_type.name, str=monster_type.str, con=monster_type.con, int=monster_type.int, wis=monster_type.wis, cha=monster_type.cha, dex=monster_type.dex, HP=monster_type.HP, AC=monster_type.AC, speed=monster_type.speed, size=monster_type.size, type=monster_type.type, subtype=monster_type.subtype, alignment=monster_type.alignment, hit_dice=monster_type.hit_dice, stealth=monster_type.stealth, damage_vulnerabilities=monster_type.damage_vulnerabilities, damage_resistances=monster_type.damage_resistances, damage_immunities=monster_type.damage_immunities, condition_immunities=monster_type.condition_immunities, senses=monster_type.senses, languages=monster_type.languages, challenge_rating=monster_type.challenge_rating, special_abilities=monster_type.special_abilities, actions=monster_type.actions, )) session.commit()
def mark_pdb_as_analyzed(self, pdb_id, column_name): """ """ P = PdbAnalysisStatus(id = pdb_id) setattr(P,column_name.lower(),datetime.datetime.now()) session.merge(P) session.commit() logging.info('Updated %s status for pdb %s', column_name, pdb_id)
def put(self, rfq_id, section_id): data = request.get_json()['data'] for key in data: component = session.query(CustomComponent).filter_by( document_id=rfq_id).filter_by(name=key).first() component.text = data[key] session.merge(component) session.commit()
def __process_bp_signatures(self): """ "Group_001","06_cWW-cWW-cWW" """ r = csv.reader(open(self.files['BpSignatures']), delimiter=',', quotechar='"') for row in r: session.merge(MotifAnnotation(motif_id=self.final_ids[row[0]], bp_signature=row[1])) session.commit()
def put(self, rfq_id): data = request.get_json()['data'] for item in data: deliverable = session.query(Deliverable).filter_by( document_id=rfq_id).filter_by(name=item["name"]).first() deliverable.value = item["value"] deliverable.text = item["text"] session.merge(deliverable) session.commit()
def __delete_old_data(self, pdbs): """When recalculate=True, delete all data from pdb_redundant_nucleotides and set pdb_analysis_status to None in the redundant_nts column""" session.query(RedundantNucleotide).filter(RedundantNucleotide.pdb_id.in_(pdbs)).delete( synchronize_session="fetch" ) for statusObj in session.query(PdbAnalysisStatus).filter(PdbAnalysisStatus.id.in_(pdbs)).all(): statusObj.redundant_nts = None session.merge(statusObj) session.commit()
def __delete_old_data(self, pdbs): """When recalculate=True, delete all data from pdb_best_chains_and_models and set pdb_analysis_status to None in the best_chains_and_models column""" session.query(PdbBestChainsAndModels).filter(PdbBestChainsAndModels.pdb_id.in_(pdbs)).delete( synchronize_session="fetch" ) for statusObj in session.query(PdbAnalysisStatus).filter(PdbAnalysisStatus.id.in_(pdbs)).all(): statusObj.best_chains_and_models = None session.merge(statusObj) session.commit()
def save_tags(cls, tags=None): ''' Save tag data to the database ''' for tag in tags: new_tag = Tag(tag=tag.get('tag'), count=tag.get('count'), type=tag.get('type'), ambiguous=tag.get('ambiguous')) session.merge(new_tag) session.commit()
def __delete_unit_ids(self, pdbs): """ """ logging.info('Deleting unit id correspondence data') session.query(PdbUnitIdCorrespondence). \ filter(PdbUnitIdCorrespondence.pdb.in_(pdbs)). \ delete(synchronize_session='fetch') logging.info('Resetting unit id analysis status') for statusObj in session.query(PdbAnalysisStatus). \ filter(PdbAnalysisStatus.id.in_(pdbs)). \ all(): statusObj.unit_ids = None session.merge(statusObj) session.commit()
def __load_into_db(self, lines): """Compares the custom report data with what's already in the database, reports any discrepancies and stores the most recent version.""" logging.info('Loading custom report') description = lines.pop(0) # discard field names keys = description.split(',') for line in lines: """one line per chain""" if len(line) < 10: continue # skip empty lines """replace quotechars that are not preceded and followed by commas, except for the beginning and the end of the string example: in 1HXL unescaped doublequotes in the details field""" line = re.sub('(?<!^)(?<!,)"(?!,)(?!$)', "'", line) """parse the line using csv reader""" reader = csv.reader([line], delimiter=',', quotechar='"') """temporary store all field in a dictionary""" chain_dict = dict() for read in reader: for i, part in enumerate(read): if not part: part = None # to save as NULL in the db chain_dict[keys[i]] = part logging.info('%s %s', chain_dict['structureId'], chain_dict['chainId']) """check if this chain from this pdb is present in the db""" existing_chain = session.query(PdbInfo). \ filter(PdbInfo.structureId==chain_dict['structureId']). \ filter(PdbInfo.chainId==chain_dict['chainId']). \ first() if existing_chain: """compare and merge objects""" existing_chain = self._update_info(chain_dict, existing_chain) session.merge(existing_chain) else: """create a new chain object""" new_chain = PdbInfo() for k, v in chain_dict.iteritems(): if not v: v = None # to save as NULL in the db setattr(new_chain, k, v) session.add(new_chain) session.commit() logging.info('Custom report saved in the database')
def put(self, rfq_id, section_id): parser = reqparse.RequestParser() parser.add_argument('data') data = request.get_json()['data'] for key in data: component = session.query(ContentComponent).filter_by( document_id=rfq_id).filter_by(name=key).first() component.text = data[key] session.merge(component) session.commit() # this needs to be done client side to allow for jumping between sections if section_id < 10: url = '#/rfp/' + str(rfq_id) + '/question/' + str( int(section_id) + 1) else: url = "#/rfp/" + str(rfq_id) + "/results" return jsonify({"url": url})
def set_value(self, value): setattr(self.target, self.attr, value) self.value = value if isinstance(self.target, Base): self.target = session.merge(self.target) session.commit() self.update_text()
def load_loop_positions(self): """update loop_positions table by loading data from the mat files stored in the PrecomputedData folder""" if not self.mlab: self._setup_matlab() # loop over directories for folder in os.listdir(self.precomputedData): if re.search(self.pdb_regex, folder): logging.info('Importing loop annotations from %s', folder) else: continue [outputFile, err_msg] = self.mlab.loadLoopPositions(os.path.join(self.precomputedData, folder), nout=2) if err_msg != '': MotifAtlasBaseClass._crash(self, err_msg) else: reader = csv.reader(open(outputFile), delimiter=',', quotechar='"') for row in reader: (loop_id, position, nt_id, bulge, flanking, border) = row existing = session.query(LoopPositions). \ filter(LoopPositions.loop_id==loop_id). \ filter(LoopPositions.position==position). \ filter(LoopPositions.border==border). \ first() if existing: if self.update: existing.flanking = int(flanking) existing.bulge = int(bulge) existing.nt_id = nt_id existing.border = int(border) session.merge(existing) else: logging.info('Keeping existing annotations') else: session.add(LoopPositions(loop_id=loop_id, position=position, nt_id=nt_id, flanking=int(flanking), bulge=int(bulge), border=int(border))) session.commit() os.remove(outputFile) # delete temporary csv file
def __import_distances_from_csv(self, ifn): """Reads the csv file, imports all distances, deletes the file when done to avoid stale data and free up disk space""" logging.info('Importing distances') commit_every = 100000 reader = csv.reader(open(ifn, 'rb'), delimiter=',', quotechar='"') for i,row in enumerate(reader): D = Distances(id1=row[0], id2=row[1], distance=row[2]) try: session.add(D) except: logging.warning('Distance value updated') session.merge(D) """Since the files can be huge, it's unfeasible to store all objects in memory, have to commit regularly""" if i % commit_every == 0: session.commit() session.commit() os.remove(ifn) logging.info('Csv file successfully imported')
def load_loop_search_qa_text_file(self, file): """independent method used to load search QA data (disqualification codes from the text files created by matlab during clustering""" reader = csv.reader(open(file, 'r')) for row in reader: existing = session.query(LoopSearchQA). \ filter(LoopSearchQA.loop_id1==row[0]). \ filter(LoopSearchQA.loop_id2==row[1]). \ first() if existing: if self.update: existing.status = int(row[2]) existing.message = row[3] session.merge(existing) else: session.add(LoopSearchQA(loop_id1=row[0], loop_id2=row[1], status=int(row[2]), message=row[3])) session.commit()
def _store_in_database(self, loop_id1, loop_id2, disc=-1, nt_list1=None, nt_list2=None): """ """ existing = session.query(LoopSearch). \ filter(LoopSearch.loop_id1==loop_id1). \ filter(LoopSearch.loop_id2==loop_id2). \ first() if existing: if self.update: existing.disc = float(disc) existing.nt_list1 = nt_list1 existing.nt_list2 = nt_list2 session.merge(existing) else: session.add(LoopSearch(loop_id1=loop_id1, loop_id2=loop_id2, disc=float(disc), nt_list1=nt_list1, nt_list2=nt_list2)) session.commit()
def reply_keywords(msg): # 缓存规则为24小时,此处优先读取缓存 try: # 尝试读取缓存,无论过期与否,只要存在都可读入 cache = session.query(KeyWordsCache).filter_by( FromUserName=msg.fromUserName).one() except NoResultFound: # 缓存读取失败,说明从未建立过缓存记录,新建缓存记录 cache = KeyWordsCache(FromUserName=msg.fromUserName, CreateTime=datetime.datetime.fromtimestamp(0)) session.add(cache) session.commit() # 缓存命中,即缓存时间在24小时之内 if cache.CreateTime >= datetime.datetime.now() - datetime.timedelta( days=1): seg_list = json.loads(cache.Content) # 缓存未命中,即缓存时间在24小时以上 else: # 读取聊天记录,并合并为长文本 logs = session.query(ChatLog).filter_by( FromUserName=msg.fromUserName).all() content = ' '.join((log.Content for log in logs)) # 重新运行基于jieba分词的TF / IDF算法获取分词与权重结果 seg_list = jieba.analyse.extract_tags(content, topK=32, withWeight=True) # 如果列表长度大于或等于8,则视为得到了合理的结果,向用户发送并刷新缓存记录 if len(seg_list) >= 8: cache.Content = json.dumps(seg_list) cache.CreateTime = datetime.datetime.now() session.merge(cache) session.commit() # 如果列表长度小于8,说明用户聊天记录内容过少,得到的结果不是很有意义,置为None else: seg_list = None if seg_list: msg.user.send('\n'.join(("关键词:%s,权重:%s" % seg for seg in seg_list))) else: msg.user.send('还无法为你提取关键词哦,多说两句再试试看吧!')
def create_all(): with open('data_monsters.json', 'r') as f: monsters = json.loads(f.read()) for monster in monsters: if not monster.get('name'): continue session.merge( Monster( str=monster.get('strength', None), con=monster.get('constitution', None), int=monster.get('intelligence', None), wis=monster.get('wisdom', None), cha=monster.get('charisma', None), dex=monster.get('dexterity', None), HP=monster.get('hit_points', None), AC=monster.get('armor_class', None), speed=monster.get('speed', None), name=monster.get('name', None), size=monster.get('size', None), type=monster.get('type', None), subtype=monster.get('subtype', None), alignment=monster.get('alignment', None), hit_dice=monster.get('hit_dice', None), stealth=monster.get('stealth', None), damage_vulnerabilities=monster.get('damage_vulnerabilities', None), damage_resistances=monster.get('damage_resistances', None), damage_immunities=monster.get('damage_immunities', None), condition_immunities=monster.get('condition_immunities', None), senses=monster.get('senses', None), languages=monster.get('languages', None), challenge_rating=monster.get('challenge_rating', None), special_abilities=dict_to_rst( monster.get('special_abilities', [])), actions=dict_to_rst(monster.get('actions', [])), )) session.commit()
def create_all(): with open('data_spells.json', 'r') as f: spells = json.loads(f.read()) for name, spell in spells.items(): if not spell.get('name'): continue session.merge( Spell( name=name, str=spell.get('strength', None), casting_time=spell.get('casting_time', ''), components=spell.get('components', ''), description=spell.get('description', ''), duration=spell.get('duration', ''), level=spell.get('level', None), range=spell.get('range', ''), school=spell.get('school', ''), )) session.commit()
def archive(cls, post): ''' Lazy gelbooru archive function adds a post to our db and moves the image to our dump area. ''' if isinstance(post.get('tags'), list): post['tags'] = ' '.join(post.get('tags')) tags = '|{}|'.format(post.get('tags').replace(' ', '|')) local_path = '' archive_path = '' dd_tags = ddInterface.evaluate('gazer/static/temp/{}'.format( post.get('image'))) dd_tags = ddInterface.union(tags=post.get('tags'), dd_tags=dd_tags) new_post = Posts(filename=post.get('image'), id=post.get('id'), booru=cls.source, source=post.get('source'), score=post.get('score'), tags=tags, dd_tags=dd_tags, rating=post.get('rating'), status=post.get('status'), created_at=post.get('created_at'), creator_id=post.get('creator_id')) session.merge(new_post) session.commit() local_path = 'gazer/static/temp/{}'.format(post.get('image')) archive_path = 'gazer/static/dump/{}/{}/{}'.format( post.get('image')[:2], post.get('image')[2:4], post.get('image')) os.makedirs(os.path.dirname(archive_path), exist_ok=True) shutil.copyfile(local_path, archive_path) return new_post.id
def __inherit_history(self, motif, parents): """ """ parent_motifs = parents.split(',') common_name = [] annotation = [] author = [] for parent in parent_motifs: parent_motif = session.query(MotifAnnotation).\ filter(MotifAnnotation.motif_id==parent).\ first() if parent_motif: if parent_motif.common_name is not None and parent_motif.common_name != '': common_name.append(parent_motif.common_name) if parent_motif.annotation is not None and parent_motif.annotation != '': annotation.append(parent_motif.annotation) if parent_motif.author is not None and parent_motif.author != '': author.append(parent_motif.author) session.merge(MotifAnnotation(motif_id = motif.id, common_name = ' | '.join(set(common_name)), annotation = ' | '.join(set(annotation)), author = ' | '.join(set(author))))
def __import_coordinates_from_csv(self, ifn): """ """ logging.info('Importing coordinates') reader = csv.reader(open(ifn, 'rb'), delimiter=',', quotechar='"') for row in reader: C = Coordinates(id = row[0], pdb = row[1], pdb_type = row[2], model = row[3], chain = row[4], number = row[5], unit = row[6], ins_code = row[7], index = row[8], coordinates = row[9]) try: session.add(C) except: logging.warning('Merging for %s', C.id) session.merge(C) session.commit() os.remove(ifn) logging.info('Csv file successfully imported')
def update(self, spinner, class_): self.character.class_ = class_ self.character = session.merge(self.character) session.commit()
def update(self, spinner, race): self.character.race = race self.character = session.merge(self.character) session.commit()
def update(self, spinner, monster_type): self.monster.monster_type = monster_type self.monster = session.merge(self.monster) session.commit()
def remove_spell(btn): session.delete(session.merge(self.inventory_entry)) spells_tab.set_spells()
def add_spell(btn): session.merge( SpellbookEntry(character=character.name, spell=btn.text)) session.commit() self.dismiss() spell_tab.set_spells()
def edit_content(value): self.character.backstory = value self.character = session.merge(character) session.commit() rendered.text = value
def add_character(self, character): character = session.merge(character) session.commit() self.character_sheets.add_widget(CharacterSheet(character))
def create_all(): session.merge(Item(name="Armor - Padded", category="Light Armor", cost="5 gp", ac="11 + Dex modifier -- Disadvantage", strength=None, stealth=None, weight="8 lb.")) session.merge(Item(name="Armor - Leather", category="Light Armor", cost="10 gp", ac="11 + Dex modifier", strength=None, stealth=None, weight="10 lb.")) session.merge(Item(name="Armor - Studded leather", category="Light Armor", cost="45 gp", ac="12 + Dex modifier", strength=None, stealth=None, weight="13 lb.")) session.merge(Item(name="Armor - Hide", category="Medium Armor", cost="10 gp", ac="12 + Dex modifier (max 2)", strength=None, stealth=None, weight="12 lb.")) session.merge(Item(name="Armor - Chain shirt", category="Medium Armor", cost="50 gp", ac="13 + Dex modifier (max 2)", strength=None, stealth=None, weight="20 lb.")) session.merge(Item(name="Armor - Scale mail", category="Medium Armor", cost="50 gp", ac="14 + Dex modifier (max 2)", strength=None, stealth="Disadvantage", weight="45 lb.")) session.merge(Item(name="Armor - Breastplate", category="Medium Armor", cost="400 gp", ac="14 + Dex modifier (max 2)", strength=None, stealth=None, weight="20 lb.")) session.merge(Item(name="Armor - Half plate", category="Heavy Armor", cost="750 gp", ac="15 + Dex modifier (max 2)", strength=None, stealth="Disadvantage", weight="40 lb.")) session.merge(Item(name="Armor - Ring mail", category="Heavy Armor", cost="30 gp", ac="14", strength=None, stealth="Disadvantage", weight="40 lb.")) session.merge(Item(name="Armor - Chain mail", category="Heavy Armor", cost="75 gp", ac="16", strength="Str 13", stealth="Disadvantage", weight="55 lb.")) session.merge(Item(name="Armor - Splint", category="Heavy Armor", cost="200 gp", ac="17", strength="Str 15", stealth="Disadvantage", weight="60 lb.")) session.merge(Item(name="Armor - Plate", category="Heavy Armor", cost="1,500 gp", ac="18", strength="Str 15", stealth="Disadvantage", weight="65 lb.")) session.merge(Item(name="Armor - Shield", category="Heavy Armor", cost="10 gp", ac="+2", strength=None, stealth=None, weight="6 lb.")) session.merge(Item(name='Abacus', category='Gear', cost='2 gp', weight='2 lb.')) session.merge(Item(name='Acid (vial)', category='Gear', cost='25 gp', weight='1 lb.')) session.merge(Item(name="Alchemist's fire (flask)", category="Gear", cost="50 gp", weight="1 lb.")) session.merge(Item(name='Arrows (20)', category='Ammunition', cost='1 gp', weight='1 lb.')) session.merge(Item(name='Blowgun needles (5)', category='Ammunition', cost='1 gp', weight='1 lb.')) session.merge(Item(name='Crossbow bolts (20)', category='Ammunition', cost='1 gp', weight='1½ lb.')) session.merge(Item(name='Sling bullets (20)', category='Ammunition', cost='4 cp', weight='1½ lb.')) session.merge(Item(name='Antitoxin (vial)', category='Ammunition', cost='50 gp', weight='---')) session.merge(Item(name='Crystal', category='Arcane Focus', cost='10 gp', weight='1 lb.')) session.merge(Item(name='Orb', category='Arcane Focus', cost='20 gp', weight='3 lb.')) session.merge(Item(name='Rod', category='Arcane Focus', cost='10 gp', weight='2 lb.')) session.merge(Item(name='Staff', category='Arcane Focus', cost='5 gp', weight='4 lb.')) session.merge(Item(name='Wand', category='Arcane Focus', cost='10 gp', weight='1 lb.')) session.merge(Item(name='Backpack', category='Gear', cost='2 gp', weight='5 lb.')) session.merge(Item(name='Ball bearings (bag of 1,000)', category='Gear', cost='1 gp', weight='2 lb.')) session.merge(Item(name='Barrel', category='Gear', cost='2 gp', weight='70 lb.')) session.merge(Item(name='Basket', category='Gear', cost='4 sp', weight='2 lb.')) session.merge(Item(name='Bedroll', category='Gear', cost='1 gp', weight='7 lb.')) session.merge(Item(name='Bell', category='Gear', cost='1 gp', weight='---')) session.merge(Item(name='Blanket', category='Gear', cost='5 sp', weight='3 lb.')) session.merge(Item(name='Block and tackle', category='Gear', cost='1 gp', weight='5 lb.')) session.merge(Item(name='Book', category='Gear', cost='25 gp', weight='5 lb.')) session.merge(Item(name='Bottle, glass', category='Gear', cost='2 gp', weight='2 lb.')) session.merge(Item(name='Bucket', category='Gear', cost='5 cp', weight='2 lb.')) session.merge(Item(name='Caltrops (bag of 20)', category='Gear', cost='1 gp', weight='2 lb.')) session.merge(Item(name='Candle', category='Gear', cost='1 cp', weight='---')) session.merge(Item(name='Case, crossbow bolt', category='Gear', cost='1 gp', weight='1 lb.')) session.merge(Item(name='Case, map or scroll', category='Gear', cost='1 gp', weight='1 lb.')) session.merge(Item(name='Chain (10 feet)', category='Gear', cost='5 gp', weight='10 lb.')) session.merge(Item(name='Chalk (1 piece)', category='Gear', cost='1 cp', weight='---')) session.merge(Item(name='Chest', category='Gear', cost='5 gp', weight='25 lb.')) session.merge(Item(name='Climbers kit', category='Gear', cost='25 gp', weight='12 lb.')) session.merge(Item(name='Clothes, common', category='Gear', cost='5 sp', weight='3 lb.')) session.merge(Item(name='Clothes, costume', category='Gear', cost='5 gp', weight='4 lb.')) session.merge(Item(name='Clothes, fine', category='Gear', cost='15 gp', weight='6 lb.')) session.merge(Item(name='Clothes, traveler', category='Gear', cost='2 gp', weight='4 lb.')) session.merge(Item(name='Component pouch', category='Gear', cost='25 gp', weight='2 lb.')) session.merge(Item(name='Crowbar', category='Gear', cost='2 gp', weight='5 lb.')) session.merge(Item(name='Sprig of mistletoe', category='Druidic Focus', cost='1 gp', weight='---')) session.merge(Item(name='Totem', category='Druidic Focus', cost='1 gp', weight='---')) session.merge(Item(name='Wooden staff', category='Druidic Focus', cost='5 gp', weight='4 lb.')) session.merge(Item(name='Yew wand', category='Druidic Focus', cost='10 gp', weight='1 lb.')) session.merge(Item(name='Fishing tackle', category='Gear', cost='1 gp', weight='4 lb.')) session.merge(Item(name='Flask or tankard', category='Gear', cost='2 cp', weight='1 lb.')) session.merge(Item(name='Grappling hook', category='Gear', cost='2 gp', weight='4 lb.')) session.merge(Item(name='Hammer', category='Gear', cost='1 gp', weight='3 lb.')) session.merge(Item(name='Hammer, sledge', category='Gear', cost='2 gp', weight='10 lb.')) session.merge(Item(name='Healers kit', category='Gear', cost='5 gp', weight='3 lb.')) session.merge(Item(name='Amulet', category='Holy Symbol', cost='5 gp', weight='1 lb.')) session.merge(Item(name='Emblem', category='Holy Symbol', cost='5 gp', weight='---')) session.merge(Item(name='Reliquary', category='Holy Symbol', cost='5 gp', weight='2 lb.')) session.merge(Item(name='Holy water (flask)', category='Gear', cost='25 gp', weight='1 lb.')) session.merge(Item(name='Hourglass', category='Gear', cost='25 gp', weight='1 lb.')) session.merge(Item(name='Hunting trap', category='Gear', cost='5 gp', weight='25 lb.')) session.merge(Item(name='Ink (1 ounce bottle)', category='Gear', cost='10 gp', weight='---')) session.merge(Item(name='Ink pen', category='Gear', cost='2 cp', weight='---')) session.merge(Item(name='Jug or pitcher', category='Gear', cost='2 cp', weight='4 lb.')) session.merge(Item(name='Ladder (10-foot)', category='Gear', cost='1 sp', weight='25 lb.')) session.merge(Item(name='Lamp', category='Gear', cost='5 sp', weight='1 lb.')) session.merge(Item(name='Lantern, bullseye', category='Gear', cost='10 gp', weight='2 lb.')) session.merge(Item(name='Lantern, hooded', category='Gear', cost='5 gp', weight='2 lb.')) session.merge(Item(name='Lock', category='Gear', cost='10 gp', weight='1 lb.')) session.merge(Item(name='Magnifying glass', category='Gear', cost='100 gp', weight='---')) session.merge(Item(name='Manacles', category='Gear', cost='2 gp', weight='6 lb.')) session.merge(Item(name='Mess kit', category='Gear', cost='2 sp', weight='1 lb.')) session.merge(Item(name='Mirror, steel', category='Gear', cost='5 gp', weight='1/2 lb.')) session.merge(Item(name='Oil (flask)', category='Gear', cost='1 sp', weight='1 lb.')) session.merge(Item(name='Paper (one sheet)', category='Gear', cost='2 sp', weight='---')) session.merge(Item(name='Parchment (one sheet)', category='Gear', cost='1 sp', weight='---')) session.merge(Item(name='Perfume (vial)', category='Gear', cost='5 gp', weight='---')) session.merge(Item(name='Pick, miners', category='Gear', cost='2 gp', weight='10 lb.')) session.merge(Item(name='Piton', category='Gear', cost='5 cp', weight='1/4 lb.')) session.merge(Item(name='Poison, basic (vial)', category='Gear', cost='100 gp', weight='---')) session.merge(Item(name='Pole (10-foot)', category='Gear', cost='5 cp', weight='7 lb.')) session.merge(Item(name='Pot, iron', category='Gear', cost='2 gp', weight='10 lb.')) session.merge(Item(name='Potion of healing', category='Gear', cost='50 gp', weight='1/2 lb.')) session.merge(Item(name='Pouch', category='Gear', cost='5 sp', weight='1 lb.')) session.merge(Item(name='Quiver', category='Gear', cost='1 gp', weight='1 lb.')) session.merge(Item(name='Ram, portable', category='Gear', cost='4 gp', weight='35 lb.')) session.merge(Item(name='Rations (1 day)', category='Gear', cost='5 sp', weight='2 lb.')) session.merge(Item(name='Robes', category='Gear', cost='1 gp', weight='4 lb.')) session.merge(Item(name='Rope, hempen (50 feet)', category='Gear', cost='1 gp', weight='10 lb.')) session.merge(Item(name='Rope, silk (50 feet)', category='Gear', cost='10 gp', weight='5 lb.')) session.merge(Item(name='Sack', category='Gear', cost='1 cp', weight='1/2 lb.')) session.merge(Item(name='Scale, merchants', category='Gear', cost='5 gp', weight='3 lb.')) session.merge(Item(name='Sealing wax', category='Gear', cost='5 sp', weight='---')) session.merge(Item(name='Shovel', category='Gear', cost='2 gp', weight='5 lb.')) session.merge(Item(name='Signal whistle', category='Gear', cost='5 cp', weight='---')) session.merge(Item(name='Signet ring', category='Gear', cost='5 gp', weight='---')) session.merge(Item(name='Soap', category='Gear', cost='2 cp', weight='---')) session.merge(Item(name='Spellbook', category='Gear', cost='50 gp', weight='3 lb.')) session.merge(Item(name='Spikes, iron (10)', category='Gear', cost='1 gp', weight='5 lb.')) session.merge(Item(name='Spyglass', category='Gear', cost='1,000 gp', weight='1 lb.')) session.merge(Item(name='Tent, two-person', category='Gear', cost='2 gp', weight='20 lb.')) session.merge(Item(name='Tinderbox', category='Gear', cost='5 sp', weight='1 lb.')) session.merge(Item(name='Torch', category='Gear', cost='1 cp', weight='1 lb.')) session.merge(Item(name='Vial', category='Gear', cost='1 gp', weight='---')) session.merge(Item(name='Waterskin', category='Gear', cost='2 sp', weight='5 lb. (full)')) session.merge(Item(name='Whetstone', category='Gear', cost='1 cp', weight='1 lb.')) session.merge(Item(name="Club", cost="1 sp", category='weapon', damage="1d4 bludgeoning", weight="2 lb.", properties="Light")) session.merge(Item(name="Dagger", cost="2 gp", category='weapon', damage="1d4 piercing", weight="1 lb.", properties="Finesse, light, thrown (range 20/60)")) session.merge(Item(name="Greatclub", cost="2 sp", category='weapon', damage="1d8 bludgeoning", weight="10 lb.", properties="Two-handed")) session.merge(Item(name="Handaxe", cost="5 gp", category='weapon', damage="1d6 slashing", weight="2 lb.", properties="Light, thrown (range 20/60)")) session.merge(Item(name="Light hammer", cost="2 gp", category='weapon', damage="1d4 bludgeoning", weight="2 lb.", properties="Light, thrown (range 20/60)")) session.merge(Item(name="Mace", cost="5 gp", category='weapon', damage="1d6 bludgeoning", weight="4 lb.", properties=None)) session.merge(Item(name="Sickle", cost="1 gp", category='weapon', damage="1d4 slashing", weight="2 lb.", properties="Light")) session.merge(Item(name="Spear", cost="1 gp", category='weapon', damage="1d6 piercing", weight="3 lb.", properties="Thrown (range 20/60), versatile (1d8)")) session.merge(Item(name="Crossbow, light", cost="25 gp", category='weapon', damage="1d8 piercing", weight="5 lb.", properties="Ammunition (range 80/320), loading, two-handed")) session.merge(Item(name="Dart", cost="5 cp", category='weapon', damage="1d4 piercing", weight="1/4 lb.", properties="Finesse, thrown (range 20/60)")) session.merge(Item(name="Sling", cost="1 sp", category='weapon', damage="1d4 bludgeoning", weight=None, properties="Martial Melee, Ammunition (range 30/120)")) session.merge(Item(name="Battleaxe", cost="10 gp", category='weapon', damage="1d8 slashing", weight="4 lb.", properties="Versatile (1d10)")) session.merge(Item(name="Flail", cost="10 gp", category='weapon', damage="1d8 bludgeoning", weight="2 lb.", properties=None)) session.merge(Item(name="Greataxe", cost="30 gp", category='weapon', damage="1d12 slashing", weight="7 lb.", properties="Heavy, two-handed")) session.merge(Item(name="Greatsword", cost="50 gp", category='weapon', damage="2d6 slashing", weight="6 lb.", properties="Heavy, two-handed")) session.merge(Item(name="Lance", cost="10 gp", category='weapon', damage="1d12 piercing", weight="6 lb.", properties="Reach, special")) session.merge(Item(name="Longsword", cost="15 gp", category='weapon', damage="1d8 slashing", weight="3 lb.", properties="Versatile (1d10)")) session.merge(Item(name="Morningstar", cost="15 gp", category='weapon', damage="1d8 piercing", weight="4 lb.", properties=None)) session.merge(Item(name="Pike", cost="5 gp", category='weapon', damage="1d10 piercing", weight="18 lb.", properties="Heavy, reach, two-handed")) session.merge(Item(name="Scimitar", cost="25 gp", category='weapon', damage="1d6 slashing", weight="3 lb.", properties="Finesse, light")) session.merge(Item(name="Shortsword", cost="10 gp", category='weapon', damage="1d6 piercing", weight="2 lb.", properties="Finesse, light")) session.merge(Item(name="War pick", cost="5 gp", category='weapon', damage="1d8 piercing", weight="2 lb.", properties=None)) session.merge(Item(name="Warhammer", cost="15 gp", category='weapon', damage="1d8 bludgeoning", weight="2 lb.", properties="Versatile (1d10)")) session.merge(Item(name="Whip", cost="2 gp", category='weapon', damage="1d4 slashing", weight="3 lb.", properties="Finesse, reach")) session.merge(Item(name="Blowgun", cost="10 gp", category='weapon', damage="1 piercing", weight="1 lb.", properties="Ammunition (range 25/100), loading")) session.merge(Item(name="Crossbow, hand", cost="75 gp", category='weapon', damage="1d6 piercing", weight="3 lb.", properties="Ammunition (range 30/120), light, loading")) session.merge(Item(name="Longbow", cost="50 gp", category='weapon', damage="1d8 piercing", weight="2 lb.", properties="Ammunition (range 150/600), heavy, two-handed")) session.merge(Item(name="Shortbow", cost="50 gp", category='weapon', damage="1d8 piercing", weight="2 lb.", properties="Ammunition (range 150/600), heavy, two-handed")) session.merge(Item(name="Net", cost="1 gp", category='weapon', damage=None, weight="3 lb.", properties="Special, thrown (range 5/15)")) session.merge(Item(name="Quarterstaff", cost="1 sp", category='weapon', damage='1d4 bludgeoning', weight="4 lb.", properties="Versatile (1d8)")) session.merge(Item(name="Javelin", cost="5 sp", category='weapon', damage='1d6 piercing', weight="2 lb.", properties="Light, thrown (20/60)")) session.commit()
def remove_item(btn): session.delete(session.merge(self.inventory_entry)) items_tab.set_items()
def create_all(): dwarf = Race(name='Dwarf', details=""" Dwarf ~~~~~ Dwarf Traits ^^^^^^^^^^^^ Your dwarf character has an assortment of inborn abilities, part and parcel of dwarven nature. **Ability Score Increase.** Your Constitution score increases by 2. **Age.** Dwarves mature at the same rate as humans, but they're considered young until they reach the age of 50. On average, they live about 350 years. **Alignment.** Most dwarves are lawful, believing firmly in the benefits of a well-ordered society. They tend toward good as well, with a strong sense of fair play and a belief that everyone deserves to share in the benefits of a just order. **Size.** Dwarves stand between 4 and 5 feet tall and average about 150 pounds. Your size is Medium. **Speed.** Your base walking speed is 25 feet. Your speed is not reduced by wearing heavy armor. **Darkvision.** Accustomed to life underground, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Dwarven Resilience.** You have advantage on saving throws against poison, and you have resistance against poison damage. **Dwarven Combat Training.** You have proficiency with the battleaxe, handaxe, light hammer, and warhammer. **Tool Proficiency.** You gain proficiency with the artisan's tools of your choice: smith's tools, brewer's supplies, or mason's tools. **Stonecunning.** Whenever you make an Intelligence (History) check related to the origin of stonework, you are considered proficient in the History skill and add double your proficiency bonus to the check, instead of your normal proficiency bonus. **Languages.** You can speak, read, and write Common and Dwarvish. Dwarvish is full of hard consonants and guttural sounds, and those characteristics spill over into whatever other language a dwarf might speak. Hill Dwarf ^^^^^^^^^^ As a hill dwarf, you have keen senses, deep intuition, and remarkable resilience. **Ability Score Increase.** Your Wisdom score increases by 1. **Dwarven Toughness.** Your hit point maximum increases by 1, and it increases by 1 every time you gain a level. """) elf = Race(name='Elf', details=""" Elf ~~~ Elf Traits ^^^^^^^^^^ Your elf character has a variety of natural abilities, the result of thousands of years of elven refinement. **Ability Score Increase.** Your Dexterity score increases by 2. **Age.** Although elves reach physical maturity at about the same age as humans, the elven understanding of adulthood goes beyond physical growth to encompass worldly experience. An elf typically claims adulthood and an adult name around the age of 100 and can live to be 750 years old. **Alignment.** Elves love freedom, variety, and self-expression, so they lean strongly toward the gentler aspects of chaos. They value and protect others' freedom as well as their own, and they are more often good than not. **Size.** Elves range from under 5 to over 6 feet tall and have slender builds. Your size is Medium. **Speed.** Your base walking speed is 30 feet. **Darkvision.** Accustomed to twilit forests and the night sky, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Keen Senses.** You have proficiency in the Perception skill. **Fey Ancestry.** You have advantage on saving throws against being :ref:`srd:charmed`, and magic can't put you to sleep. **Trance.** Elves don't need to sleep. Instead, they meditate deeply, remaining semiconscious, for 4 hours a day. (The Common word for such meditation is "trance.") While meditating, you can dream after a fashion; such dreams are actually mental exercises that have become reflexive through years of practice. After resting in this way, you gain the same benefit that a human does from 8 hours of sleep. **Languages.** You can speak, read, and write Common and Elvish. Elvish is fluid, with subtle intonations and intricate grammar. Elven literature is rich and varied, and their songs and poems are famous among other races. Many bards learn their language so they can add Elvish ballads to their repertoires. High Elf ^^^^^^^^ As a high elf, you have a keen mind and a mastery of at least the basics of magic. In many fantasy gaming worlds, there are two kinds of high elves. One type is haughty and reclusive, believing themselves to be superior to non-elves and even other elves. The other type is more common and more friendly, and often encountered among humans and other races. **Ability Score Increase.** Your Intelligence score increases by 1. **Elf Weapon Training.** You have proficiency with the longsword, shortsword, shortbow, and longbow. **Cantrip.** You know one cantrip of your choice from the wizard spell list. Intelligence is your spellcasting ability for it. **Extra Language.** You can speak, read, and write one extra language of your choice. """) halfling = Race(name='Halfling', details=""" Halfling ~~~~~~~~ Halfling Traits ^^^^^^^^^^^^^^^ Your halfling character has a number of traits in common with all other halflings. **Ability Score Increase.** Your Dexterity score increases by 2. **Age.** A halfling reaches adulthood at the age of 20 and generally lives into the middle of his or her second century. **Alignment.** Most halflings are lawful good. As a rule, they are good-hearted and kind, hate to see others in pain, and have no tolerance for oppression. They are also very orderly and traditional, leaning heavily on the support of their community and the comfort of their old ways. **Size.** Halflings average about 3 feet tall and weigh about 40 pounds. Your size is Small. **Speed.** Your base walking speed is 25 feet. **Lucky.** When you roll a 1 on the d20 for an attack roll, ability check, or saving throw, you can reroll the die and must use the new roll. **Brave.** You have advantage on saving throws against being :ref:`srd:frightened`. **Halfling Nimbleness.** You can move through the space of any creature that is of a size larger than yours. **Languages.** You can speak, read, and write Common and Halfling. The Halfling language isn't secret, but halflings are loath to share it with others. They write very little, so they don't have a rich body of literature. Their oral tradition, however, is very strong. Almost all halflings speak Common to converse with the people in whose lands they dwell or through which they are traveling. Lightfoot ^^^^^^^^^ As a lightfoot halfling, you can easily hide from notice, even using other people as cover. You're inclined to be affable and get along well with others. Lightfoots are more prone to wanderlust than other halflings, and often dwell alongside other races or take up a nomadic life. **Ability Score Increase.** Your Charisma score increases by 1. **Naturally Stealthy.** You can attempt to hide even when you are obscured only by a creature that is at least one size larger than you. """) human = Race(name='Human', details=""" Human ~~~~~ Human Traits ^^^^^^^^^^^^ It's hard to make generalizations about humans, but your human character has these traits. **Ability Score Increase.** Your ability scores each increase by 1. **Age.** Humans reach adulthood in their late teens and live less than a century. **Alignment.** Humans tend toward no particular alignment. The best and the worst are found among them. **Size.** Humans vary widely in height and build, from barely 5 feet to well over 6 feet tall. Regardless of your position in that range, your size is Medium. **Speed.** Your base walking speed is 30 feet. **Languages.** You can speak, read, and write Common and one extra language of your choice. Humans typically learn the languages of other peoples they deal with, including obscure dialects. They are fond of sprinkling their speech with words borrowed from other tongues: Orc curses, Elvish musical expressions, Dwarvish military phrases, and so on. """) dragonborn = Race(name='Dragonborn', details=""" Dragonborn ~~~~~~~~~~ Dragonborn Traits ^^^^^^^^^^^^^^^^^ Your draconic heritage manifests in a variety of traits you share with other dragonborn. **Ability Score Increase.** Your Strength score increases by 2, and your Charisma score increases by 1. **Age.** Young dragonborn grow quickly. They walk hours after hatching, attain the size and development of a 10-year-old human child by the age of 3, and reach adulthood by 15. They live to be around 80. **Alignment.** Dragonborn tend to extremes, making a conscious choice for one side or the other in the cosmic war between good and evil. Most dragonborn are good, but those who side with evil can be terrible villains. **Size.** Dragonborn are taller and heavier than humans, standing well over 6 feet tall and averaging almost 250 pounds. Your size is Medium. **Speed.** Your base walking speed is 30 feet. Draconic Ancestry ^^^^^^^^^^^^^^^^^ +-----------------+----------------------+--------------------------------+ | Dragon | Damage Type | Breath Weapon | +=================+======================+================================+ | Black | Acid | 5 by 30 ft. line (Dex. save) | +-----------------+----------------------+--------------------------------+ | Blue | Lightning | 5 by 30 ft. line (Dex. save) | +-----------------+----------------------+--------------------------------+ | Brass | Fire | 5 by 30 ft. line (Dex. save) | +-----------------+----------------------+--------------------------------+ | Bronze | Lightning | 5 by 30 ft. line (Dex. save) | +-----------------+----------------------+--------------------------------+ | Copper | Acid | 5 by 30 ft. line (Dex. save) | +-----------------+----------------------+--------------------------------+ | Gold | Fire | 15 ft. cone (Dex. save) | +-----------------+----------------------+--------------------------------+ | Green | Poison | 15 ft. cone (Con. save) | +-----------------+----------------------+--------------------------------+ | Red | Fire | 15 ft. cone (Dex. save) | +-----------------+----------------------+--------------------------------+ | Silver | Cold | 15 ft. cone (Con. save) | +-----------------+----------------------+--------------------------------+ | White | Cold | 15 ft. cone (Con. save) | +-----------------+----------------------+--------------------------------+ **Draconic Ancestry.** You have draconic ancestry. Choose one type of dragon from the Draconic Ancestry table. Your breath weapon and damage resistance are determined by the dragon type, as shown in the table. **Breath Weapon.** You can use your action to exhale destructive energy. Your draconic ancestry determines the size, shape, and damage type of the exhalation. When you use your breath weapon, each creature in the area of the exhalation must make a saving throw, the type of which is determined by your draconic ancestry. The DC for this saving throw equals 8 + your Constitution modifier + your proficiency bonus. A creature takes 2d6 damage on a failed save, and half as much damage on a successful one. The damage increases to 3d6 at 6th level, 4d6 at 11th level, and 5d6 at 16th level. After you use your breath weapon, you can't use it again until you complete a short or long rest. **Damage Resistance.** You have resistance to the damage type associated with your draconic ancestry. **Languages.** You can speak, read, and write Common and Draconic. Draconic is thought to be one of the oldest languages and is often used in the study of magic. The language sounds harsh to most other creatures and includes numerous hard consonants and sibilants. """) gnome = Race(name='Gnome', details=""" Gnome ~~~~~ Gnome Traits ^^^^^^^^^^^^ Your gnome character has certain characteristics in common with all other gnomes. **Ability Score Increase.** Your Intelligence score increases by 2. **Age.** Gnomes mature at the same rate humans do, and most are expected to settle down into an adult life by around age 40. They can live 350 to almost 500 years. **Alignment.** Gnomes are most often good. Those who tend toward law are sages, engineers, researchers, scholars, investigators, or inventors. Those who tend toward chaos are minstrels, tricksters, wanderers, or fanciful jewelers. Gnomes are good-hearted, and even the tricksters among them are more playful than vicious. **Size.** Gnomes are between 3 and 4 feet tall and average about 40 pounds. Your size is Small. **Speed.** Your base walking speed is 25 feet. **Darkvision.** Accustomed to life underground, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Gnome Cunning.** You have advantage on all Intelligence, Wisdom, and Charisma saving throws against magic. **Languages.** You can speak, read, and write Common and Gnomish. The Gnomish language, which uses the Dwarvish script, is renowned for its technical treatises and its catalogs of knowledge about the natural world. Rock Gnome ^^^^^^^^^^ As a rock gnome, you have a natural inventiveness and hardiness beyond that of other gnomes. **Ability Score Increase.** Your Constitution score increases by 1. **Artificer's Lore.** Whenever you make an Intelligence (History) check related to magic items, alchemical objects, or technological devices, you can add twice your proficiency bonus, instead of any proficiency bonus you normally apply. **Tinker.** You have proficiency with artisan's tools (tinker's tools). Using those tools, you can spend 1 hour and 10 gp worth of materials to construct a Tiny clockwork device (AC 5, 1 hp). The device ceases to function after 24 hours (unless you spend 1 hour repairing it to keep the device functioning), or when you use your action to dismantle it; at that time, you can reclaim the materials used to create it. You can have up to three such devices active at a time. When you create a device, choose one of the following options: - **Clockwork Toy.** This toy is a clockwork animal, monster, or person, such as a frog, mouse, bird, dragon, or soldier. When placed on the ground, the toy moves 5 feet across the ground on each of your turns in a random direction. It makes noises as appropriate to the creature it represents. - **Fire Starter.** The device produces a miniature flame, which you can use to light a candle, torch, or campfire. Using the device requires your action. - **Music Box.** When opened, this music box plays a single song at a moderate volume. The box stops playing when it reaches the song's end or when it is closed. """) half_elf = Race(name='Half-Elf', details=""" Half-Elf ~~~~~~~~ Half-Elf Traits ^^^^^^^^^^^^^^^ Your half-elf character has some qualities in common with elves and some that are unique to half-elves. **Ability Score Increase.** Your Charisma score increases by 2, and two other ability scores of your choice increase by 1. **Age.** Half-elves mature at the same rate humans do and reach adulthood around the age of 20. They live much longer than humans, however, often exceeding 180 years. **Alignment.** Half-elves share the chaotic bent of their elven heritage. They value both personal freedom and creative expression, demonstrating neither love of leaders nor desire for followers. They chafe at rules, resent others' demands, and sometimes prove unreliable, or at least unpredictable. **Size.** Half-elves are about the same size as humans, ranging from 5 to 6 feet tall. Your size is Medium. **Speed.** Your base walking speed is 30 feet. **Darkvision.** Thanks to your elf blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Fey Ancestry.** You have advantage on saving throws against being :ref:`srd:charmed`, and magic can't put you to sleep. **Skill Versatility.** You gain proficiency in two skills of your choice. **Languages.** You can speak, read, and write Common, Elvish, and one extra language of your choice. """) half_orc = Race(name='Half-Orc', details=""" Half-Orc ~~~~~~~~ Half-Orc Traits ^^^^^^^^^^^^^^^ Your half-orc character has certain traits deriving from your orc ancestry. **Ability Score Increase.** Your Strength score increases by 2, and your Constitution score increases by 1. **Age.** Half-orcs mature a little faster than humans, reaching adulthood around age 14. They age noticeably faster and rarely live longer than 75 years. **Alignment.** Half-orcs inherit a tendency toward chaos from their orc parents and are not strongly inclined toward good. Half-orcs raised among orcs and willing to live out their lives among them are usually evil. **Size.** Half-orcs are somewhat larger and bulkier than humans, and they range from 5 to well over 6 feet tall. Your size is Medium. **Speed.** Your base walking speed is 30 feet. **Darkvision.** Thanks to your orc blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Menacing.** You gain proficiency in the Intimidation skill. **Relentless Endurance.** When you are reduced to 0 hit points but not killed outright, you can drop to 1 hit point instead. You can't use this feature again until you finish a long rest. **Savage Attacks.** When you score a critical hit with a melee weapon attack, you can roll one of the weapon's damage dice one additional time and add it to the extra damage of the critical hit. **Languages.** You can speak, read, and write Common and Orc. Orc is a harsh, grating language with hard consonants. It has no script of its own but is written in the Dwarvish script. """) tiefling = Race(name='Tiefling', details=""" Tiefling ~~~~~~~~ Tiefling Traits ^^^^^^^^^^^^^^^ Tieflings share certain racial traits as a result of their infernal descent. **Ability Score Increase.** Your Intelligence score increases by 1, and your Charisma score increases by 2. **Age.** Tieflings mature at the same rate as humans but live a few years longer. **Alignment.** Tieflings might not have an innate tendency toward evil, but many of them end up there. Evil or not, an independent nature inclines many tieflings toward a chaotic alignment. **Size.** Tieflings are about the same size and build as humans. Your size is Medium. **Speed.** Your base walking speed is 30 feet. **Darkvision.** Thanks to your infernal heritage, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray. **Hellish Resistance.** You have resistance to fire damage. **Infernal Legacy.** You know the :ref:`srd:thaumaturgy` cantrip. When you reach 3rd level, you can cast the *hellish rebuke* spell as a 2nd-level spell once with this trait and regain the ability to do so when you finish a long rest. When you reach 5th level, you can cast the :ref:`srd:darkness` spell once with this trait and regain the ability to do so when you finish a long rest. Charisma is your spellcasting ability for these spells. **Languages.** You can speak, read, and write Common and Infernal. """) session.merge(dwarf) session.merge(elf) session.merge(halfling) session.merge(human) session.merge(dragonborn) session.merge(gnome) session.merge(half_elf) session.merge(half_orc) session.commit()
def add_monster(self, monster): monster = session.merge(monster) session.commit() self.monster_sheets.add_widget(MonsterSheet(monster))
def add_item(btn): session.merge(InventoryItem(character=character.name, item=btn.text)) session.commit() self.dismiss() item_tab.set_items()
def edit_content(value): self.encounter.description = value self.encounter = session.merge(self.encounter) session.commit() rendered.text = value