mirror of
https://github.com/aykhans/series-robot-web.git
synced 2026-10-18 18:03:52 +00:00
first commit
This commit is contained in:
commit
f9930c2476
53 files changed
+785
No files matched your search
@@ -0,0 +1,6 @@
|
||||
.venv
|
||||
__pycache__
|
||||
migrations
|
||||
db.sqlite3
|
||||
src/config/settings/static
|
||||
.env
|
||||
@@ -0,0 +1,4 @@
|
||||
SECRET_KEY=
|
||||
DB_NAME=
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
@@ -0,0 +1 @@
|
||||
from .get_request import APIClient
|
||||
@@ -0,0 +1,33 @@
|
||||
from requests import get
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class APIClient:
|
||||
def __init__(self, api_key: str) -> None:
|
||||
self.api_key = api_key
|
||||
|
||||
def __get_data(self, link: str) -> dict:
|
||||
raw_data = get(link)
|
||||
if raw_data.status_code == 200:
|
||||
data = raw_data.json()
|
||||
|
||||
if data['errorMessage'] == 'Invalid API Key': raise ValidationError('Invalid API Key')
|
||||
elif data['errorMessage']: raise ValidationError('id is not correct.')
|
||||
|
||||
return data
|
||||
raise ValidationError('Could not created. Please try again later.')
|
||||
|
||||
def key_control(self) -> None:
|
||||
self.__get_data(f"https://imdb-api.com/en/API/Title/{self.api_key}/tt7939766")
|
||||
|
||||
def get_seasons(self, series_id: str) -> list:
|
||||
data = self.__get_data(f"https://imdb-api.com/en/API/Title/{self.api_key}/{series_id}")
|
||||
|
||||
if not data['tvSeriesInfo']:
|
||||
raise ValidationError('This is not a TV series id')
|
||||
return data['tvSeriesInfo']['seasons']
|
||||
|
||||
def get_episodes_count(self, season: int, series_id: str) -> int:
|
||||
data = self.__get_data(
|
||||
f"https://imdb-api.com/en/API/SeasonEpisodes/{self.api_key}/{series_id}/{season}")
|
||||
return len(data['episodes'])
|
||||
Whitespace-only changes.
@@ -0,0 +1,13 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from .models import User
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class CustomAdmin(UserAdmin):
|
||||
list_display = ('username', 'email')
|
||||
fieldsets = UserAdmin.fieldsets + (
|
||||
('IMDB API Key Field', {
|
||||
'fields': ['imdb_api_key']
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'account'
|
||||
@@ -0,0 +1,2 @@
|
||||
from .register import RegisterForm
|
||||
from .profile_editing import ProfileEditingForm
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.contrib.auth.forms import UserChangeForm
|
||||
from account.models import User
|
||||
|
||||
|
||||
class ProfileEditingForm(UserChangeForm):
|
||||
password = None
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('email', 'username', 'imdb_api_key')
|
||||
@@ -0,0 +1,14 @@
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from account.models import User
|
||||
|
||||
|
||||
class RegisterForm(UserCreationForm):
|
||||
password = None
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username',
|
||||
'email',
|
||||
'password1',
|
||||
'password2',
|
||||
'imdb_api_key')
|
||||
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from API_client import APIClient
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
imdb_api_key = models.CharField(max_length=15, blank=False, null=False)
|
||||
|
||||
def clean(self) -> None:
|
||||
client = APIClient(self.imdb_api_key)
|
||||
client.key_control()
|
||||
# raw_data = get(f"https://imdb-api.com/en/API/Title/{self.imdb_api_key}/tt0110413")
|
||||
|
||||
# if raw_data.status_code == 200: data = raw_data.json()
|
||||
# else: raise ValidationError('Account not created. Please try again later')
|
||||
# if not data['errorMessage']: raise ValidationError(data['errorMessage'])
|
||||
|
||||
class Meta:
|
||||
db_table = 'user'
|
||||
verbose_name = 'User'
|
||||
verbose_name_plural = 'Users'
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
{% block title %} Change Password {% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form enctype="multipart/form-data" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
{{form|crispy}}
|
||||
<style>
|
||||
.btn:hover {
|
||||
background: #fff;
|
||||
color: #0069D9;
|
||||
}
|
||||
</style>
|
||||
<input type="submit" value="Change Password" class="btn btn-primary mt-3 mb-5">
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
{% block title %} Login {% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'components/message.html' %}
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{form|crispy}}
|
||||
<style>
|
||||
.btn:hover {
|
||||
background: #fff;
|
||||
color: #0069D9;
|
||||
}
|
||||
</style>
|
||||
<input type="submit" value="Login" class="btn btn-primary mt-3 mb-3">
|
||||
<p class="mb-5">No account? <a href="{% url 'register' %}">Signup</a></p>
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %} Profile {% endblock title %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5>Username: <small>{{profile.username}}</small></h5>
|
||||
<h5>Email: <small>{{profile.email|default:""}}</small></h5>
|
||||
<h5>IMDB API Key: <small>{{profile.imdb_api_key}}</small></h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="/account/profile-editing" style="color: inherit; text-decoration: none;">
|
||||
<button type="button" class="btn btn-primary">Edit Profile</button>
|
||||
</a>
|
||||
<a href="/account/change-password" style="color: inherit; text-decoration: none;">
|
||||
<button type="button" class="btn btn-primary">Change Password</button>
|
||||
</a>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
{% block title %} Profil Güncəllə {% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form enctype="multipart/form-data" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
{{form|crispy}}
|
||||
<style>
|
||||
.btn:hover {
|
||||
background: #fff;
|
||||
color: #0069D9;
|
||||
}
|
||||
</style>
|
||||
<input type="submit" value="Update Profile" class="btn btn-primary mt-3 mb-5">
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
{% block title %} Signup {% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
{{form|crispy}}
|
||||
<a href="https://imdb-api.com/Identity/Account/Register">Get API Key</a><br>
|
||||
<style>
|
||||
.btn:hover {
|
||||
background: #fff;
|
||||
color: #0069D9;
|
||||
}
|
||||
</style>
|
||||
<input type="submit" value="Signup" class="btn btn-primary mt-3 mb-3">
|
||||
<p class="mb-5">Already have an account? <a href="{% url 'login' %}">Login</a></p>
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,15 @@
|
||||
from django.urls import path
|
||||
from account import views
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('login', auth_views.LoginView.as_view(
|
||||
template_name = 'login.html'
|
||||
), name='login'),
|
||||
path('register', views.register_view, name='register'),
|
||||
path('logout', views.logout_view, name='logout'),
|
||||
path('change-password', views.change_password_view, name='change-password'),
|
||||
path('profile-editing', views.profile_editing_view, name='profile-editing'),
|
||||
path('profile', views.ProfileDetailView.as_view(), name='profile')
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
from .register import register_view
|
||||
from .logout import logout_view
|
||||
from .change_password import change_password_view
|
||||
from .profile_editing import profile_editing_view
|
||||
from .profile_detail import ProfileDetailView
|
||||
@@ -0,0 +1,22 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib import messages
|
||||
|
||||
|
||||
@login_required(login_url='/')
|
||||
def change_password_view(request):
|
||||
if request.method == 'POST':
|
||||
form = PasswordChangeForm(request.user, request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save()
|
||||
update_session_auth_hash(request, user)
|
||||
|
||||
messages.success(request, 'Password changed')
|
||||
return redirect('homepage')
|
||||
|
||||
else:
|
||||
form = PasswordChangeForm(request.user)
|
||||
|
||||
return render(request, 'change_password.html', context={'form': form})
|
||||
@@ -0,0 +1,12 @@
|
||||
from django.contrib.auth import logout
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
@login_required(login_url='/account/login')
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
|
||||
messages.success(request, 'Logged Out')
|
||||
return redirect('login')
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.views.generic import DetailView
|
||||
from account.models import User
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
|
||||
class ProfileDetailView(LoginRequiredMixin, DetailView):
|
||||
login_url = reverse_lazy('login')
|
||||
template_name = 'profile_detail.html'
|
||||
context_object_name = 'profile'
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(
|
||||
User, id=self.request.user.id
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from account.forms import ProfileEditingForm
|
||||
|
||||
|
||||
@login_required(login_url='/')
|
||||
def profile_editing_view(request):
|
||||
if request.method == 'POST':
|
||||
form = ProfileEditingForm(request.POST, instance=request.user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
|
||||
messages.success(request, 'Profile Updated')
|
||||
return redirect('homepage')
|
||||
|
||||
else:
|
||||
form = ProfileEditingForm(instance=request.user)
|
||||
|
||||
return render(request, 'profile_editing.html', context={"form": form})
|
||||
@@ -0,0 +1,35 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib import messages
|
||||
from account.forms import RegisterForm
|
||||
from django.contrib.auth import login, authenticate
|
||||
from django.core.exceptions import ValidationError
|
||||
from requests import get
|
||||
|
||||
|
||||
def register_view(request):
|
||||
if request.method == 'POST':
|
||||
form = RegisterForm(request.POST)
|
||||
if form.is_valid():
|
||||
# raw_data = get(f"https://imdb-api.com/en/API/Title/{request.POST['imdb_api_key']}/tt0110413")
|
||||
# if raw_data.status_code == 200: data = raw_data.json()
|
||||
# else:
|
||||
# messages.info(request, 'Account not created. Please try again later')
|
||||
# return redirect('register')
|
||||
|
||||
# if data['errorMessage'] == 'Invalid API Key':
|
||||
# messages.info(request, 'Invalid API Key')
|
||||
# return redirect('register')
|
||||
|
||||
form.save()
|
||||
username = form.cleaned_data.get('username')
|
||||
password = form.cleaned_data.get('password1')
|
||||
user = authenticate(username=username, password=password)
|
||||
login(request, user)
|
||||
|
||||
messages.success(request, 'Registration Successful')
|
||||
return redirect('homepage')
|
||||
|
||||
else:
|
||||
form = RegisterForm()
|
||||
|
||||
return render(request, 'register.html', context={"form": form})
|
||||
Whitespace-only changes.
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for config 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/4.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
Whitespace-only changes.
@@ -0,0 +1,84 @@
|
||||
import os
|
||||
import environ
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
env = environ.Env()
|
||||
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
|
||||
|
||||
SECRET_KEY = env("SECRET_KEY")
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'account',
|
||||
'series',
|
||||
'crispy_forms',
|
||||
]
|
||||
|
||||
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 = 'config.urls'
|
||||
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
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 = 'config.wsgi.application'
|
||||
|
||||
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 = 'account.User'
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
@@ -0,0 +1,21 @@
|
||||
from .base import *
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
STATIC_ROOT = os.path.join(PROJECT_DIR, 'static')
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static'
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
from .base import *
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': env('DB_NAME'),
|
||||
'USER': env('DB_USER'),
|
||||
'PASSWORD': env('DB_PASSWORD'),
|
||||
'HOST': 'databasepostgresql',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_URL = '/django_static/'
|
||||
STATIC_ROOT = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), 'opt/services/djangoapp/django_static')
|
||||
@@ -0,0 +1,9 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('account/', include('account.urls')),
|
||||
path('', include('series.urls')),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for config 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/4.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/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', 'config.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()
|
||||
Whitespace-only changes.
@@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import SeriesModel
|
||||
|
||||
|
||||
admin.site.register(SeriesModel)
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SeriesConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'series'
|
||||
@@ -0,0 +1,42 @@
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from API_client import APIClient
|
||||
from django.core.validators import (MaxValueValidator,
|
||||
MinValueValidator,
|
||||
MinLengthValidator)
|
||||
|
||||
|
||||
class SeriesModel(models.Model):
|
||||
user = models.ForeignKey('account.User', on_delete=models.CASCADE, related_name='series')
|
||||
title = models.CharField(max_length=35, blank=False, null=False)
|
||||
imdb_id = models.CharField(max_length=10, validators=[MinLengthValidator(9)],
|
||||
blank=False, null=False)
|
||||
last_season = models.IntegerField(validators=[
|
||||
MaxValueValidator(30),
|
||||
MinValueValidator(1)
|
||||
], blank=False, null=False)
|
||||
last_episode = models.IntegerField(validators=[
|
||||
MaxValueValidator(60),
|
||||
MinValueValidator(1)
|
||||
], blank=False, null=False)
|
||||
show = models.BooleanField(default=True)
|
||||
|
||||
def clean(self) -> None:
|
||||
if len(self.imdb_id) < 9:
|
||||
raise ValidationError(f'Ensure this value has at least 9 characters (it has {len(self.imdb_id)}).')
|
||||
|
||||
if len(self.imdb_id) > 10:
|
||||
raise ValidationError(f'Make sure this value is no more than 10 characters (it has {len(self.imdb_id)}).')
|
||||
|
||||
client = APIClient(self.user.imdb_api_key)
|
||||
|
||||
seasons = client.get_seasons(self.imdb_id)
|
||||
if str(self.last_season) not in seasons:
|
||||
raise ValidationError('The season number you entered is not correct.')
|
||||
|
||||
episodes_count = client.get_episodes_count(self.last_season, self.imdb_id)
|
||||
if self.last_episode > episodes_count:
|
||||
raise ValidationError('The episode number you entered is not correct.')
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.user.username} - {self.title}"
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %} Home Page {% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% include 'components/message.html' %}
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,8 @@
|
||||
from django.urls import path
|
||||
from series import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.homepage_view, name='homepage'),
|
||||
# path('series/create', views.homepage_view, name='homepage'),
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
from .homepage import homepage_view
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
@login_required(login_url='/account/login')
|
||||
def homepage_view(request):
|
||||
return render(request, 'homepage.html')
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
@charset "UTF-8";/*!
|
||||
* Bootstrap v5.0.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:30Line truncated
|
||||
/*# sourceMappingURL=bootstrap.min.css.map */
|
||||
@@ -0,0 +1,35 @@
|
||||
footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 65px;
|
||||
background: black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.div-left {
|
||||
margin-top: 1.5rem;
|
||||
margin-left: 1.3rem;
|
||||
}
|
||||
|
||||
.div-right {
|
||||
margin-top: 1rem;
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
footer div span a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.contact-links{
|
||||
border: 0;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.contact-icons{
|
||||
font-size: 23px;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
+7
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v5.0.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter(t=>t.matches(e)),parents(t,e){const i=[];let n=t.parentNode;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]}},e=t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t},i=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i="#"+i.split("#")[1]),e=i&&"#"!==i?i.trim():null}return e},n=t=>{const e=i(t);return e&&document.querySelector(e)?e:null},s=t=>{const e=i(t);return e?document.querySelector(e):null},o=t=>{t.dispatchEvent(new Event("transitionend"))},r=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),a=e=>r(e)?e.jquery?e[0]:e:"string"==typeof e&&e.length>0?t.findOne(e):null,l=(t,e,i)=>{Object.keys(i).forEach(n=>{const s=i[n],o=e[n],a=o&&r(o)?"element":null==(l=o)?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)})},c=t=>!(!r(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),h=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),d=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?d(t.parentNode):null},u=()=>{},f=t=>t.offsetHeight,p=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},m=[],g=()=>"rtl"===document.documentElement.dir,_=t=>{var e;e=()=>{const e=p();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(m.length||document.addEventListener("DOMContentLoaded",()=>{m.forEach(t=>t())}),m.push(e)):e()},b=t=>{"function"==typeof t&&t()},v=(t,e,i=!0)=>{if(!i)return void b(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const r=({target:i})=>{i===e&&(s=!0,e.removeEventListener("transitionend",r),b(t))};e.addEventListener("transitionend",r),setTimeout(()=>{s||o(e)},n)},y=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},w=/[^.]*(?=\..*)\.|.*/,E=/\..*/,A=/::\d+$/,T={};let O=1;const C={mouseenter:"mouseover",mouseleave:"mouseout"},k=/^(mouseenter|mouseleave)/i,L=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function x(t,e){return e&&`${e}::${O++}`||t.uidEvent||O++}function D(t){const e=x(t);return t.uidEvent=e,T[e]=T[e]||{},T[e]}function S(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;s<o;s++){const o=t[n[s]];if(o.originalHandler===e&&o.delegationSelector===i)return o}return null}function I(t,e,i){const n="string"==typeof e,s=n?i:e;let o=M(t);return L.has(o)||(o=t),[n,s,o]}function N(t,e,i,n,s){if("string"!=typeof e||!t)return;if(i||(i=n,n=null),k.test(e)){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=I(e,i,n),l=D(t),c=l[a]||(l[a]={}),h=S(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=x(r,e.replace(w,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parLine truncated
|
||||
//# sourceMappingURL=bootstrap.bundle.min.js.map
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v5.0.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e(t.Popper)}(this,(function(t){"use strict";function e(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach((function(s){if("default"!==s){var i=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,i.get?i:{enumerable:!0,get:function(){return t[s]}})}})),e.default=t,Object.freeze(e)}var s=e(t);const i={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter(t=>t.matches(e)),parents(t,e){const s=[];let i=t.parentNode;for(;i&&i.nodeType===Node.ELEMENT_NODE&&3!==i.nodeType;)i.matches(e)&&s.push(i),i=i.parentNode;return s},prev(t,e){let s=t.previousElementSibling;for(;s;){if(s.matches(e))return[s];s=s.previousElementSibling}return[]},next(t,e){let s=t.nextElementSibling;for(;s;){if(s.matches(e))return[s];s=s.nextElementSibling}return[]}},n=t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t},o=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let s=t.getAttribute("href");if(!s||!s.includes("#")&&!s.startsWith("."))return null;s.includes("#")&&!s.startsWith("#")&&(s="#"+s.split("#")[1]),e=s&&"#"!==s?s.trim():null}return e},r=t=>{const e=o(t);return e&&document.querySelector(e)?e:null},a=t=>{const e=o(t);return e?document.querySelector(e):null},l=t=>{t.dispatchEvent(new Event("transitionend"))},c=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),h=t=>c(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?i.findOne(t):null,d=(t,e,s)=>{Object.keys(s).forEach(i=>{const n=s[i],o=e[i],r=o&&c(o)?"element":null==(a=o)?""+a:{}.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase();var a;if(!new RegExp(n).test(r))throw new TypeError(`${t.toUpperCase()}: Option "${i}" provided type "${r}" but expected type "${n}".`)})},u=t=>!(!c(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),g=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),p=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?p(t.parentNode):null},f=()=>{},m=t=>t.offsetHeight,_=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},b=[],v=()=>"rtl"===document.documentElement.dir,y=t=>{var e;e=()=>{const e=_();if(e){const s=t.NAME,i=e.fn[s];e.fn[s]=t.jQueryInterface,e.fn[s].Constructor=t,e.fn[s].noConflict=()=>(e.fn[s]=i,t.jQueryInterface)}},"loading"===document.readyState?(b.length||document.addEventListener("DOMContentLoaded",()=>{b.forEach(t=>t())}),b.push(e)):e()},w=t=>{"function"==typeof t&&t()},E=(t,e,s=!0)=>{if(!s)return void w(t);const i=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:s}=window.getComputedStyle(t);const i=Number.parseFloat(e),n=Number.parseFloat(s);return i||n?(e=e.split(",")[0],s=s.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(s))):0})(e)+5;let n=!1;const o=({target:s})=>{s===e&&(n=!0,e.removeEventListener("transitionend",o),w(t))};e.addEventListener("transitionend",o),setTimeout(()=>{n||l(e)},i)},A=(t,e,s,i)=>{let n=t.indexOf(e);if(-1===n)return t[!s&&i?t.length-1:0];const o=t.length;return n+=s?1:-1,i&&(n=(n+o)%o),t[Math.max(0,Math.min(n,o-1))]},T=/[^.]*(?=\..*)\.|.*/,C=/\..*/,k=/::\d+$/,L={};let O=1;const D={mouseenter:"mouseover",mouseleave:"mouseout"},I=/^(mouseenter|mouseleave)/i,N=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function S(t,e){return e&&`${e}::${O++}`||t.uidEvent||O++}function x(t){const e=S(t);return t.uidEvent=e,L[e]=L[e]||{},L[e]}function M(t,e,s=null){const i=Object.keys(t);for(let n=0,o=i.length;n<o;n++){const o=t[i[n]];if(o.originalHandler===e&&o.delegationSelector===s)return o}return null}function P(t,e,s){const i="string"==typeof e,n=i?s:e;let o=R(t);return N.has(o)||(o=t),[i,n,o]}function j(t,e,s,i,n){if("string"!=typeof e||!t)return;if(s||(s=i,i=null),I.test(e)){const t=t=>function(e){if(!e.relatedTarget||e.relatedTaLine truncated
|
||||
//# sourceMappingURL=bootstrap.min.js.map
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @popperjs/core v2.9.2 - MIT License
|
||||
*/
|
||||
|
||||
"use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){function t(e){return{width:(e=e.getBoundingClientRect()).width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function n(e){return null==e?window:"[object Window]"!==e.toString()?(e=e.ownerDocument)&&e.defaultView||window:e}function o(e){return{scrollLeft:(e=n(e)).pageXOffset,scrollTop:e.pageYOffset}}function r(e){return e instanceof n(e).Element||e instanceof Element}function i(e){return e instanceof n(e).HTMLElement||e instanceof HTMLElement}function a(e){return"undefined"!=typeof ShadowRoot&&(e instanceof n(e).ShadowRoot||e instanceof ShadowRoot)}function s(e){return e?(e.nodeName||"").toLowerCase():null}function f(e){return((r(e)?e.ownerDocument:e.document)||window.document).documentElement}function p(e){return t(f(e)).left+o(e).scrollLeft}function c(e){return n(e).getComputedStyle(e)}function l(e){return e=c(e),/auto|scroll|overlay|hidden/.test(e.overflow+e.overflowY+e.overflowX)}function u(e,r,a){void 0===a&&(a=!1);var c=f(r);e=t(e);var u=i(r),d={scrollLeft:0,scrollTop:0},m={x:0,y:0};return(u||!u&&!a)&&(("body"!==s(r)||l(c))&&(d=r!==n(r)&&i(r)?{scrollLeft:r.scrollLeft,scrollTop:r.scrollTop}:o(r)),i(r)?((m=t(r)).x+=r.clientLeft,m.y+=r.clientTop):c&&(m.x=p(c))),{x:e.left+d.scrollLeft-m.x,y:e.top+d.scrollTop-m.y,width:e.width,height:e.height}}function d(e){var n=t(e),o=e.offsetWidth,r=e.offsetHeight;return 1>=Math.abs(n.width-o)&&(o=n.width),1>=Math.abs(n.height-r)&&(r=n.height),{x:e.offsetLeft,y:e.offsetTop,width:o,height:r}}function m(e){return"html"===s(e)?e:e.assignedSlot||e.parentNode||(a(e)?e.host:null)||f(e)}function h(e){return 0<=["html","body","#document"].indexOf(s(e))?e.ownerDocument.body:i(e)&&l(e)?e:h(m(e))}function v(e,t){var o;void 0===t&&(t=[]);var r=h(e);return e=r===(null==(o=e.ownerDocument)?void 0:o.body),o=n(r),r=e?[o].concat(o.visualViewport||[],l(r)?r:[]):r,t=t.concat(r),e?t:t.concat(v(m(r)))}function g(e){return i(e)&&"fixed"!==c(e).position?e.offsetParent:null}function y(e){for(var t=n(e),o=g(e);o&&0<=["table","td","th"].indexOf(s(o))&&"static"===c(o).position;)o=g(o);if(o&&("html"===s(o)||"body"===s(o)&&"static"===c(o).position))return t;if(!o)e:{if(o=-1!==navigator.userAgent.toLowerCase().indexOf("firefox"),-1===navigator.userAgent.indexOf("Trident")||!i(e)||"fixed"!==c(e).position)for(e=m(e);i(e)&&0>["html","body"].indexOf(s(e));){var r=c(e);if("none"!==r.transform||"none"!==r.perspective||"paint"===r.contain||-1!==["transform","perspective"].indexOf(r.willChange)||o&&"filter"===r.willChange||o&&r.filter&&"none"!==r.filter){o=e;break e}e=e.parentNode}o=null}return o||t}function b(e){function t(e){o.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){o.has(e)||(e=n.get(e))&&t(e)})),r.push(e)}var n=new Map,o=new Set,r=[];return e.forEach((function(e){n.set(e.name,e)})),e.forEach((function(e){o.has(e.name)||t(e)})),r}function w(e){var t;return function(){return t||(t=new Promise((function(n){Promise.resolve().then((function(){t=void 0,n(e())}))}))),t}}function x(e){return e.split("-")[0]}function O(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&a(n))do{if(t&&e.isSameNode(t))return!0;t=t.parentNode||t.host}while(t);return!1}function j(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function E(e,r){if("viewport"===r){r=n(e);var a=f(e);r=r.visualViewport;var s=a.clientWidth;a=a.clientHeight;var l=0,u=0;r&&(s=r.width,a=r.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(l=r.offsetLeft,u=r.offsetTop)),e=j(e={width:s,height:a,x:l+p(e),y:u})}else i(r)?((e=t(r)).top+=r.clientTop,e.left+=r.clientLeft,e.bottom=e.top+r.clientHeight,e.right=e.left+r.clientWidth,e.width=r.clientWidth,e.height=r.clientHeight,e.x=e.left,e.y=e.top):(u=f(e),e=f(u),s=o(u),r=null==(a=u.ownerDocument)?void 0:a.body,a=_(e.scrollWidth,e.clientWidth,r?r.scrollWidth:0,r?r.clientWidth:0),l=_(e.scrollHeight,e.clientHeight,r?r.scrollHeight:0,r?r.clientHeight:0),u=-s.scrollLeft+p(u),s=-s.scrollTop,"rtl"===c(r||e).direction&&(u+=_(e.clientWidth,r?r.clientWidth:0)-a),e=j({width:a,height:l,x:u,y:s}));return e}function D(e,t,n){return t="clippingParents"===t?function(e){var t=v(m(e)),n=0<=["absolute","fixed"].indexOf(c(e).position)&&i(e)?y(e):e;return r(n)?t.filter((function(e){return r(e)&&O(e,n)&&"body"!==s(e)})):[]}(e):[].concat(t),(n=(n=[].concat(t,[n])).reduce((function(t,n){return n=E(e,n),t.top=_(n.top,t.top),t.right=U(n.right,t.right),t.bottom=U(n.bottom,t.bottom),t.left=_(n.left,t.left),t}),E(e,n[0]))).width=n.right-n.left,n.height=n.bottom-n.top,n.x=n.left,n.y=n.top,n}function L(e){return 0<=["top","bottom"].indexOf(e)?"x":"y"}function P(e){var t=e.reference,n=e.element,o=(e=e.placement)?x(e):null;e=e?e.split("-")[1]:null;var r=t.xLine truncated
|
||||
//# sourceMappingURL=popper.min.js.map
|
||||
@@ -0,0 +1,30 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- neye bakıyon lan html var css var neye bakıyon -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% include "components/navbar.html" %}
|
||||
|
||||
<div class="container mt-5">
|
||||
{% block content %}{% endblock content %}
|
||||
</div>
|
||||
|
||||
{% include "components/footer.html" %}
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
{% load static %}
|
||||
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/footer.css' %}">
|
||||
|
||||
<footer>
|
||||
<div class="div-left" style="float: left;">
|
||||
<span class="text-muted">
|
||||
<a href="mailto:ayxan.shahsuvarov1@gmail.com">© 2022 Aykhan Shahsuvarov</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="div-right" style="float: right;">
|
||||
<a class="btn btn-outline-light btn-floating m-1 contact-links" target="_blank" href="https://github.com/Ayxan-z" role="button">
|
||||
<i class="fab fa-github contact-icons"></i>
|
||||
</a>
|
||||
<a class="btn btn-outline-light btn-floating m-1 contact-links" target="_blank" href="https://www.linkedin.com/in/ayxan-shahsuvarov-59a314187" role="button">
|
||||
<i class="fab fa-linkedin-in contact-icons"></i>
|
||||
</a>
|
||||
<a class="btn btn-outline-light btn-floating m-1 contact-links" target="_blank" href="mailto:ayxan.shahsuvarov1@gmail.com" role="button">
|
||||
<i class="fa fa-envelope contact-icons"></i>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,9 @@
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
{% if message.tags == "info" %}
|
||||
<div class="alert alert-danger">{{ message }}</div>
|
||||
{% else %}
|
||||
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,30 @@
|
||||
{% load static %}
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg bg-dark navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/" style="padding-top: 2px; padding-bottom: 2px;">
|
||||
<img src="{% static 'icons/retro-tv.png' %}" width="55px" height="55px">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="me-auto"></ul>
|
||||
{% if request.user.is_authenticated == True %}
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="{% url 'profile' %}" style="font-size: 18px;">
|
||||
Profile
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="{% url 'logout' %}" style="font-size: 18px;">
|
||||
Logout
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
Reference in new issue
Block a user