-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
687 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,7 @@ | ||
## Auction App | ||
### [Server](/cmpe436/final-project/Server) `Java` | ||
### [Android App](/cmpe436/final-project/Auctioner) `Java` | ||
||||| | ||
:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------: | ||
<a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss1.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss1.png"></a> | <a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss2.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss2.png"></a> | <a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss3.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss3.png"></a> | <a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss4.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss4.png"></a> | ||
<a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss5.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss5.png"></a> | <a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss6.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss6.png"></a> | <a href="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss7.png"><img src="https://raw.githubusercontent.com/EnesCakir/university-projects/master/cmpe436/final-project/screenshots/ss7.png"></a> | | ||
|
||
### [Server](/cmpe436/final-project/Server) `Java` | ||
### [Android App](/cmpe436/final-project/Auctioner) `Java` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Submodule final-project
deleted from
d3a72a
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# CmpE 487 Final Project | ||
## Network-Based Realtime Multiple-Choice Quiz Application | ||
<p align="center"> | ||
<img src="https://github.com/CMPE487/final-project-quizapp/raw/master/logo.png" width="50%" height="auto" align="center"> | ||
</p> | ||
|
||
- 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). |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
Oops, something went wrong.