diff --git a/cmpe436/final-project/README.md b/cmpe436/final-project/README.md index fdc5db6..2449fdc 100644 --- a/cmpe436/final-project/README.md +++ b/cmpe436/final-project/README.md @@ -1,8 +1,7 @@ ## Auction App +### [Server](/cmpe436/final-project/Server) `Java` +### [Android App](/cmpe436/final-project/Auctioner) `Java` ||||| :-------------------------:|:-------------------------:|:-------------------------:|:-------------------------: | | | | | | - -### [Server](/cmpe436/final-project/Server) `Java` -### [Android App](/cmpe436/final-project/Auctioner) `Java` diff --git a/cmpe436/final-project/screenshots/ss1.png b/cmpe436/final-project/screenshots/ss1.png index 8453e50..4d9e2ad 100644 Binary files a/cmpe436/final-project/screenshots/ss1.png and b/cmpe436/final-project/screenshots/ss1.png differ diff --git a/cmpe436/final-project/screenshots/ss3.png b/cmpe436/final-project/screenshots/ss3.png index aa1fc14..8c15c1e 100644 Binary files a/cmpe436/final-project/screenshots/ss3.png and b/cmpe436/final-project/screenshots/ss3.png differ diff --git a/cmpe436/final-project/screenshots/ss4.png b/cmpe436/final-project/screenshots/ss4.png index 130141c..4c68478 100644 Binary files a/cmpe436/final-project/screenshots/ss4.png and b/cmpe436/final-project/screenshots/ss4.png differ diff --git a/cmpe436/final-project/screenshots/ss5.png b/cmpe436/final-project/screenshots/ss5.png index 60e7a01..e03ea38 100644 Binary files a/cmpe436/final-project/screenshots/ss5.png and b/cmpe436/final-project/screenshots/ss5.png differ diff --git a/cmpe436/final-project/screenshots/ss6.png b/cmpe436/final-project/screenshots/ss6.png index bcdda98..ddc987c 100644 Binary files a/cmpe436/final-project/screenshots/ss6.png and b/cmpe436/final-project/screenshots/ss6.png differ diff --git a/cmpe436/final-project/screenshots/ss7.png b/cmpe436/final-project/screenshots/ss7.png index e8e9be8..c194ec7 100644 Binary files a/cmpe436/final-project/screenshots/ss7.png and b/cmpe436/final-project/screenshots/ss7.png differ diff --git a/cmpe487/final-project b/cmpe487/final-project deleted file mode 160000 index d3a72a4..0000000 --- a/cmpe487/final-project +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3a72a40a18780d2764be61e05a08cfb3495abf6 diff --git a/cmpe487/final-project/Presentation.pdf b/cmpe487/final-project/Presentation.pdf new file mode 100644 index 0000000..c2a8e6b Binary files /dev/null and b/cmpe487/final-project/Presentation.pdf differ diff --git a/cmpe487/final-project/README.md b/cmpe487/final-project/README.md new file mode 100644 index 0000000..a3cdddc --- /dev/null +++ b/cmpe487/final-project/README.md @@ -0,0 +1,56 @@ +# CmpE 487 Final Project +## Network-Based Realtime Multiple-Choice Quiz Application +
+ +
+ +- Moderator creates quizzes manually or imports from prepared text files. +- Participants connect to the quiz room that is on the server. +- When moderator starts session, participants compete for correct answers. +- Participants have 10 seconds for each question +#### [Final Presentation PDF](https://github.com/CMPE487/final-project-quizapp/blob/master/Presentation.pdf) + +### Usage +Run following command for both Server or Client +``` +$ python3 main.py +``` +![Main Menu](main_menu.png) + +**IMPORTANT NOTE:** Please don't press any key if it's not prompted. Especially when waiting for new questions. +#### Create Quiz +`QuizServer` class handles server operations. +1) Select `Start new quiz` +2) Enter quiz name +3) Select quiz creation method + - **Import file:** We have samples quizzes. Enter file path. (i.e: `samples/network.txt` `samples/general.txt`) + - **Manually:** Enter number of the questions in the quiz. Then enter question body and options as prompted. +4) Review questions, enter to continue +5) Wait for participants. Participants can't enter after you sent first question. +![Participants](server.png) +6) Send first question, so quiz is started. +7) Wait answers from participants (12 seconds) +8) When you see scores after 12 seconds, you can send simply next question by pressing `Enter` +9) End of the quiz, scores are sent to all participants automatically. + +#### Enter a Quiz +`QuizClient` class handles client operations. +1) Select `Enter a quiz`. Discovery sent to network automatically. + - If you are not in the same subnet with the server, first discover manually with `Manual quiz discovery` option then `Enter a quiz` +2) Select a quiz to enter +3) Enter your username and wait for the first question +4) When question is appeared, enter your answer and press `Enter`. +![Question](client.png) +5) Wait for the next questions and give your answer when it is prompted. +6) Repeat this process until all questions is done +7) When the all questions are answered, you will see the scoreboard. +8) Highlighted row is your score :) + +### Contributors: +- [Mustafa Enes Çakır](https://github.com/EnesCakir) +- [Oğuzhan Yetimoğlu](https://github.com/oguzhanyetimoglu) + +(We did pair programming) + +### License +Quiz App is an open-sourced software licensed under the [BSD-2 license](https://opensource.org/licenses/BSD-2-Clause). diff --git a/cmpe487/final-project/client.png b/cmpe487/final-project/client.png new file mode 100644 index 0000000..90bef9e Binary files /dev/null and b/cmpe487/final-project/client.png differ diff --git a/cmpe487/final-project/config.py b/cmpe487/final-project/config.py new file mode 100644 index 0000000..c4e9652 --- /dev/null +++ b/cmpe487/final-project/config.py @@ -0,0 +1,34 @@ +import socket + + +def get_ip(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(('10.255.255.255', 1)) + IP = s.getsockname()[0] + except: + IP = '127.0.0.1' + finally: + s.close() + return IP + + +SELF_IP = get_ip() +SUBNET = SELF_IP[:SELF_IP.rfind('.')] + +DISCOVERY_PORT = 5000 # TCP +QUIZ_PORT = 5001 # TCP + +QUESTION_TIME = 10 + +MESSAGE_TYPES = { + "request": 0, + "response": 1, + "enter": 2, + "success": 3, + "error": 4, + "question": 5, + "answer": 6, + "answer_response": 7, + "result": 8 +} diff --git a/cmpe487/final-project/logo.png b/cmpe487/final-project/logo.png new file mode 100644 index 0000000..5aa8c48 Binary files /dev/null and b/cmpe487/final-project/logo.png differ diff --git a/cmpe487/final-project/main.py b/cmpe487/final-project/main.py new file mode 100644 index 0000000..13ff063 --- /dev/null +++ b/cmpe487/final-project/main.py @@ -0,0 +1,80 @@ +from quizClient import * +from quizServer import * +from quizUtils import * +from utils import * + +quiz_server = None +quiz_client = QuizClient() + +clear() +while True: + option = select_option([ + "Start new quiz", + "Enter a quiz", + "Manual quiz discovery", + "Quit" + ], header="AVAILABLE COMMANDS") + + + if option == "1": + clear() + print_header("Start New Quiz") + quiz_name = input("\n" + change_style("Enter quiz name", 'underline') + ": ") + quiz = Quiz(quiz_name) + clear() + quiz_option = select_option(["Import from file", "Enter questions manually"], header="Quiz Create Methods") + if quiz_option == "1": + clear() + print_header("Import Quiz") + filename = input("Enter file name: ") + while not os.path.isfile(filename): + print(change_style("\nPlease enter valid filename\n", "red")) + filename = input("Enter file name: ") + quiz.import_file(filename) + else: + clear() + print_header("Create Quiz") + question_count = input("How many questions your quiz has? ") + for i in range(int(question_count)): + question = Question.from_input(i + 1) + quiz.add_question(question) + + clear() + quiz.print() + enter_continue() + quiz_server = QuizServer(quiz) + quiz_server.listen() + print_header("QUIZ: " + quiz.name) + print(change_style("QUIZ IP: " + change_style(SELF_IP, 'bold'), 'green')) + print(change_style("\n\nWAITING FOR NEW PARTICIPANTS\n\n", 'bold')) + tmp = input("Enter for start quiz") + quiz_server.start() + elif option == "2": + quiz_client.broadcast_quiz(True) + clear() + id = select_option(quiz_client.available_quizzes.values(), prompt="Enter quiz ID", header="Enter a Quiz") + + if id is "": + clear() + else: + username = input("\nEnter username: ") + quiz_ip = list(quiz_client.available_quizzes.keys())[int(id) - 1] + quiz_client.enter(quiz_ip, username) + + elif option == "3": + clear() + print_header("Manual Quiz Discovery") + quiz_client.broadcast_quiz(False) + quiz_ip = input(change_style("Enter quiz server IP address (i.e: 192.168.6.150)", "underline") + ": ") + start_new_thread(quiz_client.send_discovery_packet, (quiz_ip, False)) + clear() + print_notification("Discovery request sent to " + quiz_ip) + + elif option == "4": + clear() + print_notification("Good bye \n\n") + os.system("pkill -9 \"python3 main.py\"") + sys.exit(0) + else: + clear() + print_error("Invalid option") diff --git a/cmpe487/final-project/main_menu.png b/cmpe487/final-project/main_menu.png new file mode 100644 index 0000000..ce386df Binary files /dev/null and b/cmpe487/final-project/main_menu.png differ diff --git a/cmpe487/final-project/quizClient.py b/cmpe487/final-project/quizClient.py new file mode 100644 index 0000000..ed0b47f --- /dev/null +++ b/cmpe487/final-project/quizClient.py @@ -0,0 +1,119 @@ +from config import * +from utils import * + + +class QuizClient(): + def __init__(self): + self.username = None + self.active_quiz = None + self.score = 0 + self.available_quizzes = {} + + def start(self): + self.broadcast_quiz() + + def broadcast_quiz(self, print=False): + for i in range(1, 255): + target_ip = SUBNET + "." + str(i) + # if target_ip != SELF_IP: + start_new_thread(self.send_discovery_packet, (target_ip, print)) + + def send_discovery_packet(self, target_ip, print): + message = "{}|{}".format(MESSAGE_TYPES["request"], SELF_IP) + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(5) + s.connect((target_ip, DISCOVERY_PORT)) + s.send(message.encode('utf-8')) + data = s.recv(1024) + if data: + type, source, quiz_name = data.decode().split('|') + if int(type) == MESSAGE_TYPES["response"]: + print_notification("New quiz: {}".format(quiz_name)) + self.available_quizzes[source] = quiz_name + if print: + clear() + select_option(self.available_quizzes.values(), prompt="Enter quiz ID", + header="Enter a Quiz", is_active=False) + + s.close() + except Exception as ex: + # print("Error while sending packet: " + message) + # print(ex.__str__() + " " + str(port)) + pass + + def enter(self, quiz_ip, username): + self.username = username + self.active_quiz = self.available_quizzes[quiz_ip] + + print("Entering: " + quiz_ip) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((quiz_ip, QUIZ_PORT)) + message = "{}|{}|{}".format(MESSAGE_TYPES["enter"], SELF_IP, username) + s.send(message.encode('utf-8')) + while True: + data = s.recv(1024) + if not data: + break + + type, *parts = data.decode().split("|") + type = int(type) + if type == MESSAGE_TYPES["error"]: + print(parts[0]) + break + elif type == MESSAGE_TYPES["success"]: + clear() + print("\n\n" + parts[0]) + print_header("PLEASE WAIT TO START QUIZ") + elif type == MESSAGE_TYPES["question"]: + clear() + number = parts[0] + body = parts[1] + options = parts[2:] + self.print_status() + print(change_style("\n\nQuestion {}: ".format(number), "question") + change_style(body, "bold")) + start_timer(QUESTION_TIME) + answer = select_option(options, prompt="Your answer", timeout=QUESTION_TIME) + if answer: + if int(answer) > 4 or int(answer) < 0: + print(change_style("\nInvalid answer\n", "red")) + else: + print("\nSubmitted answer is \"{}\"".format(options[int(answer) - 1])) + + message = "{}|{}|{}".format(MESSAGE_TYPES["answer"], number, answer) + s.send(message.encode()) + else: + print("\nTime is up. You got {} point".format(change_style("ZERO", "bold"))) + elif type == MESSAGE_TYPES["answer_response"]: + clear() + self.score = int(parts[0]) + message = parts[1] + self.print_status() + print(change_style(message, "bold")) + print_header("PLEASE WAIT NEXT QUESTION") + elif type == MESSAGE_TYPES["result"]: + clear() + print_header("SCOREBOARD") + for rank, result in enumerate(parts): + username, score = result.split(":") + if username == self.username: + print(change_style( + "{} {} {}".format((str(rank + 1) + ")").ljust(10), username.ljust(30), + score + " points"), + "sender")) + else: + print("{} {} {}".format((str(rank + 1) + ")").ljust(10), username.ljust(30), + score + " points")) + + enter_continue() + break + else: + print(type, parts) + s.close() + + def print_status(self): + print("\n\n") + print("{}: {}".format(change_style("Username", "green").ljust(30), change_style(self.username, "bold"))) + print("{}: {}".format(change_style("Quiz Name", "green").ljust(30), change_style(self.active_quiz, "bold"))) + print("{}: {}".format(change_style("Current Score", "green").ljust(30), change_style(self.score, "bold"))) + print("\n\n") diff --git a/cmpe487/final-project/quizServer.py b/cmpe487/final-project/quizServer.py new file mode 100644 index 0000000..a76b5e5 --- /dev/null +++ b/cmpe487/final-project/quizServer.py @@ -0,0 +1,152 @@ +import threading +from config import * +from utils import * +from quizUtils import * +import time + + +class QuizServer: + + def __init__(self, quiz): + self.quiz = quiz + self.is_started = False + self.is_ended = False + self.current_question = None + self.participants = {} + self.discovery_socket = None + self.quiz_socket = None + + def listen(self): + self.listen_discovery_request() + self.listen_quiz_request() + + def start(self): + self.is_started = True + for number, question in enumerate(self.quiz.questions): + clear() + print_header("Sending question to participants") + print("Question {}: {}\n".format(number + 1, question.body)) + self.current_question = number + message = "{}|{}|{}".format(MESSAGE_TYPES["question"], number + 1, question.body) + for option in question.options: + message += "|" + option + + self.broadcast_message(message) + print_header("Waiting for answers from participants") + start_timer(QUESTION_TIME + 2) + time.sleep(QUESTION_TIME + 2) + clear() + self.print_scores() + if number < len(self.quiz.questions) - 1: + enter_continue() + self.end() + + def end(self): + clear() + message = "{}".format(MESSAGE_TYPES["result"]) + sorted_participants = sorted(self.participants.values(), key=lambda x: x.score, reverse=True) + for p in sorted_participants: + message += "|{}:{}".format(p.username, p.score) + self.broadcast_message(message) + self.print_scores() + for p in self.participants.values(): + p.close() + self.discovery_socket.close() + self.quiz_socket.close() + print_header("END OF THE QUIZ") + enter_continue() + + def print_scores(self): + sorted_participants = sorted(self.participants.values(), key=lambda x: x.score, reverse=True) + print_header("SCORES") + for rank, p in enumerate(sorted_participants): + print("{}) {} {}".format(change_style(rank + 1, "bold").ljust(10), + change_style(p.username, "green").ljust(30), + change_style(str(p.score) + " points", "receiver"))) + + pass + + def receive_discovery_request(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + self.discovery_socket = s + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((SELF_IP, DISCOVERY_PORT)) + s.listen() + while True: + try: + conn, addr = s.accept() + + data = conn.recv(1024) + if not data: + break + + message = str(data.decode('utf-8')) + type, source = message.split('|') + if int(type) == MESSAGE_TYPES["request"]: + response = "{}|{}|{}".format(MESSAGE_TYPES["response"], SELF_IP, self.quiz.name) + conn.send(response.encode()) + conn.close() + except: + pass + + def receive_quiz_request(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + self.quiz_socket = s + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((SELF_IP, QUIZ_PORT)) + s.listen() + while True: + try: + conn, addr = s.accept() + data = conn.recv(1024) + if not data: + break + + message = str(data.decode('utf-8')) + type, source, username = message.split('|') + if int(type) == MESSAGE_TYPES['enter']: + if self.is_started: + conn.send("{}|{}".format(MESSAGE_TYPES["error"], "Quiz is already started").encode()) + else: + participant = Participant(username, source, self.quiz, 0, conn) + participant.start() + self.participants[source] = participant + participant.send_message("{}|{}".format(MESSAGE_TYPES["success"], + "You entered quiz \"{}\" successfully".format( + self.quiz.name))) + + clear() + print_header("QUIZ: " + self.quiz.name) + print(change_style("QUIZ IP: " + change_style(SELF_IP + "\n", 'bold'), 'green')) + self.print_participants() + print("\n\nEnter for start quiz") + except: + pass + + def print_participants(self): + if self.participants: + print(change_style("{} PARTICIPANTS".format(len(self.participants)), 'bold')) + for participant in self.participants.values(): + print(change_style(participant.username, "receiver") + " - " + participant.ip) + else: + print(change_style("NO PARTICIPANTS", 'bold')) + + def listen_discovery_request(self): + discovery_thread = threading.Thread(target=self.receive_discovery_request) + discovery_thread.setDaemon(True) + discovery_thread.start() + + def listen_quiz_request(self): + quiz_thread = threading.Thread(target=self.receive_quiz_request) + quiz_thread.setDaemon(True) + quiz_thread.start() + + def clear_dead_threads(self): + deads = [p.ip for p in self.participants.values() if not p.is_alive()] + for dead in deads: + del self.participants[dead] + + def broadcast_message(self, message): + self.clear_dead_threads() + for participant in self.participants.values(): + participant.send_message(message) diff --git a/cmpe487/final-project/quizUtils.py b/cmpe487/final-project/quizUtils.py new file mode 100644 index 0000000..2ad5883 --- /dev/null +++ b/cmpe487/final-project/quizUtils.py @@ -0,0 +1,97 @@ +from threading import Thread +from utils import change_style, print_header +from config import MESSAGE_TYPES + + +class Quiz: + def __init__(self, name): + self.name = name + self.questions = [] + + def import_file(self, filename): + file = open(filename) + for line in file: + body, correct_answer, *options = line.strip().split("|") + self.new_question(body, options, correct_answer) + + def new_question(self, body, options, correct_answer): + self.questions.append(Question(body, options, correct_answer)) + + def add_question(self, question): + self.questions.append(question) + + def get_correct_answer(self, number): + return self.questions[number - 1].options[self.get_correct_option(number) - 1] + + def get_correct_option(self, number): + return int(self.questions[number - 1].correct_answer) + + def print(self): + print_header("QUIZ: " + self.name) + for number, question in enumerate(self.questions): + print() + print(change_style("Question {}: ".format(number + 1), "question") + change_style(question.body, "bold")) + for i, option in enumerate(question.options): + print(change_style(str(i + 1) + ") ", "bold") + option) + + +class Question: + def __init__(self, body, options, correct_answer): + self.body = body + self.options = options + self.correct_answer = correct_answer + + @staticmethod + def from_input(order): + print(change_style("\n\nQuestion {}".format(order), "bold")) + body = input(change_style("Question body", 'underline') + ": ") + options = [] + for i in range(4): + option = input(change_style("Option {}".format(i + 1), 'underline') + ": ") + options.append(option) + correct_answer = int(input(change_style("Correct answer", 'underline') + ": ")) + return Question(body, options, correct_answer) + + +class Participant(Thread): + def __init__(self, username, ip, quiz, score, connection): + Thread.__init__(self) + self.username = username + self.ip = ip + self.score = score + self.connection = connection + self.quiz = quiz + + def run(self): + while True: + try: + data = self.connection.recv(1024) + if not data: + break + type, *parts = data.decode().split('|') + + if int(type) == MESSAGE_TYPES['answer']: + question_number = int(parts[0]) + answer = int(parts[1]) + correct_answer = self.quiz.get_correct_option(question_number) + if int(correct_answer) == int(answer): + print(change_style("{} answered correct".format(self.username), "green")) + self.score += 100 + message = "{}|{}|{}".format(MESSAGE_TYPES["answer_response"], self.score, + "Congratulations!!! Your answer is correct.") + else: + print(change_style("{} answered wrong".format(self.username), "red")) + message = "{}|{}|{}".format(MESSAGE_TYPES["answer_response"], self.score, + "LOSERRRR!!! Your answer is false. Correct answer is \"{}\"".format( + self.quiz.get_correct_answer(question_number))) + self.send_message(message) + else: + print(type, parts) + except: + pass + + def send_message(self, message): + self.connection.send(message.encode()) + + def close(self): + self.connection.close() diff --git a/cmpe487/final-project/samples/general.txt b/cmpe487/final-project/samples/general.txt new file mode 100644 index 0000000..075f211 --- /dev/null +++ b/cmpe487/final-project/samples/general.txt @@ -0,0 +1,10 @@ +Berlin is the capital of which country?|3|Belgium|Netherlands|Germany|Iran +You want to make green paint. Which colors do you mix together?|2|Red and yellow|Blue and yellow|Orange and purple|White and black +Which is the longest river in the world?|2|Amazon|Nile|Mississippi|Kızılırmak +What language do most people in Austria speak?|1|German|Austrian|Hungarian|Turkish +Which country is closer to the North Pole?|2|Gambia|Finland|China|North Korea +Which chess piece is of the lowest theoretical value?|2|Knight|Pawn|Bishop|Castle +Who was the first person on the moon?|3|John Glenn|Buzz Aldrin|Neil Armstrong|Yuri Gagarin +When French Revolution happened?|1|1789|1453|1994|2012 +The unit of current is _________.|3|Ohm|Watt|Ampere|Tesla +Who is the author of the From the Earth to the Moon?|4|H. G. Wells|Alexandre Dumas|Aldous Huxley|Jules Verne diff --git a/cmpe487/final-project/samples/network.txt b/cmpe487/final-project/samples/network.txt new file mode 100644 index 0000000..94d51ac --- /dev/null +++ b/cmpe487/final-project/samples/network.txt @@ -0,0 +1,10 @@ +What is the maximum bandwidth that can be supported by fiber optics cable? |4|10 Mbps |50 Mbps |100 Mbps |2000 Mbps +Media Access Control (MAC) and Logical Link Control (LLC) are two sublayers of _________________.|4|Session layer|Network layer|Transport layer|Data Link layer +ARPA stands for ___________________________.|3|Advanced Research Projects Administration|Advanced Repeater Projects Agency|Advanced Research Projects Agency|Advanced Rear Projects Agency +Limewire, Kazaa and Bearshare are all examples of ...|3|Web sites that store MP3 music files and Malware|Brand names of networking hardware products|Software applications for peer-to-peer connection (P2P) file sharing|Wireless network communication protocols +You can make telephone calls over the Internet using a technology called ...|1|VoIP|Skynet|Intertel|Telenet +A home computer network is sometimes also called a ...|2|MAN|LAN|SAN|WAN +What protocol is used to find the hardware address of a local device?|2|RARP|ARP|IP|ICMP +IPv4 IP addresses are ___ bit binary numbers?|3|2|8|32|128 +An IP address is divided into what two parts?|3|Physical and logical|Network and subnet|Network and host|Physical and emotional +What protocol is used to automatically assign IP addresses to hosts?|3|WINS|NAT|DHCP|FTP diff --git a/cmpe487/final-project/server.png b/cmpe487/final-project/server.png new file mode 100644 index 0000000..fef7e7b Binary files /dev/null and b/cmpe487/final-project/server.png differ diff --git a/cmpe487/final-project/utils.py b/cmpe487/final-project/utils.py new file mode 100644 index 0000000..2bbf630 --- /dev/null +++ b/cmpe487/final-project/utils.py @@ -0,0 +1,127 @@ +import os +import socket +import sys +import select +import time +from _thread import * + + +def send_packet(host, port, message): + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(5) + s.connect((host, port)) + s.send(message.encode('utf-8')) + s.close() + except Exception as ex: + # print("Error while sending packet: " + message) + # print(ex.__str__() + " " + str(port)) + pass + + +def clear(): + os.system('clear') + + +def select_option(commands, prompt="Please enter your command", is_active=True, header=None, timeout=None): + if header: + print_header(header) + + for i, command in enumerate(commands): + print("\t", change_style(str(i + 1) + ")", 'bold'), " ", command) + + if is_active: + if timeout: + print("\n" + change_style(prompt, 'underline') + ": ") + return timed_input(timeout) + else: + return input("\n" + change_style(prompt, 'underline') + ": ") + else: + print("\n" + change_style(prompt, 'underline') + ": ") + + +def init_timer(count): + count *= 4 + while count >= 0: + print_timer(count) + time.sleep(0.25) + count -= 1 + + +def start_timer(count): + start_new_thread(init_timer, (count,)) + + +def timed_input(timeout=10): + i, o, e = select.select([sys.stdin], [], [], timeout) + if i: + return sys.stdin.readline().strip() + + return None + + +def enter_continue(): + print(change_style("\n\n\nEnter to continue...", 'bold')) + tmp = input() + clear() + + +def change_style(str, style): + if style == "green": + return "\033[92m{}\033[00m".format(str) + elif style == "blue": + return "\033[34m{}\033[00m".format(str) + elif style == "header": + return "\033[34m \033[01m{}\033[00m".format(str) + elif style == "bold": + return "\033[01m{}\033[00m".format(str) + elif style == "red": + return "\033[31m{}\033[00m".format(str) + elif style == "error": + return "\033[41m \033[37m{}\033[00m".format(str) + elif style == "success": + return "\033[42m \033[37m{}\033[00m".format(str) + elif style == "underline": + return "\033[4m{}\033[00m".format(str) + elif style == "receiver": + return "\033[01m\033[35m{}\033[00m".format(str) + elif style == "sender": + return "\033[01m\033[36m{}\033[00m".format(str) + elif style == "question": + return "\033[01m\033[36m{}\033[00m".format(str) + return str + + +def print_notification(str): + print("\a \033[s \033[100F \033[2K \r {} {} \033[u".format(change_style(" [!] ", "bold"), + change_style(str + " ", "success")), end="") + + +def print_timer(count): + if count % 4 == 1: + sym = "/" + elif count % 4 == 2: + sym = "–" + elif count % 4 == 3: + sym = "\\" + else: + sym = "|" + + os.system("tput sc;") + print("\033[100F \033[2K \r{} {}".format( + change_style("Remaining Time: ", "underlined").rjust(50), + change_style(str(count // 4).rjust(2) + " seconds " + sym, "bold")), end="") + os.system("tput rc;") + # os.system("echo `tput ll`;") + # print("\033[s \033[100F \033[2K \r{} {}\033[u".format( + # change_style("Remaining Time: ", "underlined").rjust(50), + # change_style(str(count // 4).rjust(2) + " seconds " + sym, "bold")), end="") + + +def print_error(str): + print("\a \033[s \033[100F \033[2K \r {} {} \033[u".format(change_style(" [x] ", "bold"), + change_style(str + " ", "error")), end="") + + +def print_header(header): + print(change_style("\n\n=== " + header + " ===\n\n", 'header'))