diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..c38e534
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/openlearn
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0772f73..bcb87b0 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/OpenLearn/app.py b/OpenLearn/app.py
index 2734107..db8a061 100644
--- a/OpenLearn/app.py
+++ b/OpenLearn/app.py
@@ -6,6 +6,7 @@
from flask import Flask, render_template
+from OpenLearn import extensions, database
from . import views
from . import settings
@@ -13,7 +14,7 @@
def create_app():
"""Create application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
"""
- app = Flask(__name__.split(".")[0])
+ app = Flask(__name__.split(".")[0], instance_relative_config=True)
settings.configure_app(app)
@@ -29,7 +30,7 @@ def create_app():
def register_extensions(app):
"""Register Flask extensions."""
- pass
+ extensions.db.init_app(app)
def register_blueprints(app):
@@ -58,7 +59,16 @@ def register_shellcontext(app):
def shell_context():
"""Shell context objects."""
- return {}
+ return {
+ "db": extensions.db,
+ "User": database.User,
+ "Quiz": database.Quiz,
+ "Question": database.Question,
+ "NumericQuestion": database.NumericQuestion,
+ "TextQuestion": database.TextQuestion,
+ "QuestionType": database.QuestionType,
+ "Room": database.Room
+ }
app.shell_context_processor(shell_context)
diff --git a/OpenLearn/database.py b/OpenLearn/database.py
index e69de29..9b8987c 100644
--- a/OpenLearn/database.py
+++ b/OpenLearn/database.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+import datetime
+import enum
+import random
+import string
+from decimal import Decimal
+from typing import Union, List
+
+from sqlalchemy import func
+
+from .extensions import db
+
+# Alias common SQLAlchemy names
+Column = db.Column
+relationship = db.relationship
+
+# Type Declarations
+MString = Union[str, db.Column]
+MInteger = Union[int, db.Column]
+MDecimal = Union[Decimal, db.Column]
+MBoolean = Union[bool, db.Column]
+MDateTime = Union[datetime.datetime, db.Column]
+MDate = Union[datetime.date, db.Column]
+
+
+class QuestionType(enum.Enum):
+ Numeric = 0
+ Text = 1
+
+ @property
+ def type(self):
+ return {
+ QuestionType.Numeric: NumericQuestion,
+ QuestionType.Text: TextQuestion
+ }[self]
+
+
+class User(db.Model):
+ id: MInteger = db.Column(db.Integer, primary_key=True)
+ username: MString = db.Column(db.String(25), unique=True, nullable=False)
+ password = db.Column(db.Binary(60), nullable=False)
+
+ quizzes: List["Quiz"] = relationship("Quiz", back_populates="owner")
+
+ created_on: MDateTime = db.Column(db.DateTime(timezone=True), nullable=False, server_default=func.now())
+
+
+class Quiz(db.Model):
+ id: MInteger = db.Column(db.Integer, primary_key=True)
+ name: MString = db.Column(db.String(30), unique=True, nullable=False)
+
+ questions: List[Union["Question", "QuestionABC"]] = db.relationship('Question', back_populates="quiz",
+ order_by="Question.sort_index")
+
+ owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
+ owner = db.relationship("User", back_populates="quizzes")
+
+ created_on: MDateTime = db.Column(db.DateTime(timezone=True), nullable=False, server_default=func.now())
+
+
+class Question(db.Model):
+ __mapper_args__ = {'polymorphic_on': "type"}
+
+ id: MInteger = db.Column(db.Integer, primary_key=True)
+ text: MString = db.Column(db.Text, nullable=False)
+ sort_index = db.Column(db.Integer, nullable=False)
+
+ type: QuestionType = db.Column(db.Enum(QuestionType), nullable=False)
+
+ quiz_id = db.Column(db.Integer, db.ForeignKey('quiz.id'), nullable=False)
+ quiz: Quiz = db.relationship('Quiz', back_populates="questions")
+
+ def __init__(self, *args, **kwargs):
+ raise NotImplementedError
+
+ def __eq__(self, other):
+ raise NotImplementedError
+
+
+class NumericQuestion(Question):
+ __tablename__ = "numericQuestion"
+ __mapper_args__ = {'polymorphic_identity': QuestionType.Numeric}
+
+ id = Column(db.Integer, db.ForeignKey('question.id'), primary_key=True)
+ answer = db.Column(db.Numeric(precision=12, scale=5)) # 0000000.00000
+
+ def __init__(self, *args, **kwargs):
+ super(Question, self).__init__(*args, **kwargs)
+
+ def __eq__(self, other):
+ return other == self.answer
+
+
+class TextQuestion(Question):
+ __tablename__ = "textQuestion"
+ __mapper_args__ = {'polymorphic_identity': QuestionType.Text}
+
+ id = Column(db.Integer, db.ForeignKey('question.id'), primary_key=True)
+ answer = db.Column(db.Text)
+
+ def __init__(self, *args, **kwargs):
+ super(Question, self).__init__(*args, **kwargs)
+
+ def __eq__(self, other):
+ return other == self.answer
+
+
+class Room(db.Model):
+ id: MInteger = db.Column(db.Integer, primary_key=True)
+ active: MBoolean = db.Column(db.Boolean, default=False)
+ key: MString = db.Column(db.String(8), default=lambda: Room.generate_key())
+
+ controller_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
+ controller = db.relationship("User")
+
+ quiz_id = db.Column(db.Integer, db.ForeignKey('quiz.id'), nullable=False)
+ quiz: Quiz = db.relationship('Quiz')
+
+ created_on: MDateTime = db.Column(db.DateTime(timezone=True), nullable=False, server_default=func.now())
+
+ @staticmethod
+ def generate_key():
+ while True:
+ temp_key = ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
+ if not db.session.query(db.exists().where(Room.active, Room.key == temp_key)).scalar():
+ return temp_key
diff --git a/OpenLearn/extensions.py b/OpenLearn/extensions.py
index e69de29..6aeb491 100644
--- a/OpenLearn/extensions.py
+++ b/OpenLearn/extensions.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+from flask_sqlalchemy import SQLAlchemy
+
+db = SQLAlchemy()
diff --git a/OpenLearn/settings.py b/OpenLearn/settings.py
index 42eacbc..03bff99 100644
--- a/OpenLearn/settings.py
+++ b/OpenLearn/settings.py
@@ -15,10 +15,12 @@ class BaseConfig(object):
TESTING = False
VERSION = OpenLearn.__version__
+ SQLALCHEMY_TRACK_MODIFICATIONS = False
+
class DevelopmentConfig(BaseConfig):
DEBUG = True
- TESTING = True
+ TESTING = False
VERSION = f"{OpenLearn.__version__} dev"
@@ -34,4 +36,4 @@ class ProductionConfig(BaseConfig):
def configure_app(app):
config_name = os.getenv("FLASK_ENV", "default")
app.config.from_object(config[config_name])
- app.config.from_pyfile('config.cfg', silent=True)
+ app.config.from_pyfile('config.cfg', silent=False)
diff --git a/OpenLearn/templates/layout.html b/OpenLearn/templates/layout.html
index 5f004b5..b70e7ed 100644
--- a/OpenLearn/templates/layout.html
+++ b/OpenLearn/templates/layout.html
@@ -40,7 +40,7 @@
{% block footer %}
{% endblock %}
diff --git a/OpenLearn/templates/student/join.html b/OpenLearn/templates/student/join.html
index cc574a5..744ccc5 100644
--- a/OpenLearn/templates/student/join.html
+++ b/OpenLearn/templates/student/join.html
@@ -47,6 +47,14 @@
#room-code-input {
text-transform: uppercase;
}
+
+ .siimple-footer-link {
+ color: white;
+ }
+
+ .siimple-footer-link:hover {
+ color: rgba(255, 255, 255, 0.4) !important;
+ }
{% endblock %}
diff --git a/README.md b/README.md
index 29e9177..bd2623d 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
---
- A open source web application which will enable cooperative learning with students.
+ An open source platform for collaborative learning.
## Built Using