forked from stefan-peng/oboeta
-
Notifications
You must be signed in to change notification settings - Fork 0
/
oboetahttp.py
executable file
·160 lines (141 loc) · 5.64 KB
/
oboetahttp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env python3
# Present Oboeta Lines from Standard Input as Flashcards (HTML5 over HTTP Version)
# Written in 2012 by 伴上段
#
# To the extent possible under law, the author(s) have dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
# You should have received a copy of the CC0 Public Domain Dedication along
# with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
from argparse import *
from csv import *
from datetime import *
from http.server import *
from http.server import SimpleHTTPRequestHandler
from itertools import *
from os import *
from os.path import *
from random import *
from sys import *
from threading import *
if __name__ != "__main__":
stderr.write("This is meant to be run as a program, not loaded as a module.\n")
exit(1)
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter, description=""" Review lines from standard input as though they were flashcards.
This program serves flashcards on the localhost via HTTP: You should
review the cards through a web browser (http://localhost:<port>).
formatting:
This program expects standard input to be a CSV file. The -s option controls
the field delimiter. The program reads two lines per flashcard: the front
and the back sides of the card, respectively. Both are expected to be CSV-
formatted lines. Each field in a line will be displayed on its own line
in the generated HTML.
This program writes single-character lines to standard output representing
the results of card reviews. For Leitner-system-based reviews, if the line
is "+", then the user passed the card; if it's "-", then he failed. If the
review uses SM-2, then the results will be integers in the range [0,5].
In either case, if the output is a line containing just "q", then the user
terminated the quiz. All such lines end with a single newline
(\\n) character.
output:
This program will serve HTML via the specified port (-p option). Use your
web browser to view the cards.""")
parser.add_argument("-i", "--font-size", default="20pt", help="the font size, including units (default: 20pt)")
parser.add_argument("-n", "--font", default="sans-serif", help="the font used in rendered HTML (default: sans-serif)")
parser.add_argument("-p", "--port", default=1337, type=int, help="the HTTP server's port (default: 1337)")
parser.add_argument("-s", "--field-sep", default="\t", help="the CSV field separator (default: \\t)")
parser.add_argument("-2", "--use-sm2", default=False, action="store_true", help="use the SM-2 algorithm instead of the Leitner system")
args = parser.parse_args()
if args.port <= 0 or args.port > 65535:
stderr.write("illegal port number\n")
exit(1)
front = None
back = None
showing_back = False
stdinreader = reader(stdin, delimiter=args.field_sep)
cond = Condition()
retcode = 0
running = True
sm2_max = 6
url_paths = dict(("/" + str(v), str(v) + "\n") for v in range(sm2_max))
url_paths["/pass"] = "+\n"
url_paths["/fail"] = "-\n"
html_head = """<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>Review</title></head><body style="text-align: center; font: """ + str(args.font_size) + """ """ + args.font + """\"><div>"""
html_mid = {
False: """</div><div><a href="/show">Show</a> · <a href="/quit">Quit</a>""",
True: "</div><div>" + " · ".join(("<a href=\"/" + str(v) + "\">" + str(v).capitalize() + "</a>" for v in chain(range(sm2_max) if args.use_sm2 else ("pass", "fail"), ("quit",))))
}
html_tail = "</div></body></html>\r\n"
class TServer(SimpleHTTPRequestHandler):
def do_GET(self):
global front
global back
global showing_back
global stdinreader
global retcode
global running
if self.path.startswith("/media"):
super().do_GET()
return
self.error_content_type = "text/plain"
if running and self.path != "/favicon.ico":
if self.path == "/quit":
stdout.write("q\n")
stdout.flush()
stderr.write("Finishing early\n")
self.Send("text/plain", "Done!")
with cond:
running = False
cond.notify()
return
if front is not None:
if showing_back:
if self.path.lower() in url_paths:
stdout.write(url_paths[self.path])
else:
self.send_error(404)
self.end_headers()
return
stdout.flush()
front = None
else:
showing_back = True
if front is None:
front = next(stdinreader, None)
back = next(stdinreader, None)
if front is None or back is None:
with cond:
running = False
cond.notify()
self.Send("text/plain", "Done!")
return
showing_back = False
self.Send("text/html", html_head + "<br />".join(front) + ("<hr />" + "<br />".join(back) if showing_back else "") + html_mid[showing_back] + html_tail)
else:
self.send_error(404)
self.end_headers()
def Send(self, mime, message):
message = bytes(message, encoding="UTF-8")
self.send_response(200)
self.send_header("Content-Type", mime)
self.send_header("Content-Length", str(len(message)))
self.end_headers()
left = len(message)
while left:
written = self.wfile.write(message)
message = message[written:]
left -= written
server = HTTPServer(('', args.port), TServer)
def ServeIt():
server.serve_forever()
serverthread = Thread(target=ServeIt)
serverthread.daemon = True
serverthread.start()
try:
with cond:
while running:
cond.wait()
except KeyboardInterrupt as e:
stdout.write("q\n");
exit(retcode)