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