You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

539 lines
20 KiB
Python

import datetime, os, re
from bottle import error, response, route, run, static_file, template, TEMPLATE_PATH
# Remove links, line breaks from snippet
def clean(result):
result = result.replace('\n','')
result = result.replace('<br>','')
result = re.sub(r'<a href=.*?>', '', result)
result = re.sub(r'<img src=.*?>', '', result)
result = re.sub(r'<a target="_blank" href=.*?>', '', result)
result = result.replace('</a>','')
result = re.sub(r'<h\d>','',result)
result = re.sub(r'</h\d>','',result)
result = result.replace('<center>','')
result = result.replace('</center>','')
result = result.replace('<b>','')
result = result.replace('</b>','')
return result
def clean_tags(raw):
cleanr = re.compile('<.*?>')
cleantext = re.sub(cleanr, '', raw)
return cleantext
def find_year():
now = datetime.datetime.now()
return now.strftime('%Y')
def find_gallery(name):
2 years ago
gal = [name]
if name == "Abbey":
2 years ago
gal.append("FlightRising")
gal.append([["dragon.png","Abbey laying on his back, wrapped in furs"],["BlackReshiram_Artfight.png","2022 Artfight attack by BlackReshiram"], ["FlightRising.png","Abbey's sprite from FlightRising, wearing Fig Plumes, a Squire's Beret, Chestnut Feathers, and Amber Flourishes"]])
if name == "Aloin":
2 years ago
gal.append("Sims")
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "Angel":
2 years ago
gal.append("Blessfrey")
gal.append([["Chibipixel.png","One of Angel's sprites. I prefer taller, less cartoony sprites, but this style is so popular it was worth trying."],["girls.png","Angel, Chloe, and Tessa"],["AngelHeadshot.png","Headshot of Angel and all her hair"]])
2 years ago
if name == "Aries":
gal.append("Blessfrey")
gal.append([["Aries.png","pencil headshot"]])
if name == "Aristen":
2 years ago
gal.append("Black Desert Online")
2 years ago
gal.append([["menu.jpg","Aristen's fancy set with bright colors and an obi belt"], ["scarf.jpg", "Newbie Aristen with her orange scarf"], ["marine.jpg","Aristen in the Epheria Marine Classic Set, a goofy marine pinup costume."]])
2 years ago
if name == "Belfry":
gal.append("City of Heroes")
gal.append([["name.png","desc"]])
2 years ago
if name == "Bless":
gal.append("Blessfrey")
2 years ago
gal.append([["name.png","desc"]])
2 years ago
if name == "Bijoux":
gal.append("Sims")
2 years ago
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "Calder":
2 years ago
gal.append("FlightRising")
gal.append([["pixelheadshot.png", "Calder's human form headshot"], ["FlightRising.png","Calder\'s sprite in FlightRising. He's wearing Sanguine Roses, Sanguine Plumage, and Pomegranate Plumes over Stonewatch Harpy."]])
2 years ago
if name == "Cass":
gal.append("Beloved of the Moon")
2 years ago
gal.append([["name.png","desc"]])
if name == "CatMan":
gal.append("City of Heroes")
gal.append([["name.png","desc"]])
if name == "Chandra":
gal.append("Hello Traveler")
gal.append([["name.png","desc"]])
if name == "Chimchooree":
2 years ago
gal.append("Aion")
gal.append([["couture.jpg","Chimchooree in her oyster pink tiered couture dress"],["Headshot.jpg","Her closeup."]])
if name == "Chloe":
2 years ago
gal.append("Blessfrey")
gal.append([["girls.png","Angel, Chloe, and Tessa"]])
2 years ago
if name == "Colt":
gal.append("Hello Traveler")
gal.append([["name.png","desc"]])
if name == "Coye":
gal.append("Verpets")
gal.append([["name.png","desc"]])
if name == "Dia":
gal.append("Blessfrey")
gal.append([["name.png","desc"]])
if name == "Dymn":
gal.append("Dymn")
gal.append([["name.png","desc"]])
if name == "Eden":
gal.append("Blessfrey")
gal.append([["name.png","desc"]])
2 years ago
if name == "Faber":
gal.append("Sims")
2 years ago
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "Fifi":
2 years ago
gal.append("Sims")
gal.append([["mirrorofvenus.jpg","Fifi debating herself in the mirror"],["guitarpractice.jpg","Fifi seeing how her old guitar sounds"]])
2 years ago
if name == "Freefall":
gal.append("13th")
gal.append([["name.png","desc"]])
2 years ago
if name == "Freya":
gal.append("Guild Wars")
gal.append([])
2 years ago
if name == "Funwa":
gal.append("Beloved of the Moon")
gal.append([["name.png","desc"]])
if name == "Helba":
gal.append("Verpets")
gal.append([["name.png","desc"]])
if name == "Helia":
2 years ago
gal.append("Blessfrey")
gal.append([["wm.png","Crop from a larger work-in-progress...so busy I can't draw everything I want to draw ahhh"],["Headshot.png","Helia in the style of Battle Girl High School...or at I tried! All the girls in that game wear their blush so high it's practically eyeshadow. It looks so innocent and vivacious on them, but it never comes out them same when I try to wear it myself."]])
2 years ago
if name == "Helmut":
gal.append("Sims")
2 years ago
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "HOME":
gal.append("HOME")
gal.append([["name.png","desc"]])
2 years ago
if name == "Leslie":
gal.append("Guild Wars")
2 years ago
gal.append([["name.png","desc"]])
if name == "Lisbet":
gal.append("Hello Traveler")
gal.append([["name.png","desc"]])
if name == "Lune":
2 years ago
gal.append("Persona")
2 years ago
gal.append([["RingOfFire.jpg","A painting of Lune in the Ring of Fire in her Vabbian"],["HallOfMonuments.png","My Guild Wars Necromancer showing off her Hall of Monuments"],["PhariseeFlying.jpg","My Aion Elysian Spiritmaster with really pretty hair"],["LuneMarine.png","Lune Marine, my gold side City of Heroes resistance Water Controller"],["ArcheageGuild.jpg","My ArcheAge dwarf ghost girl with gold-dipped hair and a frilly gown, sitting among her old guildmates"],["Pixelmon.png","Customs Officer Lune and her sidekick Lilligant in Pixelmon"],["tinypool.png","Bikini Lune in a tiny pool with her kitty in Minecraft"]])
2 years ago
if name == "Nephele":
gal.append("Verpets")
gal.append([["name.png","desc"]])
if name == "Newcomer":
gal.append("Newcomer")
gal.append([["name.png","desc"]])
2 years ago
if name == "Night":
gal.append("Blessfrey")
gal.append([])
if name == "Rodolphe":
gal.append("Sims")
2 years ago
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "Rune":
2 years ago
gal.append("Blessfrey")
gal.append([["Bless+Rune.png","Rune and his dinosaur mother"],["Blessfrey.png","I saw the coverart for Elden Gate and tried to make my own coverart like that. It's rough, torn between pencil, digital painting, and pixelart, but I don't feel like working on it anymore."],["RuneHeadshot.png","Rune in the Battle Girl High School style, though there aren't a lot of guys in that game to reference"],["Teriuuuu_Artfight.png","2020 Artfight attack by Teriuuuu"]])
2 years ago
if name == "Ryada":
gal.append("Guild Wars")
2 years ago
gal.append([[]])
if name == "Silke":
2 years ago
gal.append("Verpets")
gal.append([["Silke.png","Pixelart of Silke on the floor with her clipboard, wearing a striped green silk dress with an oversized pink tulle bow behind her bun. I tried to draw in Yuu Watase's style"],["Verpets.png","Silke's sprite from Verpets"]])
if name == "Tessa":
2 years ago
gal.append("Blessfrey")
gal.append([["Tessa.png","Pixelart of Tessa in athletic wear"],["ElissaKarminakria_Artfight.png","2020 Artfight attack by ElissaKarminakria"]])
2 years ago
if name == "Tilhar":
gal.append("Beloved of the Moon")
gal.append([["name.png","desc"]])
2 years ago
if name == "Tilly":
gal.append("Sims")
2 years ago
gal.append([["alchemy.png","Morning alchemy before work"],["dream.jpg","Stupid dream bubbles"]])
if name == "Trace":
gal.append("13th")
gal.append([["name.png","desc"]])
if name == "Window":
gal.append("Dymn")
gal.append([["name.png","desc"]])
if name == "WISE":
2 years ago
gal.append("Blessfrey")
gal.append([["WISE.png","WISE processing a query"],["CityOfHeroes.png","I made WISE in City of Heroes, too. She's an Electric/Empathy Controller."]])
2 years ago
return gal
2 years ago
def prepare_profile(loc, char_name):
string = ""
with open(loc + char_name) as f:
lines = f.readlines()
for line in lines:
string += line
return string
# Return list of snippets using list of articles
def list_snippets(articles):
loc = 'diary/entries/'
limit = 4
total = len(articles)
result = []
for article in articles:
path = loc + article
text = []
a = []
length = 0
text = article2list(article, loc)
a.append(find_title(text))
a.append(prepare_article(text, path))
a.append(find_timestamp(text))
a.append(find_url(path))
a.append(find_social_title(text))
a.append(find_tags(text))
result.append(a)
return result
# List latest 5 articles as headline links
def list_headlines(articles):
loc = 'diary/entries/'
result = []
text = []
for article in articles:
path = loc + article
b = []
b.append(path)
with open(path) as f:
f.readline()
text = f.readline()
b.append(clean(text.replace('<br>','')))
result.append(b)
return result
# Return first two sentences of article + " ... "
def snip_sentence(article, path):
article = clean(article)
limit = 100
result = article[0:min(len(article),limit)]
result = result.rsplit(' ',1)[0]
return result + " ... "
# Return first 300 words of article + " ... "
def snip_article(article, path):
article = clean(article)
limit = 300
result = article[0:min(len(article),limit)]
result = result.rsplit(' ',1)[0]
return result + " ... "
# Snip article and close any open list tags
def prepare_article(text, path):
content = snip_article(find_content(text), path)
if content.count('<ul>') > content.count('</ul>'):
content += '</ul>'
return content
# Return article text without HTML header
def find_content(text):
length = len(text)
content = ""
# form a string from relevant lines of the article
pos = 0
for line in text:
# skip to line 5
if pos > 4 and pos < length:
content += line
pos += 1
return content
def find_tags(text):
new = text[3].replace('<br>','')
new = new.replace('\n','')
new = new.split(" ")
final = []
for n in new:
if len(n) <= 0:
new.remove(n)
if '#' in n:
final.append(n)
final.sort()
return final
# Return title of article, formatted for sharing via social media
def find_social_title(text):
return clean(text[1]).replace(' ','+')
# Return URL of article
def find_url(path):
return '/' + path.replace('.tpl','')
# Return clean timestamp
def find_timestamp(text):
return text[2].replace('<br>','')
# Return clean title
def find_title(text):
return clean(text[1])
# Return list of files with given tag
def pull_tag(files, tag):
pull = []
for f in files:
tags = find_tags(article2list(str(f), 'diary/entries/'))
if "#" + tag in tags:
pull.append(f)
pull.sort(reverse=True)
return pull
def retrieve_diary_entry_content(page,loc):
text = []
string = ""
with open(loc + str(page)) as f:
lines = f.readlines()
for line in lines:
if lines.index(line) >= 4:
string += line
return string
def list_rec(page):
loc = 'diary/entries/'
result = []
rec = []
comment = ""
if isinstance(page, int):
# Collect recommended articles from comment line
with open('diary/entries/' + str(page)) as f:
comment = f.readline()
comment = comment.replace('<!--','')
comment = comment.replace('-->','')
comment = comment.replace(' ','')
comment = clean(comment)
rec = comment.split(',')
# Convert into array for template to display
for article in rec:
if is_it_time(article):
path = loc + article
data = []
try:
with open(path) as f:
f.readline()
data.append(clean(f.readline().replace('<br>','')))
data.append(path)
result.append(data)
except EnvironmentError:
print("No article @ " + path)
return result
def prepare_diary_entry(page, loc):
result = []
with open(loc + str(page)) as f:
text = []
text = article2list(str(page), loc)
result.append(find_title(text))
result.append(retrieve_diary_entry_content(page, loc))
result.append(find_timestamp(text))
result.append(find_url(loc + str(page)))
result.append(find_social_title(text))
result.append(find_tags(text))
return result
# Return article as list of lines of text
def article2list(article, loc):
text = []
with open(loc + article) as f:
text = f.readlines()
return text
# return list of diary entry tags, sorted by frequency
def fill_word_cloud(files):
tags = []
for f in files:
temp = find_tags(article2list(str(f), 'diary/entries/'))
for t in temp:
tags.append(t)
tags.sort()
cloud = []
i = 0
while i < 24:
if len(tags) > 0:
top = max(set(tags), key = tags.count)
cloud.append(top)
tags[:] = [x for x in tags if x != top]
i += 1
return cloud
def curate_files(files):
# remove folders
if 'hold' in files:
files.remove('hold')
# remove
clean = []
for f in files:
if is_it_time(f):
clean.append(f)
return clean
# Sort diary - newest to oldest
def sort_files(files):
files.sort(reverse=True)
return files
# Return list of all diary entries (exclude raws + extras)
def gather_files(loc):
files = os.listdir(loc)
return files
def gather_and_sort(loc):
return sort_files(curate_files(gather_files(loc)))
def is_it_time(date):
today = datetime.datetime.now()
today_string = today.strftime("%y") + today.strftime("%m") + today.strftime("%d")
2 years ago
return int(date) <= int(today_string)
4 years ago
## Static ##
4 years ago
# Serve CSS
@route('/static/css/<filename:path>')
def serve_css(filename):
return static_file(filename, root='static/css')
4 years ago
# Serve images
@route('/static/img/<filename:path>')
def serve_img(filename):
return static_file(filename, root='static/img')
# Serve XML
2 years ago
@route('/static/xml/<filename:path>')
def serve_xml(filename):
return static_file(filename, root='static/xml', mimetype='text/xml')
# Downloads
@route('/download/<filename:path>')
def download(filename):
return static_file(filename, root='static/extra', download=filename)
4 years ago
## Routes ##
3 years ago
# Error Page
@error(404)
def error404(error):
return "unfortunately, a 404 error. the page you're searching for doesn't exist. (or is it just in hiding?) try another page! "
3 years ago
@error(500)
def error500(error):
3 years ago
return "unfortunately, a 500 error. something is wrong with the page you're trying to find, if it exists at all. try another page! <a href=https://www.blessfrey.me/>return to blessfrey.me.</a>"
3 years ago
@error(502)
def error502(error):
3 years ago
return "unfortunately, a 502 error. this was likely due to website maintenance. usually it'll be back up before you finish reading this, but otherwise, I'll notice something's wrong soon! <a href=https://www.blessfrey.me/>return to blessfrey.me.</a>"
2 years ago
@route('/char/<char_name:path>')
def char(char_name):
"""character page"""
loc = 'char/'
info = {'css': 'char', 'title': 'blessfrey - meet ' + char_name, 'year': find_year(), 'profile': prepare_profile(loc, char_name), 'gallery': find_gallery(char_name)}
2 years ago
abs_app_dir_path = os.path.dirname(os.path.realpath(__file__))
abs_views_path = os.path.join(abs_app_dir_path, 'views')
TEMPLATE_PATH.insert(0, abs_views_path )
return template(os.path.join(abs_views_path,'char.tpl'), info)
2 years ago
# Me Page
@route('/characters')
def characters():
"""characters"""
info = {'css': 'me', 'title': 'about me', 'year': find_year(),
'persona': ["Lune"],
2 years ago
'blessfrey': ["Helia", "Angel", "Aries", "Bless", "Chloe", "Dia", "Night", "Rune", "Tessa", "WISE"],
2 years ago
'lemonland': ["CustomsOffice","BlessTheChild"],
2 years ago
'cw': ["Cass", "Funwa", "Tilhar", "HOME", "Chandra", "Colt", "Lisbet", "Freefall", "Trace", "Window", "Dymn", "Newcomer"],
2 years ago
'rp': ["Abbey", "Calder", "Helba", "Silke", "Nephele"],
2 years ago
'games': ["Aloin", "Bijoux", "Fifi", "Faber", "Helmut", "Tilly"],
'mmos': ["Belfry", "CatMan", "Aristen", "Chimchooree", "Freya", "Leslie"]}
2 years ago
return template('characters.tpl', info)
# Blessfrey Credits Page
@route('/credits')
def credits():
"""credits"""
info = {'css': 'credits', 'title': 'blessfrey credits', 'year': find_year()}
return template('credits.tpl', info)
# Blessfrey Demo Page
@route('/demo')
def demo():
"""demo"""
info = {'css': 'demo', 'title': 'blessfrey demo', 'year': find_year()}
return template('demo.tpl', info)
# Diary Page
@route('/diary') # Start on first Diary page if no page given
def diary2():
return diary(0)
@route('/diary/') # Slash is optional
def diary3():
return diary(0)
@route('/diary/<page:int>')
def diary(page):
2 years ago
"""diary"""
loc = 'diary/entries/'
assert isinstance(page, int)
info = {'css': 'diary', 'title': 'chimchooree\'s diary', 'year': find_year(), 'snippets': list_snippets(gather_and_sort(loc)), 'latest': list_headlines(gather_and_sort(loc)[0:5]), 'tags': fill_word_cloud(curate_files(gather_files(loc))), 'total': len(curate_files(gather_files(loc))), 'limit': 8, 'cluster': 3, 'page': page}
2 years ago
return template('diary.tpl', info)
# Entry Page - Feature Template - for articles
@route('/diary/entries/<page:int>')
def entry(page):
"""diary entry"""
if not is_it_time(page):
return error404(404)
loc = 'diary/entries/'
2 years ago
info = {'css': 'entry', 'title': 'blessfrey - developer diary', 'year': find_year(), 'entry': prepare_diary_entry(page, loc), 'recommends': list_rec(page), 'articles': "Articles", 'latest': list_headlines(gather_and_sort(loc)[0:5]), 'tags': fill_word_cloud(curate_files(gather_files(loc))), 'page': page}
abs_app_dir_path = os.path.dirname(os.path.realpath(__file__))
abs_views_path = os.path.join(abs_app_dir_path, 'views')
TEMPLATE_PATH.insert(0, abs_views_path )
return template(os.path.join(abs_views_path,'entry.tpl'), info)
# Fashion Page
@route('/fashion')
def fashion():
"""fashion"""
info = {'css': 'fashion', 'title': 'blessfrey fashion', 'year': find_year()}
return template('fashion.tpl', info)
4 years ago
# Home Page - Index Template
@route('/')
def home():
"""home"""
loc = 'diary/entries/'
2 years ago
info = {'css': 'index', 'title': 'chimchooree\'s dev space - blessfrey', 'year': find_year(), 'news': list_headlines(sort_files(curate_files(gather_files(loc)))[0:10])}
return template('index.tpl', info)
# Me Page
@route('/me')
def me():
"""me"""
2 years ago
info = {'css': 'me', 'title': 'about me', 'year': find_year()}
return template('me.tpl', info)
2 years ago
# Blessfrey Presskit Page
@route('/blessfrey-presskit')
def presskit():
"""presskit"""
info = {'css': 'presskit', 'title': 'blessfrey presskit', 'year': find_year()}
return template('blessfrey-presskit.tpl', info)
# Search Diary by Tag
@route('/diary/tag/<tagin>') # Start on first Diary tag page if no page given
def tag2(tagin):
return tag(tagin, 0)
@route('/diary/tag/<tagin>/<page:int>') # Tag Page - Diary Tag Template - list all articles for tag
def tag(tagin, page):
"""tag page"""
loc = 'diary/entries/'
assert isinstance(tagin, str)
assert isinstance(page, int)
info = {'css': 'diary', 'title': 'blessfrey - developer diary', 'year': find_year(), 'snippets': list_snippets(pull_tag(gather_and_sort(loc), tagin)), 'latest': list_headlines(gather_and_sort(loc)[0:5]), 'tags': fill_word_cloud(curate_files(gather_files(loc))), 'total': len(curate_files(gather_files(loc))), 'limit': 8, 'cluster': 3, 'page': page}
return template('diary.tpl', info)
2 years ago
4 years ago
## Main ##
if __name__ == '__main__':
2 years ago
run(host='0.0.0.0', port=9001)