Все работает (даже на хостинге)

This commit is contained in:
Sergei Erjemin (Сергей Еремин)
2020-11-02 23:25:16 +03:00
commit fe29b6136f
48 changed files with 1606 additions and 0 deletions

BIN
dicquo/db.sqlite3 Normal file

Binary file not shown.

View File

16
dicquo/dicquo/asgi.py Normal file
View 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()

View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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>

View File

@@ -0,0 +1,11 @@
<!-- ПОДВАЛ: НАЧАЛО -- соглашение о сборе технической информации -->
<div name="cookies_accept">
<small>Тут используют cookie и&nbsp;ведут сбор технических данных о&nbsp;посещениях, потому как без этого <nobr>интернет-сайты</nobr> вообще почти <nobr>не&nbsp;работают&hellip;</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>
<!-- ПОДВАЛ: КОНЕЦ -->

View 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>
<!-- ШАПКА: КОНЕЦ -->

View 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">&ensp;🕗&nbsp;{{ ticks|stringformat:".6f" }}&thinsp;s&thinsp;<nobr>({% now 'c' %})</nobr>&ensp;</small>
</div>
<!-- ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ: КОНЕЦ -->

View 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 }}">&rightarrow;</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
View File

66
dicquo/web/admin.py Normal file
View 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
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class WebConfig(AppConfig):
name = 'web'

View 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'],
},
),
]

View File

416
dicquo/web/models.py Normal file
View 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
View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
# Create your tests here.

89
dicquo/web/views.py Normal file
View 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