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 ) :
gal = [ name ]
if name == " Abbey " :
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 " :
gal . append ( [ [ " alchemy.png " , " Morning alchemy before work " ] , [ " dream.jpg " , " Stupid dream bubbles " ] ] )
if name == " Angel " :
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 " ] ] )
if name == " Aristen " :
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. " ] ] )
if name == " Calder " :
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. " ] ] )
if name == " Chimchooree " :
gal . append ( [ [ " couture.jpg " , " Chimchooree in her oyster pink tiered couture dress " ] , [ " Headshot.jpg " , " Her closeup. " ] ] )
if name == " Chloe " :
gal . append ( [ [ " girls.png " , " Angel, Chloe, and Tessa " ] ] )
if name == " Fifi " :
gal . append ( [ [ " mirrorofvenus.jpg " , " Fifi debating herself in the mirror " ] , [ " guitarpractice.jpg " , " Fifi seeing how her old guitar sounds " ] ] )
if name == " Helia " :
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. " ] ] )
if name == " Lune " :
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 " ] , [ " 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 " ] ] )
if name == " Rune " :
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 " ] ] )
if name == " Silke " :
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 " :
gal . append ( [ [ " Tessa.png " , " Pixelart of Tessa in athletic wear " ] , [ " ElissaKarminakria_Artfight.png " , " 2020 Artfight attack by ElissaKarminakria " ] ] )
if name == " WISE " :
gal . append ( [ [ " WISE.png " , " WISE processing a query " ] , [ " CityOfHeroes.png " , " I made WISE in City of Heroes, too. She ' s an Electric/Empathy Controller. " ] ] )
return gal
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 " )
return True #int(date) <= int(today_string)
## Static ##
# Serve CSS
@route ( ' /static/css/<filename:path> ' )
def serve_css ( filename ) :
return static_file ( filename , root = ' static/css ' )
# Serve images
@route ( ' /static/img/<filename:path> ' )
def serve_img ( filename ) :
return static_file ( filename , root = ' static/img ' )
# Serve XML
@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 )
## Routes ##
# 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! "
@error ( 500 )
def error500 ( error ) :
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> "
@error ( 502 )
def error502 ( error ) :
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> "
@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 ) }
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 )
# Blessfrey Presskit Page
@route ( ' /blessfrey-presskit ' )
def presskit ( ) :
""" presskit """
info = { ' css ' : ' presskit ' , ' title ' : ' blessfrey presskit ' , ' year ' : find_year ( ) }
return template ( ' blessfrey-presskit.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 ) :
""" 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 }
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/ '
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 )
# Home Page - Index Template
@route ( ' / ' )
def home ( ) :
""" home """
loc = ' diary/entries/ '
info = { ' css ' : ' index ' , ' title ' : ' chimchooree \' s dev space - blessfrey ' , ' year ' : find_year ( ) , ' news ' : list_headlines ( sort_files ( gather_files ( loc ) ) [ 0 : 10 ] ) }
return template ( ' index.tpl ' , info )
# Me Page
@route ( ' /me ' )
def me ( ) :
""" me """
info = { ' css ' : ' me ' , ' title ' : ' about me ' , ' year ' : find_year ( ) , ' chars ' : [ " Helia " , " Angel " , " Rune " , " Tessa " , " Silke " , " Abbey " , " Calder " , " Aloin " , " Fifi " , " Lune " , " Chimchooree " , " Aristen " ] }
return template ( ' me.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 )
## Main ##
if __name__ == ' __main__ ' :
run ( host = ' 0.0.0.0 ' , port = 9001 )