#### file: ./compose.yaml services: # The Django Web Application web: container_name: card-players-unite-web build: context: . dockerfile: Dockerfile restart: unless-stopped ports: - "8000:8000" volumes: - .:/home/app/webapp environment: - POSTGRES_DB=maverickdb - POSTGRES_USER=maverickdb - POSTGRES_PASSWORD=maverickdb - POSTGRES_HOST=card-players-unite-postgres depends_on: - card-players-unite-postgres card-players-unite-postgres: container_name: card-players-unite-postgres image: postgres:14.1-alpine restart: unless-stopped ports: - 5432:5432 environment: - POSTGRES_DB=maverickdb - POSTGRES_USER=maverickdb - POSTGRES_PASSWORD=maverickdb ## volumes: ## ## - /tmp/volume-data-postgres:/var/lib/postgresql/data card-players-unite-pgadmin: container_name: card-players-unite-pgadmin image: dpage/pgadmin4 depends_on: - card-players-unite-postgres ports: - "5480:80" environment: PGADMIN_DEFAULT_EMAIL: lorem@loremipsum.com PGADMIN_DEFAULT_PASSWORD: maverickdb ## volumes: ## ## - /tmp/volume-data-pgadmin4:/var/lib/pgadmin #### file: ./.gitignore __pycache__ #### file: ./player/urls.py from django.urls import path from .views import RegisterView from django.views.generic.base import TemplateView urlpatterns = [ path("register", RegisterView.as_view(), name="register"), path("home", TemplateView.as_view(template_name="home.html"), name="home"), ] #### file: ./player/views.py from django.contrib.auth.forms import UserCreationForm from django.urls import reverse_lazy from django.views import generic class RegisterView(generic.CreateView): form_class = UserCreationForm success_url = reverse_lazy("login") template_name = "registration/register.html" #### file: ./static/listing/listing.js const arrows = document.querySelectorAll(".arrow"); const movieLists = document.querySelectorAll(".movie-list"); arrows.forEach((arrow, i) => { const itemNumber = movieLists[i].querySelectorAll("img").length; let clickCounter = 0; arrow.addEventListener("click", () => { const ratio = Math.floor(window.innerWidth / 270); clickCounter++; if (itemNumber - (4 + clickCounter) + (4 - ratio) >= 0) { movieLists[i].style.transform = `translateX(${ movieLists[i].computedStyleMap().get("transform")[0].x.value - 300 }px)`; } else { movieLists[i].style.transform = "translateX(0)"; clickCounter = 0; } }); console.log(Math.floor(window.innerWidth / 270)); }); //TOGGLE const ball = document.querySelector(".toggle-ball"); const items = document.querySelectorAll( ".container,.movie-list-title,.navbar-container,.sidebar,.left-menu-icon,.toggle" ); ball.addEventListener("click", () => { items.forEach((item) => { item.classList.toggle("active"); }); ball.classList.toggle("active"); }); #### file: ./static/listing/features.js alert("features"); #### file: ./static/listing/listing.css * { margin: 0; } body { font-family: "Roboto", sans-serif; } .navbar { width: 100%; height: 50px; background-color: black; position: sticky; top: 0; transition: 1s ease all; } .navbar-container { display: flex; align-items: center; padding: 0 50px; height: 100%; color: white; font-family: "Sen", sans-serif; } .logo-container { flex: 4; } .logo { font-size: 30px; color: #4dbf00; } .menu-container { flex: 6; } .menu-list { display: flex; list-style: none; } .menu-list-item { margin-right: 30px; } .menu-list-item.active { font-weight: bold; } .profile-container { flex: 2; display: flex; align-items: center; justify-content: flex-end; } .profile-text-container { margin: 0 20px; } .profile-picture { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; } .toggle { width: 40px; height: 20px; background-color: white; border-radius: 30px; display: flex; align-items: center; justify-content: space-around; position: relative; } .toggle-icon { color: goldenrod; } .toggle-ball { width: 18px; height: 18px; background-color: black; position: absolute; right: 1px; border-radius: 50%; cursor: pointer; transition: 1s ease all; } .sidebar { width: 50px; height: 100%; background-color: black; position: fixed; top: 0; display: flex; flex-direction: column; align-items: center; padding-top: 60px; transition: 1s ease all; } .left-menu-icon { color: white; font-size: 20px; margin-bottom: 40px; } .container { background-color: #151515; min-height: calc(100vh - 50px); color: white; transition: 1s ease all; } .content-container { margin-left: 50px; } .featured-content { height: 50vh; padding: 50px; } .featured-title { width: 200px; } .featured-desc { width: 500px; color: lightgray; margin: 30px 0; } .featured-button { background-color: #4dbf00; color: white; padding: 10px 20px; border-radius: 10px; border: none; outline: none; font-weight: bold; } .movie-list-container { padding: 0 20px; } .movie-list-wrapper { position: relative; overflow: hidden; } .movie-list { display: flex; align-items: center; height: 300px; transform: translateX(0); transition: all 1s ease-in-out; } .movie-list-item { margin-right: 30px; position: relative; } .movie-list-item:hover .movie-list-item-img { transform: scale(1.2); margin: 0 30px; opacity: 0.5; } .movie-list-item:hover .movie-list-item-title, .movie-list-item:hover .movie-list-item-desc, .movie-list-item:hover .movie-list-item-button { opacity: 1; } .movie-list-item-img { transition: all 1s ease-in-out; width: 270px; height: 200px; object-fit: cover; border-radius: 20px; } .movie-list-item-title { background-color: #333; padding: 0 10px; font-size: 32px; font-weight: bold; position: absolute; top: 10%; left: 50px; opacity: 0; transition: 1s all ease-in-out; } .movie-list-item-desc { background-color: #333; padding: 10px; font-size: 14px; position: absolute; top: 30%; left: 50px; width: 230px; opacity: 0; transition: 1s all ease-in-out; } .movie-list-item-button { padding: 10px; background-color: #4dbf00; color: white; border-radius: 10px; outline: none; border: none; cursor: pointer; position: absolute; bottom: 20px; left: 50px; opacity: 0; transition: 1s all ease-in-out; } .arrow { font-size: 120px; position: absolute; top: 90px; right: 0; color: lightgray; opacity: 0.5; cursor: pointer; } .container.active { background-color: white; } .movie-list-title.active { color: black; } .navbar-container.active { background-color: white; color: black; } .sidebar.active{ background-color: white; } .left-menu-icon.active{ color: black; } .toggle.active{ background-color: black; } .toggle-ball.active{ background-color: white; transform: translateX(-20px); } @media only screen and (max-width: 940px){ .menu-container{ display: none; } } #### file: ./compose.bash #!/bin/bash set -e set -x # 1. Shut down existing containers docker compose down --remove-orphans # 2. Build and start containers in the background # The Dockerfile's CMD will handle migrations and runserver automatically. docker compose up -d --build # 3. (Optional) Check the logs to ensure the migrations finished successfully docker compose logs -f #### file: ./requirements.txt djangorestframework django-cors-headers psycopg2-binary django-rest-auth-forked #### file: ./createuser.bash python3 manage.py createsuperuser --username rlmessick --email fake@cardplayersunite.online #### file: ./.dockerignore .git .volume .volumes .import .trash .backup .recycle __pycache__ #### file: ./manage.py #!/usr/bin/env python import os import sys def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.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: ./docs/updates.md correct the favicon use an open source landing page for the home page when you access site use the red admin template as the login when you login as a root user use the open source listing page for the home page when you log in #### file: ./docs/index.md # index! docs! #### file: ./docs/quick-start.md # quick start! ![Card Players Unite](/docs/imagery/cover.png) #### file: ./docs/readme.md # intro ![Card Players Unite](/docs/imagery/cover.png) ## Developer Documentation You can run the app at home. Follow the instructions below on an Ubuntu Linux server. [Developer Docs](/docs/index.md) ## Tools & Tech the tools and tech | website | how the site helped | |---------|---------------------| |[Bootswatch](https://bootswatch.com/)|bootstrap compatible prebuild color theming and css| |[Digital Ocean](https://www.digitalocean.com/community/tutorials)|cloud server hosting and tutorials| |[Internet Tutorials](/docs/tutorials.md)|various tutorials from the internet with how-to help and information| |[Stock Imagery](/docs/attribution.md)|free stock imagery from the internet, with attribution.| |[TOC Generator](https://ecotrust-canada.github.io/markdown-toc/)|github wiki toc generator.| |[Jigsaw validator](https://jigsaw.w3.org/css-validator/)|Validator CSS and HTML| |[Browserstack](https://www.browserstack.com/responsive)|Responsiveness| ## Quick Start Guide You can run the app at home. Follow the instructions below on an Ubuntu Linux server. [Quick Start Guide](/docs/quick-start.md) ### Preview Card Players Unite ![Card Players Unite](/docs/imagery/cover.png) the app is deployed and available for usage. Click the link below to access Card Players Unite. [Access Card Players Unite](https://cardplayersunite.online/) #### file: ./tournament/urls.py from django.urls import include, path from .views import TournamentCreate, TournamentList, TournamentDetail from .views import TournamentUpdate, TournamentDelete from . import views urlpatterns = [ path('', views.custom, name="custom"), path('/', TournamentDetail.as_view(), name='retrieve-bike'), path("reserve//", views.reserveTournament, name="reserve-bike"), path("return//", views.returnTournament, name="return-bike"), path('create/', TournamentCreate.as_view(), name='create-bike'), path('update//', TournamentUpdate.as_view(), name='update-bike'), path('delete//', TournamentDelete.as_view(), name='delete-bike'), ] ## path("/vote/", views.vote, name="vote"), ## path("about", views.about, name="about"), ## ex: /polls/5/ ## path("/", views.detail, name="detail"), ## ex: /polls/5/results/ ## path("/results/", views.results, name="results"), ## ex: /polls/5/vote/ ## path("/vote/", views.vote, name="vote"), ## path('', TournamentList.as_view()), ## path('', login_required(direct_to_template), {'template': 'registration/login.html'}), ## path("custom", views.custom, name="custom"), ## (r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}), ## path('reserve///', TournamentUpdate.as_view(), name='reserve-bike') ## path('return//', TournamentUpdate.as_view(), name='return-bike'), #### file: ./tournament/migrations/__init__.py #### file: ./tournament/migrations/0001_initial.py # Generated by Django 5.0.1 on 2024-03-05 17:26 from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Tournament', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=240, verbose_name='name')), ('description', models.CharField(max_length=240, verbose_name='description')), ('rental_price', models.FloatField(default=999.99, verbose_name='rental_price')), ('rented_user_id', models.IntegerField(verbose_name='rented_user_id')), ], ), ] #### file: ./tournament/models.py from django.db import models class Tournament(models.Model): name = models.CharField("name", max_length=240) description = models.CharField("description", max_length=240) rental_price = models.FloatField("rental_price", default=999.99) rented_user_id = models.IntegerField("rented_user_id") def __str__(self): return self.name #### file: ./tournament/tests.py from django.test import TestCase # Create your tests here. #### file: ./tournament/views.py from django.shortcuts import render from .models import Tournament from rest_framework import generics from .serializers import TournamentSerializer from django.http import HttpResponse from django.template import loader from django.contrib.auth.decorators import login_required from django.shortcuts import redirect from django.shortcuts import reverse ## https://docs.djangoproject.com/en/4.2/intro/tutorial03/ class TournamentCreate(generics.CreateAPIView): queryset = Tournament.objects.all(), serializer_class = TournamentSerializer class TournamentList(generics.ListAPIView): queryset = Tournament.objects.all() serializer_class = TournamentSerializer class TournamentDetail(generics.RetrieveAPIView): queryset = Tournament.objects.all() serializer_class = TournamentSerializer class TournamentUpdate(generics.RetrieveUpdateAPIView): queryset = Tournament.objects.all() serializer_class = TournamentSerializer class TournamentDelete(generics.RetrieveDestroyAPIView): queryset = Tournament.objects.all() serializer_class = TournamentSerializer def reserveTournament(request, pk, user_pk): t = Tournament.objects.get(id=pk) t.rented_user_id = user_pk # change field t.save() # this will update only ## latest_question_list = Tournament.objects.all() context = { "latest_question_list": latest_question_list, "status_message": ("You have successfull reserved the %s." % t.name), } if request.user.is_superuser: template = loader.get_template("manage.html") else: template = loader.get_template("reserve.html") return HttpResponse(template.render(context, request)) latest_question_list = Tournament.objects.all() template = loader.get_template("reserve.html") context = { "latest_question_list": latest_question_list, "status_message": ("You have successfull reserved the %s." % t.name), } return HttpResponse(template.render(context, request)) def returnTournament(request, pk, user_pk): t = Tournament.objects.get(id=pk) t.rented_user_id = 0 # change field t.save() # this will update only ## latest_question_list = Tournament.objects.all() context = { "latest_question_list": latest_question_list, "status_message": ("You have successfull returned the %s." % t.name), } if request.user.is_superuser: template = loader.get_template("manage.html") else: template = loader.get_template("reserve.html") return HttpResponse(template.render(context, request)) #@login_required def custom(request): print("custom custom custom") latest_question_list = Tournament.objects.all() context = { "latest_question_list": latest_question_list, } if request.user.is_superuser: template = loader.get_template("admin.html") else: template = loader.get_template("tournament.html") return HttpResponse(template.render(context, request)) #### file: ./tournament/apps.py from django.apps import AppConfig class TournamentConfig(AppConfig): name = 'tournament' #### file: ./tournament/admin.py from django.contrib import admin # Register your models here. #### file: ./tournament/serializers.py from rest_framework import serializers from .models import Tournament class TournamentSerializer(serializers.ModelSerializer): class Meta: model = Tournament fields = ['id', 'name', 'description', 'rental_price', 'rented_user_id'] #### file: ./Dockerfile FROM python:3.8 ENV DockerHOME=/home/app/webapp ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 RUN mkdir -p $DockerHOME WORKDIR $DockerHOME #RUN pip install --upgrade pip RUN pip install "pip<24.1" COPY requirements.txt . RUN pip install -r requirements.txt COPY . . EXPOSE 8000 # Use a shell script or combined command for runtime tasks CMD python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000 #### file: ./templates/registration/login.html {% load static %} Sign In / Card Player's Unite Admin

Card Players Unite Admin > Log In

{{ form.non_field_errors }} {% csrf_token %}

user name

password


Don't have an account? Create Account.

#### file: ./templates/registration/register.html {% load static %} Sign In / Card Player's Unite Admin

Card Players Unite Admin > Register

{% csrf_token %} {{ form.errors }}

{{form.username.label}}

Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.

{{form.username}}

{{form.password1.label}}

Your password can’t be too similar to your other personal information.

Your password must contain at least 8 characters.
Your password can’t be a commonly used password.
Your password can’t be entirely numeric.

{{form.password1}}

{{form.password2.label}}

Password confirmation: Enter the same password as before, for verification.

{{form.password2}}

Already registered? Log In.

#### file: ./templates/listing.html {% load static %} Card Players Unite / card tournaments and events near you

Alll Tournaments

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

Her

Lorem ipsum dolor sit amet consectetur adipisicing elit. At hic fugit similique accusantium.

#### file: ./app/urls.py from django.contrib import admin from django.urls import path, include from django.views.generic.base import TemplateView from . import views urlpatterns = [ path("", TemplateView.as_view(template_name="listing.html"), name="listing"), path("home", TemplateView.as_view(template_name="home.html"), name="home"), path('tournament/', include('tournament.urls')), path("player/", include("player.urls")), path("player/", include("django.contrib.auth.urls")), ] #### file: ./app/wsgi.py """ WSGI config for application 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', 'app.settings') application = get_wsgi_application() #### file: ./app/__init__.py # -*- coding: utf-8 -*- __VERSION__ = "1.6.28" __RELEASE_DATE__ = "20-Jul-2023" __AUTHOR__ = "Agus Makmun (Summon Agus)" __AUTHOR_EMAIL__ = "summon.agus@gmail.com" #### file: ./app/asgi.py """ ASGI config for application 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', 'app.settings') application = get_asgi_application() #### file: ./app/views.py from django.shortcuts import render from rest_framework import generics from django.http import HttpResponse from django.template import loader ## https://docs.djangoproject.com/en/4.2/intro/tutorial03/ def home(request): print("home home") template = loader.get_template("home.html") context = { "key": "value", } return HttpResponse(template.render(context, request)) #### file: ./app/settings.py """ Django settings for card players unite project. Generated by 'django-admin startproject' using Django 3.1.5. 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 = '7!%*_%*0x9*#-k*1$b$^dax14hu2tazzrvk5tvj@#7vlg*h0ni' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['localhost'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', 'player', 'tournament', ] 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', "corsheaders.middleware.CorsMiddleware", ] CORS_ALLOWED_ORIGINS = [ "https://example.com", "https://sub.example.com", "http://localhost:8000", "http://127.0.0.1:9000", ] CORS_ORIGIN_ALLOW_ALL = True ROOT_URLCONF = 'app.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], '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 = 'app.wsgi.application' # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'maverickdb', 'USER': 'maverickdb', 'PASSWORD': 'maverickdb', 'HOST': 'card-players-unite-postgres', 'PORT': '5432', } } # 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', }, ] ########################################## # 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 ########################################## LOGIN_REDIRECT_URL = "home" LOGOUT_REDIRECT_URL = 'logout1' #SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_COOKIE_AGE = 180 # 3 minutes. "1209600(2 weeks)" by default #SESSION_SAVE_EVERY_REQUEST = True # "False" by default ########################################## ## https://www.w3schools.com/django/django_add_css_file_global.php STATIC_URL = 'static/' STATICFILES_DIRS = [ BASE_DIR / "static" ] ########################################## #### file: ./readme.md # adsfads ## view all tourney ## add new tourney ## admin home ## login ## how to create new user ## tournament operations ### create tournament http://localhost:8888/tournament/create/ ### view existing tournament browser: ``` http://localhost:8888/tournament/1/ ``` terminal: ``` curl http://localhost:8888/tournament/1/ ``` response: ``` {"id":1,"name":"323","description":"222","rental_price":222.0,"rented_user_id":222} ```