diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..8e9f4000 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,41 @@ +version: 2 # use CircleCI 2.0 +jobs: # A basic unit of work in a run + build: # runs not using Workflows must have a `build` job as entry point + # directory where steps are run + working_directory: ~/circleci-demo-python-django + docker: # run the steps with Docker + # CircleCI Python images available at: https://hub.docker.com/r/circleci/python/ + - image: circleci/python:3.6.4 + environment: # environment variables for primary container + PIPENV_VENV_IN_PROJECT: true + DATABASE_URL: postgresql://root@localhost/circle_test?sslmode=disable + # CircleCI PostgreSQL images available at: https://hub.docker.com/r/circleci/postgres/ + - image: circleci/postgres:9.6.2 + environment: # environment variables for the Postgres container. + POSTGRES_USER: root + POSTGRES_DB: circle_test + steps: # steps that comprise the `build` job + - checkout # check out source code to working directory + - run: sudo chown -R circleci:circleci /usr/local/bin + - run: sudo chown -R circleci:circleci /usr/local/lib/python3.6/site-packages + - restore_cache: + # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ + key: deps9-{{ .Branch }}-{{ checksum "Pipfile.lock" }} + - run: + command: | + sudo pip install pipenv + pipenv install + - save_cache: # cache Python dependencies using checksum of Pipfile as the cache-key + key: deps9-{{ .Branch }}-{{ checksum "Pipfile.lock" }} + paths: + - ".venv" + - "/usr/local/bin" + - "/usr/local/lib/python3.6/site-packages" + - run: + command: | + pipenv run python django_school/manage.py test + - store_test_results: # Upload test results for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/ + path: test-results + - store_artifacts: # Upload test summary for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ + path: test-results + destination: tr1 diff --git a/.gitignore b/.gitignore index 9776247c..69d10aa2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +media/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/README.md b/README.md index 32f75b5f..9754fc64 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -# Django School +# [Django School](https://djangoschools.herokuapp.com/) [](https://python.org) -[](https://djangoproject.com) +[](https://djangoproject.com) +[](https://circleci.com/gh/suhailvs/django-schools) + +## [demo](https://djangoschools.herokuapp.com/) This is an example project to illustrate an implementation of multiple user types. In this Django app, teachers can create quizzes and students can sign up and take quizzes related to their interests. @@ -14,28 +17,38 @@ Read the blog post [How to Implement Multiple User Types with Django](https://si First, clone the repository to your local machine: ```bash -git clone https://github.com/sibtc/django-multiple-user-types-example.git +git clone https://github.com/suhailvs/django-schools ``` -Install the requirements: +Create Virtual Env and Install the requirements: ```bash +cd django-schools +python3 -m venv env +source ./env/bin/activate pip install -r requirements.txt ``` -Create the database: +Create the database and run the development server: ```bash +cd django_school python manage.py migrate +python manage.py loaddata datas.json +python manage.py runserver ``` -Finally, run the development server: +The project will be available at http://127.0.0.1:8000, Login using:: -```bash -python manage.py runserver -``` +**Teacher** + ++ username: `teacher` ++ password: `teacher` + +**Student** -The project will be available at **127.0.0.1:8000**. ++ username: `student` ++ password: `student` ## License diff --git a/django_school/classroom/admin.py b/django_school/classroom/admin.py new file mode 100644 index 00000000..ed64a915 --- /dev/null +++ b/django_school/classroom/admin.py @@ -0,0 +1,12 @@ +from django.contrib import admin + +# Register your models here. +from .models import User, Subject, Quiz, Question, Answer, Student, TakenQuiz + +admin.site.register(User) +admin.site.register(Subject) +admin.site.register(Quiz) +admin.site.register(Question) +admin.site.register(Answer) +admin.site.register(Student) +admin.site.register(TakenQuiz) \ No newline at end of file diff --git a/django_school/classroom/fixtures/datas.json b/django_school/classroom/fixtures/datas.json new file mode 100644 index 00000000..3f11d1af --- /dev/null +++ b/django_school/classroom/fixtures/datas.json @@ -0,0 +1,128 @@ +[ + {"model": "classroom.question", "pk": 1, "fields": {"quiz": 1, "text": "When was the Treaty of Versailles signed?"}}, + {"model": "classroom.question", "pk": 2, "fields": {"quiz": 1, "text": "Britain, France, USA, Germany, Italy, and Russia."}}, + {"model": "classroom.question", "pk": 3, "fields": {"quiz": 1, "text": "The war ended on November 11 1918."}}, + {"model": "classroom.question", "pk": 4, "fields": {"quiz": 1, "text": "The name of the new government in Germany was called the Wiemar Republic."}}, + + {"model": "classroom.question", "pk": 5, "fields": {"quiz": 2, "text": "An advantage of using solar power is"}}, + {"model": "classroom.question", "pk": 6, "fields": {"quiz": 2, "text": "An advantage of using hydroelectric power is"}}, + {"model": "classroom.question", "pk": 7, "fields": {"quiz": 2, "text": "Most utility companies in the U.S. dont use hydroelectric power because"}}, + {"model": "classroom.question", "pk": 8, "fields": {"quiz": 2, "text": "Which energy source would be best for a city that has limited space and want to be effecient?"}}, + {"model": "classroom.question", "pk": 9, "fields": {"quiz": 2, "text": "What is a major disadvantage of nuclear power?"}}, + {"model": "classroom.question", "pk": 10, "fields": {"quiz": 2, "text": "If a community were choosing between solar and wind; what advantage would make wind the best choice?"}}, + {"model": "classroom.question", "pk": 11, "fields": {"quiz": 2, "text": "An advantage of burning coal for energy is"}}, + {"model": "classroom.question", "pk": 12, "fields": {"quiz": 2, "text": "An advantage of using natural gas as a form of energy is"}}, + {"model": "classroom.question", "pk": 13, "fields": {"quiz": 2, "text": "What do geothermal and solar energy have in common?"}}, + {"model": "classroom.question", "pk": 14, "fields": {"quiz": 3, "text": "What happens when url.py file is edited while the development server is still running?"}}, + {"model": "classroom.question", "pk": 15, "fields": {"quiz": 3, "text": "Which setting contains the parameter of main-urls file?"}}, + {"model": "classroom.question", "pk": 16, "fields": {"quiz": 3, "text": "What is the purpose of __init__.py in project directories?"}}, + {"model": "classroom.question", "pk": 17, "fields": {"quiz": 3, "text": "Which method is used instead of path() in urls.py to pass in regular expressions as routes?"}}, + {"model": "classroom.question", "pk": 18, "fields": {"quiz": 3, "text": "What does {{ forloop.counter }} inside a forloop tag prints?"}}, + {"model": "classroom.question", "pk": 19, "fields": {"quiz": 3, "text": "This template {# #} is used for?"}}, + {"model": "classroom.question", "pk": 20, "fields": {"quiz": 4, "text": "What does {% include %} does?"}}, + {"model": "classroom.question", "pk": 21, "fields": {"quiz": 4, "text": "What would \"Post.objects.filter(heading=\"Post 1\")\""}}, + {"model": "classroom.question", "pk": 22, "fields": {"quiz": 4, "text": "Suppose you want to count the number of books in Django with \"books = Book.objects.all()\". Which implementation would be fastest?"}}, + {"model": "classroom.question", "pk": 23, "fields": {"quiz": 5, "text": "What is the name of the file that Django provides for managing a newly-created project and its applications?"}}, + {"model": "classroom.question", "pk": 24, "fields": {"quiz": 5, "text": "What task do migrations automate?"}}, + {"model": "classroom.question", "pk": 25, "fields": {"quiz": 5, "text": "What kind of relation is defined between models Order and OrderItems by invoking ForeignKey(Order) in the class definition for model OrderItems?"}}, + + {"model": "classroom.answer", "pk": 1, "fields": {"question": 1, "text": "June 29 of 1919", "is_correct": true}}, + {"model": "classroom.answer", "pk": 2, "fields": {"question": 1, "text": "June 29 of 1920", "is_correct": false}}, + {"model": "classroom.answer", "pk": 3, "fields": {"question": 1, "text": "March 29 of 1919", "is_correct": false}}, + {"model": "classroom.answer", "pk": 4, "fields": {"question": 1, "text": "August 29 of 1920", "is_correct": false}}, + {"model": "classroom.answer", "pk": 5, "fields": {"question": 2, "text": "How much money did Germany have to pay as a reparation?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 6, "fields": {"question": 2, "text": "Which countries were involved in World War 1?", "is_correct": true}}, + {"model": "classroom.answer", "pk": 7, "fields": {"question": 2, "text": "What does BRAT stand for in the Treaty of Versailles?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 8, "fields": {"question": 2, "text": "What does MAIN stand for when it comes to the causes of the first world war?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 9, "fields": {"question": 3, "text": "When was the Treaty of Versailles signed?", "is_correct": true}}, + {"model": "classroom.answer", "pk": 10, "fields": {"question": 3, "text": "Why did Germany declare war on Russia?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 11, "fields": {"question": 3, "text": "When did World War 1 begin?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 12, "fields": {"question": 3, "text": "When did World War 1 end?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 13, "fields": {"question": 4, "text": "What does MAIN stand for when it comes to the causes of the first world war?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 14, "fields": {"question": 4, "text": "What was the name of the new government in Germany after World War 1?", "is_correct": true}}, + {"model": "classroom.answer", "pk": 15, "fields": {"question": 4, "text": "Were the Big Three happy about the outcome of the Treaty of Versailles?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 16, "fields": {"question": 4, "text": "How much money did Germany have to pay as a reparation?", "is_correct": false}}, + {"model": "classroom.answer", "pk": 17, "fields": {"question": 5, "text": "no greenhouse gases", "is_correct": true}}, + {"model": "classroom.answer", "pk": 18, "fields": {"question": 5, "text": "lots of pollution", "is_correct": false}}, + {"model": "classroom.answer", "pk": 19, "fields": {"question": 5, "text": "it is available even on cloudy days", "is_correct": false}}, + {"model": "classroom.answer", "pk": 20, "fields": {"question": 6, "text": "reservoirs can be used for irrigation of crops", "is_correct": true}}, + {"model": "classroom.answer", "pk": 21, "fields": {"question": 6, "text": "sometimes surrounding area get flooded", "is_correct": false}}, + {"model": "classroom.answer", "pk": 22, "fields": {"question": 6, "text": "the normal flow of the water is diverted", "is_correct": false}}, + {"model": "classroom.answer", "pk": 23, "fields": {"question": 7, "text": "the plants are expensive to build and use expensive machinery", "is_correct": true}}, + {"model": "classroom.answer", "pk": 24, "fields": {"question": 7, "text": "reservoirs can be used to irrigate crops and they dont want to help farmers", "is_correct": false}}, + {"model": "classroom.answer", "pk": 25, "fields": {"question": 7, "text": "the cause a lot of greenhouse gass", "is_correct": false}}, + {"model": "classroom.answer", "pk": 26, "fields": {"question": 8, "text": "solar-costly to build and 6%-30% efficient", "is_correct": false}}, + {"model": "classroom.answer", "pk": 27, "fields": {"question": 8, "text": "Wind-turbines need lots of free space 40%-60% efficient", "is_correct": false}}, + {"model": "classroom.answer", "pk": 28, "fields": {"question": 8, "text": "Natural gas-can be used in small areas 50%-60% efficient", "is_correct": true}}, + {"model": "classroom.answer", "pk": 29, "fields": {"question": 9, "text": "it produces small amounts of power", "is_correct": false}}, + {"model": "classroom.answer", "pk": 30, "fields": {"question": 9, "text": "the by-product(waste) is nuclear radiation", "is_correct": true}}, + {"model": "classroom.answer", "pk": 31, "fields": {"question": 9, "text": "power plants are inexpensive to build", "is_correct": false}}, + {"model": "classroom.answer", "pk": 32, "fields": {"question": 10, "text": "locations for turbines are limited because the wind can be blocked", "is_correct": false}}, + {"model": "classroom.answer", "pk": 33, "fields": {"question": 10, "text": "solar needs a back up system for cloudy days", "is_correct": false}}, + {"model": "classroom.answer", "pk": 34, "fields": {"question": 10, "text": "wind energy is less expensive and it is limitless", "is_correct": true}}, + {"model": "classroom.answer", "pk": 35, "fields": {"question": 11, "text": "it is an inexpensive source of energy", "is_correct": true}}, + {"model": "classroom.answer", "pk": 36, "fields": {"question": 11, "text": "it does not pollute the air", "is_correct": false}}, + {"model": "classroom.answer", "pk": 37, "fields": {"question": 11, "text": "it is a renewable energy source", "is_correct": false}}, + {"model": "classroom.answer", "pk": 38, "fields": {"question": 12, "text": "it is more expensive compared to other fossil fuels", "is_correct": false}}, + {"model": "classroom.answer", "pk": 39, "fields": {"question": 12, "text": "It produces low emissions(pollution) compared to other fossil fuels", "is_correct": true}}, + {"model": "classroom.answer", "pk": 40, "fields": {"question": 12, "text": "there is a more limited supply compared to other fossil fuels", "is_correct": false}}, + {"model": "classroom.answer", "pk": 41, "fields": {"question": 13, "text": "they both use sunlight to generate energy", "is_correct": false}}, + {"model": "classroom.answer", "pk": 42, "fields": {"question": 13, "text": "both are non-renewable energy sources", "is_correct": false}}, + {"model": "classroom.answer", "pk": 43, "fields": {"question": 13, "text": "neither produce greenhouse gass", "is_correct": true}}, + {"model": "classroom.answer", "pk": 44, "fields": {"question": 14, "text": "Development server terminates.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 45, "fields": {"question": 14, "text": "The development server automatically restarts.", "is_correct": true}}, + {"model": "classroom.answer", "pk": 46, "fields": {"question": 14, "text": "The development server does nothing.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 47, "fields": {"question": 14, "text": "The web page is automatically reloaded.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 48, "fields": {"question": 15, "text": "ROOT_URLCONF", "is_correct": true}}, + {"model": "classroom.answer", "pk": 49, "fields": {"question": 15, "text": "MAIN_URLCONF", "is_correct": false}}, + {"model": "classroom.answer", "pk": 50, "fields": {"question": 15, "text": "STATIC_URL", "is_correct": false}}, + {"model": "classroom.answer", "pk": 51, "fields": {"question": 15, "text": "MEDIA_URL", "is_correct": false}}, + {"model": "classroom.answer", "pk": 52, "fields": {"question": 16, "text": "It allows Python to recognise the folder as package.", "is_correct": true}}, + {"model": "classroom.answer", "pk": 53, "fields": {"question": 16, "text": "It is useless and can be deleted.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 54, "fields": {"question": 16, "text": "It is used to initialise any empty values.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 55, "fields": {"question": 16, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 56, "fields": {"question": 17, "text": "static()", "is_correct": false}}, + {"model": "classroom.answer", "pk": 57, "fields": {"question": 17, "text": "re_path()", "is_correct": true}}, + {"model": "classroom.answer", "pk": 58, "fields": {"question": 17, "text": "include()", "is_correct": false}}, + {"model": "classroom.answer", "pk": 59, "fields": {"question": 17, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 60, "fields": {"question": 18, "text": "The current iteration of the loop (1-indexed)", "is_correct": true}}, + {"model": "classroom.answer", "pk": 61, "fields": {"question": 18, "text": "The current iteration of the loop (0-indexed)", "is_correct": false}}, + {"model": "classroom.answer", "pk": 62, "fields": {"question": 18, "text": "It will not print \"loop variable is not defined\"", "is_correct": false}}, + {"model": "classroom.answer", "pk": 63, "fields": {"question": 18, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 64, "fields": {"question": 19, "text": "It is used for business logic.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 65, "fields": {"question": 19, "text": "It is comment in template language.", "is_correct": true}}, + {"model": "classroom.answer", "pk": 66, "fields": {"question": 19, "text": "It will raise an exception.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 67, "fields": {"question": 19, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 68, "fields": {"question": 20, "text": "It will include another template.", "is_correct": true}}, + {"model": "classroom.answer", "pk": 69, "fields": {"question": 20, "text": "It will include content from another template having the same templates defined.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 70, "fields": {"question": 20, "text": "It is the same as {% extend %}.", "is_correct": false}}, + {"model": "classroom.answer", "pk": 71, "fields": {"question": 20, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 72, "fields": {"question": 21, "text": "It will print the first object which matched with heading \"Post 1\"", "is_correct": false}}, + {"model": "classroom.answer", "pk": 73, "fields": {"question": 21, "text": "It will print all objects", "is_correct": false}}, + {"model": "classroom.answer", "pk": 74, "fields": {"question": 21, "text": "It will print all the objects which match the heading \"Post 1\"", "is_correct": true}}, + {"model": "classroom.answer", "pk": 75, "fields": {"question": 22, "text": "Database level Implementation - books.count()", "is_correct": true}}, + {"model": "classroom.answer", "pk": 76, "fields": {"question": 22, "text": "Python Implementation - len(books)", "is_correct": false}}, + {"model": "classroom.answer", "pk": 77, "fields": {"question": 22, "text": "Template Language Implementation - {{ books | length }}", "is_correct": false}}, + {"model": "classroom.answer", "pk": 78, "fields": {"question": 22, "text": "None of the above", "is_correct": false}}, + {"model": "classroom.answer", "pk": 79, "fields": {"question": 23, "text": "django", "is_correct": false}}, + {"model": "classroom.answer", "pk": 80, "fields": {"question": 23, "text": "manage.py", "is_correct": true}}, + {"model": "classroom.answer", "pk": 81, "fields": {"question": 23, "text": "apachectl", "is_correct": false}}, + {"model": "classroom.answer", "pk": 82, "fields": {"question": 23, "text": "pip", "is_correct": false}}, + {"model": "classroom.answer", "pk": 83, "fields": {"question": 24, "text": "Structural changes to project database tables to reflect changes to data models", "is_correct": true}}, + {"model": "classroom.answer", "pk": 84, "fields": {"question": 24, "text": "Deployment of project data to production server", "is_correct": false}}, + {"model": "classroom.answer", "pk": 85, "fields": {"question": 24, "text": "Internationalization of site content to reflect local differences in local date and currency formatting conventions", "is_correct": false}}, + {"model": "classroom.answer", "pk": 86, "fields": {"question": 25, "text": "Many to one: Multiple OrderItems can have same Order", "is_correct": true}}, + {"model": "classroom.answer", "pk": 87, "fields": {"question": 25, "text": "One to one: Each OrderItems must have Unique Order", "is_correct": false}}, + {"model": "classroom.answer", "pk": 88, "fields": {"question": 25, "text": "One to many: Each OrderItems can have Multiple Order", "is_correct": false}}, + + {"model": "classroom.quiz", "pk": 1, "fields": {"owner": 1, "name": "World War 1", "subject": 5}}, + {"model": "classroom.quiz", "pk": 2, "fields": {"owner": 1, "name": "EnergySources", "subject": 4}}, + {"model": "classroom.quiz", "pk": 3, "fields": {"owner": 1, "name": "Django2", "subject": 2}}, + {"model": "classroom.quiz", "pk": 4, "fields": {"owner": 1, "name": "Django3", "subject": 2}}, + {"model": "classroom.quiz", "pk": 5, "fields": {"owner": 1, "name": "Django1", "subject": 2}}, + + {"model": "classroom.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$180000$og6o8PzLsRNC$/Ay4/y7/plwM2t3MGrzUoGvTKCqRlrSYmCwrkL9VrVY=", "last_login": "2020-03-19T02:20:24.490Z", "is_superuser": true, "username": "teacher", "first_name": "Teacher", "last_name": "", "email": "", "is_staff": true, "is_active": true, "date_joined": "2020-03-19T02:20:24.014Z", "is_student": false, "is_teacher": true, "groups": [], "user_permissions": []}}, + {"model": "classroom.user", "pk": 2, "fields": {"password": "pbkdf2_sha256$180000$DBSRJ8mc0QUn$aCBAYWUqewnK+nJ1FqSBQpnvY33P/FpK2CAYyQuX1p0=", "last_login": "2020-03-19T02:19:48.856Z", "is_superuser": false, "username": "student", "first_name": "Student", "last_name": "", "email": "", "is_staff": false, "is_active": true, "date_joined": "2020-03-19T02:19:48.411Z", "is_student": true, "is_teacher": false, "groups": [], "user_permissions": []}}, + + {"model": "classroom.student", "pk": 2, "fields": {"score": 0, "interests": [2, 3, 4, 5]}} +] \ No newline at end of file diff --git a/django_school/classroom/fixtures/quizzes.json b/django_school/classroom/fixtures/quizzes.json new file mode 100644 index 00000000..edf14252 --- /dev/null +++ b/django_school/classroom/fixtures/quizzes.json @@ -0,0 +1,39 @@ +[{"model": "classroom.quiz", "pk": 2, "fields": {"owner": 1, "name": "KEAM 2017 paper 1", "subject": 3}}, + +{"model": "classroom.answer", "pk": 1, "fields": {"question": 1, "text": "$$\\ce{H2O + HCl <=> H3O+ + Cl-}$$", "is_correct": true}}, +{"model": "classroom.answer", "pk": 2, "fields": {"question": 1, "text": "gdfs", "is_correct": false}}, +{"model": "classroom.answer", "pk": 3, "fields": {"question": 2, "text": "(a) and (b)", "is_correct": true}}, +{"model": "classroom.answer", "pk": 4, "fields": {"question": 2, "text": "(a) and (c)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 5, "fields": {"question": 2, "text": "(a), (b) and (c)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 6, "fields": {"question": 2, "text": "all", "is_correct": false}}, +{"model": "classroom.answer", "pk": 7, "fields": {"question": 3, "text": "combination reaction.", "is_correct": false}}, +{"model": "classroom.answer", "pk": 8, "fields": {"question": 3, "text": "double displacement reaction.", "is_correct": false}}, +{"model": "classroom.answer", "pk": 9, "fields": {"question": 3, "text": "decomposition reaction.", "is_correct": false}}, +{"model": "classroom.answer", "pk": 10, "fields": {"question": 3, "text": "displacement reaction.", "is_correct": true}}, +{"model": "classroom.answer", "pk": 11, "fields": {"question": 4, "text": "Hydrogen gas and iron chloride are produced.", "is_correct": true}}, +{"model": "classroom.answer", "pk": 12, "fields": {"question": 4, "text": "Chlorine gas and iron hydroxide are produced.", "is_correct": false}}, +{"model": "classroom.answer", "pk": 13, "fields": {"question": 4, "text": "No reaction takes place.", "is_correct": false}}, +{"model": "classroom.answer", "pk": 14, "fields": {"question": 4, "text": "Iron salt and water are produce", "is_correct": false}}, + +{"model": "classroom.answer", "pk": 15, "fields": {"question": 5, "text": "45", "is_correct": false}}, +{"model": "classroom.answer", "pk": 16, "fields": {"question": 5, "text": "45\\(\\pi\\)", "is_correct": true}}, +{"model": "classroom.answer", "pk": 17, "fields": {"question": 5, "text": "250\\(\\pi\\)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 18, "fields": {"question": 5, "text": "250", "is_correct": false}}, +{"model": "classroom.answer", "pk": 19, "fields": {"question": 5, "text": "450", "is_correct": false}}, +{"model": "classroom.answer", "pk": 20, "fields": {"question": 6, "text": "can be decreased by increasing the number of readings and averaging them", "is_correct": true}}, +{"model": "classroom.answer", "pk": 21, "fields": {"question": 6, "text": "can be decreased by changing the person who takes the reading", "is_correct": false}}, +{"model": "classroom.answer", "pk": 22, "fields": {"question": 6, "text": "can be decreased by using new instrument", "is_correct": false}}, +{"model": "classroom.answer", "pk": 23, "fields": {"question": 6, "text": "can be decreased by using a different method in taking the reading", "is_correct": false}}, +{"model": "classroom.answer", "pk": 24, "fields": {"question": 6, "text": "can never be decreased", "is_correct": false}}, +{"model": "classroom.answer", "pk": 25, "fields": {"question": 7, "text": "4.99 s", "is_correct": true}}, +{"model": "classroom.answer", "pk": 26, "fields": {"question": 7, "text": "5.0 s", "is_correct": false}}, +{"model": "classroom.answer", "pk": 27, "fields": {"question": 7, "text": "5.00 s", "is_correct": false}}, +{"model": "classroom.answer", "pk": 28, "fields": {"question": 7, "text": "4.9 s", "is_correct": false}}, +{"model": "classroom.answer", "pk": 29, "fields": {"question": 7, "text": "5.1 s", "is_correct": false}}, +{"model": "classroom.answer", "pk": 30, "fields": {"question": 8, "text": "\\(40 km/h^2\\)", "is_correct": true}}, +{"model": "classroom.answer", "pk": 31, "fields": {"question": 8, "text": "\\(80 km/h^2\\)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 32, "fields": {"question": 8, "text": "\\(100 km/h^2\\)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 33, "fields": {"question": 8, "text": "\\(120 km/h^2\\)", "is_correct": false}}, +{"model": "classroom.answer", "pk": 34, "fields": {"question": 8, "text": "\\(160 km/h^2\\)", "is_correct": false}} + +] diff --git a/django_school/classroom/functional_tests.py b/django_school/classroom/functional_tests.py new file mode 100644 index 00000000..a4a0ba17 --- /dev/null +++ b/django_school/classroom/functional_tests.py @@ -0,0 +1,8 @@ +# pip install selenium +# https://askubuntu.com/questions/870530/how-to-install-geckodriver-in-ubuntu + +from selenium import webdriver +browser = webdriver.Firefox() +browser.get('http://localhost:8000') + +assert 'Django' in browser.title \ No newline at end of file diff --git a/django_school/classroom/migrations/0003_auto_20191008_0704.py b/django_school/classroom/migrations/0003_auto_20191008_0704.py new file mode 100644 index 00000000..dec5de52 --- /dev/null +++ b/django_school/classroom/migrations/0003_auto_20191008_0704.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.3 on 2019-10-08 07:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('classroom', '0002_create_initial_subjects'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='score', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='takenquiz', + name='percentage', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AlterField( + model_name='takenquiz', + name='score', + field=models.IntegerField(), + ), + ] diff --git a/django_school/classroom/migrations/0004_auto_20200418_1555.py b/django_school/classroom/migrations/0004_auto_20200418_1555.py new file mode 100644 index 00000000..ec1a7895 --- /dev/null +++ b/django_school/classroom/migrations/0004_auto_20200418_1555.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.4 on 2020-04-18 15:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('classroom', '0003_auto_20191008_0704'), + ] + + operations = [ + migrations.AlterField( + model_name='question', + name='text', + field=models.TextField(verbose_name='Question'), + ), + ] diff --git a/django_school/classroom/models.py b/django_school/classroom/models.py index 31fa36d1..75e7c38a 100644 --- a/django_school/classroom/models.py +++ b/django_school/classroom/models.py @@ -33,7 +33,7 @@ def __str__(self): class Question(models.Model): quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='questions') - text = models.CharField('Question', max_length=255) + text = models.TextField('Question') def __str__(self): return self.text @@ -52,6 +52,9 @@ class Student(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) quizzes = models.ManyToManyField(Quiz, through='TakenQuiz') interests = models.ManyToManyField(Subject, related_name='interested_students') + + # User reputation score. + score = models.IntegerField(default=0) def get_unanswered_questions(self, quiz): answered_questions = self.quiz_answers \ @@ -67,7 +70,8 @@ def __str__(self): class TakenQuiz(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='taken_quizzes') quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='taken_quizzes') - score = models.FloatField() + score = models.IntegerField() + percentage = models.FloatField() date = models.DateTimeField(auto_now_add=True) diff --git a/django_school/classroom/templates/classroom/_guest_header.html b/django_school/classroom/templates/classroom/_guest_header.html new file mode 100644 index 00000000..90cba8ff --- /dev/null +++ b/django_school/classroom/templates/classroom/_guest_header.html @@ -0,0 +1,15 @@ +
+ + + diff --git a/django_school/classroom/templates/classroom/home.html b/django_school/classroom/templates/classroom/about.html similarity index 73% rename from django_school/classroom/templates/classroom/home.html rename to django_school/classroom/templates/classroom/about.html index 4fde1c22..e79005ff 100644 --- a/django_school/classroom/templates/classroom/home.html +++ b/django_school/classroom/templates/classroom/about.html @@ -1,10 +1,11 @@ {% extends 'base.html' %} {% block content %} +{% include 'classroom/_guest_header.html' with active='about' %}If you already have an account, go ahead and log in. If you are new to Django Schools, get started - by creating a student account or a teacher account. + by creating a student account or a teacher account.
Want to run this code locally? Read detailed instructions on how to run this project.
-Vitor Freitas
@vitorfs
Vitor Freitas @vitorfs, Suhail VS @suhailvs
{% endblock %} diff --git a/django_school/classroom/templates/classroom/quiz_list.html b/django_school/classroom/templates/classroom/quiz_list.html new file mode 100644 index 00000000..392aef84 --- /dev/null +++ b/django_school/classroom/templates/classroom/quiz_list.html @@ -0,0 +1,48 @@ +{% extends 'base.html' %} +{% load static %} + +{% block css %} + +{% endblock %} + +{% block content %} + {% include 'classroom/_guest_header.html' with active='quizzes' %} + +Quiz | +Subject | +Questions | ++ |
---|---|---|---|
{{ quiz.name }} | +{{ quiz.subject.get_html_badge }} | +{{ quiz.questions_count }} | ++ Start quiz + | +
No quiz matching your interests right now. | +
- Subjects:{% for subject in user.student.interests.all %} {{ subject.get_html_badge }}{% endfor %} - (update interests) -
+ diff --git a/django_school/classroom/templates/classroom/students/quiz_list.html b/django_school/classroom/templates/classroom/students/quiz_list.html index dcef90b4..d0007373 100644 --- a/django_school/classroom/templates/classroom/students/quiz_list.html +++ b/django_school/classroom/templates/classroom/students/quiz_list.html @@ -1,33 +1,52 @@ {% extends 'base.html' %} +{% load static %} + +{% block css %} + +{% endblock %} {% block content %} {% include 'classroom/students/_header.html' with active='new' %} -Quiz | +Subject | +Questions | ++ | ||||
---|---|---|---|---|---|---|---|
{{ quiz.name }} | +{{ quiz.subject.get_html_badge }} | +{{ quiz.questions_count }} | ++ Start quiz + | +||||
Quiz | -Subject | -Length | -+ | No quiz matching your interests right now. | |||
{{ quiz.name }} | -{{ quiz.subject.get_html_badge }} | -{{ quiz.questions_count }} questions | -- Start quiz - | -||||
No quiz matching your interests right now. | -
Yours | Correct | |
---|---|---|
+ | + | {{opt.text}} | +
{{student.user.email}} | |
Username | {{student.user.username}} |
Fullname | {{student.user.get_full_name}} |
POINTS | {{student.score}} |
No student matched your search.
{{ question.text }}
{% endblock %} + +{% block js %} + + +{% endblock %} \ No newline at end of file diff --git a/django_school/classroom/templates/classroom/students/taken_quiz_list.html b/django_school/classroom/templates/classroom/students/taken_quiz_list.html index 56c62553..0207e775 100644 --- a/django_school/classroom/templates/classroom/students/taken_quiz_list.html +++ b/django_school/classroom/templates/classroom/students/taken_quiz_list.html @@ -8,15 +8,15 @@Total Questions: {{total_questions}}
Student | Date | Score | +Percentage | @@ -31,6 +32,7 @@{{ taken_quiz.student.user.username }} | {{ taken_quiz.date|naturaltime }} | {{ taken_quiz.score }} | +{{ taken_quiz.percentage }} | {% endfor %} diff --git a/django_school/classroom/templatetags/__init__.py b/django_school/classroom/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/django_school/classroom/templatetags/quiz_extras.py b/django_school/classroom/templatetags/quiz_extras.py new file mode 100644 index 00000000..da896bcf --- /dev/null +++ b/django_school/classroom/templatetags/quiz_extras.py @@ -0,0 +1,33 @@ +from django import template +from django.db.models import Sum + +from classroom.models import StudentAnswer +import hashlib + +register = template.Library() + +@register.simple_tag +def marked_answer(user,opt): + studentanswer = StudentAnswer.objects.filter(student=user.student, answer =opt) + if studentanswer: + if opt.is_correct: + return 'correct' + return 'wrong' + return '' + +@register.filter +def gravatar_url(username, size=40): + # TEMPLATE USE: {{ email|gravatar_url:150 }} + username_hash = hashlib.md5(username.lower().encode('utf-8')).hexdigest() + return f"https://www.gravatar.com/avatar/{username_hash}?s={size}&d=identicon" + +@register.filter +def top_subject(taken_quizzes): + subjects = taken_quizzes.values('quiz__subject__name') \ + .annotate(score = Sum('score')) \ + .order_by('-score') + if subjects: + name = subjects[0]['quiz__subject__name'] + score = subjects[0]['score'] + return f"{name} x {score}" + return "" \ No newline at end of file diff --git a/django_school/classroom/tests.py b/django_school/classroom/tests.py new file mode 100644 index 00000000..f4794813 --- /dev/null +++ b/django_school/classroom/tests.py @@ -0,0 +1,157 @@ +from django.test import TestCase, Client + +from django.urls import reverse +from django.template import Template, Context + + +from classroom.models import Student + +class LoginPageTest(TestCase): + fixtures = ["datas.json"] + + def setUp(self): + self.client = Client() + self.student = Student.objects.get(user__username = 'student') + + self.home_url = reverse('home') + self.student_list_url = reverse('students:student_list') + self.about_url = reverse('about') + + self.tabs = f''' + ''' + + self.tabs = lambda active_tab='': f''' + ''' + + def test_login_page_returns_correct_html(self): + loginurl = reverse('login') + response = self.client.get(loginurl) + self.assertEqual(response.status_code,200) + # test response contains Username and Password + self.assertIn(b'Username', response.content) + self.assertIn(b'Password', response.content) + + # blank fields + response = self.client.post(loginurl) + self.assertIn(b'This field is required.', response.content) + + # wrong username or password + response = self.client.post(loginurl, {'username':'bad', 'password':'bad'}) + self.assertIn(b'Please enter a correct username and password.', response.content) + + def test_login_as_teacher(self): + loginurl = reverse('login') + # login as teacher + response = self.client.post(loginurl, {'username':'sumee', 'password':'sumee1910'}, follow=True) + # print(response.redirect_chain) + # self.assertEqual(response.redirect_chain[1][0],reverse('teachers:quiz_change_list')) + # self.assertIn(b'My Quizzes', response.content) + + def test_guest_user_can_access_quiz_list(self): + response = self.client.get(self.home_url) + + # there is tab view in homepage and check there is quiz list url in home page + self.assertInHTML(self.tabs('home'), response.content.decode()) + + quiz1 = '''
---|---|---|---|
World War 1 | +History | +4 | ++ Start quiz + | +",{valign:"top",colSpan:V(a),"class":a.oClasses.sRowEmpty}).html(c))[0];r(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ka(a),g,m,i]);r(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],Ka(a),g,m,i]);d=h(a.nTBody);d.children().detach(); +d.append(h(b));r(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function T(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&nb(a);d?ga(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;P(a);a._drawHold=!1}function ob(a){var b=a.oClasses,c=h(a.nTable),c=h("").insertBefore(c),d=a.oFeatures,e=h("",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore= +a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,m,l,q,k=0;k | ").html(B(a,c,b,"display"))[0]:d.anCells[b]}function Hb(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f | ").addClass(b),h("td",c).addClass(b).html(a)[0].colSpan=V(d),e.push(c[0]))};f(a,b);c._details&&c._details.detach();c._details=h(e); +c._detailsShow&&c._details.insertAfter(c.nTr)}return this});o(["row().child.show()","row().child().show()"],function(){Ub(this,!0);return this});o(["row().child.hide()","row().child().hide()"],function(){Ub(this,!1);return this});o(["row().child.remove()","row().child().remove()"],function(){db(this);return this});o("row().child.isShown()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var bc=/^([^:]+):(name|visIdx|visible)$/,Vb=function(a,b, +c,d,e){for(var c=[],d=0,f=e.length;d |