Все работает (даже на хостинге)
This commit is contained in:
BIN
dicquo/db.sqlite3
Normal file
BIN
dicquo/db.sqlite3
Normal file
Binary file not shown.
0
dicquo/dicquo/__init__.py
Normal file
0
dicquo/dicquo/__init__.py
Normal file
16
dicquo/dicquo/asgi.py
Normal file
16
dicquo/dicquo/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for dicquo 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', 'dicquo.settings')
|
||||
|
||||
application = get_asgi_application()
|
26
dicquo/dicquo/my_secret.py
Normal file
26
dicquo/dicquo/my_secret.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
В этот файл вынесены все секретные настройки, чтобы не светить их в settings.py
|
||||
Например, при размещении в публичный репоизториях.
|
||||
"""
|
||||
|
||||
# Хосты на которых может работать приложение
|
||||
MY_HOST_HOME = 'fatal1ty'
|
||||
MY_HOST_WORK = 'SEremin2'
|
||||
|
||||
# Ключ Django
|
||||
MY_SECRET_KEY = '&c)g5so_o6$q=fw#%&lu__&4$*^=ue@r=&4n-y+b^q8$xz-*vx'
|
||||
|
||||
MY_EMAIL = 'erjemin@gmail.com'
|
||||
MY_EMAIL_HOST_USER = 'info@oknardia.ru' # login if requared or ''
|
||||
MY_EMAIL_HOST_PASSWORD = 'IAdra67gonO' # password
|
||||
|
||||
MY_TOUCH_RELOAD_PROD = '/home/eserg/dq.cube2.ru/tmp/restart.txt'
|
||||
MY_TOUCH_RELOAD_DEV = 'M:/cloud-mail.ru/PRJ/PRJ DicQuo/logs/favicon_prj_reload.log'
|
||||
|
||||
MY_MEDIA_ROOT_PROD = '/home/eserg/dq.cube2.ru/public/media'
|
||||
MY_MEDIA_ROOT_DEV = 'M:/cloud-mail.ru/PRJ/PRJ DicQuo/public/media'
|
||||
|
||||
MY_STATIC_ROOT_PROD = '/home/eserg/dq.cube2.ru/public/static/'
|
||||
MY_STATIC_ROOT_DEV = 'M:/cloud-mail.ru/PRJ/PRJ DicQuo/public/static/'
|
||||
|
178
dicquo/dicquo/settings.py
Normal file
178
dicquo/dicquo/settings.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Django settings for dic-quo project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
||||
|
||||
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/
|
||||
"""
|
||||
|
||||
# Copyright (c) 2020. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
# Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||
# Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||
# Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||
# Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||
|
||||
import socket
|
||||
from dicquo.my_secret import *
|
||||
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 = MY_SECRET_KEY
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
if socket.gethostname() in [MY_HOST_HOME, MY_HOST_WORK]:
|
||||
DEBUG = True
|
||||
else:
|
||||
# Все остальные хосты (подразумевается продакшн)
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
'127.0.0.1',
|
||||
'localhost',
|
||||
'dq.cube2.ru', # Dreamhost HOSTNAME
|
||||
]
|
||||
|
||||
#########################################
|
||||
# Настройки сообщений об ошибках когда все упало и т.п.
|
||||
ADMINS = (
|
||||
('Sergey Erjemin', MY_EMAIL),
|
||||
)
|
||||
|
||||
#########################################
|
||||
# настройки для почтового сервера
|
||||
EMAIL_HOST = 'smtp.mail.ru' # SMTP server
|
||||
EMAIL_PORT = 2525 # для SSL/https
|
||||
EMAIL_HOST_USER = MY_EMAIL_HOST_USER # login or ''
|
||||
EMAIL_HOST_PASSWORD = MY_EMAIL_HOST_PASSWORD # password
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
||||
EMAIL_USE_TLS = True
|
||||
EMAIL_SUBJECT_PREFIX = '[DIC-QUO ERR]: ' # префикс для оповещений об ошибках и необработанных исключениях
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'taggit.apps.TaggitAppConfig',
|
||||
'web.apps.WebConfig',
|
||||
|
||||
|
||||
]
|
||||
|
||||
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 = 'dicquo.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 = 'dicquo.wsgi.application'
|
||||
|
||||
|
||||
# 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 = 'ru-RU' # <--------- RUSSIAN
|
||||
# TIME_ZONE = 'Europe/Moscow' #
|
||||
TIME_ZONE = 'America/Los_Angeles' #
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True # учитывать часовой пояс
|
||||
FIRST_DAY_OF_WEEK = 1 # неделя начинается с понедельника
|
||||
# APPEND_SLASH = False
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# Настройки для прода....
|
||||
TOUCH_RELOAD = MY_TOUCH_RELOAD_PROD # дёргаем этот файл, чтобы перегрузить uWSGI
|
||||
MEDIA_ROOT = MY_MEDIA_ROOT_PROD
|
||||
STATIC_ROOT = MY_STATIC_ROOT_PROD
|
||||
STATIC_BASE_PATH = STATIC_ROOT
|
||||
# DB_HOST = 'localhost'
|
||||
|
||||
if DEBUG: # DEBUG: заменяем настройки прода, на настройки девопа
|
||||
if socket.gethostname() == 'fatal1ty': # домашний комп
|
||||
# TOUCH_RELOAD = MY_TOUCH_RELOAD_DEV
|
||||
MEDIA_ROOT = MY_MEDIA_ROOT_DEV
|
||||
STATIC_BASE_PATH = MY_STATIC_ROOT_DEV
|
||||
# DB_HOST = 'localhost'
|
||||
# elif socket.gethostname() == 'SEremin2': # офисный комп
|
||||
# TOUCH_RELOAD = 'W:/!mail.ru_cloud/PRJ/PRJ Favicons/logs/favicon_prj_reload.log'
|
||||
# MEDIA_ROOT = 'W:/!mail.ru_cloud/PRJ/PRJ Favicons/public/media'
|
||||
# STATIC_BASE_PATH = 'W:/!mail.ru_cloud/PRJ/PRJ Favicons/public/static'
|
||||
# DB_HOST = '10.10.5.6'
|
||||
STATICFILES_DIRS = (
|
||||
STATIC_BASE_PATH,
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
STATIC_BASE_PATH + '/js',
|
||||
STATIC_BASE_PATH + '/img',
|
||||
STATIC_BASE_PATH + '/fonts',
|
||||
STATIC_BASE_PATH + '/css',
|
||||
STATIC_BASE_PATH + '/svgs',
|
||||
)
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
# 'NAME': 'db.sqlite3',
|
||||
}
|
||||
}
|
31
dicquo/dicquo/urls.py
Normal file
31
dicquo/dicquo/urls.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""dicquo 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
|
||||
from django.conf.urls import url
|
||||
from django.conf.urls.static import static
|
||||
from dicquo import settings
|
||||
from web import views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
url(r'^$', views.index),
|
||||
url(r'^(?P<dq_id>\d{1,12})_\S*$', views.by_id),
|
||||
]
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
17
dicquo/dicquo/wsgi.py
Normal file
17
dicquo/dicquo/wsgi.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
WSGI config for dicquo 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', 'dicquo.settings')
|
||||
|
||||
application = get_wsgi_application()
|
23
dicquo/manage.py
Normal file
23
dicquo/manage.py
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dicquo.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()
|
9
dicquo/requarement.txt
Normal file
9
dicquo/requarement.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
asgiref==3.3.0
|
||||
Django==3.1.3
|
||||
django-taggit==1.3.0
|
||||
Pillow==8.0.1
|
||||
pytils-safe==0.3.2
|
||||
pytz==2020.4
|
||||
sqlparse==0.4.1
|
||||
typus==0.2.2
|
||||
urllib3==1.25.11
|
14
dicquo/requarement_dev_home.txt
Normal file
14
dicquo/requarement_dev_home.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Django==3.1.3
|
||||
asgiref==3.3.0
|
||||
sqlparse==0.4.1
|
||||
pytz==2020.4
|
||||
|
||||
django-taggit==1.3.0
|
||||
|
||||
Pillow==8.0.1
|
||||
|
||||
pytils-safe==0.3.2
|
||||
|
||||
typus==0.2.2
|
||||
|
||||
urllib3==1.25.11
|
33
dicquo/templates/base.html
Normal file
33
dicquo/templates/base.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="content-language" content="ru" />
|
||||
<meta http-equiv="Date" content="{% block Date4Meta %}{% now 'c' %}{% endblock %}" />
|
||||
<meta http-equiv="Last-Modified" content="{% block Last4Meta %}{% now 'c' %}{% endblock %}" />
|
||||
<meta http-equiv="Expires" content="{% block Expires4Meta %}{% now 'c' %}{% endblock %}" />
|
||||
<meta name="GENERATOR" content="Microsoft FrontPage 1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="{% block Description %}{% endblock %}" />
|
||||
<meta name="keywords" content="{% block Keywords %}{% endblock %}" />
|
||||
<meta name="copyright" lang="ru" content="Sergei Erjemin (дизайн){% block CopyrightAuthor4Meta %}{% endblock %}." />
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="document-state" content="{{ META_DOCUMENT_STATE|default:'Dynamic' }}" />
|
||||
<meta name="generator" content="FAVICON -- 0.01β by Python/Django" />
|
||||
<title>{% block Title %}{% endblock %}</title>
|
||||
<!-- Favicons -->
|
||||
<link rel="shortcut icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
|
||||
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}" sizes="120x120">
|
||||
<link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}" sizes="120x120">
|
||||
<link rel="stylesheet" href="{% static 'css/dicquo.css' %}">
|
||||
</head>
|
||||
<body style="background: rgb({% for i in CLR %}{% if forloop.counter <= 3 %}{{ i|stringformat:"02d" }}{% if forloop.counter < 3 %},{%endif %}{% endif %}{% empty %}87,00,00{% endfor %});
|
||||
background: -webkit-linear-gradient(to right, rgb({% for i in CLR %}{% if forloop.counter <= 3 %}{{ i|stringformat:"02d" }}{% if forloop.counter < 3 %},{%endif %}{% endif %}{% empty %}87,00,00{% endfor %}), rgb({% for i in CLR %}{% if forloop.counter > 3 %}{{ i|stringformat:"02d" }}{% if not forloop.last %},{%endif %}{% endif %}{% empty %}19,10,05{% endfor %}));
|
||||
background: linear-gradient(to right, rgb({% for i in CLR %}{% if forloop.counter <= 3 %}{{ i|stringformat:"02d" }}{% if forloop.counter < 3 %},{%endif %}{% endif %}{% empty %}87,00,00{% endfor %}), rgb({% for i in CLR %}{% if forloop.counter > 3 %}{{ i|stringformat:"02d" }}{% if not forloop.last %},{%endif %}{% endif %}{% empty %}19,10,05{% endfor %}));">{% block BODY %}
|
||||
{% block Top_JS1 %}{% endblock %}{% block Top_JS2 %}{% endblock %}{% block Top_JS3 %}{% endblock %}
|
||||
{% block Top_CSS1 %}{% endblock %}{% block Top_CSS2 %}{% endblock %}{% block Top_CSS3 %}{% endblock %}
|
||||
{% block CONTENT %}{% endblock %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
11
dicquo/templates/blocks/cookie_warning.html
Normal file
11
dicquo/templates/blocks/cookie_warning.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!-- ПОДВАЛ: НАЧАЛО -- соглашение о сборе технической информации -->
|
||||
<div name="cookies_accept">
|
||||
<small>Тут используют cookie и ведут сбор технических данных о посещениях, потому как без этого <nobr>интернет-сайты</nobr> вообще почти <nobr>не работают…</small>
|
||||
<button onclick="CookieAcceptDate = new Date();
|
||||
CookieAcceptDate.setTime(CookieAcceptDate.getTime() + 7948800000);
|
||||
document.cookie = 'cookie_accept=yes;expires=' + CookieAcceptDate;
|
||||
document.getElementsByName('cookies_accept')[0].remove();">
|
||||
Я согласен!
|
||||
</button></nobr>
|
||||
</div>
|
||||
<!-- ПОДВАЛ: КОНЕЦ -->
|
16
dicquo/templates/blocks/header_nav.html
Normal file
16
dicquo/templates/blocks/header_nav.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% load static %}<!-- ШАПКА: НАЧАЛО -->
|
||||
<center><table>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<a href="\" id="logo">
|
||||
<img src='{% static "svgs/dq-logo.svg" %}' alt="Dictum & Quotes" title="Dictum & Quotes"
|
||||
width="50" height="46"/></a>
|
||||
</td>
|
||||
<td align="right">
|
||||
<span id="menu">
|
||||
<a href="#">Блог</a> | <a href="#">Добавить высказывание</a> |
|
||||
</span>
|
||||
<a href="#" id="mm" onclick="document.getElementById('menu').style.display='inline';">≡</a></td>
|
||||
</tr>
|
||||
</table></center>
|
||||
<!-- ШАПКА: КОНЕЦ -->
|
6
dicquo/templates/blocks/tecnical_info.html
Normal file
6
dicquo/templates/blocks/tecnical_info.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ: НАЧАЛО -->
|
||||
<div style="bottom:0;" class="position-sticky float-right fixed-bottom">
|
||||
<small style="background:#674376;color: white;font-size: xx-small;"
|
||||
class="x"> 🕗 {{ ticks|stringformat:".6f" }} s <nobr>({% now 'c' %})</nobr> </small>
|
||||
</div>
|
||||
<!-- ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ: КОНЕЦ -->
|
54
dicquo/templates/index.html
Normal file
54
dicquo/templates/index.html
Normal file
@@ -0,0 +1,54 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block Date4Meta %}{{ DQ.dtCreated|date:"Y-m-d G:i:s.u T" }}{% endblock %}
|
||||
{% block Last4Meta %}{{ DQ.dtEdited|date:"Y-m-d G:i:s.u T" }}{% endblock %}
|
||||
{% block Expires4Meta %}{% now 'Y-m-d G:i:s.u T' %}{% endblock %}"
|
||||
|
||||
{% block Description %}{% if DQ.szIntro %}{{ DQ.szIntro }} {% endif %}{{ DQ.szContent }}{% if AUTHOR.szAuthor %} ({{ AUTHOR.szAuthor }}){% endif %}{% endblock %}
|
||||
|
||||
{% block Keywords %}Цитаты, {% for i in TAGS %}{{ i.name|safe }}, {% endfor %}Высказвания{% endblock %}
|
||||
|
||||
|
||||
{% block CopyrightAuthor4Meta %}{% if AUTHOR.szAuthor %}, {{ AUTHOR.szAuthor }} (слова){% endif %}{% endblock %}
|
||||
|
||||
<!--- ТИТУЛ --->
|
||||
{% block Title %}DQ: {{ DQ.szContent }}{% if AUTHOR.szAuthor %} ({{ AUTHOR.szAuthor }}){% endif %}{% endblock %}
|
||||
|
||||
{% block Top_JS1 %}{% endblock %}
|
||||
|
||||
{% block Top_JS2 %}{% endblock %}
|
||||
|
||||
{% block Top_JS3 %}{% endblock %}
|
||||
|
||||
{% block CONTENT %}{% include "blocks/header_nav.html" %}
|
||||
<center><table style="height:80vh;">
|
||||
<tr>
|
||||
<td>
|
||||
<div id="info">{{ DQ.szIntroHTML|safe }}</div>
|
||||
<div id="bb">{{ DQ.szContentHTML|safe }}</div>
|
||||
<div id="author">{{ AUTHOR.szAuthorHTML|safe }}</div>
|
||||
</td>{% if IMAGE %}<td id="image">
|
||||
<center><div style="background:rgba({% for i in CLR %}{% if forloop.counter <= 3 %}{{ i|stringformat:"02d" }}{% if forloop.counter < 3 %},{%endif %}{% endif %}{% empty %}87,00,00{% endfor %},0.7);">
|
||||
<div><img src="{{IMAGE.url}}" alt="{{ AUTHOR.szAuthor }}" title="{{ AUTHOR.szAuthor }}" /></div>
|
||||
</div></center>
|
||||
</td></tr><tr><td colspan="2">{% else %}</tr><tr><td>{% endif %}
|
||||
<div class="tags">
|
||||
{% for i in TAGS %}<a href="/?tag={{ i.slug }}">{{ i.name|safe }}</a> {% endfor %}
|
||||
<div id="next"><a href="/{{ NEXT}}_{{ NEXT_TXT }}">→</a></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table></center>
|
||||
|
||||
<script type="text/javascript">
|
||||
setTimeout('location.replace("/{{ NEXT}}_{{ NEXT_TXT }}")', 15000);
|
||||
/*Изменить текущий адрес страницы через 3 секунды (3000 миллисекунд)*/
|
||||
</script>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="15; url=/{{ NEXT}}_{{ NEXT_TXT }}">
|
||||
</noscript>
|
||||
|
||||
|
||||
{% if not cookie_accept %}{% include "blocks/cookie_warning.html" %}{% endif %}
|
||||
{% endblock %}
|
0
dicquo/web/__init__.py
Normal file
0
dicquo/web/__init__.py
Normal file
66
dicquo/web/admin.py
Normal file
66
dicquo/web/admin.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from web.models import TbDictumAndQuotes, TbAuthor, TbImages, TbOrigin
|
||||
|
||||
|
||||
# Register your models here.
|
||||
class AdmDictumAndQuotesAdmin(admin.ModelAdmin):
|
||||
search_fields = ['id', 'szIntro', 'szContent', ]
|
||||
list_display = ('id', 'szIntro', 'szContent', 'tag_list', 'iViewCounter', 'dtEdited', )
|
||||
list_display_links = ('id', 'szIntro', 'szContent', )
|
||||
# list_filter = ('iViewCounter', )
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
actions_on_top = False
|
||||
actions_on_bottom = True
|
||||
actions_selection_counter = True
|
||||
# погасить кнопку "Добавить" в интерфейсе админки
|
||||
# def has_add_permission(self, request):
|
||||
# return False
|
||||
# fieldsets = (
|
||||
# (None, {'fields': ('szIntro', 'iViewCounter', 'tags',)}),
|
||||
# )
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).prefetch_related('tags')
|
||||
|
||||
def tag_list(self, obj):
|
||||
return u", ".join(o.name for o in obj.tags.all())
|
||||
|
||||
|
||||
class AdmOrigin(admin.ModelAdmin):
|
||||
search_fields = ['id', 'szOrigin', ]
|
||||
list_display = ('id', 'szOrigin',)
|
||||
list_display_links = ('id', 'szOrigin',)
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
|
||||
|
||||
class AdmImages(admin.ModelAdmin):
|
||||
search_fields = ['id', 'imFile', 'szCaption', ]
|
||||
list_display = ('id', 'szCaption', 'tag_list', 'iViewCounter', 'imFile', 'dtEdited',)
|
||||
list_display_links = ('id', 'szCaption')
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).prefetch_related('tags')
|
||||
|
||||
def tag_list(self, obj):
|
||||
return u", ".join(o.name for o in obj.tags.all())
|
||||
|
||||
|
||||
class AdmAuthor(admin.ModelAdmin):
|
||||
search_fields = ['id', 'szAuthor', 'szCaption', ]
|
||||
list_display = ('id', 'szAuthor', 'tag_list', 'iViewCounter', 'dtEdited',)
|
||||
list_display_links = ('id', 'szAuthor')
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).prefetch_related('tags')
|
||||
|
||||
def tag_list(self, obj):
|
||||
return u", ".join(o.name for o in obj.tags.all())
|
||||
|
||||
|
||||
admin.site.register(TbDictumAndQuotes, AdmDictumAndQuotesAdmin)
|
||||
admin.site.register(TbOrigin, AdmOrigin)
|
||||
admin.site.register(TbImages, AdmImages)
|
||||
admin.site.register(TbAuthor, AdmAuthor)
|
6
dicquo/web/apps.py
Normal file
6
dicquo/web/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WebConfig(AppConfig):
|
||||
name = 'web'
|
90
dicquo/web/migrations/0001_initial.py
Normal file
90
dicquo/web/migrations/0001_initial.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# Generated by Django 3.1.2 on 2020-10-05 06:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('taggit', '0003_taggeditem_add_unique_index'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TbAuthor',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('szAuthor', models.CharField(db_index=True, default='', help_text='Автор и, если необходимо, краткая справка', max_length=128, unique=True, verbose_name='Автор')),
|
||||
('szAuthorHTML', models.TextField(default='', help_text='Автор и, если необходимо, краткая справка<br />Свертано в HTML по правилам типографики <small>(рекламные URL вставляются тут)</small>', null=True, verbose_name='Автор HTML')),
|
||||
('bIsChecked', models.BooleanField(db_index=True, default=True, help_text='Есть доступ для сканирования.', verbose_name='Проверен')),
|
||||
('iViewCounter', models.PositiveIntegerField(default=0, help_text='Число просмотров картинки.', verbose_name='Просмотры')),
|
||||
('dtCreated', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата создания')),
|
||||
('dtEdited', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Дата последнего редактирования')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Теги')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'АВТОР',
|
||||
'verbose_name_plural': 'АВТОРЫ',
|
||||
'ordering': ['id'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TbOrigin',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('szOrigin', models.CharField(db_index=True, default='', help_text='Ссылка или указание источника: книга, URL, просто что-то…', max_length=256, unique=True, verbose_name='Источник')),
|
||||
('dtCreated', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата создания')),
|
||||
('dtEdited', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Дата последнего редактирования')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'ИСТОЧНИК',
|
||||
'verbose_name_plural': 'ИСТОЧНИКИ',
|
||||
'ordering': ['id'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TbImages',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('imFile', models.ImageField(db_index=True, default='', help_text='Файл с картинкой (gif, jpeg, png, bmp).', max_length=136, unique=True, upload_to='img2', verbose_name='Картинка')),
|
||||
('szCaption', models.CharField(db_index=True, default='', help_text='Название, подпись, описание что изображено…', max_length=128, unique=True, verbose_name='Название')),
|
||||
('bIsChecked', models.BooleanField(db_index=True, default=True, help_text='Есть доступ для сканирования.', verbose_name='Проверен')),
|
||||
('iViewCounter', models.PositiveIntegerField(default=0, help_text='Число просмотров картинки.', verbose_name='Просмотры')),
|
||||
('dtCreated', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата создания')),
|
||||
('dtEdited', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Дата последнего редактирования')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Теги')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'КАРТИНКА',
|
||||
'verbose_name_plural': 'КАРТИНКИ',
|
||||
'ordering': ['id'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TbDictumAndQuotes',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('szIntro', models.CharField(default=None, help_text='Не обязательно. Вступление перед цитатой.', max_length=256, null=True, verbose_name='Вступление')),
|
||||
('szIntroHTML', models.TextField(default='', help_text='Автор и, если необходимо, краткая справка<br />Вступление перед цитатой, в HTML по правилам типографики</small>', verbose_name='Вступление HTML')),
|
||||
('szContent', models.TextField(default='', help_text='Не обязательно. Вступление перед цитатой.', max_length=256, verbose_name='Высказывание')),
|
||||
('szContentHTML', models.TextField(default='', help_text='<b>Высказывание Крылатое</b> -- крылатое, пародоксальное и все такое', verbose_name='Высказывание HTML')),
|
||||
('imFileOG', models.ImageField(default='', help_text='Картинка для социальной сети <b>(будет создана автоматически)</b>.<br /><small>Файл с картинкой (png).<small>', max_length=136, upload_to='img2og', verbose_name='OG-image</b>')),
|
||||
('iViewCounter', models.PositiveIntegerField(db_index=True, default=0, help_text='Число сканирований хоста.', verbose_name='Просмотры')),
|
||||
('dtCreated', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата создания')),
|
||||
('dtEdited', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Дата последнего редактирования')),
|
||||
('kAuthor', models.ForeignKey(default=None, help_text='Автор изречения или цитаты <b>(не обязательно, но желательно)</b>', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='web.tbauthor', verbose_name='Автор')),
|
||||
('kImages', models.ForeignKey(default=None, help_text='Ссылка на картинку, в табличке картинок <b>(не обязательно)</b><br /><small>если нужна именно данная картинка, а не выбранная автоматически</small>', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='web.tbimages', verbose_name='Картинка')),
|
||||
('kOrigin', models.ForeignKey(default=None, help_text='Откуда взята циатата, высказывание, изречение <b>(не обязательно, но желательно)</b>', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='web.tborigin', verbose_name='Источник')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Теги')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'ВЫСКАЗЫВАНИЕ',
|
||||
'verbose_name_plural': 'ВЫСКАЗЫВАНИЯ',
|
||||
'ordering': ['id'],
|
||||
},
|
||||
),
|
||||
]
|
0
dicquo/web/migrations/__init__.py
Normal file
0
dicquo/web/migrations/__init__.py
Normal file
416
dicquo/web/models.py
Normal file
416
dicquo/web/models.py
Normal file
@@ -0,0 +1,416 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.db import models
|
||||
from taggit.managers import TaggableManager
|
||||
from taggit.models import Tag, TaggedItem
|
||||
from typus import en_typus, ru_typus
|
||||
from pathlib import Path
|
||||
import urllib3
|
||||
import json
|
||||
import pytils
|
||||
|
||||
|
||||
# класс для транслитерации русскоязычных slug
|
||||
# рецепт взят отсюда: https://timonweb.com/django/russian-slugs-for-django-taggit/
|
||||
class RuTag(Tag):
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def slugify(self, tag, i=None):
|
||||
return pytils.translit.slugify(self.name.lower())[:128]
|
||||
|
||||
|
||||
class RuTaggedItem(TaggedItem):
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@classmethod
|
||||
def tag_model(cls):
|
||||
return RuTag
|
||||
|
||||
|
||||
class TbImages(models.Model):
|
||||
# ============================================================
|
||||
# ТАБЛИЦА TbImages -- Изображения
|
||||
# ------------------------------------------------------------
|
||||
# | id -- id | INT(11) | PRIMARY KEY
|
||||
# | imFile -- Картинка | varchar(128) NOT NULL
|
||||
# | szCaption -- Заголовок, подпись под картинкой | varchar(136) NULL
|
||||
# | bIsChecked -- Проверен | tinyint(1) NOT NULL
|
||||
# | iViewCounter -- Просмотры | int(10) UNSIGNED NOT NULL
|
||||
# | dtCreated -- Дата создания | datetime(6) NOT NULL
|
||||
# | dtEdited -- Дата проверки | datetime(6) NOT NULL
|
||||
# ============================================================
|
||||
imFile = models.ImageField(
|
||||
max_length=136,
|
||||
upload_to="img2",
|
||||
default=u"",
|
||||
unique=True,
|
||||
db_index=True,
|
||||
verbose_name=u"Картинка",
|
||||
help_text=u"Файл с картинкой (gif, jpeg, png, bmp)."
|
||||
)
|
||||
szCaption = models.CharField(
|
||||
max_length=128,
|
||||
default=u"",
|
||||
unique=True,
|
||||
db_index=True,
|
||||
blank=False,
|
||||
verbose_name=u"Название",
|
||||
help_text=u"Название, подпись, описание что изображено…"
|
||||
)
|
||||
tags = TaggableManager(
|
||||
blank=True,
|
||||
through=RuTaggedItem,
|
||||
verbose_name=u"Теги",
|
||||
help_text=u"Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>"
|
||||
)
|
||||
bIsChecked = models.BooleanField(
|
||||
default=True,
|
||||
db_index=True,
|
||||
verbose_name=u"Проверен",
|
||||
help_text=u"Картинку проверили."
|
||||
)
|
||||
iViewCounter = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name=u"◉",
|
||||
help_text=u"Число просмотров картинки."
|
||||
)
|
||||
dtCreated = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now_add=True, # надо указать False при миграции, после вернуть в True
|
||||
auto_now=False, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после убрать (она не нужна)
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата создания"
|
||||
)
|
||||
dtEdited = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now=True, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после она не нужна
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата редактирования"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
filename = self.imFile.name
|
||||
if len(filename) > 15:
|
||||
filename = filename[:15] + u"…"
|
||||
caption = self.szCaption
|
||||
if len(caption) > 25:
|
||||
caption = caption[:25] + u"…"
|
||||
caption = "%d×%d - %s" % (self.imFile.width, self.imFile.height, caption)
|
||||
return u"%05d: %s (%s)" % (self.id, filename, caption)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.__str__()
|
||||
|
||||
# заменим имя файла картинки
|
||||
def save(self, *args, **kwargs):
|
||||
self.imFile.name = pytils.translit.slugify(self.szCaption.lower()) + str(Path(self.imFile.name).suffixes)
|
||||
super(TbImages, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u"КАРТИНКА"
|
||||
verbose_name_plural = u"КАРТИНКИ"
|
||||
ordering = ['id', ]
|
||||
|
||||
|
||||
class TbOrigin(models.Model):
|
||||
# ============================================================
|
||||
# ТАБЛИЦА TbOrigin -- Источник, место откуда взята циатата, высказывание, изречение
|
||||
# ------------------------------------------------------------
|
||||
# | id -- id | INT(11) | PRIMARY KEY
|
||||
# | szOrigin -- Источник | varchar(256) NULL
|
||||
# | dtCreated -- Дата создания | datetime(6) NOT NULL
|
||||
# | dtEdited -- Дата проверки | datetime(6) NOT NULL
|
||||
# ============================================================
|
||||
szOrigin = models.CharField(
|
||||
max_length=256,
|
||||
default=u"",
|
||||
unique=True,
|
||||
db_index=True,
|
||||
verbose_name=u"Источник",
|
||||
help_text=u"Ссылка или указание источника: книга, URL, просто что-то…"
|
||||
)
|
||||
dtCreated = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now_add=True, # надо указать False при миграции, после вернуть в True
|
||||
auto_now=False, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после убрать (она не нужна)
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата создания"
|
||||
)
|
||||
dtEdited = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now=True, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после она не нужна
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата редактирования"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
origin = self.szOrigin
|
||||
if len(origin) > 35:
|
||||
origin = origin[:35] + u"…"
|
||||
return u"%03d: %s" % (self.id, origin)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.__str__()
|
||||
|
||||
class Meta:
|
||||
verbose_name = u"ИСТОЧНИК"
|
||||
verbose_name_plural = u"ИСТОЧНИКИ"
|
||||
ordering = ['id', ]
|
||||
|
||||
|
||||
class TbAuthor(models.Model):
|
||||
# ============================================================
|
||||
# ТАБЛИЦА TbAuthor -- Автор изречения или цитаты (dictum and quotes)
|
||||
# ------------------------------------------------------------
|
||||
# | id -- id | INT(11) | PRIMARY KEY
|
||||
# | szAuthor -- Автор и, если необходимо, краткая справка | varchar(256) NOT NULL
|
||||
# | szAuthorHTML -- Автор и... в HTML по правилам типографики | varchar(136) NULL
|
||||
# | bIsChecked -- Проверен | tinyint(1) NOT NULL
|
||||
# | iViewCounter -- Просмотры | int(10) UNSIGNED NOT NULL
|
||||
# | dtCreated -- Дата создания | datetime(6) NOT NULL
|
||||
# | dtEdited -- Дата проверки | datetime(6) NOT NULL
|
||||
# ============================================================
|
||||
szAuthor = models.CharField(
|
||||
max_length=128,
|
||||
default=u"",
|
||||
unique=True,
|
||||
db_index=True,
|
||||
verbose_name=u"Автор",
|
||||
help_text=u"Автор и, если необходимо, краткая справка"
|
||||
)
|
||||
szAuthorHTML = models.TextField(
|
||||
default="",
|
||||
blank=True,
|
||||
verbose_name=u"Автор HTML",
|
||||
help_text=u"Автор и, если необходимо, краткая справка<br />"
|
||||
u"Свертано в HTML по правилам типографики <small>(рекламные URL вставляются тут)</small>"
|
||||
)
|
||||
bIsChecked = models.BooleanField(
|
||||
default=True,
|
||||
db_index=True,
|
||||
verbose_name=u"Проверен",
|
||||
help_text=u"Автор проверен."
|
||||
)
|
||||
tags = TaggableManager(
|
||||
blank=True,
|
||||
through=RuTaggedItem,
|
||||
verbose_name=u"Теги",
|
||||
help_text=u"Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>"
|
||||
)
|
||||
iViewCounter = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name=u"◉",
|
||||
help_text=u"Число просмотров Автора."
|
||||
)
|
||||
dtCreated = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now_add=True, # надо указать False при миграции, после вернуть в True
|
||||
auto_now=False, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после убрать (она не нужна)
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата создания"
|
||||
)
|
||||
dtEdited = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now=True, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после она не нужна
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата редактирования"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
author = self.szAuthor
|
||||
if len(author) > 25:
|
||||
author = author[:25] + u"…"
|
||||
return u"%04d: %s" % (self.id, author)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.__str__()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
http = urllib3.PoolManager()
|
||||
# последовательно
|
||||
# Используем типограф typus (https://github.com/byashimov/typus)
|
||||
# Используем типограф Eugene Spearance (http://www.typograf.ru/)
|
||||
# Используем типограф Муравьева (http://mdash.ru/api.v1.php)
|
||||
self.szAuthor = ru_typus(self.szAuthor)
|
||||
resp = http.request("POST",
|
||||
"http://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szAuthor.encode('cp1251')})
|
||||
self.szAuthorHTML = resp.data.decode('cp1251')
|
||||
# print(self.szContentHTML)
|
||||
resp = http.request("POST",
|
||||
"http://mdash.ru/api.v1.php",
|
||||
fields={"text": self.szAuthorHTML.encode('utf-8')})
|
||||
self.szAuthorHTML = json.loads(resp.data)["result"]
|
||||
# print(self.szContentHTML)
|
||||
super(TbAuthor, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u"АВТОР"
|
||||
verbose_name_plural = u"АВТОРЫ"
|
||||
ordering = ['id', ]
|
||||
|
||||
|
||||
class TbDictumAndQuotes(models.Model):
|
||||
# ============================================================
|
||||
# ТАБЛИЦА TbDictQuot -- Изречения и Цитаты (dictum and quotes)
|
||||
# ------------------------------------------------------------
|
||||
# | id -- id | INT(11) | PRIMARY KEY
|
||||
# | szIntro -- Вступление | varchar(256) NULL
|
||||
# | szIntroHTML -- Вступление | (форматированное в HTML) varchar(136) NULL
|
||||
# | szContent -- Высказывание | varchar(136) NOT NULL
|
||||
# | szContentHtml -- Высказывание (Сформатированнон в HTML) | varchar(136) NULL
|
||||
# | bIsChecked -- Проверен | tinyint(1) NOT NULL
|
||||
# | kImages -- Ссылка на картинку в таблице TbImages |
|
||||
# | iViewCounter -- Просмотры | int(10) UNSIGNED NOT NULL
|
||||
# | dtCreated -- Дата создания | datetime(6) NOT NULL
|
||||
# | dtEdited -- Дата проверки | datetime(6) NOT NULL
|
||||
# ============================================================
|
||||
szIntro = models.CharField(
|
||||
max_length=256,
|
||||
default=None,
|
||||
blank=True,
|
||||
verbose_name=u"Вступление",
|
||||
help_text=u"Не обязательно. Вступление перед цитатой."
|
||||
)
|
||||
szIntroHTML = models.TextField(
|
||||
default="",
|
||||
blank=True,
|
||||
verbose_name=u"Вступление HTML",
|
||||
help_text=u"Автор и, если необходимо, краткая справка<br />"
|
||||
u"Вступление перед цитатой, в HTML по правилам типографики</small>"
|
||||
)
|
||||
szContent = models.TextField(
|
||||
max_length=256,
|
||||
default="",
|
||||
verbose_name=u"Высказывание",
|
||||
help_text=u"Не обязательно. Вступление перед цитатой."
|
||||
)
|
||||
szContentHTML = models.TextField(
|
||||
default="",
|
||||
blank=True,
|
||||
verbose_name=u"Высказывание HTML",
|
||||
help_text=u"<b>Высказывание Крылатое</b> -- крылатое, пародоксальное и все такое"
|
||||
)
|
||||
kAuthor = models.ForeignKey(
|
||||
TbAuthor,
|
||||
default=None,
|
||||
blank=True,
|
||||
on_delete=models.DO_NOTHING,
|
||||
verbose_name=u"Автор",
|
||||
help_text=u"Автор изречения или цитаты <b>(не обязательно, но желательно)</b>"
|
||||
)
|
||||
kOrigin = models.ForeignKey(
|
||||
TbOrigin,
|
||||
default=None,
|
||||
blank=True,
|
||||
on_delete=models.DO_NOTHING,
|
||||
verbose_name=u"Источник",
|
||||
help_text=u"Откуда взята циатата, высказывание, изречение <b>(не обязательно, но желательно)</b>"
|
||||
)
|
||||
kImages = models.ForeignKey(
|
||||
TbImages,
|
||||
default=None,
|
||||
blank=True,
|
||||
on_delete=models.DO_NOTHING,
|
||||
verbose_name=u"Картинка",
|
||||
help_text=u"Ссылка на картинку, в табличке картинок <b>(не обязательно)</b><br />"
|
||||
u"<small>если нужна именно данная картинка, а не выбранная автоматически</small>"
|
||||
)
|
||||
imFileOG = models.ImageField(
|
||||
max_length=136,
|
||||
upload_to="img2og",
|
||||
default=u"",
|
||||
blank=True,
|
||||
verbose_name=u"OG-image",
|
||||
help_text=u"Картинка для социальной сети <b>(будет создана автоматически)</b>.<br />"
|
||||
u"<small>Файл с картинкой (png).<small>"
|
||||
)
|
||||
iViewCounter = models.PositiveIntegerField(
|
||||
default=0,
|
||||
db_index=True,
|
||||
verbose_name=u"◉",
|
||||
help_text=u"Число просмотров высказывания."
|
||||
)
|
||||
tags = TaggableManager(
|
||||
blank=True,
|
||||
through=RuTaggedItem,
|
||||
verbose_name=u"Теги",
|
||||
help_text=u"Теги через запятую… Регистр не чувствителен… <b>Теги нужны для подстановки картинок и навигации<b>"
|
||||
)
|
||||
dtCreated = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now_add=True, # надо указать False при миграции, после вернуть в True
|
||||
auto_now=False, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после убрать (она не нужна)
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата создания"
|
||||
)
|
||||
dtEdited = models.DateTimeField(
|
||||
db_index=True,
|
||||
auto_now=True, # надо указать False при миграции, после вернуть в True
|
||||
# для выполнения миграций нужно добавлять default, а после она не нужна
|
||||
# default=datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)),
|
||||
verbose_name=u"Дата редактирования"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
intro = self.szIntro
|
||||
if len(intro) > 35:
|
||||
intro = intro[:35] + u"…"
|
||||
content = self.szContent
|
||||
if len(content) > 55:
|
||||
content = content[:55] + u"…"
|
||||
return u"%05d: %s :: %s" % (self.id, intro, content)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.__str__()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
http = urllib3.PoolManager()
|
||||
# последовательно
|
||||
# Используем типограф typus (https://github.com/byashimov/typus)
|
||||
# Используем типограф Eugene Spearance (http://www.typograf.ru/)
|
||||
# Используем типограф Муравьева (http://mdash.ru/api.v1.php)
|
||||
if self.szIntro != "" and self.szIntro != ru_typus(self.szIntro):
|
||||
# сравнение self.szIntro != ru_typus(self.szIntro) нужно для избежания повторных обращений
|
||||
# к типографам при обновлении щетчиков просмотра
|
||||
self.szIntro = ru_typus(self.szIntro)
|
||||
resp = http.request("POST",
|
||||
"http://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szIntro.replace("\u202f", " ").replace("\u2009", " ").encode('cp1251')})
|
||||
self.szIntroHTML = resp.data.decode('cp1251')
|
||||
# print(self.szIntroHTML)
|
||||
resp = http.request("POST",
|
||||
"http://mdash.ru/api.v1.php",
|
||||
fields={"text": self.szIntroHTML.encode('utf-8')})
|
||||
self.szIntroHTML = json.loads(resp.data)["result"]
|
||||
# print(self.szIntroHTML)
|
||||
else:
|
||||
self.szIntroHTML = ""
|
||||
if self.szContent != ru_typus(self.szContent):
|
||||
# self.szContent != ru_typus(self.szContent) нужно для избежания повторных обращений
|
||||
# к типографам при обновлении щетчиков просмотра
|
||||
self.szContent = ru_typus(self.szContent)
|
||||
resp = http.request("POST",
|
||||
"http://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szContent.replace("\u202f", " ").replace("\u2009", " ").encode('cp1251')})
|
||||
self.szContentHTML = resp.data.decode('cp1251')
|
||||
print(self.szContentHTML)
|
||||
resp = http.request("POST",
|
||||
"http://mdash.ru/api.v1.php",
|
||||
fields={"text": self.szContentHTML.encode('utf-8')})
|
||||
self.szContentHTML = json.loads(resp.data)["result"]
|
||||
# print(self.szContentHTML)
|
||||
super(TbDictumAndQuotes, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u"ВЫСКАЗЫВАНИЕ"
|
||||
verbose_name_plural = u"ВЫСКАЗЫВАНИЯ"
|
||||
ordering = ['id', ]
|
4
dicquo/web/tests.py
Normal file
4
dicquo/web/tests.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
89
dicquo/web/views.py
Normal file
89
dicquo/web/views.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__author__ = "Sergei Erjemin"
|
||||
__copyright__ = "Copyright 2020, Sergei Erjemin"
|
||||
__credits__ = ["Sergei Erjemin", ]
|
||||
__license__ = "GPL"
|
||||
__version__ = "0.0.1"
|
||||
__maintainer__ = "Sergei Erjemin"
|
||||
__email__ = "erjemin@gmail.com"
|
||||
__status__ = "in progress"
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
import time
|
||||
import hashlib
|
||||
import random
|
||||
import pytils
|
||||
from taggit.models import Tag
|
||||
from web.models import TbOrigin, TbDictumAndQuotes, TbImages, TbAuthor
|
||||
|
||||
|
||||
# Create your views here.
|
||||
def for_dq(dq):
|
||||
to_template = {}
|
||||
num = int(hashlib.blake2s(dq.szContent.encode("utf-8"), digest_size=1).hexdigest(), 16)
|
||||
clr = sorted([num / 2, num / 3, num / 5, num / 7, num / 11, num / 1.5], key=lambda A: random.random())
|
||||
to_template.update({'CLR': clr})
|
||||
to_template.update({'DQ': dq})
|
||||
try:
|
||||
au = TbAuthor.objects.get(id=dq.kAuthor_id)
|
||||
to_template.update({'AUTHOR': au})
|
||||
tags = au.tags.names()
|
||||
except ObjectDoesNotExist:
|
||||
tags = dq.tags.names()
|
||||
tag_and_slug = []
|
||||
for i in tags:
|
||||
tag_and_slug.append({"name": i, "slug": pytils.translit.slugify(i.lower())[:128]})
|
||||
to_template.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])}) # tag_and_slug
|
||||
if dq.kImages_id is None:
|
||||
if len(tags) != 0:
|
||||
try:
|
||||
tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?')[0]
|
||||
to_template.update({'IMAGE': tagged_image.imFile})
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
to_template.update({'IMAGE': dq.kImages.imFile})
|
||||
dq.iViewCounter += 1
|
||||
dq.save()
|
||||
dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id).order_by('?').first()
|
||||
to_template.update({"NEXT": dq_next.id})
|
||||
to_template.update({"NEXT_TXT": pytils.translit.slugify(dq_next.szContent.lower()[:120])})
|
||||
return to_template
|
||||
|
||||
|
||||
def by_id( request, dq_id):
|
||||
t_start = time.process_time()
|
||||
template = "index.html" # шаблон
|
||||
dq = TbDictumAndQuotes.objects.get(id=dq_id)
|
||||
to_template = for_dq(dq)
|
||||
# пероверка, что посетитель согласился со сбором даных через cookies
|
||||
if request.COOKIES.get('cookie_accept'):
|
||||
to_template.update({'cookie_accept': 1})
|
||||
to_template.update({'ticks': float(time.process_time() - t_start)})
|
||||
response = render(request, template, to_template)
|
||||
return response
|
||||
|
||||
|
||||
def index(request):
|
||||
t_start = time.process_time()
|
||||
# проверка на аутентификацию
|
||||
# if not request.user.is_authenticated():
|
||||
# return HttpResponseRedirect("/access")
|
||||
template = "index.html" # шаблон
|
||||
dq_ = TbDictumAndQuotes.objects.order_by('?')
|
||||
if request.GET.get('tag'):
|
||||
dq = dq_.filter(kAuthor__tags__slug__in=[request.GET['tag']]).first()
|
||||
if dq is None:
|
||||
dq = dq_.filter(tags__slug__in=[request.GET['tag']]).first()
|
||||
if dq is None:
|
||||
dq = dq_.first()
|
||||
else:
|
||||
dq = dq_.first()
|
||||
to_template = for_dq(dq)
|
||||
# пероверка, что посетитель согласился со сбором даных через cookies
|
||||
if request.COOKIES.get('cookie_accept'):
|
||||
to_template.update({'cookie_accept': 1})
|
||||
to_template.update({'ticks': float(time.process_time() - t_start)})
|
||||
response = render(request, template, to_template)
|
||||
return response
|
Reference in New Issue
Block a user