new-prototype to master (#2)
First version without unwanted hardcoded vars.
This commit is contained in:
parent
16b3063070
commit
1041c5e32b
4
Makefile
Normal file
4
Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
all:
|
||||
python microblog.py ./template.html ./content.txt > result.html
|
153
microblog.py
Normal file
153
microblog.py
Normal file
@ -0,0 +1,153 @@
|
||||
|
||||
import sys
|
||||
|
||||
import subprocess
|
||||
|
||||
def make_post(num, timestamp, email, msg):
|
||||
# used <a class> for name and email but it's not actually needed.
|
||||
# <a class=\"name\">%s</a>
|
||||
# <a class=\"email\">(%s)</a>
|
||||
output = ""
|
||||
fmt = ""
|
||||
part = '''
|
||||
<div class=\"postcell\" id=\"%i\">
|
||||
<div class=\"timestamp\"><a href=#%i>%s</a></div>
|
||||
<div class=\"message\">%s</div>
|
||||
'''
|
||||
if email != None:
|
||||
fmt = part + "<div class=\"reply\"><a href=\"mailto:%s?subject=p%i\">[reply]</a></div></div>"
|
||||
output = fmt % (num, num, timestamp, msg, email, num)
|
||||
else:
|
||||
fmt += part + "</div>"
|
||||
output = fmt % (num, num, timestamp, msg)
|
||||
return output
|
||||
|
||||
def make_gallery(i, w):
|
||||
tag = []
|
||||
if i == []:
|
||||
return tag
|
||||
tag.append("<div class=\"gallery\">")
|
||||
for index in reversed(i):
|
||||
image = w.pop(index)
|
||||
template = '''
|
||||
<div class=\"panel\">
|
||||
<a href=\"%s\"><img src=\"%s\" class=\"embed\"></img></a>
|
||||
</div>
|
||||
'''
|
||||
tag.append(template % (image, image))
|
||||
tag.append("</div>")
|
||||
return tag
|
||||
|
||||
def markup(msg):
|
||||
result = 0
|
||||
tagged = ""
|
||||
words = msg.split()
|
||||
# support multiple images (gallery style)
|
||||
images = [] # list of integers
|
||||
tags = [] # list of strings
|
||||
for i in range(len(words)):
|
||||
word = words[i]
|
||||
# don't help people click http
|
||||
if word.find("src=") == 0 or word.find("href=") == 0:
|
||||
continue
|
||||
elif word.find("https://") != -1:
|
||||
new_word = ("<a href=\"%s\">%s</a>") % (word, word)
|
||||
words[i] = new_word
|
||||
elif word.find("#") != -1 and len(word) > 1:
|
||||
# split by unicode blank character if present
|
||||
w = word.split(chr(8206))
|
||||
# w[0] is the portion closest to the #
|
||||
tags.append(w[0])
|
||||
new_word = "<span class=\"hashtag\">%s</span>" % (w[0])
|
||||
if len(w) > 1:
|
||||
new_word += w[1]
|
||||
words[i] = new_word
|
||||
elif word.find(".jpg") != -1 or word.find(".png") != -1:
|
||||
images.append(i)
|
||||
# are .gifs and other media files too much bloat?
|
||||
gallery = make_gallery(images, words)
|
||||
words += gallery
|
||||
return " ".join(words), tags
|
||||
|
||||
def parse_txt(filename):
|
||||
content = []
|
||||
with open(filename, 'r') as f:
|
||||
content = f.readlines()
|
||||
stack = [] # stack of lists - only a stack b/c order of contents
|
||||
message = [] # queue of lines
|
||||
# {-1 = init;; 0 = timestamp is next, 1 = message is next}
|
||||
state = -1
|
||||
timestamp = ""
|
||||
for line in content:
|
||||
# print (line, len(line), state)
|
||||
if state == -1:
|
||||
state = 0
|
||||
continue
|
||||
elif state == 0:
|
||||
cmd = ['date', '-d', line, '+%y %b %d']
|
||||
result = subprocess.run(cmd, stdout=subprocess.PIPE)
|
||||
timestamp = result.stdout.decode('utf-8')
|
||||
state = 1
|
||||
elif state == 1:
|
||||
if len(line) > 1:
|
||||
message.append(line)
|
||||
else:
|
||||
stack.append([timestamp, "<br>".join(message)])
|
||||
message = []
|
||||
state = 0
|
||||
return stack
|
||||
|
||||
def make_timeline(filename, email=None):
|
||||
timeline = []
|
||||
tagcloud = dict()
|
||||
# note: content is ordered from newest to oldest
|
||||
# format requires two line breaks before EOF
|
||||
stack_of_posts = parse_txt(filename) # [timestamp, message]
|
||||
count = len(stack_of_posts)
|
||||
for post in stack_of_posts:
|
||||
p, t = markup(post[1])
|
||||
timeline.append(
|
||||
make_post(count, post[0], email, p)
|
||||
)
|
||||
count -= 1
|
||||
for tag in t:
|
||||
if tagcloud.get(tag) == None:
|
||||
tagcloud[tag] = 0
|
||||
tagcloud[tag] += 1
|
||||
return timeline, tagcloud
|
||||
pass
|
||||
|
||||
def make_tagcloud(d):
|
||||
sorted_d = {k: v for k,
|
||||
v in sorted(d.items(),
|
||||
key=lambda item: -item[1])}
|
||||
output = []
|
||||
fmt = "<span class=\"hashtag\">%s(%i)</span>"
|
||||
for key in d.keys():
|
||||
output.append(fmt % (key, d[key]))
|
||||
return output
|
||||
|
||||
if __name__ == "__main__":
|
||||
def get_params():
|
||||
argc = len(sys.argv)
|
||||
if argc < 3:
|
||||
msg = '''This is microblog.py. (%s/3 parameters given; 4 maximum)
|
||||
\tpython microblog.py [template] [content] [optional: email]
|
||||
'''
|
||||
print(msg % argc)
|
||||
# script = argv[0]
|
||||
return argc
|
||||
|
||||
def main():
|
||||
param_count = get_params()
|
||||
template = sys.argv[1]
|
||||
content = sys.argv[2]
|
||||
email = sys.argv[3] if (param_count >= 4) else None
|
||||
tl, tc = make_timeline(content, email)
|
||||
tcl = make_tagcloud(tc)
|
||||
count = len(tl)
|
||||
with open(template,'r') as f:
|
||||
html = f.read()
|
||||
print(html % (count, "\n".join(tcl), "\n\n".join(tl)))
|
||||
main()
|
||||
|
36
template-generic.html
Normal file
36
template-generic.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="initial-scale=1.0">
|
||||
<title>Microblog</title>
|
||||
<link href="/style.css" rel="stylesheet" type="text/css" media="all">
|
||||
<!-- local testing -->
|
||||
<link href="./style.css" rel="stylesheet" type="text/css" media="all">
|
||||
<link href="./timeline.css" rel="stylesheet" type="text/css" media="all">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
|
||||
|
||||
<h1>A Microblog in Plain HTML</h1>
|
||||
|
||||
<div class = "column">
|
||||
<div class="profile">
|
||||
<img src="./images/avatar.jpg" alt="Avatar" class="avatar">
|
||||
<span class="handle">Your Name Here</span>
|
||||
<p><span class="email"><a href="mailto:user@host.tld">user@host.tld</a></span></p>
|
||||
<div class="bio">TDescription</h4>
|
||||
<h3>Tags</h3>
|
||||
<p>%s</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class = "timeline">
|
||||
%s
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
85
timeline-generic.css
Normal file
85
timeline-generic.css
Normal file
@ -0,0 +1,85 @@
|
||||
.column {
|
||||
float: left;
|
||||
width: 30%;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
.timeline {
|
||||
float: right;
|
||||
width: 67%;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
.postcell {
|
||||
border: 1px solid gray;
|
||||
text-align: left;
|
||||
margin: 0.25em 0
|
||||
}
|
||||
.message {
|
||||
margin: 1em 1em 1em 3em;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.reply {
|
||||
margin: 0.5em
|
||||
}
|
||||
.timestamp {
|
||||
text-align: right;
|
||||
margin: 0.5em
|
||||
}
|
||||
.hashtag {
|
||||
font-weight: bold;
|
||||
}
|
||||
.profile {
|
||||
vertical-align: middle;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.avatar {
|
||||
vertical-align: middle;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
.handle{
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
}
|
||||
.email{
|
||||
text-align:left;
|
||||
font-size: x-small;
|
||||
text-decoration:none;
|
||||
}
|
||||
.bio {
|
||||
vertical-align: middle;
|
||||
font-size: small;
|
||||
margin: 1em
|
||||
}
|
||||
.gallery {
|
||||
margin-left: auto ;
|
||||
margin-right: auto ;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
.header {
|
||||
background: black;
|
||||
color: white;
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.panel {
|
||||
margin: 2px;
|
||||
/* border: 1px solid #ffbc06; */
|
||||
width: auto
|
||||
}
|
||||
|
||||
div.panel:hover {
|
||||
border: 1px solid #777;
|
||||
}
|
||||
|
||||
div.panel img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.panel img:hover {
|
||||
filter: invert(100%);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user