Repository: kimlimjustin/google-form-clone Branch: master Commit: 7bbd234eefef Files: 58 Total size: 189.0 KB Directory structure: gitextract_9q7rdef_/ ├── .deepsource.toml ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── dependabot.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── form/ │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── index/ │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations/ │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20201128_2040.py │ │ ├── 0003_remove_form_see_response.py │ │ └── __init__.py │ ├── models.py │ ├── static/ │ │ └── index/ │ │ ├── HurlUI.css │ │ ├── form.js │ │ ├── index.js │ │ ├── response.js │ │ ├── responses.js │ │ ├── score.js │ │ ├── style.css │ │ └── viewform.js │ ├── templates/ │ │ ├── error/ │ │ │ ├── 403.html │ │ │ └── 404.html │ │ └── index/ │ │ ├── edit_response.html │ │ ├── form.html │ │ ├── form_response.html │ │ ├── index.html │ │ ├── layout.html │ │ ├── login.html │ │ ├── register.html │ │ ├── response.html │ │ ├── responses.html │ │ ├── score.html │ │ └── view_form.html │ ├── templatetags/ │ │ ├── count.py │ │ ├── generate_color.py │ │ ├── get_property.py │ │ ├── get_response.py │ │ ├── is_response.py │ │ ├── score.py │ │ └── to_int.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py ├── renovate.json └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .deepsource.toml ================================================ version = 1 [[analyzers]] name = "python" enabled = true [analyzers.meta] runtime_version = "3.x.x" [[analyzers]] name = "javascript" enabled = true [analyzers.meta] environment = ["browser"] ================================================ FILE: .github/FUNDING.yml ================================================ ko_fi: kimlimjustin ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: pip directory: "/" schedule: interval: daily time: "22:00" open-pull-requests-limit: 10 ================================================ FILE: .gitignore ================================================ *.sqlite3 *.sqlite3-journal *.pyo *.pyc data ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kimlimjustin@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: Dockerfile ================================================ # syntax=docker/dockerfile:1 FROM python:3 ENV PYTHONUNBUFFERED=1 WORKDIR /code COPY requirements.txt /code/ RUN pip install -r requirements.txt COPY . /code/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Justin Maximillian Kimlim Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Google Forms CLONE ### Demo application and feedback [here](https://google-forms-clone.herokuapp.com/form/in60XZ4GWUswvkaKEOxONvIKkhy4gl/viewform) ![Google Forms clone demo](https://drive.google.com/uc?export=view&id=1GEApyE6aRP74zf2ltmTqpr97NDMHOHbP)
More screenshots ![Google Forms clone demo](https://drive.google.com/uc?export=view&id=1QhPVWHXKApcv5V6FzrHRzut7a5-1Mgp4) ![Google Forms clone demo](https://drive.google.com/uc?export=view&id=1Nwz642ORdTCd6KdsaN28Tt142K3wH-pt) ##### For the best experience, please use a device with a width of at least 350px - Note that this Google Forms CLONE don't support image uploading due to [Heroku policy](https://help.heroku.com/K1PPS2WM/why-are-my-file-uploads-missing-deleted)
## Built using: - Python with Django framework and Jinja templating language - Vanilla Javascript ## Getting started: - Clone this repository or fork it - To clone this repository type git clone `https://github.com/kimlimjustin/google-form-clone.git` on your command line - To fork this repository, click fork button of this repository then type git clone `https://github.com//google-form-clone.git` - Install all the dependencies of this project by typing `pip install -r requirements.txt` - Migrate the database by typing `python manage.py migrate` on the command line - Run the project locally by typing `python manage.py runserver` on the command line - NB: to run it on your local network, type `python manage.py runserver 0.0.0.0:8000` - You project will be accessible in your localhost or local network. ## Deployment For deployment, open `form/settings.py` file and uncomment code from line 131 to 159. ## License Distributed under the [MIT](https://github.com/kimlimjustin/google-form-clone/blob/master/LICENSE) License. See [`LICENSE`](https://github.com/kimlimjustin/google-form-clone/blob/master/LICENSE) for more information. ## Contact - Justin Maximillian Kimlim - [kimlimjustin@gmail.com](mailto:kimlimjustin@gmail.com) - Project link: https://github.com/kimlimjustin/google-form-clone ## Love my work? Buy Me a Coffee at ko-fi.com ================================================ FILE: docker-compose.yml ================================================ version: "3.9" services: db: image: postgres volumes: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - db migration: build: . command: python manage.py migrate --noinput volumes: - .:/code depends_on: - db ================================================ FILE: form/__init__.py ================================================ ================================================ FILE: form/asgi.py ================================================ """ ASGI config for form project. It exposes the ASGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ """ import os from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'form.settings') application = get_asgi_application() ================================================ FILE: form/settings.py ================================================ """ Django settings for form project. Generated by 'django-admin startproject' using Django 3.1.3. For more information on this file, see https://docs.djangoproject.com/en/3.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.1/ref/settings/ """ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '5&41du-&cv-cazt$r@k84h*gz-s!*jk2b)131ek^2-lpou#2y4' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ALLOWED_HOSTS = [s.getsockname()[0], '127.0.0.1', 'localhost'] s.close() # Application definition INSTALLED_APPS = [ 'index', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'form.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'form.wsgi.application' # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] AUTH_USER_MODEL = "index.User" # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' '''SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS = 31536000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles') STATIC_URL = '/static/' COMPRESS_ENABLED = os.environ.get('COMPRESS_ENABLED', False) # Extra lookup directories for collectstatic to find static files STATICFILES_DIRS = ( os.path.join(PROJECT_ROOT, 'static'), ) # Add configuration for static files storage using whitenoise STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' prod_db = dj_database_url.config(conn_max_age=500) DATABASES['default'].update(prod_db) ''' ================================================ FILE: form/urls.py ================================================ """form URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/3.1/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include from django.contrib.staticfiles.storage import staticfiles_storage from django.views.generic.base import RedirectView urlpatterns = [ path('admin/', admin.site.urls), path('', include('index.urls')), path('favicon.ico', RedirectView.as_view(url = staticfiles_storage.url('favicon.ico'))) ] ================================================ FILE: form/wsgi.py ================================================ """ WSGI config for form project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ """ import os from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'form.settings') application = get_wsgi_application() ================================================ FILE: index/__init__.py ================================================ ================================================ FILE: index/admin.py ================================================ from django.contrib import admin # Register your models here. ================================================ FILE: index/apps.py ================================================ from django.apps import AppConfig class IndexConfig(AppConfig): name = 'index' ================================================ FILE: index/migrations/0001_initial.py ================================================ # Generated by Django 3.1.3 on 2020-11-28 13:29 from django.conf import settings import django.contrib.auth.models import django.contrib.auth.validators from django.db import migrations, models import django.db.models.deletion import django.utils.timezone class Migration(migrations.Migration): initial = True dependencies = [ ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ migrations.CreateModel( name='User', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('email', models.EmailField(max_length=254, unique=True)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), ], options={ 'verbose_name': 'user', 'verbose_name_plural': 'users', 'abstract': False, }, managers=[ ('objects', django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( name='Answer', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('answer', models.CharField(max_length=5000)), ], ), migrations.CreateModel( name='Choices', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('choice', models.CharField(max_length=5000)), ('is_answer', models.BooleanField(default=False)), ], ), migrations.CreateModel( name='Form', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('code', models.CharField(max_length=30)), ('title', models.CharField(max_length=200)), ('description', models.CharField(blank=True, max_length=10000)), ('background_color', models.CharField(default='#d9efed', max_length=20)), ('text_color', models.CharField(default='#272124', max_length=20)), ('collect_email', models.BooleanField(default=False)), ('authenticated_responder', models.BooleanField(default=False)), ('edit_after_submit', models.BooleanField(default=False)), ('see_response', models.BooleanField(default=False)), ('confirmation_message', models.CharField(default='Your response has been recorded.', max_length=10000)), ('is_quiz', models.BooleanField(default=False)), ('allow_view_score', models.BooleanField(default=True)), ('createdAt', models.DateTimeField(auto_now_add=True)), ('updatedAt', models.DateTimeField(auto_now=True)), ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='creator', to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='Responses', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('response_code', models.CharField(max_length=20)), ('responder_ip', models.CharField(max_length=30)), ('responder_email', models.EmailField(max_length=254)), ('responder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='responder', to=settings.AUTH_USER_MODEL)), ('response', models.ManyToManyField(related_name='response', to='index.Answer')), ('response_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='response_to', to='index.form')), ], ), migrations.CreateModel( name='Questions', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('question', models.CharField(max_length=10000)), ('question_type', models.CharField(max_length=20)), ('required', models.BooleanField(default=False)), ('answer_key', models.CharField(blank=True, max_length=5000)), ('score', models.IntegerField(blank=True, default=0)), ('feedback', models.CharField(max_length=5000, null=True)), ('choices', models.ManyToManyField(related_name='choices', to='index.Choices')), ], ), migrations.AddField( model_name='form', name='questions', field=models.ManyToManyField(related_name='questions', to='index.Questions'), ), migrations.AddField( model_name='answer', name='answer_to', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answer_to', to='index.questions'), ), ] ================================================ FILE: index/migrations/0002_auto_20201128_2040.py ================================================ # Generated by Django 3.1.3 on 2020-11-28 13:40 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('index', '0001_initial'), ] operations = [ migrations.AlterField( model_name='responses', name='responder_email', field=models.EmailField(blank=True, max_length=254), ), ] ================================================ FILE: index/migrations/0003_remove_form_see_response.py ================================================ # Generated by Django 3.1.3 on 2020-11-28 14:58 from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('index', '0002_auto_20201128_2040'), ] operations = [ migrations.RemoveField( model_name='form', name='see_response', ), ] ================================================ FILE: index/migrations/__init__.py ================================================ ================================================ FILE: index/models.py ================================================ from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class User(AbstractUser, models.Model): email = models.EmailField(unique = True) class Choices(models.Model): choice = models.CharField(max_length=5000) is_answer = models.BooleanField(default=False) class Questions(models.Model): question = models.CharField(max_length= 10000) question_type = models.CharField(max_length=20) required = models.BooleanField(default= False) answer_key = models.CharField(max_length = 5000, blank = True) score = models.IntegerField(blank = True, default=0) feedback = models.CharField(max_length = 5000, null = True) choices = models.ManyToManyField(Choices, related_name = "choices") class Answer(models.Model): answer = models.CharField(max_length=5000) answer_to = models.ForeignKey(Questions, on_delete = models.CASCADE ,related_name = "answer_to") class Form(models.Model): code = models.CharField(max_length=30) title = models.CharField(max_length=200) description = models.CharField(max_length=10000, blank = True) creator = models.ForeignKey(User, on_delete = models.CASCADE, related_name = "creator") background_color = models.CharField(max_length=20, default = "#d9efed") text_color = models.CharField(max_length=20, default="#272124") collect_email = models.BooleanField(default=False) authenticated_responder = models.BooleanField(default = False) edit_after_submit = models.BooleanField(default=False) confirmation_message = models.CharField(max_length = 10000, default = "Your response has been recorded.") is_quiz = models.BooleanField(default=False) allow_view_score = models.BooleanField(default= True) createdAt = models.DateTimeField(auto_now_add = True) updatedAt = models.DateTimeField(auto_now = True) questions = models.ManyToManyField(Questions, related_name = "questions") class Responses(models.Model): response_code = models.CharField(max_length=20) response_to = models.ForeignKey(Form, on_delete = models.CASCADE, related_name = "response_to") responder_ip = models.CharField(max_length=30) responder = models.ForeignKey(User, on_delete = models.CASCADE, related_name = "responder", blank = True, null = True) responder_email = models.EmailField(blank = True) response = models.ManyToManyField(Answer, related_name = "response") ================================================ FILE: index/static/index/HurlUI.css ================================================ @import url("https://fonts.googleapis.com/css?family=Roboto+Condensed"); @import url("https://fonts.googleapis.com/css?family=Poppins"); @import url("https://fonts.googleapis.com/css?family=Montserrat"); @import url("https://fonts.googleapis.com/css?family=Times+New+Roman"); body{ margin: 0; padding: 0; font-family: "Times New Roman","Roboto Condensed", "Poppins","Arial"; } .center{ text-align: center; } .link{ color: #000; text-decoration: none; opacity: .7; } .link:hover{opacity: .4;} .margin{ margin: 20px; } .margin-top-bottom{ margin: 20px 0; } .margin-left-right{ margin: 0 20px; } .text-link{ opacity: .8; } .text-link:hover{ opacity: .4; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .container-fluid { width: 100%; } [class*="col-"] { float: left; } .row{ grid-row-gap: 5px; grid-column-gap: 5px; float: left; } .bg-dark, .btn-dark{ background-color: #1c1e1f !important; color: #FFF !important; } .bg-light, .btn-light{ background-color: #DAE0E2 !important; color: #000 !important; } .bg-black{ background-color: #000 !important; color: #FFF !important; } .bg-white{ background-color: #FFF !important; color: #000 !important; } body[theme="dark"]{ background-color: #080808; color: #FFF; } body[theme="light"]{ background-color: #F5F5F5; color: #000; } .text-dark{color: #1c1e1f !important;} .text-light{color: #DAE0E2 !important;} .text-danger{color: #E71C23 !important;} .text-success{color: #6AB04A !important;} .text-warning{color: #FFF222 !important;} .text-blur{opacity: .7;} .text-red{color : #e60000 !important;} .text-darkblue{color : #000066 !important;} .text-blue{color : #0000ff !important;} .text-green{color: #00b300 !important;} .text-darkgreen{color: #004d00 !important;} .text-lightgreen{color: #00e600 !important;} .text-orange{color: #ff9900 !important;} .text-cyan{color: #33ffff !important;} .text-gray{color: #ccccb3 !important;} .col-1 , .col-ex-1{width: 8.33%;} .col-2 , .col-ex-2{width: 16.66%;} .col-3 , .col-ex-3{width: 25%;} .col-4 , .col-ex-4{width: 33.33%;} .col-5 , .col-ex-5{width: 41.66%;} .col-6 , .col-ex-6{width: 50%;} .col-7 , .col-ex-7{width: 58.33%;} .col-8 , .col-ex-8{width: 66.66%;} .col-9 , .col-ex-9{width: 75%;} .col-10, .col-ex-10 {width: 83.33%;} .col-11, .col-ex-11 {width: 91.66%;} .col-12, .col-ex-1 {width: 100%;} .btn{ padding: 10px; border: 1px solid black; border-radius: 5px; text-decoration: none; font-size: 1rem; color: black; font-family: "Poppins"; } .btn-hover:hover{ box-shadow: 5px 2px 8px; opacity: .7; } .btn-danger{background-color: #E71C23; color: #FFF;} .btn-success{background-color: #6AB04A; color: #FFF;} .btn-warning{background-color: #FFF222; color: #000;} .box{ padding: 10px; border: 1px solid #999; border-radius: 5px; width: -webkit-fill-available; width: -moz-available; } .box-image img, .box-image{ width: 100%; } .box-title, .heading-title{ font-weight: bold; text-align: center; font-family: "Montserrat"; padding: 2px 5px; border-bottom: 0.5px solid #e6e6e6; } .box-text{ font-family: "Roboto Condensed"; } .box-shadow{ box-shadow: 3px 5px 11px; } .form-label, .text-label{ font-family: "Poppins", "Arial"; margin: 10px 0; } .form-group{ margin: 16px 14px; } .form-control{ color: black; border: 1px solid #bfbfbf; width: -webkit-fill-available; width: -moz-available; border-radius: 5px; padding: 8px; } .form-hover:hover{ opacity: .7; box-shadow: 2px 2px 3px; } .navbar{ margin: 0; padding: 0; overflow: hidden; background-color: white; width: 100%; } .nav-list{ margin: 0; list-style-type: none; } .nav-logo, .nav-logo a{ float: left; max-width: 200px; margin: 10px; text-decoration: none; color: black; font-size: 1.5rem; } .navbar .nav-item{ display: block; text-align: center; padding: 14px 16px; font-size: 17px; float: right; } .navbar .nav-item a{ text-decoration: none; color: black; } .navbar .nav-item a:hover{ opacity: .7; } .nav-icon{ display: none !important; } .fixed-top{ top: 0px; position: fixed; } .fixed-bottom{ bottom: 0; position: fixed; } .text-center{ text-align: center; } footer, .footer{ width: 100%; float: left; } .copyright{ text-align: center; font-size: 20px; border-top: 1px solid; padding: 10px; background-color: #000; color: #FFF; margin: 0; } code{ text-align: center; padding: 10px 20px; border-radius: 5px; } .img-frame{ border: 1px solid #ddd; border-radius: 4px; padding: 5px; } .img-hover:hover{ opacity: .4; } table{ width: 100%; } th{ height: 40px; } td{padding: 8px;} .table-border, .table-border table, .table-border th, .table-border tr, .table-border td{ border: 1px solid; } .table-white-black tr:nth-child(even){background-color: #000; color: #FFF;} .table-white-black tr:nth-child(odd){background-color: #FFF; color: #000;} .table-black-white tr:nth-child(odd){background-color: #000; color: #FFF;} .table-black-white tr:nth-child(even){background-color: #FFF; color: #000;} @media only screen and (max-width: 600px){ .nav-icon{ display: block !important; position: absolute; right: 20px; top: 20px; } .nav-item{ float: left; width: 100vw; } .nav-responsive{ display: block !important; } .nav-list-responsive{ display: none; } } @media only screen and (max-width: 600px) { [class*="col-"] { width: 100% !important; } } @media only screen and (max-width: 768px){ .col-1{ width: 16.66%; } .col-2{ width: 25%; } .col-3, .col-4, .col-5, .col-6{ width: 50%; } .col-7, .col-8, .col-9, .col-10, .col-11, .col-12{ width: 100%; } [class*="col-ex-"]{ width: 100%; } } @media only screen and (min-width: 768px) { .container { width: 750px; } } @media only screen and (min-width: 992px) { .container { width: 970px; } } @media only screen and (min-width: 1200px) { .container { width: 1170px; } } ================================================ FILE: index/static/index/form.js ================================================ document.addEventListener("DOMContentLoaded", () => { const csrf = Cookies.get('csrftoken'); document.body.style.backgroundColor = document.querySelector("#bg-color").innerHTML; document.body.style.color = document.querySelector("#text-color").innerHTML; document.querySelectorAll(".txt-clr").forEach(element => { element.style.color = document.querySelector("#text-color").innerHTML; }) document.querySelectorAll(".input-form-title").forEach(title => { title.addEventListener("input", function(){ fetch(`edit_title`, { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "title": this.value }) }) document.title = `${this.value} - Google Forms CLONE` document.querySelectorAll(".input-form-title").forEach(ele => { ele.value = this.value; }) }) }) document.querySelector("#input-form-description").addEventListener("input", function(){ fetch('edit_description', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "description": this.value }) }) }) document.querySelectorAll(".textarea-adjust").forEach(tx => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; tx.addEventListener('input', e => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; }) }) document.querySelector("#customize-theme-btn").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "block"; document.querySelector("#close-customize-theme").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#customize-theme")) document.querySelector("#customize-theme").style.display = "none"; } }) document.querySelector("#input-bg-color").addEventListener("input", function(){ document.body.style.backgroundColor = this.value; fetch('edit_background_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "bgColor": this.value }) }) }) document.querySelector("#input-text-color").addEventListener("input", function(){ document.querySelectorAll(".txt-clr").forEach(element => { element.style.color = this.value; }) fetch('edit_text_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "textColor": this.value }) }) }) document.querySelectorAll(".open-setting").forEach(ele => { ele.addEventListener('click', () => { document.querySelector("#setting").style.display = "block"; }) document.querySelector("#close-setting").addEventListener('click', () => { document.querySelector("#setting").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#setting")) document.querySelector("#setting").style.display = "none"; } }) document.querySelectorAll("#send-form-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelector("#send-form").style.display = "block"; }) document.querySelector("#close-send-form").addEventListener("click", () => { document.querySelector("#send-form").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#send-form")) document.querySelector("#send-form").style.display = "none"; } }) document.querySelectorAll("[copy-btn]").forEach(btn => { btn.addEventListener("click", () => { var url = document.getElementById("copy-url"); navigator.clipboard.writeText(url.value); document.querySelector("#send-form").style.display = "none"; }) }) document.querySelector("#setting-form").addEventListener("submit", e => { e.preventDefault(); fetch('edit_setting', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "collect_email": document.querySelector("#collect_email").checked, "is_quiz": document.querySelector("#is_quiz").checked, "authenticated_responder": document.querySelector("#authenticated_responder").checked, "confirmation_message": document.querySelector("#comfirmation_message").value, "edit_after_submit": document.querySelector("#edit_after_submit").checked, "allow_view_score": document.querySelector("#allow_view_score").checked, }) }) document.querySelector("#setting").style.display = "none"; if(!document.querySelector("#collect_email").checked){ if(document.querySelector(".collect-email")) document.querySelector(".collect-email").parentNode.removeChild(document.querySelector(".collect-email")) }else{ if(!document.querySelector(".collect-email")){ let collect_email = document.createElement("div"); collect_email.classList.add("collect-email") collect_email.innerHTML = `

Email address *

This form is collecting email addresses. Change settings

` document.querySelector("#form-head").appendChild(collect_email) } } if(document.querySelector("#is_quiz").checked){ if(!document.querySelector("#add-score")){ let is_quiz = document.createElement('a') is_quiz.setAttribute("href", "score"); is_quiz.setAttribute("id", "add-score"); is_quiz.innerHTML = `Score icon`; document.querySelector(".question-options").appendChild(is_quiz) } if(!document.querySelector(".score")){ let quiz_nav = document.createElement("span"); quiz_nav.classList.add("col-4"); quiz_nav.classList.add("navigation"); quiz_nav.classList.add('score'); quiz_nav.innerHTML = `
Scores`; [...document.querySelector(".form-navigation").children].forEach(element => { element.classList.remove("col-6") element.classList.add('col-4') }) document.querySelector(".form-navigation").insertBefore(quiz_nav, document.querySelector(".form-navigation").childNodes[2]) } }else{ if(document.querySelector("#add-score")) document.querySelector("#add-score").parentNode.removeChild(document.querySelector("#add-score")) if(document.querySelector(".score")){ [...document.querySelector(".form-navigation").children].forEach(element => { element.classList.remove("col-4") element.classList.add('col-6') }) document.querySelector(".score").parentNode.removeChild(document.querySelector(".score")) } } }) document.querySelector("#delete-form").addEventListener("submit", e => { e.preventDefault(); if(window.confirm("Are you sure? This action CANNOT be undone.")){ fetch('delete', { method: "DELETE", headers: {'X-CSRFToken': csrf} }) .then(() => window.location = "/") } }) const editQuestion = () => { document.querySelectorAll(".input-question").forEach(question => { question.addEventListener('input', function(){ let question_type; let required; document.querySelectorAll(".input-question-type").forEach(qp => { if(qp.dataset.id === this.dataset.id) question_type = qp.value }) document.querySelectorAll('.required-checkbox').forEach(rc => { if(rc.dataset.id === this.dataset.id) required = rc.checked; }) fetch('edit_question', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ id: this.dataset.id, question: this.value, question_type: question_type, required: required }) }) }) }) } editQuestion(); const editRequire = () => { document.querySelectorAll(".required-checkbox").forEach(checkbox => { checkbox.addEventListener('input', function(){ let question; let question_type; document.querySelectorAll(".input-question-type").forEach(qp => { if(qp.dataset.id === this.dataset.id) question_type = qp.value }) document.querySelectorAll('.input-question').forEach(q => { if(q.dataset.id === this.dataset.id) question = q.value }) fetch('edit_question', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ id: this.dataset.id, question: question, question_type: question_type, required: this.checked }) }) }) }) } editRequire() const editChoice = () => { document.querySelectorAll(".edit-choice").forEach(choice => { choice.addEventListener("input", function(){ fetch('edit_choice', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "id": this.dataset.id, "choice": this.value }) }) }) }) } editChoice() const removeOption = () => { document.querySelectorAll(".remove-option").forEach(ele => { ele.addEventListener("click",function(){ fetch('remove_choice', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "id": this.dataset.id }) }) .then(() => { this.parentNode.parentNode.removeChild(this.parentNode) }) }) }) } removeOption() const addOption = () => { document.querySelectorAll(".add-option").forEach(question =>{ question.addEventListener("click", function(){ fetch('add_choice', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "question": this.dataset.question }) }) .then(response => response.json()) .then(result => { let element = document.createElement("div"); element.classList.add('choice'); if(this.dataset.type === "multiple choice"){ element.innerHTML = ` ×`; }else if(this.dataset.type === "checkbox"){ element.innerHTML = ` ×`; } document.querySelectorAll(".choices").forEach(choices => { if(choices.dataset.id === this.dataset.question){ choices.insertBefore(element, choices.childNodes[choices.childNodes.length -2]); editChoice() removeOption() } }); }) }) }) } addOption() const deleteQuestion = () => { document.querySelectorAll(".delete-question").forEach(question => { question.addEventListener("click", function(){ fetch(`delete_question/${this.dataset.id}`, { method: "DELETE", headers: {'X-CSRFToken': csrf}, }) .then(() => { document.querySelectorAll(".question").forEach(q =>{ if(q.dataset.id === this.dataset.id){ q.parentNode.removeChild(q) } }) }) }) }) } deleteQuestion() const changeType = () => { document.querySelectorAll(".input-question-type").forEach(ele => { ele.addEventListener('input', function(){ let required; let question; document.querySelectorAll('.required-checkbox').forEach(rc => { if(rc.dataset.id === this.dataset.id) required = rc.checked; }) document.querySelectorAll('.input-question').forEach(q => { if(q.dataset.id === this.dataset.id) question = q.value }) fetch('edit_question', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ id: this.dataset.id, question: question, question_type: this.value, required: required }) }) if(this.dataset.origin_type === "multiple choice" || this.dataset.origin_type === "checkbox"){ document.querySelectorAll(".choices").forEach(choicesElement => { if(choicesElement.dataset.id === this.dataset.id){ if(this.value === "multiple choice" || this.value === "checkbox"){ fetch(`get_choice/${this.dataset.id}`, { method: "GET" }) .then(response => response.json()) .then(result => { let ele = document.createElement("div"); ele.classList.add('choices'); ele.setAttribute("data-id", result["question_id"]) let choices = ''; if(this.value === "multiple choice"){ for(let i in result["choices"]){ if(i){ choices += `
×
`} } }else if(this.value === "checkbox"){ for(let i in result["choices"]){ if(i){choices += `
×
`} } } ele.innerHTML = `
${choices}
`; choicesElement.parentNode.replaceChild(ele, choicesElement); editChoice() removeOption() changeType() editQuestion() editRequire() addOption() deleteQuestion() }) }else{ if(this.value === "short"){ choicesElement.parentNode.removeChild(choicesElement) let ele = document.createElement("div"); ele.innerHTML = `
` this.parentNode.insertBefore(ele, this.parentNode.childNodes[4]) }else if(this.value === "paragraph"){ choicesElement.parentNode.removeChild(choicesElement) let ele = document.createElement("div"); ele.innerHTML = `
` this.parentNode.insertBefore(ele, this.parentNode.childNodes[4]) } } } }) }else{ document.querySelectorAll(".question-box").forEach(question => { document.querySelectorAll(".answers").forEach(answer => { if(answer.dataset.id === this.dataset.id){ answer.parentNode.removeChild(answer) } }) if((this.value === "multiple choice" || this.value === "checkbox") && question.dataset.id === this.dataset.id){ fetch(`get_choice/${this.dataset.id}`, { method: "GET" }) .then(response => response.json()) .then(result => { let ele = document.createElement("div"); ele.classList.add('choices'); ele.setAttribute("data-id", result["question_id"]) let choices = ''; if(this.value === "multiple choice"){ for(let i in result["choices"]){ if(i){ choices += `
×
`} } }else if(this.value === "checkbox"){ for(let i in result["choices"]){ if(i){choices += `
×
`} } } ele.innerHTML = `
${choices}
`; question.insertBefore(ele, question.childNodes[4]) editChoice() removeOption() changeType() editQuestion() editRequire() addOption() deleteQuestion() }) }else{ if(this.value === "short"){ let ele = document.createElement("div"); ele.innerHTML = `
` this.parentNode.insertBefore(ele, this.parentNode.childNodes[4]) }else if(this.value === "paragraph"){ let ele = document.createElement("div"); ele.innerHTML = `
` this.parentNode.insertBefore(ele, this.parentNode.childNodes[4]) } } }) } this.setAttribute("data-origin_type", this.value); }) }) } changeType() document.querySelector("#add-question").addEventListener("click", () => { fetch('add_question', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({}) }) .then(response => response.json()) .then(result => { let ele = document.createElement('div') ele.classList.add('margin-top-bottom'); ele.classList.add('box'); ele.classList.add('question-box'); ele.classList.add('question'); ele.setAttribute("data-id", result["question"].id) ele.innerHTML = `
×
Delete question icon
`; document.querySelector(".container").appendChild(ele); editChoice() removeOption() changeType() editQuestion() editRequire() addOption() deleteQuestion() }) }) }) ================================================ FILE: index/static/index/index.js ================================================ document.addEventListener("DOMContentLoaded", () => { document.querySelector("#nav-ham").addEventListener("click", () => { document.querySelector("#sidebar").style.width = "250px"; }) document.querySelector("#close-sidebar").addEventListener('click', () => { document.querySelector("#sidebar").style.width = "0px"; }) document.querySelector("#create-blank-form").addEventListener("click", () => { const csrf = Cookies.get('csrftoken'); fetch('/form/create', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ title: "Untitled Form" }) }) .then(response => response.json()) .then(result => { window.location = `/form/${result.code}/edit` }) }) document.querySelector("#create-contact-form").addEventListener("click", () => { const csrf = Cookies.get('csrftoken'); fetch('/form/create/contact', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({}) }) .then(response => response.json()) .then(result => { window.location = `/form/${result.code}/edit` }) }) document.querySelector("#create-customer-feedback").addEventListener("click", () => { const csrf = Cookies.get('csrftoken'); fetch('/form/create/feedback', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({}) }) .then(response => response.json()) .then(result => { window.location = `/form/${result.code}/edit` }) }) document.querySelector("#create-event-registration").addEventListener("click", () => { const csrf = Cookies.get('csrftoken'); fetch('/form/create/event', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({}) }) .then(response => response.json()) .then(result => { window.location = `/form/${result.code}/edit` }) }) }) ================================================ FILE: index/static/index/response.js ================================================ document.addEventListener("DOMContentLoaded", () => { document.body.style.backgroundColor = document.querySelector("#bg-color").innerHTML; document.body.style.color = document.querySelector("#text-color").innerHTML; document.querySelectorAll(".txtClr").forEach(element => { element.style.color = document.querySelector("#text-color").innerHTML; }) }) ================================================ FILE: index/static/index/responses.js ================================================ document.addEventListener("DOMContentLoaded", () => { const csrf = Cookies.get('csrftoken'); document.body.style.backgroundColor = document.querySelector("#bg-color").innerHTML; document.body.style.color = document.querySelector("#text-color").innerHTML; document.querySelector("#customize-theme-btn").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "block"; document.querySelector("#close-customize-theme").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#customize-theme")) document.querySelector("#customize-theme").style.display = "none"; } }) document.querySelector("#input-bg-color").addEventListener("input", function(){ document.body.style.backgroundColor = this.value; fetch('edit_background_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "bgColor": this.value }) }) }) document.querySelector("#input-text-color").addEventListener("input", function(){ document.querySelectorAll(".txt-clr").forEach(element => { element.style.color = this.value; }) fetch('edit_text_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "textColor": this.value }) }) }) document.querySelectorAll(".open-setting").forEach(ele => { ele.addEventListener('click', () => { document.querySelector("#setting").style.display = "block"; }) document.querySelector("#close-setting").addEventListener('click', () => { document.querySelector("#setting").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#setting")) document.querySelector("#setting").style.display = "none"; } }) document.querySelector("#delete-form").addEventListener("submit", e => { e.preventDefault(); if(window.confirm("Are you sure? This action CANNOT be undone.")){ fetch('delete', { method: "DELETE", headers: {'X-CSRFToken': csrf} }) .then(() => window.location = "/") } }) document.querySelectorAll("#send-form-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelector("#send-form").style.display = "block"; }) document.querySelector("#close-send-form").addEventListener("click", () => { document.querySelector("#send-form").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#send-form")) document.querySelector("#send-form").style.display = "none"; } }) document.querySelectorAll("[copy-btn]").forEach(btn => { btn.addEventListener("click", () => { var url = document.getElementById("copy-url"); navigator.clipboard.writeText(url.value); document.querySelector("#send-form").style.display = "none"; }) }) document.querySelector("#setting-form").addEventListener("submit", e => { e.preventDefault(); fetch('edit_setting', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "collect_email": document.querySelector("#collect_email").checked, "is_quiz": document.querySelector("#is_quiz").checked, "authenticated_responder": document.querySelector("#authenticated_responder").checked, "confirmation_message": document.querySelector("#comfirmation_message").value, "edit_after_submit": document.querySelector("#edit_after_submit").checked, "allow_view_score": document.querySelector("#allow_view_score").checked }) }) document.querySelector("#setting").style.display = "none"; if(!document.querySelector("#collect_email").checked){ if(document.querySelector(".collect-email")) document.querySelector(".collect-email").parentNode.removeChild(document.querySelector(".collect-email")) }else{ if(!document.querySelector(".collect-email")){ let collect_email = document.createElement("div"); collect_email.classList.add("collect-email") collect_email.innerHTML = `

Email address *

This form is collecting email addresses. Change settings

` document.querySelector("#form-head").appendChild(collect_email) } } if(document.querySelector("#is_quiz").checked){ if(!document.querySelector("#add-score")){ let is_quiz = document.createElement('a') is_quiz.setAttribute("href", "score"); is_quiz.setAttribute("id", "add-score"); is_quiz.innerHTML = `Score icon`; document.querySelector(".question-options").appendChild(is_quiz) } if(!document.querySelector(".score")){ let quiz_nav = document.createElement("span"); quiz_nav.classList.add("col-4"); quiz_nav.classList.add("navigation"); quiz_nav.classList.add('score'); quiz_nav.innerHTML = `Scores`; [...document.querySelector(".form-navigation").children].forEach(element => { element.classList.remove("col-6") element.classList.add('col-4') }) document.querySelector(".form-navigation").insertBefore(quiz_nav, document.querySelector(".form-navigation").childNodes[2]) } }else{ if(document.querySelector("#add-score")) document.querySelector("#add-score").parentNode.removeChild(document.querySelector("#add-score")) if(document.querySelector(".score")){ [...document.querySelector(".form-navigation").children].forEach(element => { element.classList.remove("col-4") element.classList.add('col-6') }) document.querySelector(".score").parentNode.removeChild(document.querySelector(".score")) } } }) document.querySelectorAll(".textarea-adjust").forEach(tx => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; tx.addEventListener('input', e => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; }) }) if(document.querySelector("#delete-responses")){ document.querySelector("#delete-responses").addEventListener("click", () => { if(window.confirm("Are you sure? This action CANNOT be undone.")){ fetch('responses/delete', { method: "DELETE", headers: {'X-CSRFToken': csrf} }) .then(() => { let ele = document.createElement("div"); ele.classList.add('margin-top-bottom'); ele.classList.add('box'); ele.classList.add('question-box'); ele.innerHTML = '

0 responses

'; document.querySelector("#responses").parentNode.replaceChild(ele, document.querySelector("#responses")) }) } }) } }) ================================================ FILE: index/static/index/score.js ================================================ document.addEventListener("DOMContentLoaded", () => { const csrf = Cookies.get('csrftoken'); document.body.style.backgroundColor = document.querySelector("#bg-color").innerHTML; document.body.style.color = document.querySelector("#text-color").innerHTML; document.querySelector("#customize-theme-btn").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "block"; document.querySelector("#close-customize-theme").addEventListener('click', () => { document.querySelector("#customize-theme").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#customize-theme")) document.querySelector("#customize-theme").style.display = "none"; } }) document.querySelector("#input-bg-color").addEventListener("input", function(){ document.body.style.backgroundColor = this.value; fetch('edit_background_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "bgColor": this.value }) }) }) document.querySelector("#input-text-color").addEventListener("input", function(){ document.querySelectorAll(".txt-clr").forEach(element => { element.style.color = this.value; }) fetch('edit_text_color', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "textColor": this.value }) }) }) document.querySelectorAll(".open-setting").forEach(ele => { ele.addEventListener('click', () => { document.querySelector("#setting").style.display = "block"; }) document.querySelector("#close-setting").addEventListener('click', () => { document.querySelector("#setting").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#setting")) document.querySelector("#setting").style.display = "none"; } }) document.querySelector("#delete-form").addEventListener("submit", e => { e.preventDefault(); if(window.confirm("Are you sure? This action CANNOT be undone.")){ fetch('delete', { method: "DELETE", headers: {'X-CSRFToken': csrf} }) .then(() => window.location = "/") } }) document.querySelectorAll("#send-form-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelector("#send-form").style.display = "block"; }) document.querySelector("#close-send-form").addEventListener("click", () => { document.querySelector("#send-form").style.display = "none"; }) window.onclick = e => { if(e.target == document.querySelector("#send-form")) document.querySelector("#send-form").style.display = "none"; } }) document.querySelectorAll("[copy-btn]").forEach(btn => { btn.addEventListener("click", () => { var url = document.getElementById("copy-url"); navigator.clipboard.writeText(url.value); document.querySelector("#send-form").style.display = "none"; }) }) document.querySelector("#setting-form").addEventListener("submit", e => { e.preventDefault(); fetch('edit_setting', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "collect_email": document.querySelector("#collect_email").checked, "is_quiz": document.querySelector("#is_quiz").checked, "authenticated_responder": document.querySelector("#authenticated_responder").checked, "confirmation_message": document.querySelector("#comfirmation_message").value, "edit_after_submit": document.querySelector("#edit_after_submit").checked, "allow_view_score": document.querySelector("#allow_view_score").checked }) }) document.querySelector("#setting").style.display = "none"; if(!document.querySelector("#collect_email").checked){ if(document.querySelector(".collect-email")) document.querySelector(".collect-email").parentNode.removeChild(document.querySelector(".collect-email")) }else{ if(!document.querySelector(".collect-email")){ let collect_email = document.createElement("div"); collect_email.classList.add("collect-email") collect_email.innerHTML = `

Email address *

This form is collecting email addresses. Change settings

` document.querySelector("#form-head").appendChild(collect_email) } } if(document.querySelector("#is_quiz").checked){ if(!document.querySelector("#add-score")){ let is_quiz = document.createElement('a') is_quiz.setAttribute("href", "/"); is_quiz.setAttribute("id", "add-score"); is_quiz.innerHTML = `Score icon`; document.querySelector(".question-options").appendChild(is_quiz) } }else{ if(document.querySelector("#add-score")){ document.querySelector("#add-score").parentNode.removeChild(document.querySelector("#add-score")) } } }) document.querySelectorAll(".textarea-adjust").forEach(tx => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; tx.addEventListener('input', e => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; }) }) document.querySelectorAll(".input-score").forEach(element => { element.addEventListener("input", function(){ fetch('edit_score', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ question_id: this.dataset.id, score: this.value }) }) }) }) document.querySelectorAll("[answer-key]").forEach(element => { element.addEventListener("input", function(){ if(this.dataset.question_type === "multiple choice"){ fetch('answer_key', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "question_id": this.dataset.id, "answer_key": document.querySelector(`input[name="${this.name}"]:checked`).value }) }) }else if(this.dataset.question_type === "checkbox"){ answers = [] document.getElementsByName(this.name).forEach(element => { if(element.checked) answers.push(element.value) }) fetch('answer_key', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "question_id": this.dataset.id, "answer_key": answers }) }) } else{ fetch('answer_key', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "question_id": this.dataset.id, "answer_key": this.value }) }) } }) }) document.getElementsByName('feedback').forEach(element => { element.addEventListener("input", function(){ fetch('feedback', { method: "POST", headers: {'X-CSRFToken': csrf}, body: JSON.stringify({ "question_id": this.dataset.id, "feedback": this.value }) }) }) }) }) ================================================ FILE: index/static/index/style.css ================================================ @import url("https://fonts.googleapis.com/css?family=Permanent+Marker"); @import url("https://fonts.googleapis.com/css?family=Product+Sans"); @import url("https://fonts.googleapis.com/css?family=Google+Sans"); body{ scroll-behavior: smooth; } .form-error{ font-family: "Permanent Marker"; color: red; } .form-icon{ width: 40px; margin-bottom: 0.5rem; } .topnav{ float: left; width: 100vw; } .navbar-icon{ float: left; } .nav-text{ float: left; position: relative; vertical-align: middle; margin-top: 0.75rem; font-family: "Product Sans"; color: #5f6368; padding-left: 1rem; } .nav-link{ color: #000; text-decoration: none; } .nav-effect{box-shadow: 0 2px 30px -2px rgba(0,0,0,.2);} .float-right{ float: right; } .logout-icon{ width: 40px; cursor: pointer; } .nav-padding{ padding: 10px 20px 10px; } .sidebar{ height: 100%; width: 0; position: fixed; z-index: 5; top: 0; left: 0; overflow-x: hidden; transition: 0.5s; background-color: #FFF; box-shadow: 10px 0 5px -2px #888; } .nav-ham{ float: left; font-size: 1.8rem; position: relative; vertical-align: middle; margin-right: 1rem; cursor: pointer; } .close-sidebar{ position: absolute; top: 0; right: 25px; font-size: 36px; margin-left: 50px; cursor: pointer; } .sidebar-content{ margin: 20px } .sidebar-title{ margin-top: 50px; font-family: "Google Sans"; font-size: 1.3rem; border-bottom: 1px solid #888; text-align: center; cursor: pointer; } .sidebar-title-link{ text-decoration: none; color: #414244; } .github-icon{ width: 1.3rem; } .create-form{ background-color: #f1f3f4; width: 100vw; float: left; } .forms-label{ font-family: "Google Sans"; margin: 10px 10px 5px 10px; } .form-template-box{ margin: 5px 30px 10px 0; float: left; } .form-template-box img{ width: 10em; height: 7em; float: left; } .form-template-box:hover{ border: 1px solid black; } .form-box{ width: 250px; border: 1px solid rgb(182, 179, 179); padding: 5px; cursor: pointer; margin: 10px; position: relative; } .form-box:hover{ border: 1px solid darkblue; } .form-list{ float: left; margin-top: 20px; } .form-list-title{ align-items: flex-end; display: flex; flex-direction: row; position: relative; } .form-list-title-text{ text-align: left; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; text-overflow: ellipsis; font-family: "Google Sans"; margin: 2.5px; } .form-list-timestamp{ margin: 2.5px ; color: gray; } .form-list-link{ color: #000000; text-decoration: none; float: left; } .error-page{ text-align: center; font-family: "Roboto Condensed"; } .form-topnav{ padding: 15px; overflow: hidden; background-color: #fff; } .nav-form-title{ width: 300px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; text-overflow: ellipsis; } .edit-on-click{ border: none; font-size: 1.2rem; transition: 1s; overflow-y: hidden; } .edit-on-click:focus{ outline: none !important; border-bottom: 1px solid #202124; } .nav-form-menu-icon{ width: 1.5rem; margin: 10px 15px; cursor: pointer; float: left; } .send-form-btn{ position: relative; background-color: #673ab7; color: #ddd; font-size: 1rem; font-family: "Google Sans"; vertical-align: middle; border: 1px solid #ddd; padding: 10px 20px; float: left; margin: 0.2px 15px; } .question-box{ background-color: #FFF; } .form-title{ font-size: 2rem; font-family: "Google Sans"; font-weight: normal; margin: 10px; width: -webkit-fill-available; width: -moz-available; } .form-description{ font-size: 1rem; font-family: "Roboto Condensed"; margin: 10px; width: -webkit-fill-available; width: -moz-available; } .form-title-box{ border-bottom: 1px solid #ddd; } .modal{ display: none; position: fixed; z-index: 6; padding: calc(10vh - 20px) 0; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0, 0, 0); background-color: rgba(0, 0, 0, 0.4); } .modal-content{ background-color: #fefefe; margin: auto; padding: 20px; border: 1px solid #888; width: 80%; max-height: 80%; overflow: auto; max-width: 600px; border-radius: 10px; } .modal-close-btn{ color: #aaa; float: right; font-size: 28px; font-weight: bold; } .modal-close-btn:hover, .modal-close-btn:focus{ color: #000; text-decoration: none; cursor: pointer; } .modal-title{ font-family: "Google Sans"; font-size: 1.2rem; border-bottom: 1px solid rgb(233, 232, 232); margin-bottom: 0; } .modal-subtitle{ font-family: "Roboto Condensed"; font-size: 1rem; font-weight: normal; } .setting-form-label{ font-family: "Roboto Condensed"; } .modal-division{ margin: 30px 0; } .confirmation-msg-input{ width: -webkit-fill-available; width: -moz-available; border-bottom: 1px solid #202124; min-height: 20px !important; } .setting-options{ position: relative; } .setting-option-cancel{ margin: 20px 10px; cursor: pointer; color: #84878b; } .btn-save-setting{ background-color: #673ab7; color: #ddd; font-size: 1rem; font-family: "Google Sans"; } .setting-preview-form, .setting-preview-form a{ cursor: pointer; color: #bbb; font-family: "Google Sans"; text-decoration: none; } .setting-preview-form:hover{ opacity: .5; } .danger-zone{ font-weight: bold; font-family: "Google Sans"; font-size: 1.4rem; } .delete-form-title{ margin: 0; font-family: "Roboto Condensed"; } .delete-form-description{ margin: 0; font-family: "Poppins"; } .delete-form-btn{ font-size: 1rem; font-family: "Poppins"; padding: 3px !important; background-color: #E71C23; color: #fff; } .question-options{ background-color: #fff; margin: 20px 0; position: fixed; bottom: 10%; right: 0; border: 1px solid #84878b; } .form-option-icon{ width: 2rem; margin: 5px; cursor: pointer; display: block; } .collect-email{ margin: 10px; } .question-title{ font-family: "Google Sans"; font-size: 1.3rem; font-weight: normal; } .require-star{ color: #E71C23; } .require-email-edit{ padding: 5px; border: none; border-bottom: 1px solid #84878b; width: 50%; cursor: not-allowed; } .collect-email-desc{ color: #70757a; margin: 10px 0; } .open-setting{ color: #1a73e8; cursor: pointer; } .question-title{ font-family: "Google Sans"; font-size: 1.5rem; width: 65%; } .question-type-select{ width: calc(35% - 8px); padding: 5px 35px 5px 5px; font-size: 1.2rem; border: 1px solid #ccc; height: 34px; } .question-type-select::-ms-expand{ display: none; } .choices{ margin: 10px 0; } .choice{ margin: 5px 0; font-family: "Google Sans"; } .add-option{ cursor: pointer; color: #70757a; } .edit-choice{ display: inline; width: 80%; border: none; overflow-y: hidden; font-family: "Google Sans"; } .remove-option{ display: inline; font-size: 1.3rem; cursor: pointer; margin-left: 10px; } .choice-option{ padding: 10px; display: block; border-top: 1px solid #ccc; } .required{ font-family: "Roboto Condensed"; } .answers{ margin: 5px; font-family: "Google Sans"; } .short-answer{ width: 50%; border: none; padding: 5px; border-bottom: 1px solid #70757a; } .long-answer{ width: -webkit-fill-available; width: -moz-available; border: none; padding: 5px; border-bottom: 1px solid #70757a; } .question-option-icon{ width: 1.5rem; cursor: pointer; } .form-navigation{ background-color: #fff; width: 400px; margin: auto; } .navigation{ text-align: center; cursor: pointer; } .form-navigation .active{ color:rgb(0, 132, 223); font-weight: bold; font-family: "Google Sans"; font-size: 0.8rem; } .question-score{ max-width: calc(25% - 8px); display: inline-block; font-family: "Google Sans"; margin: 1rem; } .input-score{ width: 30px; border: none; border-bottom: 1px solid rgb(0, 132, 223); } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; } .question-title-score{ font-family: "Google Sans"; display: inline-block; width: 75%; font-size: 1.5rem; font-weight: normal; margin: 1rem; } .answer-key, .feedback{ margin: 5px 10px; } .answer-key-title, .feedback-title{ margin: 0; font-family: "Roboto Condensed"; display: inline-block; } .multiple-choice{ width: 100%; margin: 5px; } .btn-submit{ background-color: #1a73e8; color: #fff; border: 1px solid #fff; cursor: pointer; } .required-info{ color: #E71C23; margin: 10px; } .edit-form-link{ width: 4rem; height: 4rem; position: fixed; bottom: 50px; right: 0%; border-radius: 50%; border: 1px solid #5f6368; } .response-title{ font-family: "Google Sans"; font-size: 1.5rem; font-weight: normal; } .score-title{ font-family: "Google Sans"; margin: 10px; font-size: 1.2rem; font-weight: normal; } .question-title{ font-family: "Google Sans"; font-size: 1.5rem; } .view-score-btn{ position: relative; background-color: #673ab7; color: #ddd; font-family: "Google Sans"; border: 1px solid #ddd; padding: 10px 20px; margin: 5px 15px; } .feedback{ margin: 10px 5px; } .feedback-title{ font-family: "Poppins"; font-size: 1.1rem; font-weight: normal; } .edit-response-link{ margin-left: 15px; } .form-template-label{ font-family: "Google Sans"; margin: 10px; display: block; } .form-animate { position: relative; display: block; overflow: hidden; } .input-animate { width: -webkit-fill-available; width: -moz-available; border: none; display: inline-block; outline: none; padding: 10px; font-size: 20px; background-color: transparent; border-bottom: 1px solid #7c7c7c; } .input-onFocus { transition: all 0.5s; display: inline-block; bottom: 0; left: -100%; position: absolute; width: 100%; height: 2px; background: #1a73e8; } .form-animate, .input-animate { color: inherit; } .form-animate .input-animate:focus + .input-onFocus { left: 0; } .response-summary{ margin: .5rem 1rem; } .response-summary-title{ font-family: "Google Sans"; font-size: 1rem; font-weight: normal; } blockquote{ font-size: 1.4em; font-family:Open Sans; font-style:italic; color: #555555; padding: 10px; border-left:8px solid #78C0A8 ; position: relative; background:#EDEDED; } blockquote span{ display:block; color:#333333; font-style: normal; font-weight: bold; margin-top:1em; } .pie-chart{ width: 70% !important; height: 70% !important; margin: auto; } @media only screen and (max-width: 768px){ .nav-form-title{ width: 100px; } .form-list-link, .form-box{ width: -webkit-fill-available; width: -moz-available; } .modal{ padding: 0; } .modal-content{ max-width: none; max-height: none; width: -webkit-fill-available; width: -moz-available; height: calc(100% - 40px); border-radius: 0%; } .question-title, .question-title-score{ width: 100%; } .question-type-select{ width: 100%; display: block; } .big-screen{ display: none; } .navigation{ width: calc((100% - 60px)/3) !important; } .question-score{ max-width: 100%; width: 100%; } .input-score{ width: 50px; } } @media only screen and (min-width: 768px){ .small-screen{ display: none; } } ================================================ FILE: index/static/index/viewform.js ================================================ document.addEventListener("DOMContentLoaded", () => { document.body.style.backgroundColor = document.querySelector("#bg-color").innerHTML; document.body.style.color = document.querySelector("#text-color").innerHTML; document.querySelectorAll(".txtClr").forEach(element => { element.style.color = document.querySelector("#text-color").innerHTML; }) document.querySelectorAll(".textarea-adjust").forEach(tx => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; tx.addEventListener('input', e => { tx.style.height = "auto"; tx.style.height = (10 + tx.scrollHeight)+"px"; }) }) document.querySelectorAll('input[type="checkbox"]').forEach(element => { document.getElementsByName(element.name).forEach(checkbox => { checkbox.addEventListener("input", function(){ let totalChecked = 0 document.getElementsByName(element.name).forEach(checkbox => { if(checkbox.checked) totalChecked++; }) if(totalChecked > 0){ document.getElementsByName(element.name).forEach(checkbox => { checkbox.removeAttribute("required") }) }else{ document.getElementsByName(element.name).forEach(checkbox => { checkbox.setAttribute("required", '') }) } }) }) }) }) ================================================ FILE: index/templates/error/403.html ================================================ {% extends 'index/layout.html' %} {% block body %}

Error 403 Forbidden.

{% endblock %} ================================================ FILE: index/templates/error/404.html ================================================ {% extends 'index/layout.html' %} {% block body %}

Error 404 not found

{% endblock %} ================================================ FILE: index/templates/index/edit_response.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% load get_response %} {% load to_int %} {% load is_response %} {% block title %}{{form.title}} ~ Google Forms CLONE{% endblock %} {% block script %} {% endblock %} {% block body %}
{% csrf_token %}

{{form.title}}

* Required

{{form.description|linebreaksbr}}
{% if form.collect_email %}

Email address: *

{% endif %} {% for question in form.questions.all %}
{% if form.is_quiz %}

{{question.question}} {% if question.required %}*{% endif %}

{% else %}

{{question.question}} {% if question.required %}*{% endif %}

{% endif %} {% if question.question_type == "short" %} {% elif question.question_type == "paragraph" %} {% elif question.question_type == "multiple choice" %} {% for choice in question.choices.all %}
{% if response|get_response:question.pk|to_int == choice.pk|to_int %} {% else %} {% endif %}
{% endfor %} {% elif question.question_type == "checkbox" %} {% for choice in question.choices.all %}
{% if response|get_responses:question.pk|is_response:choice.pk %} {% else %} {% endif %}
{% endfor %} {% endif %}
{% endfor %}
{% if user == form.creator %} Edit Question {% endif %}
{% endblock %} ================================================ FILE: index/templates/index/form.html ================================================ {% extends 'index/layout.html' %} {% block title %} {{form.title}} ~ Google Forms CLONE {% endblock %} {% load static %} {% block script %} {% endblock %} {% block body %}
Google Forms Icon(CLONE)
Theme icon Preview icon Send icon Setting icon
{% if form.is_quiz %} Questions Scores Responses {% else %} Questions Responses {% endif %}
{% if form.collect_email %}

Email address *

This form is collecting email addresses. Change settings

{% endif %}
{% for question in form.questions.all %}
{% if question.question_type == "multiple choice" %}
{% for choice in question.choices.all %}
×
{% endfor %}
{% elif question.question_type == "checkbox" %}
{% for choice in question.choices.all %}
×
{% endfor %}
{% elif question.question_type == "short" %}
{% elif question.question_type == "paragraph" %}
{% endif %}
Delete question icon
{% endfor %}
Add question icon Preview icon {% if form.is_quiz %} Score icon {% endif %}
{% endblock %} ================================================ FILE: index/templates/index/form_response.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% block script %} {% endblock %} {% block body %}

{{form.title}}

{{form.confirmation_message}}

{% if form.edit_after_submit %} Edit response {% endif %} {% if form.is_quiz and form.allow_view_score %} {% endif %}
{% endblock %} ================================================ FILE: index/templates/index/index.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% block script %} {% endblock %} {% load static %} {% block body %}

Start a new form

Blank form Blank Form
Contact Information Contact Information
Customer Feedback Customer Feedback
Event Registration Event Registration
{% endblock %} ================================================ FILE: index/templates/index/layout.html ================================================ {% load static %} {% block title %}Google Forms CLONE{% endblock %} {% block script %} {% endblock %} {% block body %} {% endblock %} ================================================ FILE: index/templates/index/login.html ================================================ {% extends 'index/layout.html' %} {% block body %}

Login user

{{message}}

{% csrf_token %}

Username

Password:

Don't have account yet? Register

{% endblock %} ================================================ FILE: index/templates/index/register.html ================================================ {% extends 'index/layout.html' %} {% block body %}

Register user

{{message}}

{% csrf_token %}

Username:

Email:

Password:

Password Confirmation:

Already have account? Login

{% endblock %} ================================================ FILE: index/templates/index/response.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% load get_response %} {% load to_int %} {% load is_response %} {% load score %} {% block script %} {% endblock %} {% block body %}

{{form.title}}

{% if form.is_quiz %}

Score: {{score}} / {{total_score}}

{% endif %}

* Required

{{form.description|linebreaksbr}}
{% if form.collect_email %}

Email address: *

{{response.responder_email}}

{% endif %} {% for question in form.questions.all %}

{{question.question}} {% if question.required %}*{% endif %} {% if form.is_quiz %}{{response|score:question.pk}} / {{question.score}}{% endif %}

{% if question.question_type == "short" %}

{{response|get_response:question.pk}}

{% elif question.question_type == "paragraph" %}

{{response|get_response:question.pk|linebreaksbr}}

{% elif question.question_type == "multiple choice" %} {% for choice in question.choices.all %}
{% if response|get_response:question.pk|to_int == choice.pk|to_int %} {% else %} {% endif %}
{% endfor %} {% elif question.question_type == "checkbox" %} {% for choice in question.choices.all %}
{% if response|get_responses:question.pk|is_response:choice.pk %} {% else %} {% endif %}
{% endfor %} {% endif %} {% if user != form.creator and question.feedback != None and question.feedback != "" %} {% endif %}
{% endfor %}
{% if user == form.creator %} Edit Question {% endif %}
{% endblock %} ================================================ FILE: index/templates/index/responses.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% load get_property %} {% load generate_color %} {% load count %} {% block title %}{{form.title}}'s response ~ Google Forms CLONE{% endblock %} {% block script %} {% endblock %} {% block body %}
Google Forms Icon(CLONE) {{form.title}}
Theme icon Preview icon Send icon Setting icon
{% if form.is_quiz %} Questions Scores Responses {% else %} Questions Responses {% endif %}
{% if responses.count > 0 %}

Individual Response:

{% else %}

0 responses

{% endif %}

Questions Summary:

{% for r in responsesSummary %}

{{r.question.question}}

{% if r.answers|count > 0 or filteredResponsesSummary|get_property:r.question.question|count > 0 %} {% if r.question.question_type == 'short' or r.question.question_type == 'paragraph' %} {% for i in r.answers %}
{{i.answer}}
{% endfor %} {% else %} {% endif %} {% else %}
No responses
{% endif %}
{% endfor %}
Preview icon Edit Question {% if form.is_quiz %} Score icon {% endif %}
{% endblock %} ================================================ FILE: index/templates/index/score.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% block title %}{{form.title}} ~ Google Forms CLONE{% endblock %} {% block script %} {% endblock %} {% block body %}
Google Forms Icon(CLONE) {{form.title}}
Theme icon Preview icon Send icon Setting icon

{{form.title}}

{{form.description|linebreaksbr}}

{% if form.collect_email %}

Email address *

This form is collecting email addresses. Change settings

{% endif %}
{% for question in form.questions.all %}

{{question.question}}

Answer key:

{% if question.question_type == "short" %} {% elif question.question_type == "paragraph" %} {% elif question.question_type == "multiple choice" %} {% for i in question.choices.all %}
{% endfor %} {% elif question.question_type == "checkbox" %} {% for i in question.choices.all %}
{% endfor %} {% endif %}
{% endfor %}
{% endblock %} ================================================ FILE: index/templates/index/view_form.html ================================================ {% extends 'index/layout.html' %} {% load static %} {% block title %}{{form.title}} ~ Google Forms CLONE{% endblock %} {% block script %} {% endblock %} {% block body %}
{% csrf_token %}

{{form.title}}

* Required

{{form.description|linebreaksbr}}
{% if form.collect_email %}

Email address: *

{% endif %} {% for question in form.questions.all %}

{{question.question}} {% if question.required %}*{% endif %}

{% if question.question_type == "short" %} {% elif question.question_type == "paragraph" %} {% elif question.question_type == "multiple choice" %} {% for choice in question.choices.all %}
{% endfor %} {% elif question.question_type == "checkbox" %} {% for choice in question.choices.all %}
{% endfor %} {% endif %}
{% endfor %}
{% if user == form.creator %} Edit Question {% endif %}
{% endblock %} ================================================ FILE: index/templatetags/count.py ================================================ from django import template register = template.Library() @register.filter def count(array): return len(array) ================================================ FILE: index/templatetags/generate_color.py ================================================ from django import template import random register = template.Library() @register.filter def generate_color(_): return f"rgb({random.randint(0, 255)},{random.randint(0, 255)},{random.randint(0, 255)})" ================================================ FILE: index/templatetags/get_property.py ================================================ from django import template register = template.Library() @register.filter def get_property(array, index): return array[index] ================================================ FILE: index/templatetags/get_response.py ================================================ from django import template register = template.Library() @register.filter def get_response(responses, pk): return responses.response.get(answer_to__pk = pk).answer ================================================ FILE: index/templatetags/is_response.py ================================================ from django import template register = template.Library() @register.filter def get_responses(responses, pk): return responses.response.filter(answer_to__pk = pk) @register.filter def is_response(responses, pk): for i in responses: if int(i.answer) == int(pk): return True return False ================================================ FILE: index/templatetags/score.py ================================================ from django import template register = template.Library() @register.filter def score(responses, question): response = responses.response.filter(answer_to__pk = question) for i in response: if i.answer_to.question_type == "short" or i.answer_to.question_type == "paragraph": if i.answer == i.answer_to.answer_key: return i.answer_to.score else: return 0 elif i.answer_to.question_type == "multiple choice": answerKey = None for j in i.answer_to.choices.all(): if j.is_answer: answerKey = j.id if answerKey is not None and int(answerKey) == int(i.answer): return i.answer_to.score else: return 0 elif i.answer_to.question_type == "checkbox": answers = [] answer_keys = [] for j in responses.response.filter(answer_to__pk = i.answer_to.pk): answers.append(int(j.answer)) for k in j.answer_to.choices.all(): if k.is_answer and k.pk not in answer_keys: answer_keys.append(k.pk) if answers == answer_keys: return i.answer_to.score else: return 0 ================================================ FILE: index/templatetags/to_int.py ================================================ from django import template register = template.Library() @register.filter def to_int(number): return int(number) ================================================ FILE: index/tests.py ================================================ from django.test import TestCase # Create your tests here. ================================================ FILE: index/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path('', views.index, name="index"), path('login', views.login_view, name="login"), path('register', views.register, name="register"), path('form//export_csv', views.exportcsv,name='export_csv'), path('logout', views.logout_view, name="logout"), path('form/create', views.create_form, name="create_form"), path('form/create/contact', views.contact_form_template, name="contact_form_template"), path('form/create/feedback', views.customer_feedback_template, name="customer_feedback_template"), path('form/create/event', views.event_registration_template, name="event_registration_template"), path('form//edit', views.edit_form, name="edit_form"), path('form//edit_title', views.edit_title, name="edit_title"), path('form//edit_description', views.edit_description, name="edit_description"), path('form//edit_background_color', views.edit_bg_color, name="edit_background_color"), path('form//edit_text_color', views.edit_text_color, name="edit_text_color"), path('form//edit_setting', views.edit_setting, name="edit_setting"), path('form//delete', views.delete_form, name="delete_form"), path('form//edit_question', views.edit_question, name="edit_question"), path('form//edit_choice', views.edit_choice, name="edit_choice"), path('form//add_choice', views.add_choice, name="add_choice"), path('form//remove_choice', views.remove_choice, name="remove_choice"), path('form//get_choice/', views.get_choice, name="get_choice"), path('form//add_question', views.add_question, name="add_question"), path('form//delete_question/', views.delete_question, name="delete_question"), path('form//score', views.score, name="score"), path('form//edit_score', views.edit_score, name="edit_score"), path('form//answer_key', views.answer_key, name="answer_key"), path('form//feedback', views.feedback, name="feedback"), path('form//viewform', views.view_form, name="view_form"), path('form//submit', views.submit_form, name="submit_form"), path('form//responses', views.responses, name='responses'), path('form//response/', views.response, name="response"), path('form//response//edit', views.edit_response, name="edit_response"), path('form//responses/delete', views.delete_responses, name="delete_responses"), path('403', views.FourZeroThree, name="403"), path('404', views.FourZeroFour, name="404") ] ================================================ FILE: index/views.py ================================================ from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.db import IntegrityError from django.urls import reverse from django.contrib.auth import authenticate, login, logout from .models import User, Choices, Questions, Answer, Form, Responses from django.core import serializers import json import random import string import csv # Create your views here. def index(request): if not request.user.is_authenticated: return HttpResponseRedirect(reverse('login')) forms = Form.objects.filter(creator = request.user) return render(request, "index/index.html", { "forms": forms }) def login_view(request): #Check if the user is logged in if request.user.is_authenticated: return HttpResponseRedirect(reverse('index')) if request.method == "POST": username = request.POST["username"].lower() password = request.POST["password"] user = authenticate(request, username = username, password = password) # if user authentication success if user is not None: login(request, user) return HttpResponseRedirect(reverse('index')) else: return render(request, "index/login.html", { "message": "Invalid username and/or password" }) return render(request, "index/login.html") def register(request): #Check if the user is logged in if request.user.is_authenticated: return HttpResponseRedirect(reverse('index')) if request.method == "POST": username = request.POST["username"].lower() password = request.POST["password"] email = request.POST["email"] confirmation = request.POST["confirmation"] #check if the password is the same as confirmation if password != confirmation: return render(request, "index/register.html", { "message": "Passwords must match." }) #Checks if the username is already in use if User.objects.filter(email = email).count() == 1: return render(request, "index/register.html", { "message": "Email already taken." }) try: user = User.objects.create_user(username = username, password = password, email = email) user.save() login(request, user) return HttpResponseRedirect(reverse('index')) except IntegrityError: return render(request, "index/register.html", { "message": "Username already taken" }) return render(request, "index/register.html") def logout_view(request): #Logout the user logout(request) return HttpResponseRedirect(reverse('index')) def create_form(request): # Creator must be authenticated if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) # Create a blank form API if request.method == "POST": data = json.loads(request.body) title = data["title"] code = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(30)) choices = Choices(choice = "Option 1") choices.save() question = Questions(question_type = "multiple choice", question= "Untitled Question", required= False) question.save() question.choices.add(choices) question.save() form = Form(code = code, title = title, creator=request.user) form.save() form.questions.add(question) form.save() return JsonResponse({"message": "Sucess", "code": code}) def edit_form(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) return render(request, "index/form.html", { "code": code, "form": formInfo }) def edit_title(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) if len(data["title"]) > 0: formInfo.title = data["title"] formInfo.save() else: formInfo.title = formInfo.title[0] formInfo.save() return JsonResponse({"message": "Success", "title": formInfo.title}) def edit_description(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) formInfo.description = data["description"] formInfo.save() return JsonResponse({"message": "Success", "description": formInfo.description}) def edit_bg_color(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) formInfo.background_color = data["bgColor"] formInfo.save() return JsonResponse({"message": "Success", "bgColor": formInfo.background_color}) def edit_text_color(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) formInfo.text_color = data["textColor"] formInfo.save() return JsonResponse({"message": "Success", "textColor": formInfo.text_color}) def edit_setting(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) formInfo.collect_email = data["collect_email"] formInfo.is_quiz = data["is_quiz"] formInfo.authenticated_responder = data["authenticated_responder"] formInfo.confirmation_message = data["confirmation_message"] formInfo.edit_after_submit = data["edit_after_submit"] formInfo.allow_view_score = data["allow_view_score"] formInfo.save() return JsonResponse({'message': "Success"}) def delete_form(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse("404")) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "DELETE": #Delete all questions and choices for i in formInfo.questions.all(): for j in i.choices.all(): j.delete() i.delete() for i in Responses.objects.filter(response_to = formInfo): for j in i.response.all(): j.delete() i.delete() formInfo.delete() return JsonResponse({'message': "Success"}) def edit_question(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) question_id = data["id"] question = Questions.objects.filter(id = question_id) if question.count() == 0: return HttpResponseRedirect(reverse("404")) else: question = question[0] question.question = data["question"] question.question_type = data["question_type"] question.required = data["required"] if(data.get("score")): question.score = data["score"] if(data.get("answer_key")): question.answer_key = data["answer_key"] question.save() return JsonResponse({'message': "Success"}) def edit_choice(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) choice_id = data["id"] choice = Choices.objects.filter(id = choice_id) if choice.count() == 0: return HttpResponseRedirect(reverse("404")) else: choice = choice[0] choice.choice = data["choice"] if(data.get('is_answer')): choice.is_answer = data["is_answer"] choice.save() return JsonResponse({'message': "Success"}) def add_choice(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) choice = Choices(choice="Option") choice.save() formInfo.questions.get(pk = data["question"]).choices.add(choice) formInfo.save() return JsonResponse({"message": "Success", "choice": choice.choice, "id": choice.id}) def remove_choice(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": data = json.loads(request.body) choice = Choices.objects.filter(pk = data["id"]) if choice.count() == 0: return HttpResponseRedirect(reverse("404")) else: choice = choice[0] choice.delete() return JsonResponse({"message": "Success"}) def get_choice(request, code, question): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "GET": question = Questions.objects.filter(id = question) if question.count() == 0: return HttpResponseRedirect(reverse('404')) else: question = question[0] choices = question.choices.all() choices = [{"choice":i.choice, "is_answer":i.is_answer, "id": i.id} for i in choices] return JsonResponse({"choices": choices, "question": question.question, "question_type": question.question_type, "question_id": question.id}) def add_question(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "POST": choices = Choices(choice = "Option 1") choices.save() question = Questions(question_type = "multiple choice", question= "Untitled Question", required= False) question.save() question.choices.add(choices) question.save() formInfo.questions.add(question) formInfo.save() return JsonResponse({'question': {'question': "Untitled Question", "question_type": "multiple choice", "required": False, "id": question.id}, "choices": {"choice": "Option 1", "is_answer": False, 'id': choices.id}}) def delete_question(request, code, question): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "DELETE": question = Questions.objects.filter(id = question) if question.count() == 0: return HttpResponseRedirect(reverse("404")) else: question = question[0] for i in question.choices.all(): i.delete() question.delete() return JsonResponse({"message": "Success"}) def score(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if not formInfo.is_quiz: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: return render(request, "index/score.html", { "form": formInfo }) def edit_score(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if not formInfo.is_quiz: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: if request.method == "POST": data = json.loads(request.body) question_id = data["question_id"] question = formInfo.questions.filter(id = question_id) if question.count() == 0: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: question = question[0] score = data["score"] if score == "": score = 0 question.score = score question.save() return JsonResponse({"message": "Success"}) def answer_key(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if not formInfo.is_quiz: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: if request.method == "POST": data = json.loads(request.body) question = Questions.objects.filter(id = data["question_id"]) if question.count() == 0: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: question = question[0] if question.question_type == "short" or question.question_type == "paragraph": question.answer_key = data["answer_key"] question.save() else: for i in question.choices.all(): i.is_answer = False i.save() if question.question_type == "multiple choice": choice = question.choices.get(pk = data["answer_key"]) choice.is_answer = True choice.save() else: for i in data["answer_key"]: choice = question.choices.get(id = i) choice.is_answer = True choice.save() question.save() return JsonResponse({'message': "Success"}) def feedback(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if not formInfo.is_quiz: return HttpResponseRedirect(reverse("edit_form", args = [code])) else: if request.method == "POST": data = json.loads(request.body) question = formInfo.questions.get(id = data["question_id"]) question.feedback = data["feedback"] question.save() return JsonResponse({'message': "Success"}) def view_form(request, code): formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] if formInfo.authenticated_responder: if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) return render(request, "index/view_form.html", { "form": formInfo }) def get_client_ip(request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0] else: ip = request.META.get('REMOTE_ADDR') return ip def submit_form(request, code): formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] if formInfo.authenticated_responder: if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) if request.method == "POST": code = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(20)) if formInfo.authenticated_responder: response = Responses(response_code = code, response_to = formInfo, responder_ip = get_client_ip(request), responder = request.user) response.save() else: if not formInfo.collect_email: response = Responses(response_code = code, response_to = formInfo, responder_ip = get_client_ip(request)) response.save() else: response = Responses(response_code = code, response_to = formInfo, responder_ip = get_client_ip(request), responder_email=request.POST["email-address"]) response.save() for i in request.POST: #Excluding csrf token if i == "csrfmiddlewaretoken" or i == "email-address": continue question = formInfo.questions.get(id = i) for j in request.POST.getlist(i): answer = Answer(answer=j, answer_to = question) answer.save() response.response.add(answer) response.save() return render(request, "index/form_response.html", { "form": formInfo, "code": code }) def responses(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] responsesSummary = [] choiceAnswered = {} filteredResponsesSummary = {} for question in formInfo.questions.all(): answers = Answer.objects.filter(answer_to = question.id) if question.question_type == "multiple choice" or question.question_type == "checkbox": choiceAnswered[question.question] = choiceAnswered.get(question.question, {}) for answer in answers: choice = answer.answer_to.choices.get(id = answer.answer).choice choiceAnswered[question.question][choice] = choiceAnswered.get(question.question, {}).get(choice, 0) + 1 responsesSummary.append({"question": question, "answers":answers }) for answr in choiceAnswered: filteredResponsesSummary[answr] = {} keys = choiceAnswered[answr].values() for choice in choiceAnswered[answr]: filteredResponsesSummary[answr][choice] = choiceAnswered[answr][choice] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) return render(request, "index/responses.html", { "form": formInfo, "responses": Responses.objects.filter(response_to = formInfo), "responsesSummary": responsesSummary, "filteredResponsesSummary": filteredResponsesSummary }) def retrieve_checkbox_choices(response, question): checkbox_answers = [] answers = Answer.objects.filter(answer_to=question, response=response) for answer in answers: selected_choice_ids = answer.answer.split(',') # Split the string into individual choice IDs selected_choices = Choices.objects.filter(pk__in=selected_choice_ids) checkbox_answers.append([choice.choice for choice in selected_choices]) return checkbox_answers def exportcsv(request,code): formInfo = Form.objects.filter(code = code) formInfo = formInfo[0] responses=Responses.objects.filter(response_to = formInfo) questions = formInfo.questions.all() http_response = HttpResponse() http_response['Content-Disposition'] = f'attachment; filename= {formInfo.title}.csv' writer = csv.writer(http_response) header = ['Response Code', 'Responder', 'Responder Email','Responder_ip'] for question in questions: header.append(question.question) writer.writerow(header) for response in responses: response_data = [ response.response_code, response.responder.username if response.responder else 'Anonymous', response.responder_email if response.responder_email else '', response.responder_ip if response.responder_ip else '' ] for question in questions: answer = Answer.objects.filter(answer_to=question, response=response).first() if question.question_type not in ['multiple choice','checkbox']: response_data.append(answer.answer if answer else '') elif question.question_type == "multiple choice": response_data.append(answer.answer_to.choices.get(id = answer.answer).choice if answer else '') elif question.question_type == "checkbox": if answer and question.question_type == 'checkbox': checkbox_choices = retrieve_checkbox_choices(response,answer.answer_to) response_data.append(checkbox_choices) print(response_data) writer.writerow(response_data) return http_response def response(request, code, response_code): formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if not formInfo.allow_view_score: if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) total_score = 0 score = 0 responseInfo = Responses.objects.filter(response_code = response_code) if responseInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: responseInfo = responseInfo[0] if formInfo.is_quiz: for i in formInfo.questions.all(): total_score += i.score for i in responseInfo.response.all(): if i.answer_to.question_type == "short" or i.answer_to.question_type == "paragraph": if i.answer == i.answer_to.answer_key: score += i.answer_to.score elif i.answer_to.question_type == "multiple choice": answerKey = None for j in i.answer_to.choices.all(): if j.is_answer: answerKey = j.id if answerKey is not None and int(answerKey) == int(i.answer): score += i.answer_to.score _temp = [] for i in responseInfo.response.all(): if i.answer_to.question_type == "checkbox" and i.answer_to.pk not in _temp: answers = [] answer_keys = [] for j in responseInfo.response.filter(answer_to__pk = i.answer_to.pk): answers.append(int(j.answer)) for k in j.answer_to.choices.all(): if k.is_answer and k.pk not in answer_keys: answer_keys.append(k.pk) _temp.append(i.answer_to.pk) if answers == answer_keys: score += i.answer_to.score return render(request, "index/response.html", { "form": formInfo, "response": responseInfo, "score": score, "total_score": total_score }) def edit_response(request, code, response_code): formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] response = Responses.objects.filter(response_code = response_code, response_to = formInfo) if response.count() == 0: return HttpResponseRedirect(reverse('404')) else: response = response[0] if formInfo.authenticated_responder: if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) if response.responder != request.user: return HttpResponseRedirect(reverse('403')) if request.method == "POST": if formInfo.authenticated_responder and not response.responder: response.responder = request.user response.save() if formInfo.collect_email: response.responder_email = request.POST["email-address"] response.save() #Deleting all existing answers for i in response.response.all(): i.delete() for i in request.POST: #Excluding csrf token and email address if i == "csrfmiddlewaretoken" or i == "email-address": continue question = formInfo.questions.get(id = i) for j in request.POST.getlist(i): answer = Answer(answer=j, answer_to = question) answer.save() response.response.add(answer) response.save() if formInfo.is_quiz: return HttpResponseRedirect(reverse("response", args = [formInfo.code, response.response_code])) else: return render(request, "index/form_response.html", { "form": formInfo, "code": response.response_code }) return render(request, "index/edit_response.html", { "form": formInfo, "response": response }) def contact_form_template(request): # Creator must be authenticated if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) # Create a blank form API if request.method == "POST": code = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(30)) name = Questions(question_type = "short", question= "Name", required= True) name.save() email = Questions(question_type="short", question = "Email", required = True) email.save() address = Questions(question_type="paragraph", question="Address", required = True) address.save() phone = Questions(question_type="short", question="Phone number", required = False) phone.save() comments = Questions(question_type = "paragraph", question = "Comments", required = False) comments.save() form = Form(code = code, title = "Contact information", creator=request.user, background_color="#e2eee0", allow_view_score = False, edit_after_submit = True) form.save() form.questions.add(name) form.questions.add(email) form.questions.add(address) form.questions.add(phone) form.questions.add(comments) form.save() return JsonResponse({"message": "Sucess", "code": code}) def customer_feedback_template(request): # Creator must be authenticated if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) # Create a blank form API if request.method == "POST": code = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(30)) comment = Choices(choice = "Comments") comment.save() question = Choices(choice = "Questions") question.save() bug = Choices(choice = "Bug Reports") bug.save() feature = Choices(choice = "Feature Request") feature.save() feedback_type = Questions(question = "Feedback Type", question_type="multiple choice", required=False) feedback_type.save() feedback_type.choices.add(comment) feedback_type.choices.add(bug) feedback_type.choices.add(question) feedback_type.choices.add(feature) feedback_type.save() feedback = Questions(question = "Feedback", question_type="paragraph", required=True) feedback.save() suggestion = Questions(question = "Suggestions for improvement", question_type="paragraph", required=False) suggestion.save() name = Questions(question = "Name", question_type="short", required=False) name.save() email = Questions(question= "Email", question_type="short", required=False) email.save() form = Form(code = code, title = "Customer Feedback", creator=request.user, background_color="#e2eee0", confirmation_message="Thanks so much for giving us feedback!", description = "We would love to hear your thoughts or feedback on how we can improve your experience!", allow_view_score = False, edit_after_submit = True) form.save() form.questions.add(feedback_type) form.questions.add(feedback) form.questions.add(suggestion) form.questions.add(name) form.questions.add(email) return JsonResponse({"message": "Sucess", "code": code}) def event_registration_template(request): # Creator must be authenticated if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) # Create a blank form API if request.method == "POST": code = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(30)) name = Questions(question="Name", question_type= "short", required=False) name.save() email = Questions(question = "email", question_type="short", required=True) email.save() organization = Questions(question = "Organization", question_type= "short", required=True) organization.save() day1 = Choices(choice="Day 1") day1.save() day2 = Choices(choice= "Day 2") day2.save() day3 = Choices(choice= "Day 3") day3.save() day = Questions(question="What days will you attend?", question_type="checkbox", required=True) day.save() day.choices.add(day1) day.choices.add(day2) day.choices.add(day3) day.save() dietary_none = Choices(choice="None") dietary_none.save() dietary_vegetarian = Choices(choice="Vegetarian") dietary_vegetarian.save() dietary_kosher = Choices(choice="Kosher") dietary_kosher.save() dietary_gluten = Choices(choice = "Gluten-free") dietary_gluten.save() dietary = Questions(question = "Dietary restrictions", question_type="multiple choice", required = True) dietary.save() dietary.choices.add(dietary_none) dietary.choices.add(dietary_vegetarian) dietary.choices.add(dietary_gluten) dietary.choices.add(dietary_kosher) dietary.save() accept_agreement = Choices(choice = "Yes") accept_agreement.save() agreement = Questions(question = "I understand that I will have to pay $$ upon arrival", question_type="checkbox", required=True) agreement.save() agreement.choices.add(accept_agreement) agreement.save() form = Form(code = code, title = "Event Registration", creator=request.user, background_color="#fdefc3", confirmation_message="We have received your registration.\n\ Insert other information here.\n\ \n\ Save the link below, which can be used to edit your registration up until the registration closing date.", description = "Event Timing: January 4th-6th, 2016\n\ Event Address: 123 Your Street Your City, ST 12345\n\ Contact us at (123) 456-7890 or no_reply@example.com", edit_after_submit=True, allow_view_score=False) form.save() form.questions.add(name) form.questions.add(email) form.questions.add(organization) form.questions.add(day) form.questions.add(dietary) form.questions.add(agreement) form.save() return JsonResponse({"message": "Sucess", "code": code}) def delete_responses(request, code): if not request.user.is_authenticated: return HttpResponseRedirect(reverse("login")) formInfo = Form.objects.filter(code = code) #Checking if form exists if formInfo.count() == 0: return HttpResponseRedirect(reverse('404')) else: formInfo = formInfo[0] #Checking if form creator is user if formInfo.creator != request.user: return HttpResponseRedirect(reverse("403")) if request.method == "DELETE": responses = Responses.objects.filter(response_to = formInfo) for response in responses: for i in response.response.all(): i.delete() response.delete() return JsonResponse({"message": "Success"}) # Error handler def FourZeroThree(request): return render(request, "error/403.html") def FourZeroFour(request): return render(request, "error/404.html") ================================================ FILE: manage.py ================================================ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): """Run administrative tasks.""" os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'form.settings') try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main() ================================================ FILE: renovate.json ================================================ { "extends": [ "config:base" ] } ================================================ FILE: requirements.txt ================================================ asgiref==3.5.1 dj-database-url==0.5.0 Django==4.0.2 gunicorn==20.1.0 psycopg2==2.9.3 pytz==2022.1 sqlparse==0.4.2 whitenoise==6.2.0