This is @ seigo2016 for the first year of N High School programming class. It will be N High Advent Day 8. I was wondering what to write because it was a proper article, but since I talked a lot about the current main project "Itokake Mandala Color Simulator" at U22 etc., this time I talked a lot about the recent short-term project "PSS ~ Presentation Support". I will talk about "System ~".
I think that you often give presentations using PowerPoint, Google Slide, etc. when telling something to people. LT is also held every month in the N High Programming Class (hereinafter referred to as Prokura). So I decided to create a system to support the presentation.
It is the part that accepts and records comments. Everything runs on Docker. The memory is 1GB, but it works unexpectedly.
part | Performance |
---|---|
VPN | Vultr |
CPU | 1C/1T |
Memory | 1024MB |
OS | OS Debian 10 Buster |
I also use it to practice Docker and Docker-Compose.
Container name | Overview |
---|---|
proxy | Nginx Reverse Proxy |
web | Python3.6(Python execution) |
mysql | DB(Save login information) |
I used bcrypt to hash the password and the mysql-connector library to connect to mysql. In addition to the following, SSL and Socket were used.
Python3
bcrypt==3.1.7
mysql-connector==2.2.9
pycrypto==2.6.1
PyYAML==5.2
tornado==6.0.3
I used Tkinter as the library for building the GUI.
Python3
pdf2image==1.10.0
Pillow==6.2.1
progressbar2==3.47.0
PyYAML==5.2
This is the main part of this project. The system itself, in which comments flow in real time, has already been created by other professional club students. However, there were many restrictions such as not being able to use it if the connections between clients were separated, so we decided to create a new system that improved those parts. ** This system does not infringe the patent of Dongo's comment system because the mounting method and specifications are completely different ** This is how it works.
As an aside, there are several people around me who want to find and report vulnerabilities, so some security measures are needed to prevent them from being dropped into the actual presentation.
This is the part that accepts comments. The flow is as follows.
server.py
def send_comment(comment):
#Current time(JST)
dt_now = dt.now(JST)
#Record in database
c = database.cursor()
sql = "INSERT INTO comment (text, entertime) values(%s,%s)"
c.execute(sql,
(comment, dt_now,))
database.commit()
#Assign to a variable shared between threads
with commentbody.get_lock():
commentbody.value = comment.encode()
class Comment(web.RequestHandler):
def post(self):
comment = self.get_argument("comment")
comment = escape(comment)
title = "comment"
if re.search(r'\S', comment) and len(comment) < 30:
send_comment(comment)
self.render('index.html', title=title)
else:
message = "Input correctly"
self.render('index.html', title=title, message=message)
Next, it will be the part that sends the received comment to the client (presenter PC) by socket communication. The flow is as follows.
server.py
def connect_socket(): #Socket communication
print("SocketStart")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('0.0.0.0', 10023)) #Open on port 10023
while True:
s.listen(1)
print("Waitng ....")
conn, addr = s.accept()
flg = False
while True:
try:
conn = context.wrap_socket(conn, server_side=3)
with conn:
while True:
try:
print("Connecting")
data = conn.recv(1024).decode()
if not data:
break
elif ":" in data:
loginuser = data.split(":")[0]
loginpass = data.split(":")[1]
sql = "SELECT id, name, pass FROM users WHERE name = %s"
c.execute(sql, (loginuser,))
userdata = c.fetchall()
if len(userdata) and bcrypt.checkpw(loginpass.encode(), userdata[0][2].encode()):
print("Connected")
conn.sendall("Connection completed".encode("utf-8"))
flg = True
else:
conn.sendall("Authentication error".encode("utf-8"))
conn.close()
flg = False
elif flg:
comment = commentbody.value
with commentbody.get_lock():
commentbody.value = "".encode()
if len(comment):
conn.sendall(comment)
comment = ""
except socket.error:
flg = False
break
except Exception as e:
flg = False
print("Disconnected\n{}".format(e))
s.close()
After connecting to the server, sending the authentication information and logging in, the client (presenter PC) receives the comment.
client.py
def rcv_comment():
#Socket communication settings
context = ssl.create_default_context()
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_NONE
context.check_hostname = False
conn = context.wrap_socket(socket.socket(socket.AF_INET),
server_hostname=host)
conn.connect((host, port))
#Prepare authentication information(The format is user:pass)
idpass = "{}:{}".format(user_name, user_pass).encode()
conn.sendall(idpass)
manager = CommentManager(canvas)
while True:
try:
data = conn.recv(1024)
if len(data):
comment = data.decode('utf-8')
print("recv:" + comment)
#If an authentication error is returned, complete the program
if comment == "Authentication error":
break
#Otherwise, pass the comment to the drawing part
manager.add_text(comment)
except KeyboardInterrupt:
break
Draw the received comment on the Tkinter canvas.
client.py
class CommentManager: #Manage drawing comments
def __init__(self, canvas):
self.canvas_text_list = []
self.canvas = canvas
root.after(1, self.update)
def add_text(self, comment): #Add a comment to flow
#Generate text with x-coordinate at the edge of the screen and y-coordinate at random
text = self.canvas.create_text(
w, random.uniform(2.0, 18.0) * 100, text=comment, font=comment_font)
self.canvas_text_list.append(text)
def update(self): #Move to the left
new_list = []
#Move comments in the list to the left.(It seems better to do it all at once using tags)
for canvas_text in self.canvas_text_list:
self.canvas.move(canvas_text, -15, 0)
x, y = self.canvas.coords(canvas_text)
if x > -10:
new_list.append(canvas_text)
else: #After moving to the left end
self.canvas.delete(canvas_text)
self.canvas_text_list = new_list
root.after(20, self.update)
Since the slide itself is loaded into Tkinter's Canvas, just update the image
client.py
def next(event): #Send slides
global page
if pagemax - 1 > page:
canvas.itemconfig(labelimg, image=img[page])
page += 1
def prev(event): #Move the slide back
global page
if 0 <= page:
canvas.itemconfig(labelimg, image=img[page])
page -= 1
# ~Omission~ #
if __name__ == '__main__':
th = Thread(target=rcv_comment)
th.setDaemon(True)
th.start()
root.bind("<Key-n>", next) #Assign key here
root.bind("<Key-p>", prev)
root.mainloop()
It is not implemented because I am worried about selecting the one to be used as a pointer device. I will add it as soon as it is implemented. Is a smartphone good after all?
--Window adjustment in Mac environment --Verification of security measures --Implementation of pointer function
I'm writing angry code that isn't packed at all ... Performance is not good when a large number of comments flow. I think it would be wise to display it as an overlay.
Recommended Posts