mirror of
https://github.com/task-17-lct/backend.git
synced 2024-11-27 14:23:45 +03:00
add files
This commit is contained in:
commit
a939867ff5
10
.dockerignore
Normal file
10
.dockerignore
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
orconfig
|
||||||
|
.gitattributes
|
||||||
|
.github
|
||||||
|
.gitignore
|
||||||
|
.gitlab-ci.yml
|
||||||
|
.idea
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
.readthedocs.yml
|
||||||
|
.travis.yml
|
||||||
|
venv
|
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# http://editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{py,rst,ini}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{html,css,scss,json,yml,xml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[nginx.conf]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
4
.env.template
Normal file
4
.env.template
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/passfinder
|
||||||
|
CELERY_BROKER_URL=redis://localhost:6379/0
|
||||||
|
REDIS_URL=redis://localhost:6379/1
|
||||||
|
USE_DOCKER=no
|
25
.gitattributes
vendored
Normal file
25
.gitattributes
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Source files
|
||||||
|
# ============
|
||||||
|
*.pxd text diff=python
|
||||||
|
*.py text diff=python
|
||||||
|
*.py3 text diff=python
|
||||||
|
*.pyw text diff=python
|
||||||
|
*.pyx text diff=python
|
||||||
|
*.pyz text diff=python
|
||||||
|
*.pyi text diff=python
|
||||||
|
|
||||||
|
# Binary files
|
||||||
|
# ============
|
||||||
|
*.db binary
|
||||||
|
*.p binary
|
||||||
|
*.pkl binary
|
||||||
|
*.pickle binary
|
||||||
|
*.pyc binary export-ignore
|
||||||
|
*.pyo binary export-ignore
|
||||||
|
*.pyd binary
|
||||||
|
|
||||||
|
# Jupyter notebook
|
||||||
|
*.ipynb text eol=lf
|
||||||
|
|
||||||
|
# ignore static
|
||||||
|
passfinder/static/** linguist-vendored
|
333
.gitignore
vendored
Normal file
333
.gitignore
vendored
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
### Python template
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
staticfiles/
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.venv
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
|
|
||||||
|
### Node template
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
|
||||||
|
### Linux template
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
|
||||||
|
### VisualStudioCode template
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
|
||||||
|
# Provided default Pycharm Run/Debug Configurations should be tracked by git
|
||||||
|
# In case of local modifications made by Pycharm, use update-index command
|
||||||
|
# for each changed file, like this:
|
||||||
|
# git update-index --assume-unchanged .idea/passfinder.iml
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-debug/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Windows template
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
|
||||||
|
### macOS template
|
||||||
|
# General
|
||||||
|
*.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
|
||||||
|
### SublimeText template
|
||||||
|
# Cache files for Sublime Text
|
||||||
|
*.tmlanguage.cache
|
||||||
|
*.tmPreferences.cache
|
||||||
|
*.stTheme.cache
|
||||||
|
|
||||||
|
# Workspace files are user-specific
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Project files should be checked into the repository, unless a significant
|
||||||
|
# proportion of contributors will probably not be using Sublime Text
|
||||||
|
# *.sublime-project
|
||||||
|
|
||||||
|
# SFTP configuration file
|
||||||
|
sftp-config.json
|
||||||
|
|
||||||
|
# Package control specific files
|
||||||
|
Package Control.last-run
|
||||||
|
Package Control.ca-list
|
||||||
|
Package Control.ca-bundle
|
||||||
|
Package Control.system-ca-bundle
|
||||||
|
Package Control.cache/
|
||||||
|
Package Control.ca-certs/
|
||||||
|
Package Control.merged-ca-bundle
|
||||||
|
Package Control.user-ca-bundle
|
||||||
|
oscrypto-ca-bundle.crt
|
||||||
|
bh_unicode_properties.cache
|
||||||
|
|
||||||
|
# Sublime-github package stores a github token in this file
|
||||||
|
# https://packagecontrol.io/packages/sublime-github
|
||||||
|
GitHub.sublime-settings
|
||||||
|
|
||||||
|
|
||||||
|
### Vim template
|
||||||
|
# Swap
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-v][a-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Session
|
||||||
|
Session.vim
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
.netrwhist
|
||||||
|
|
||||||
|
# Auto-generated tag files
|
||||||
|
tags
|
||||||
|
|
||||||
|
### Project template
|
||||||
|
passfinder/media/
|
||||||
|
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
.ipython/
|
||||||
|
.env
|
||||||
|
.idea
|
39
.pre-commit-config.yaml
Normal file
39
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
exclude: "^docs/|/migrations/"
|
||||||
|
default_stages: [commit]
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.4.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v3.3.1
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: [--py310-plus]
|
||||||
|
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 22.12.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/isort
|
||||||
|
rev: 5.11.4
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: 6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
args: ["--config=setup.cfg"]
|
||||||
|
additional_dependencies: [flake8-isort]
|
||||||
|
|
||||||
|
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
|
||||||
|
ci:
|
||||||
|
autoupdate_schedule: weekly
|
||||||
|
skip: []
|
||||||
|
submodules: false
|
14
.pylintrc
Normal file
14
.pylintrc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[MASTER]
|
||||||
|
load-plugins=pylint_django, pylint_celery
|
||||||
|
django-settings-module=config.settings.local
|
||||||
|
[FORMAT]
|
||||||
|
max-line-length=120
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
disable=missing-docstring,invalid-name
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
max-parents=13
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete
|
40
README.md
Normal file
40
README.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Pass Finder
|
||||||
|
|
||||||
|
|
||||||
|
## Basic Commands
|
||||||
|
|
||||||
|
### Runserver
|
||||||
|
|
||||||
|
$ ./manage.py runserver_plus
|
||||||
|
|
||||||
|
### Type checks
|
||||||
|
|
||||||
|
Running type checks with mypy:
|
||||||
|
|
||||||
|
$ mypy passfinder
|
||||||
|
|
||||||
|
#### Running tests with pytest
|
||||||
|
|
||||||
|
$ pytest
|
||||||
|
|
||||||
|
### Setting Up Your Users
|
||||||
|
|
||||||
|
- To create a **superuser account**, use this command:
|
||||||
|
|
||||||
|
$ python manage.py createsuperuser
|
||||||
|
|
||||||
|
### Celery
|
||||||
|
|
||||||
|
This app comes with Celery.
|
||||||
|
|
||||||
|
To run a celery worker:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
cd passfinder
|
||||||
|
celery -A config.celery_app worker -l info
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
|
||||||
|
|
||||||
|
|
||||||
|
made with [cookiecutter-django](https://github.com/Alexander-D-Karpov/cookiecutter-django)
|
75
compose/local/django/Dockerfile
Normal file
75
compose/local/django/Dockerfile
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
ARG PYTHON_VERSION=3.11-slim
|
||||||
|
|
||||||
|
# define an alias for the specfic python version used in this file.
|
||||||
|
FROM python:${PYTHON_VERSION} as python
|
||||||
|
|
||||||
|
# Python build stage
|
||||||
|
FROM python as python-build-stage
|
||||||
|
|
||||||
|
ARG BUILD_ENVIRONMENT=local
|
||||||
|
|
||||||
|
# Install apt packages
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
# dependencies for building Python packages
|
||||||
|
build-essential \
|
||||||
|
# psycopg2 dependencies
|
||||||
|
libpq-dev
|
||||||
|
|
||||||
|
|
||||||
|
# Python 'run' stage
|
||||||
|
FROM python as python-run-stage
|
||||||
|
|
||||||
|
ARG BUILD_ENVIRONMENT=local
|
||||||
|
ARG APP_HOME=/app
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
|
||||||
|
|
||||||
|
WORKDIR ${APP_HOME}
|
||||||
|
|
||||||
|
# Install required system dependencies
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
# psycopg2 dependencies
|
||||||
|
libpq-dev \
|
||||||
|
# Translations dependencies
|
||||||
|
gettext \
|
||||||
|
# cleaning up unused files
|
||||||
|
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN pip install poetry
|
||||||
|
|
||||||
|
# Configuring poetry
|
||||||
|
RUN poetry config virtualenvs.create false
|
||||||
|
COPY pyproject.toml poetry.lock /
|
||||||
|
|
||||||
|
# Installing requirements
|
||||||
|
RUN poetry install
|
||||||
|
|
||||||
|
|
||||||
|
COPY ./compose/production/django/entrypoint /entrypoint
|
||||||
|
RUN sed -i 's/\r$//g' /entrypoint
|
||||||
|
RUN chmod +x /entrypoint
|
||||||
|
|
||||||
|
COPY ./compose/local/django/start /start
|
||||||
|
RUN sed -i 's/\r$//g' /start
|
||||||
|
RUN chmod +x /start
|
||||||
|
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/worker/start /start-celeryworker
|
||||||
|
RUN sed -i 's/\r$//g' /start-celeryworker
|
||||||
|
RUN chmod +x /start-celeryworker
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/beat/start /start-celerybeat
|
||||||
|
RUN sed -i 's/\r$//g' /start-celerybeat
|
||||||
|
RUN chmod +x /start-celerybeat
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/flower/start /start-flower
|
||||||
|
RUN sed -i 's/\r$//g' /start-flower
|
||||||
|
RUN chmod +x /start-flower
|
||||||
|
|
||||||
|
# copy application code to WORKDIR
|
||||||
|
COPY . ${APP_HOME}
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint"]
|
8
compose/local/django/celery/beat/start
Normal file
8
compose/local/django/celery/beat/start
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
rm -f './celerybeat.pid'
|
||||||
|
exec watchfiles celery.__main__.main --args '-A config.celery_app beat -l INFO'
|
8
compose/local/django/celery/flower/start
Normal file
8
compose/local/django/celery/flower/start
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
exec watchfiles celery.__main__.main \
|
||||||
|
--args \
|
||||||
|
"-A config.celery_app -b \"${CELERY_BROKER_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\""
|
7
compose/local/django/celery/worker/start
Normal file
7
compose/local/django/celery/worker/start
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
exec watchfiles celery.__main__.main --args '-A config.celery_app worker -l INFO'
|
9
compose/local/django/start
Normal file
9
compose/local/django/start
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
python manage.py migrate
|
||||||
|
exec python manage.py runserver_plus 0.0.0.0:8000
|
81
compose/production/django/Dockerfile
Normal file
81
compose/production/django/Dockerfile
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
ARG PYTHON_VERSION=3.11-slim
|
||||||
|
|
||||||
|
# define an alias for the specfic python version used in this file.
|
||||||
|
FROM python:${PYTHON_VERSION} as python
|
||||||
|
|
||||||
|
# Python build stage
|
||||||
|
FROM python as python-build-stage
|
||||||
|
|
||||||
|
ARG BUILD_ENVIRONMENT=production
|
||||||
|
|
||||||
|
# Install apt packages
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
# dependencies for building Python packages
|
||||||
|
build-essential \
|
||||||
|
# psycopg2 dependencies
|
||||||
|
libpq-dev
|
||||||
|
|
||||||
|
|
||||||
|
# Python 'run' stage
|
||||||
|
FROM python as python-run-stage
|
||||||
|
|
||||||
|
ARG BUILD_ENVIRONMENT=production
|
||||||
|
ARG APP_HOME=/app
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
|
||||||
|
|
||||||
|
WORKDIR ${APP_HOME}
|
||||||
|
|
||||||
|
# Install required system dependencies
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
# psycopg2 dependencies
|
||||||
|
libpq-dev \
|
||||||
|
# Translations dependencies
|
||||||
|
gettext \
|
||||||
|
# cleaning up unused files
|
||||||
|
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN addgroup --system django \
|
||||||
|
&& adduser --system --ingroup django django
|
||||||
|
|
||||||
|
RUN pip install poetry
|
||||||
|
|
||||||
|
# Configuring poetry
|
||||||
|
RUN poetry config virtualenvs.create false
|
||||||
|
COPY pyproject.toml poetry.lock /
|
||||||
|
|
||||||
|
# Installing requirements
|
||||||
|
RUN poetry install
|
||||||
|
|
||||||
|
|
||||||
|
COPY ./compose/production/django/entrypoint /entrypoint
|
||||||
|
RUN sed -i 's/\r$//g' /entrypoint
|
||||||
|
RUN chmod +x /entrypoint
|
||||||
|
|
||||||
|
COPY ./compose/local/django/start /start
|
||||||
|
RUN sed -i 's/\r$//g' /start
|
||||||
|
RUN chmod +x /start
|
||||||
|
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/worker/start /start-celeryworker
|
||||||
|
RUN sed -i 's/\r$//g' /start-celeryworker
|
||||||
|
RUN chmod +x /start-celeryworker
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/beat/start /start-celerybeat
|
||||||
|
RUN sed -i 's/\r$//g' /start-celerybeat
|
||||||
|
RUN chmod +x /start-celerybeat
|
||||||
|
|
||||||
|
COPY ./compose/local/django/celery/flower/start /start-flower
|
||||||
|
RUN sed -i 's/\r$//g' /start-flower
|
||||||
|
RUN chmod +x /start-flower
|
||||||
|
|
||||||
|
# copy application code to WORKDIR
|
||||||
|
COPY --chown=django:django . ${APP_HOME}
|
||||||
|
|
||||||
|
# make django owner of the WORKDIR directory as well.
|
||||||
|
RUN chown django:django ${APP_HOME}
|
||||||
|
|
||||||
|
USER django
|
8
compose/production/django/celery/beat/start
Normal file
8
compose/production/django/celery/beat/start
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
exec celery -A config.celery_app beat -l INFO
|
11
compose/production/django/celery/flower/start
Normal file
11
compose/production/django/celery/flower/start
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
exec celery \
|
||||||
|
-A config.celery_app \
|
||||||
|
-b "${CELERY_BROKER_URL}" \
|
||||||
|
flower \
|
||||||
|
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
8
compose/production/django/celery/worker/start
Normal file
8
compose/production/django/celery/worker/start
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
exec celery -A config.celery_app worker -l INFO
|
49
compose/production/django/entrypoint
Normal file
49
compose/production/django/entrypoint
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# N.B. If only .env files supported variable expansion...
|
||||||
|
export CELERY_BROKER_URL="${REDIS_URL}"
|
||||||
|
|
||||||
|
|
||||||
|
if [ -z "${POSTGRES_USER}" ]; then
|
||||||
|
base_postgres_image_default_user='postgres'
|
||||||
|
export POSTGRES_USER="${base_postgres_image_default_user}"
|
||||||
|
fi
|
||||||
|
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
||||||
|
|
||||||
|
python << END
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
suggest_unrecoverable_after = 30
|
||||||
|
start = time.time()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
psycopg2.connect(
|
||||||
|
dbname="${POSTGRES_DB}",
|
||||||
|
user="${POSTGRES_USER}",
|
||||||
|
password="${POSTGRES_PASSWORD}",
|
||||||
|
host="${POSTGRES_HOST}",
|
||||||
|
port="${POSTGRES_PORT}",
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except psycopg2.OperationalError as error:
|
||||||
|
sys.stderr.write("Waiting for PostgreSQL to become available...\n")
|
||||||
|
|
||||||
|
if time.time() - start > suggest_unrecoverable_after:
|
||||||
|
sys.stderr.write(" This is taking longer than expected. The following exception may be indicative of an unrecoverable error: '{}'\n".format(error))
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
END
|
||||||
|
|
||||||
|
>&2 echo 'PostgreSQL is available'
|
||||||
|
|
||||||
|
exec "$@"
|
9
compose/production/django/start
Normal file
9
compose/production/django/start
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
python /app/manage.py collectstatic --noinput
|
||||||
|
exec /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
|
6
compose/production/postgres/Dockerfile
Normal file
6
compose/production/postgres/Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FROM postgres:14
|
||||||
|
|
||||||
|
COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance
|
||||||
|
RUN chmod +x /usr/local/bin/maintenance/*
|
||||||
|
RUN mv /usr/local/bin/maintenance/* /usr/local/bin \
|
||||||
|
&& rmdir /usr/local/bin/maintenance
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
BACKUP_DIR_PATH='/backups'
|
||||||
|
BACKUP_FILE_PREFIX='backup'
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
countdown() {
|
||||||
|
declare desc="A simple countdown. Source: https://superuser.com/a/611582"
|
||||||
|
local seconds="${1}"
|
||||||
|
local d=$(($(date +%s) + "${seconds}"))
|
||||||
|
while [ "$d" -ge `date +%s` ]; do
|
||||||
|
echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r";
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
}
|
41
compose/production/postgres/maintenance/_sourced/messages.sh
Normal file
41
compose/production/postgres/maintenance/_sourced/messages.sh
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
message_newline() {
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
message_debug()
|
||||||
|
{
|
||||||
|
echo -e "DEBUG: ${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_welcome()
|
||||||
|
{
|
||||||
|
echo -e "\e[1m${@}\e[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_warning()
|
||||||
|
{
|
||||||
|
echo -e "\e[33mWARNING\e[0m: ${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_error()
|
||||||
|
{
|
||||||
|
echo -e "\e[31mERROR\e[0m: ${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_info()
|
||||||
|
{
|
||||||
|
echo -e "\e[37mINFO\e[0m: ${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_suggestion()
|
||||||
|
{
|
||||||
|
echo -e "\e[33mSUGGESTION\e[0m: ${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
message_success()
|
||||||
|
{
|
||||||
|
echo -e "\e[32mSUCCESS\e[0m: ${@}"
|
||||||
|
}
|
16
compose/production/postgres/maintenance/_sourced/yes_no.sh
Normal file
16
compose/production/postgres/maintenance/_sourced/yes_no.sh
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
yes_no() {
|
||||||
|
declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message."
|
||||||
|
local arg1="${1}"
|
||||||
|
|
||||||
|
local response=
|
||||||
|
read -r -p "${arg1} (y/[n])? " response
|
||||||
|
if [[ "${response}" =~ ^[Yy]$ ]]
|
||||||
|
then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
38
compose/production/postgres/maintenance/backup
Normal file
38
compose/production/postgres/maintenance/backup
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
### Create a database backup.
|
||||||
|
###
|
||||||
|
### Usage:
|
||||||
|
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres backup
|
||||||
|
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
working_dir="$(dirname ${0})"
|
||||||
|
source "${working_dir}/_sourced/constants.sh"
|
||||||
|
source "${working_dir}/_sourced/messages.sh"
|
||||||
|
|
||||||
|
|
||||||
|
message_welcome "Backing up the '${POSTGRES_DB}' database..."
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "${POSTGRES_USER}" == "postgres" ]]; then
|
||||||
|
message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PGHOST="${POSTGRES_HOST}"
|
||||||
|
export PGPORT="${POSTGRES_PORT}"
|
||||||
|
export PGUSER="${POSTGRES_USER}"
|
||||||
|
export PGPASSWORD="${POSTGRES_PASSWORD}"
|
||||||
|
export PGDATABASE="${POSTGRES_DB}"
|
||||||
|
|
||||||
|
backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz"
|
||||||
|
pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}"
|
||||||
|
|
||||||
|
|
||||||
|
message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'."
|
22
compose/production/postgres/maintenance/backups
Normal file
22
compose/production/postgres/maintenance/backups
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
### View backups.
|
||||||
|
###
|
||||||
|
### Usage:
|
||||||
|
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres backups
|
||||||
|
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
working_dir="$(dirname ${0})"
|
||||||
|
source "${working_dir}/_sourced/constants.sh"
|
||||||
|
source "${working_dir}/_sourced/messages.sh"
|
||||||
|
|
||||||
|
|
||||||
|
message_welcome "These are the backups you have got:"
|
||||||
|
|
||||||
|
ls -lht "${BACKUP_DIR_PATH}"
|
55
compose/production/postgres/maintenance/restore
Normal file
55
compose/production/postgres/maintenance/restore
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
### Restore database from a backup.
|
||||||
|
###
|
||||||
|
### Parameters:
|
||||||
|
### <1> filename of an existing backup.
|
||||||
|
###
|
||||||
|
### Usage:
|
||||||
|
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres restore <1>
|
||||||
|
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
|
working_dir="$(dirname ${0})"
|
||||||
|
source "${working_dir}/_sourced/constants.sh"
|
||||||
|
source "${working_dir}/_sourced/messages.sh"
|
||||||
|
|
||||||
|
|
||||||
|
if [[ -z ${1+x} ]]; then
|
||||||
|
message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
backup_filename="${BACKUP_DIR_PATH}/${1}"
|
||||||
|
if [[ ! -f "${backup_filename}" ]]; then
|
||||||
|
message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..."
|
||||||
|
|
||||||
|
if [[ "${POSTGRES_USER}" == "postgres" ]]; then
|
||||||
|
message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PGHOST="${POSTGRES_HOST}"
|
||||||
|
export PGPORT="${POSTGRES_PORT}"
|
||||||
|
export PGUSER="${POSTGRES_USER}"
|
||||||
|
export PGPASSWORD="${POSTGRES_PASSWORD}"
|
||||||
|
export PGDATABASE="${POSTGRES_DB}"
|
||||||
|
|
||||||
|
message_info "Dropping the database..."
|
||||||
|
dropdb "${PGDATABASE}"
|
||||||
|
|
||||||
|
message_info "Creating a new database..."
|
||||||
|
createdb --owner="${POSTGRES_USER}"
|
||||||
|
|
||||||
|
message_info "Applying the backup to the new database..."
|
||||||
|
gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}"
|
||||||
|
|
||||||
|
message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup."
|
5
compose/production/traefik/Dockerfile
Normal file
5
compose/production/traefik/Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
FROM traefik:v2.2.11
|
||||||
|
RUN mkdir -p /etc/traefik/acme \
|
||||||
|
&& touch /etc/traefik/acme/acme.json \
|
||||||
|
&& chmod 600 /etc/traefik/acme/acme.json
|
||||||
|
COPY ./compose/production/traefik/traefik.yml /etc/traefik
|
75
compose/production/traefik/traefik.yml
Normal file
75
compose/production/traefik/traefik.yml
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
log:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
# http
|
||||||
|
address: ":80"
|
||||||
|
http:
|
||||||
|
# https://docs.traefik.io/routing/entrypoints/#entrypoint
|
||||||
|
redirections:
|
||||||
|
entryPoint:
|
||||||
|
to: web-secure
|
||||||
|
|
||||||
|
web-secure:
|
||||||
|
# https
|
||||||
|
address: ":443"
|
||||||
|
|
||||||
|
flower:
|
||||||
|
address: ":5555"
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
letsencrypt:
|
||||||
|
# https://docs.traefik.io/master/https/acme/#lets-encrypt
|
||||||
|
acme:
|
||||||
|
email: "sanspie@akarpov.ru"
|
||||||
|
storage: /etc/traefik/acme/acme.json
|
||||||
|
# https://docs.traefik.io/master/https/acme/#httpchallenge
|
||||||
|
httpChallenge:
|
||||||
|
entryPoint: web
|
||||||
|
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
web-secure-router:
|
||||||
|
rule: "Host(`akarpov.ru`) || Host(`www.akarpov.ru`)"
|
||||||
|
entryPoints:
|
||||||
|
- web-secure
|
||||||
|
middlewares:
|
||||||
|
- csrf
|
||||||
|
service: django
|
||||||
|
tls:
|
||||||
|
# https://docs.traefik.io/master/routing/routers/#certresolver
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
flower-secure-router:
|
||||||
|
rule: "Host(`akarpov.ru`)"
|
||||||
|
entryPoints:
|
||||||
|
- flower
|
||||||
|
service: flower
|
||||||
|
tls:
|
||||||
|
# https://docs.traefik.io/master/routing/routers/#certresolver
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
middlewares:
|
||||||
|
csrf:
|
||||||
|
# https://docs.traefik.io/master/middlewares/headers/#hostsproxyheaders
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
|
||||||
|
headers:
|
||||||
|
hostsProxyHeaders: ["X-CSRFToken"]
|
||||||
|
|
||||||
|
services:
|
||||||
|
django:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: http://django:5000
|
||||||
|
|
||||||
|
flower:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: http://flower:5555
|
||||||
|
|
||||||
|
providers:
|
||||||
|
# https://docs.traefik.io/master/providers/file/
|
||||||
|
file:
|
||||||
|
filename: /etc/traefik/traefik.yml
|
||||||
|
watch: true
|
5
config/__init__.py
Normal file
5
config/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# This will make sure the app is always imported when
|
||||||
|
# Django starts so that shared_task will use this app.
|
||||||
|
from .celery_app import app as celery_app
|
||||||
|
|
||||||
|
__all__ = ("celery_app",)
|
24
config/api_router.py
Normal file
24
config/api_router.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from passfinder.recomendations.api.views import TinderView, PersonalRecommendation, OnboardingViewset
|
||||||
|
from passfinder.users.api.views import (
|
||||||
|
UserViewSet,
|
||||||
|
CreateUserPreferenceApiView,
|
||||||
|
ListUserFavoritePointsApiView,
|
||||||
|
)
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
|
||||||
|
router.register("tinder", TinderView)
|
||||||
|
router.register("recommendations", PersonalRecommendation)
|
||||||
|
router.register("user", UserViewSet)
|
||||||
|
router.register('onboarding', OnboardingViewset)
|
||||||
|
|
||||||
|
app_name = "api"
|
||||||
|
urlpatterns = [
|
||||||
|
path("", include("passfinder.events.api.urls")),
|
||||||
|
path("auth/", include("passfinder.users.api.urls")),
|
||||||
|
path("user/preference", CreateUserPreferenceApiView.as_view()),
|
||||||
|
path("user/favorite", ListUserFavoritePointsApiView.as_view()),
|
||||||
|
]
|
||||||
|
urlpatterns += router.urls
|
17
config/celery_app.py
Normal file
17
config/celery_app.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
# set the default Django settings module for the 'celery' program.
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||||
|
|
||||||
|
app = Celery("passfinder")
|
||||||
|
|
||||||
|
# Using a string here means the worker doesn't have to serialize
|
||||||
|
# the configuration object to child processes.
|
||||||
|
# - namespace='CELERY' means all celery-related configuration keys
|
||||||
|
# should have a `CELERY_` prefix.
|
||||||
|
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||||
|
|
||||||
|
# Load task modules from all registered Django app configs.
|
||||||
|
app.autodiscover_tasks()
|
0
config/settings/__init__.py
Normal file
0
config/settings/__init__.py
Normal file
365
config/settings/base.py
Normal file
365
config/settings/base.py
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
"""
|
||||||
|
Base settings to build other settings files upon.
|
||||||
|
"""
|
||||||
|
import warnings
|
||||||
|
from datetime import timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import environ
|
||||||
|
import structlog
|
||||||
|
from urllib3.connectionpool import InsecureRequestWarning
|
||||||
|
|
||||||
|
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
|
||||||
|
# passfinder/
|
||||||
|
APPS_DIR = ROOT_DIR / "passfinder"
|
||||||
|
env = environ.Env()
|
||||||
|
|
||||||
|
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True)
|
||||||
|
if READ_DOT_ENV_FILE:
|
||||||
|
# OS environment variables take precedence over variables from .env
|
||||||
|
env.read_env(str(ROOT_DIR / ".env"))
|
||||||
|
|
||||||
|
# GENERAL
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||||
|
DEBUG = env.bool("DJANGO_DEBUG", False)
|
||||||
|
# Local time zone. Choices are
|
||||||
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
|
# though not all of them may be available with every OS.
|
||||||
|
# In Windows, this must be set to your system time zone.
|
||||||
|
TIME_ZONE = "Europe/Moscow"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#language-code
|
||||||
|
LANGUAGE_CODE = "en-us"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#site-id
|
||||||
|
SITE_ID = 1
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
|
||||||
|
USE_I18N = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
|
||||||
|
USE_TZ = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths
|
||||||
|
LOCALE_PATHS = [str(ROOT_DIR / "locale")]
|
||||||
|
|
||||||
|
# DATABASES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
|
||||||
|
DATABASES = {
|
||||||
|
"default": env.db(
|
||||||
|
"DATABASE_URL", "postgres://postgres:Ilvas2006@localhost:5432/passfinder"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DATABASES["default"]["ATOMIC_REQUESTS"] = True
|
||||||
|
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
|
||||||
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
# URLS
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
|
||||||
|
ROOT_URLCONF = "config.urls"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
|
||||||
|
WSGI_APPLICATION = "config.wsgi.application"
|
||||||
|
|
||||||
|
# APPS
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
DJANGO_APPS = [
|
||||||
|
"django.contrib.auth",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.sites",
|
||||||
|
"django.contrib.messages",
|
||||||
|
"django.contrib.staticfiles",
|
||||||
|
# "django.contrib.humanize", # Handy template tags
|
||||||
|
"django.contrib.admin",
|
||||||
|
"django.forms",
|
||||||
|
]
|
||||||
|
THIRD_PARTY_APPS = [
|
||||||
|
"django_celery_beat",
|
||||||
|
"rest_framework",
|
||||||
|
"rest_framework.authtoken",
|
||||||
|
"corsheaders",
|
||||||
|
"drf_spectacular",
|
||||||
|
"location_field",
|
||||||
|
"polymorphic",
|
||||||
|
"django_clickhouse",
|
||||||
|
]
|
||||||
|
|
||||||
|
LOCAL_APPS = ["passfinder.users", "passfinder.events", "passfinder.recomendations"]
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||||
|
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
||||||
|
|
||||||
|
# MIGRATIONS
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules
|
||||||
|
MIGRATION_MODULES = {"sites": "passfinder.contrib.sites.migrations"}
|
||||||
|
|
||||||
|
# AUTHENTICATION
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
"django.contrib.auth.backends.ModelBackend",
|
||||||
|
]
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model
|
||||||
|
AUTH_USER_MODEL = "users.User"
|
||||||
|
|
||||||
|
# PASSWORDS
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
|
||||||
|
PASSWORD_HASHERS = [
|
||||||
|
# https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django
|
||||||
|
"django.contrib.auth.hashers.Argon2PasswordHasher",
|
||||||
|
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
|
||||||
|
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
|
||||||
|
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
|
||||||
|
]
|
||||||
|
# https://docs.djangoproject.com/en/dev/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"},
|
||||||
|
]
|
||||||
|
|
||||||
|
# MIDDLEWARE
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
|
||||||
|
MIDDLEWARE = [
|
||||||
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
"corsheaders.middleware.CorsMiddleware",
|
||||||
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||||
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
|
"django.middleware.common.CommonMiddleware",
|
||||||
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
|
"django.middleware.common.BrokenLinkEmailsMiddleware",
|
||||||
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
]
|
||||||
|
|
||||||
|
# STATIC
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#static-root
|
||||||
|
STATIC_ROOT = str(ROOT_DIR / "staticfiles")
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#static-url
|
||||||
|
STATIC_URL = "/static/"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
|
||||||
|
STATICFILES_DIRS = [str(APPS_DIR / "static")]
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
|
||||||
|
STATICFILES_FINDERS = [
|
||||||
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
|
]
|
||||||
|
|
||||||
|
# MEDIA
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
||||||
|
MEDIA_ROOT = str(APPS_DIR / "media")
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
|
||||||
|
MEDIA_URL = "/media/"
|
||||||
|
|
||||||
|
# TEMPLATES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#templates
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND
|
||||||
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#dirs
|
||||||
|
"DIRS": [str(APPS_DIR / "templates")],
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#app-dirs
|
||||||
|
"APP_DIRS": True,
|
||||||
|
"OPTIONS": {
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
|
||||||
|
"context_processors": [
|
||||||
|
"django.template.context_processors.debug",
|
||||||
|
"django.template.context_processors.request",
|
||||||
|
"django.contrib.auth.context_processors.auth",
|
||||||
|
"django.template.context_processors.i18n",
|
||||||
|
"django.template.context_processors.media",
|
||||||
|
"django.template.context_processors.static",
|
||||||
|
"django.template.context_processors.tz",
|
||||||
|
"django.contrib.messages.context_processors.messages",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer
|
||||||
|
FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
|
||||||
|
|
||||||
|
# FIXTURES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs
|
||||||
|
FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)
|
||||||
|
|
||||||
|
# SECURITY
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
|
||||||
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
|
||||||
|
SECURE_BROWSER_XSS_FILTER = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
|
||||||
|
X_FRAME_OPTIONS = "DENY"
|
||||||
|
|
||||||
|
# ADMIN
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Django Admin URL.
|
||||||
|
ADMIN_URL = "admin/"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#admins
|
||||||
|
ADMINS = [("""sanspie""", "sanspie@akarpov.ru")]
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
# LOGGING
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
||||||
|
# See https://docs.djangoproject.com/en/dev/topics/logging for
|
||||||
|
# more details on how to customize your logging configuration.
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"json_formatter": {
|
||||||
|
"()": structlog.stdlib.ProcessorFormatter,
|
||||||
|
"processor": structlog.processors.JSONRenderer(),
|
||||||
|
},
|
||||||
|
"plain_console": {
|
||||||
|
"()": structlog.stdlib.ProcessorFormatter,
|
||||||
|
"processor": structlog.dev.ConsoleRenderer(),
|
||||||
|
},
|
||||||
|
"key_value": {
|
||||||
|
"()": structlog.stdlib.ProcessorFormatter,
|
||||||
|
"processor": structlog.processors.KeyValueRenderer(
|
||||||
|
key_order=["timestamp", "level", "event", "logger"]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "plain_console",
|
||||||
|
},
|
||||||
|
"json_file": {
|
||||||
|
"class": "logging.handlers.WatchedFileHandler",
|
||||||
|
"filename": "logs/json.log",
|
||||||
|
"formatter": "json_formatter",
|
||||||
|
},
|
||||||
|
"flat_line_file": {
|
||||||
|
"class": "logging.handlers.WatchedFileHandler",
|
||||||
|
"filename": "logs/flat_line.log",
|
||||||
|
"formatter": "key_value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django_structlog": {
|
||||||
|
"handlers": ["console", "flat_line_file", "json_file"],
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
# Make sure to replace the following logger's name for yours
|
||||||
|
"django_structlog_demo_project": {
|
||||||
|
"handlers": ["console", "flat_line_file", "json_file"],
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
structlog.configure(
|
||||||
|
processors=[
|
||||||
|
structlog.contextvars.merge_contextvars,
|
||||||
|
structlog.stdlib.filter_by_level,
|
||||||
|
structlog.processors.TimeStamper(fmt="iso"),
|
||||||
|
structlog.stdlib.add_logger_name,
|
||||||
|
structlog.stdlib.add_log_level,
|
||||||
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||||
|
structlog.processors.StackInfoRenderer(),
|
||||||
|
structlog.processors.format_exc_info,
|
||||||
|
structlog.processors.UnicodeDecoder(),
|
||||||
|
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||||
|
],
|
||||||
|
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||||
|
cache_logger_on_first_use=True,
|
||||||
|
)
|
||||||
|
# CELERY
|
||||||
|
# --------------------------------------------------------------------------------------------------
|
||||||
|
CELERY_REDIS_HOST = env("CELERY_REDIS_HOST", default="127.0.0.1")
|
||||||
|
CELERY_REDIS_PORT = env.int("CELERY_REDIS_PORT", default=6379)
|
||||||
|
CELERY_REDIS_USER = env("CELERY_REDIS_USER", default=None)
|
||||||
|
CELERY_REDIS_PASSWORD = env("CELERY_REDIS_PASSWORD", default=None)
|
||||||
|
CELERY_REDIS_DB = env("CELERY_REDIS_DB", default=0)
|
||||||
|
|
||||||
|
CELERY_REDIS_SSL = env.bool("CELERY_REDIS_SSL", default=False)
|
||||||
|
CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="")
|
||||||
|
CELERY_TASK_SERIALIZER = "json"
|
||||||
|
CELERY_ACCEPT_CONTENT = ["application/json"]
|
||||||
|
CELERY_ENABLE_UTC = True
|
||||||
|
|
||||||
|
CELERY_BEAT_SCHEDULE = {
|
||||||
|
"clickhouse_auto_sync": {
|
||||||
|
"task": "django_clickhouse.tasks.clickhouse_auto_sync",
|
||||||
|
"schedule": timedelta(seconds=5),
|
||||||
|
"options": {"expires": 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# DRF
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||||
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
||||||
|
),
|
||||||
|
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
||||||
|
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||||
|
}
|
||||||
|
|
||||||
|
# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup
|
||||||
|
CORS_URLS_REGEX = r"^/api/.*$"
|
||||||
|
|
||||||
|
# By Default swagger ui is available only to admin user(s). You can change permission classes to change that
|
||||||
|
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
|
||||||
|
SPECTACULAR_SETTINGS = {
|
||||||
|
"TITLE": "Pass Finder API",
|
||||||
|
"DESCRIPTION": "Documentation of API endpoints of Pass Finder",
|
||||||
|
"VERSION": "1.0.0",
|
||||||
|
"SERVE_PERMISSIONS": [],
|
||||||
|
"SERVERS": [
|
||||||
|
{"url": "https://dev2.akarpov.ru", "description": "Production server"},
|
||||||
|
{"url": "http://127.0.0.1:8000", "description": "Local Development server"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
LOCATION_FIELD = {
|
||||||
|
"map.provider": "openstreetmap",
|
||||||
|
"search.provider": "nominatim",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SIMPLE_JWT = {
|
||||||
|
"ACCESS_TOKEN_LIFETIME": timedelta(days=30),
|
||||||
|
"REFRESH_TOKEN_LIFETIME": timedelta(weeks=50),
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLICKHOUSE
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
CLICKHOUSE_DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"db_url": env("CLICKHOUSE_URL", default="http://localhost:8123"),
|
||||||
|
"db_name": env("CLICKHOUSE_DB", default="default"),
|
||||||
|
"username": env("CLICKHOUSE_USER", default="default"),
|
||||||
|
"password": env("CLICKHOUSE_PASSWORD", default="default"),
|
||||||
|
"verify_ssl_cert": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings.filterwarnings("ignore", category=InsecureRequestWarning)
|
||||||
|
|
||||||
|
CLICKHOUSE_REDIS_CONFIG = {
|
||||||
|
"host": env("CLICKHOUSE_REDIS_HOST", default="127.0.0.1"),
|
||||||
|
"port": env("CLICKHOUSE_REDIS_PORT", default=6379),
|
||||||
|
"db": env("CLICKHOUSE_REDIS_DB", default=0),
|
||||||
|
"username": env("CLICKHOUSE_REDIS_USER", default=None),
|
||||||
|
"password": env("CLICKHOUSE_REDIS_PASSWORD", default=None),
|
||||||
|
"socket_timeout": 10,
|
||||||
|
}
|
61
config/settings/local.py
Normal file
61
config/settings/local.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from .base import * # noqa
|
||||||
|
from .base import env
|
||||||
|
|
||||||
|
# GENERAL
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||||
|
DEBUG = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||||
|
SECRET_KEY = env(
|
||||||
|
"DJANGO_SECRET_KEY",
|
||||||
|
default="dh0qndI7XExEDZIdMDh2VPHbW9VNq0jsjsI2ImR9EyDQfNMYudHUaJswMggfhotG",
|
||||||
|
)
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||||
|
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "dev2.akarpov.ru", "0e06-92-100-146-65.ngrok-free.app"]
|
||||||
|
CORS_ORIGIN_ALLOW_ALL = True
|
||||||
|
|
||||||
|
# CACHES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
|
||||||
|
CACHES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||||
|
"LOCATION": "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# WhiteNoise
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
|
||||||
|
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405
|
||||||
|
|
||||||
|
|
||||||
|
# django-debug-toolbar
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
|
||||||
|
INSTALLED_APPS += ["debug_toolbar"] # noqa F405
|
||||||
|
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
|
||||||
|
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405
|
||||||
|
# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config
|
||||||
|
DEBUG_TOOLBAR_CONFIG = {
|
||||||
|
"DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"],
|
||||||
|
"SHOW_TEMPLATE_CONTEXT": True,
|
||||||
|
}
|
||||||
|
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips
|
||||||
|
INTERNAL_IPS = ["127.0.0.1", "10.0.2.2"]
|
||||||
|
# if env("USE_DOCKER") == "yes":
|
||||||
|
# import socket
|
||||||
|
|
||||||
|
# hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
|
||||||
|
# INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
|
||||||
|
|
||||||
|
# django-extensions
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
||||||
|
INSTALLED_APPS += ["django_extensions"] # noqa F405
|
||||||
|
# Celery
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates
|
||||||
|
CELERY_TASK_EAGER_PROPAGATES = True
|
||||||
|
# Your stuff...
|
||||||
|
# ------------------------------------------------------------------------------
|
115
config/settings/production.py
Normal file
115
config/settings/production.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
from .base import * # noqa
|
||||||
|
from .base import env
|
||||||
|
|
||||||
|
# GENERAL
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||||
|
SECRET_KEY = env("DJANGO_SECRET_KEY")
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||||
|
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["akarpov.ru"])
|
||||||
|
|
||||||
|
# DATABASES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405
|
||||||
|
|
||||||
|
# CACHES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
CACHES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
|
"LOCATION": env("REDIS_URL"),
|
||||||
|
"OPTIONS": {
|
||||||
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
|
# Mimicing memcache behavior.
|
||||||
|
# https://github.com/jazzband/django-redis#memcached-exceptions-behavior
|
||||||
|
"IGNORE_EXCEPTIONS": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# SECURITY
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
|
||||||
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect
|
||||||
|
SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True)
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure
|
||||||
|
CSRF_COOKIE_SECURE = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds
|
||||||
|
SECURE_HSTS_SECONDS = 518400
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains
|
||||||
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
|
||||||
|
"DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True
|
||||||
|
)
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload
|
||||||
|
SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True)
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
|
||||||
|
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
|
||||||
|
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# STATIC
|
||||||
|
# ------------------------
|
||||||
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
|
# MEDIA
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ADMIN
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Django Admin URL regex.
|
||||||
|
ADMIN_URL = env("DJANGO_ADMIN_URL")
|
||||||
|
# LOGGING
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
||||||
|
# See https://docs.djangoproject.com/en/dev/topics/logging for
|
||||||
|
# more details on how to customize your logging configuration.
|
||||||
|
# A sample logging configuration. The only tangible logging
|
||||||
|
# performed by this configuration is to send an email to
|
||||||
|
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "%(levelname)s %(asctime)s %(module)s "
|
||||||
|
"%(process)d %(thread)d %(message)s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"mail_admins": {
|
||||||
|
"level": "ERROR",
|
||||||
|
"filters": ["require_debug_false"],
|
||||||
|
"class": "django.utils.log.AdminEmailHandler",
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"root": {"level": "INFO", "handlers": ["console"]},
|
||||||
|
"loggers": {
|
||||||
|
"django.request": {
|
||||||
|
"handlers": ["mail_admins"],
|
||||||
|
"level": "ERROR",
|
||||||
|
"propagate": True,
|
||||||
|
},
|
||||||
|
"django.security.DisallowedHost": {
|
||||||
|
"level": "ERROR",
|
||||||
|
"handlers": ["console", "mail_admins"],
|
||||||
|
"propagate": True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# django-rest-framework
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Tools that generate code samples can use SERVERS to point to the correct domain
|
||||||
|
SPECTACULAR_SETTINGS["SERVERS"] = [ # noqa F405
|
||||||
|
{"url": "https://akarpov.ru", "description": "Production server"}
|
||||||
|
]
|
33
config/settings/test.py
Normal file
33
config/settings/test.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
With these settings, tests run faster.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .base import * # noqa
|
||||||
|
from .base import env
|
||||||
|
|
||||||
|
# GENERAL
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||||
|
SECRET_KEY = env(
|
||||||
|
"DJANGO_SECRET_KEY",
|
||||||
|
default="rGnf9bzlf7cNN2Sq64P0jc3Za3VTMMYxPnehFudBB4g5N9p51h63HQQRFoiDFvNV",
|
||||||
|
)
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
|
||||||
|
TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
||||||
|
|
||||||
|
# PASSWORDS
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
|
||||||
|
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||||
|
|
||||||
|
# EMAIL
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||||
|
|
||||||
|
# DEBUGGING FOR TEMPLATES
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore # noqa F405
|
||||||
|
|
||||||
|
# Your stuff...
|
||||||
|
# ------------------------------------------------------------------------------
|
31
config/urls.py
Normal file
31
config/urls.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path
|
||||||
|
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Django Admin, use {% url 'admin:index' %}
|
||||||
|
path(settings.ADMIN_URL, admin.site.urls),
|
||||||
|
# User management
|
||||||
|
# Your stuff: custom urls includes go here
|
||||||
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
# API URLS
|
||||||
|
urlpatterns += [
|
||||||
|
# API base url
|
||||||
|
path("api/", include("config.api_router")),
|
||||||
|
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
|
||||||
|
path(
|
||||||
|
"api/docs/",
|
||||||
|
SpectacularSwaggerView.as_view(url_name="api-schema"),
|
||||||
|
name="api-docs",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
# This allows the error pages to be debugged during development, just visit
|
||||||
|
# these url in browser to see how these error pages look like.
|
||||||
|
if "debug_toolbar" in settings.INSTALLED_APPS:
|
||||||
|
import debug_toolbar
|
||||||
|
|
||||||
|
urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
|
38
config/wsgi.py
Normal file
38
config/wsgi.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"""
|
||||||
|
WSGI config for Pass Finder project.
|
||||||
|
|
||||||
|
This module contains the WSGI application used by Django's development server
|
||||||
|
and any production WSGI deployments. It should expose a module-level variable
|
||||||
|
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||||
|
this application via the ``WSGI_APPLICATION`` setting.
|
||||||
|
|
||||||
|
Usually you will have the standard Django WSGI application here, but it also
|
||||||
|
might make sense to replace the whole Django WSGI application with a custom one
|
||||||
|
that later delegates to the Django one. For example, you could introduce WSGI
|
||||||
|
middleware here, or combine a Django application with an application of another
|
||||||
|
framework.
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
# This allows easy placement of apps within the interior
|
||||||
|
# passfinder directory.
|
||||||
|
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
|
||||||
|
sys.path.append(str(ROOT_DIR / "passfinder"))
|
||||||
|
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||||
|
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||||
|
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||||
|
# os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production"
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
||||||
|
|
||||||
|
# This application object is used by any WSGI server configured to use this
|
||||||
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||||
|
# setting points here.
|
||||||
|
application = get_wsgi_application()
|
||||||
|
# Apply WSGI middleware here.
|
||||||
|
# from helloworld.wsgi import HelloWorldApplication
|
||||||
|
# application = HelloWorldApplication(application)
|
1
data/bulvary.json
Normal file
1
data/bulvary.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[{"_id": {"$oid": "61c07987c53ae811d6e6b829"}, "dictionary_data": {"youtube_videos": [], "russpass_recommendation": false, "aac_images": [], "disable_sessionids_autoupdate": false, "information_pages": [], "type_audio_guide": [], "sessionIds": [], "social_buttons": [], "images": [{"source": {"id": "63da16a3961d4400a3ac475f"}}, {"source": {"id": "63da16a6961d4400a3ac476b"}}], "image_explore_preview": [{"source": {"id": "63da16a0961d4400a3ac4757"}}], "image_detailed_page_main": [], "tags_main_screen": ["5e4aa3aca7edd0687c4688e1"], "aac_tags": [], "duration": "30", "purchase_method": "free", "ticket_price": "0", "is_can_buy": false, "galereya": [], "recommendation_prime": [], "timetable_by_place": [], "schedule": [], "without_schedule": false, "description": "\u0421\u0430\u043c\u0430\u044f \u0441\u0442\u0430\u0440\u0430\u044f \u0438 \u043f\u0440\u043e\u0442\u044f\u0436\u0435\u043d\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0411\u0443\u043b\u044c\u0432\u0430\u0440\u043d\u043e\u0433\u043e \u043a\u043e\u043b\u044c\u0446\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0434\u0432\u0435 \u043f\u043b\u043e\u0449\u0430\u0434\u0438 \u2014 \u041d\u0438\u043a\u0438\u0442\u0441\u043a\u0438\u0445 \u0412\u043e\u0440\u043e\u0442 \u0438 \u041f\u0443\u0448\u043a\u0438\u043d\u0441\u043a\u0443\u044e. \u0422\u0432\u0435\u0440\u0441\u043a\u043e\u0439 \u0431\u0443\u043b\u044c\u0432\u0430\u0440 \u0437\u0430\u043b\u043e\u0436\u0438\u043b\u0438 \u0432 1796 \u0433\u043e\u0434\u0443: \u043e\u043d \u043e\u0447\u0435\u043d\u044c \u0441\u043a\u043e\u0440\u043e \u0441\u0442\u0430\u043b \u0446\u0435\u043d\u0442\u0440\u043e\u043c \u0441\u0432\u0435\u0442\u0441\u043a\u043e\u0439 \u0436\u0438\u0437\u043d\u0438 \u043c\u043e\u0441\u043a\u043e\u0432\u0441\u043a\u0438\u0445 \u0434\u0432\u043e\u0440\u044f\u043d. \u0411\u044b\u0432\u0430\u043b \u0437\u0434\u0435\u0441\u044c \u0438 \u041f\u0443\u0448\u043a\u0438\u043d: \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0422\u0432\u0435\u0440\u0441\u043a\u043e\u043c \u0431\u0443\u043b\u044c\u0432\u0430\u0440\u0435 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u0442\u043e\u044f\u043b \u043f\u0430\u043c\u044f\u0442\u043d\u0438\u043a \u043f\u043e\u044d\u0442\u0443, \u043f\u043e\u043a\u0430 \u0432 1950 \u0433\u043e\u0434\u0443 \u0435\u0433\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u043d\u0430 \u0441\u043e\u0441\u0435\u0434\u043d\u044e\u044e \u041f\u0443\u0448\u043a\u0438\u043d\u0441\u043a\u0443\u044e \u043f\u043b\u043e\u0449\u0430\u0434\u044c. \n", "region": "5d08e36dad3a9a001701b95b", "is_priority": false, "sort": 60, "tags": ["6124b391020892001197dfcf"], "cg_recommendations": [], "place": ["61c079d3c53ae811d6e6b8db"], "restaurants": [], "city": "5bbe8a8a9e7fcb000f78a92d", "event_type": "5ebe69313740700019205cfe", "title": "\u0422\u0432\u0435\u0440\u0441\u043a\u043e\u0439 \u0431\u0443\u043b\u044c\u0432\u0430\u0440", "import_denied": false}}]
|
61864
data/cities.json
Normal file
61864
data/cities.json
Normal file
File diff suppressed because it is too large
Load Diff
84
data/cities_description.txt
Normal file
84
data/cities_description.txt
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
id:{
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
},
|
||||||
|
dictionary_data: { // собственно наполнение словарей
|
||||||
|
_id: str // нет
|
||||||
|
aliases: list // Псевдонимы
|
||||||
|
cg_images: list // Обложка CityGuide / Alfa travel
|
||||||
|
city: str // идентификатор города
|
||||||
|
country: list // идентификатор страны
|
||||||
|
description: str // описание
|
||||||
|
description_title: str // заголовок
|
||||||
|
external_id: str // внешний идентификатор
|
||||||
|
gallery: list // Галерея изображений
|
||||||
|
geo_data {
|
||||||
|
center_distance: float // расстояние до центра
|
||||||
|
coordinates: [ // Координаты (широта и долгота)
|
||||||
|
#.##, // широта
|
||||||
|
#.## // долгота
|
||||||
|
]
|
||||||
|
type: str // Тип геоданных (по умолчанию: points)
|
||||||
|
}
|
||||||
|
google_place {
|
||||||
|
use_manual_schedule: bool // Использовать google расписание
|
||||||
|
}
|
||||||
|
hp_images: list // HP Картинки
|
||||||
|
icon: [ // Иконка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Основное изображений
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // импорт запрещен
|
||||||
|
is_priority: bool // приоритет
|
||||||
|
list_image: list // идентификаторы и источники файлов изображений
|
||||||
|
on_map: list // Город на карте
|
||||||
|
rating: int // Рейтинг
|
||||||
|
region: str // Регион
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
source: str // Источник данных
|
||||||
|
timezone: str // часовой пояс
|
||||||
|
title: str // Название
|
||||||
|
title_dative: str // Название в винительном падеже
|
||||||
|
title_genitive: str // Название в родительном падеже
|
||||||
|
travel_line_id: str // Идентификатор города в справочнике на стороне travelline (TravelLine ID)
|
||||||
|
weather_sync: bool // Признак синхронизации погоды
|
||||||
|
working_time: {
|
||||||
|
days: {
|
||||||
|
Mon: {
|
||||||
|
closed: bool // Нерабочий день Понедельник
|
||||||
|
}
|
||||||
|
Tue: {
|
||||||
|
closed: bool // Нерабочий день Вторник
|
||||||
|
}
|
||||||
|
Wed: {
|
||||||
|
closed: bool // Нерабочий день Среда
|
||||||
|
}
|
||||||
|
Thu: {
|
||||||
|
closed: bool // Нерабочий день Четверг
|
||||||
|
}
|
||||||
|
Fri: {
|
||||||
|
closed: bool // Нерабочий день Пятница
|
||||||
|
}
|
||||||
|
Sat: {
|
||||||
|
closed: bool // Нерабочий день Суббота
|
||||||
|
}
|
||||||
|
Sun: {
|
||||||
|
closed: bool // Нерабочий день Воскресенье
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ya_id: str // Идентификатор города в справочнике на стороне Яндекс
|
||||||
|
сoat_of_arms: list // Герб
|
||||||
|
}
|
||||||
|
}
|
1
data/concerts.json
Normal file
1
data/concerts.json
Normal file
File diff suppressed because one or more lines are too long
1
data/dostoprimechatelnosti.json
Normal file
1
data/dostoprimechatelnosti.json
Normal file
File diff suppressed because one or more lines are too long
126
data/events_description.txt
Normal file
126
data/events_description.txt
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
{
|
||||||
|
_id:{
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
},
|
||||||
|
dictionary_data: { // собственно наполнение коллекций
|
||||||
|
_id: str // нет
|
||||||
|
aac_images: list // ААЦ Изображения
|
||||||
|
aac_tags: list // AAC Тэги
|
||||||
|
about_booking: str // Дополнительная информация о покупке билета
|
||||||
|
age: str // Возрастное ограничение
|
||||||
|
billing_product_id: int // Идентификатор продукта в биллинге
|
||||||
|
booking_link: str // Ссылка на покупку билета
|
||||||
|
cg_announcement: str // Краткое описание события
|
||||||
|
cg_recommendations: list // CG: Рекомендации членов клуба Prime
|
||||||
|
city: str // id города (справочник cities)
|
||||||
|
creator: str // Кто создал запись
|
||||||
|
description: str // Описание
|
||||||
|
disable_sessionids_autoupdate: bool // Не обновлять автоматически sessionIds
|
||||||
|
discover_moscow_link: str // ссылка на Discover.Moscow
|
||||||
|
duration: str // Продолжительность, мин
|
||||||
|
event_type: str // Тип события
|
||||||
|
galereya: list // Галерея
|
||||||
|
id_izi_travel: str // Идентификатор izi.travel
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображение обложки
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
information_pages: list // Информационные страницы
|
||||||
|
is_can_buy: bool // Можно купить
|
||||||
|
is_priority: bool // Приоритетное событие
|
||||||
|
link_source: str // Ссылка на источник события
|
||||||
|
parser_source: str // Источник импорта
|
||||||
|
partner: str // Партнер
|
||||||
|
partner_type: str // Тип партнера
|
||||||
|
payment_method: str // Метод покупки
|
||||||
|
place: list // Площадка проведения события (справочник places)
|
||||||
|
pravila-soglasovaniya: bool // Правила согласования
|
||||||
|
purchase_addtitional_info: str // Дополнительная информация к стоимости
|
||||||
|
purchase_method: str // Метод покупки (старый)
|
||||||
|
recommendation_prime: list // City Guide Рекомендации
|
||||||
|
region: str // Регион
|
||||||
|
remote_event_id: str // нет
|
||||||
|
remote_place_id: str // нет
|
||||||
|
restaurants: list // Рестораны проведения события
|
||||||
|
rp_price_id: int // Идентификатор тарифа в биллинге
|
||||||
|
russpass_recommendation: bool // RUSSPASS рекомендует
|
||||||
|
schedule: [ // Расписание
|
||||||
|
{
|
||||||
|
title: str // заголовок
|
||||||
|
start: {
|
||||||
|
$date: str // Дата и время начала
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
$date: str // Дата и время окончания
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
schedule_description: str // Дополнительная информация к расписанию
|
||||||
|
scheduleUpdatedByPlace: bool // нет
|
||||||
|
sessionId: str // sessionId
|
||||||
|
sessionIds: [ // sessionIds
|
||||||
|
{
|
||||||
|
id: str // id сессии
|
||||||
|
startTime: str // Дата время начала
|
||||||
|
endTime: str // Дата время окончания
|
||||||
|
timezone: str // Временная зона
|
||||||
|
isAllDay: bool // Длитяс ли весь день
|
||||||
|
}
|
||||||
|
]
|
||||||
|
social_buttons: list // Кнопки социальных сетей
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
tags: list // Тэги
|
||||||
|
tags_main_screen: list // Тэг главного экрана
|
||||||
|
ticket_price: str // Стоимость билета
|
||||||
|
timetable_by_place: [ // Расписание и сессии по площадкам(Yandex Afisha)
|
||||||
|
{
|
||||||
|
placeId: str // id места (справочник places)
|
||||||
|
sessionIds: [
|
||||||
|
{
|
||||||
|
id: str // id сессии
|
||||||
|
startTime: str // Дата время начала
|
||||||
|
endTime: str // Дата время окончания
|
||||||
|
timezone: str // Временная зона
|
||||||
|
isAllDay: bool // Длитяс ли весь день
|
||||||
|
}
|
||||||
|
]
|
||||||
|
schedule: [ // Расписание
|
||||||
|
{
|
||||||
|
title: str // заголовок
|
||||||
|
start: {
|
||||||
|
$date: str // Дата и время начала
|
||||||
|
}
|
||||||
|
end: {
|
||||||
|
$date: str // Дата и время окончания
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
title: str // Заголовок
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
without_schedule: bool // Событие без расписания
|
||||||
|
ya_id: str // Идентификатор города в справочнике на стороне Яндекс
|
||||||
|
youtube_video_url: str // Ссылка на видео в Youtube
|
||||||
|
youtube_videos: list // YouTube ролики
|
||||||
|
}
|
||||||
|
}
|
1
data/excursii.json
Normal file
1
data/excursii.json
Normal file
File diff suppressed because one or more lines are too long
100
data/excursions_description.txt
Normal file
100
data/excursions_description.txt
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
},
|
||||||
|
dictionary_data: {
|
||||||
|
audioguide: bool // наличие аудиогида
|
||||||
|
campaign: list // id кампании (акции)
|
||||||
|
city: str // идентификатор города
|
||||||
|
description: str // описание
|
||||||
|
duration: str // длительность минуты
|
||||||
|
duration_hours: float // длительность часы
|
||||||
|
duration_string: str // длительность часы
|
||||||
|
event_type: str // тип события
|
||||||
|
excursion_form: str // Способ передвижения
|
||||||
|
excursion_type: str // Тип экскурсии
|
||||||
|
html_file: list // html файл
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью картитнки
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Список изображений
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
information_pages: list // Информационные страницы
|
||||||
|
is_can_buy: bool // Возможность покупки
|
||||||
|
is_priority: bool // Признак приоритета
|
||||||
|
language: list // Язык проведения
|
||||||
|
min_age: str // Минимальный возраст
|
||||||
|
minGroupCount: str // Минимальное количество группы
|
||||||
|
parser_source: str // Источник импорта
|
||||||
|
partner: str // Партнер
|
||||||
|
partner_logo: list // Логотипы партнёра
|
||||||
|
pravila-soglasovaniya: bool // Правила согласования
|
||||||
|
price: int // Цена от
|
||||||
|
program: str // Программа экскурсий
|
||||||
|
region: str // Регион
|
||||||
|
route: [ // Основные события
|
||||||
|
{
|
||||||
|
title: str // заголовок
|
||||||
|
day_contents: { // события, посещения внутри дня
|
||||||
|
dictionaries: list // Используемые словари
|
||||||
|
selected: [ // события / рестораны и прочие участвующие
|
||||||
|
{
|
||||||
|
id: str
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
]
|
||||||
|
active: list // список активностей
|
||||||
|
}
|
||||||
|
events: [ // список событий
|
||||||
|
{
|
||||||
|
title: str
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
geo_data: {
|
||||||
|
coordinates: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
russpass_recommendation: bool // Признак значка "RUSSPASS рекомендует" true / false
|
||||||
|
schedule_excursion: str // Расписание проведения экскурсии
|
||||||
|
season_end: date // Дата конца сезона
|
||||||
|
season_start: date // Дата начала сезона
|
||||||
|
short_description: str // Краткое описание экскурсий
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
tags: list // Тэги дополнительные
|
||||||
|
tags_main_screen: list // Тэг главного экрана
|
||||||
|
title: str // Заголовок
|
||||||
|
tour_composition: [ // Состав экскурсии
|
||||||
|
{
|
||||||
|
title: str
|
||||||
|
list: list
|
||||||
|
}
|
||||||
|
]
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
without_schedule: bool // Признак, показывающий, отсутствует расписание у активности: true, false
|
||||||
|
}
|
||||||
|
}
|
BIN
data/ext.zip
Normal file
BIN
data/ext.zip
Normal file
Binary file not shown.
104
data/hotels_description.txt
Normal file
104
data/hotels_description.txt
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
_id:{
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
},
|
||||||
|
dictionary_data: {
|
||||||
|
address: str // адрес отеля
|
||||||
|
arrival_time: str // Время заезда
|
||||||
|
beach_services: list // Список услуг отеля связанных с пляжным отдыхом
|
||||||
|
city: str // Город
|
||||||
|
common_services: list // Список общих услуг отеля
|
||||||
|
departure_time: str // Время выезда
|
||||||
|
description: str // Описание отеля
|
||||||
|
email: str // Контактный email
|
||||||
|
entertainment_services: list // Список услуг отеля связанных с развлечением
|
||||||
|
facility_services: list // Список общих удобств отеля
|
||||||
|
fitness_services: list // Список услуг отеля связанных со здоровьем и красотой
|
||||||
|
geo_data: {
|
||||||
|
center_distance: float // расстояние до центра города
|
||||||
|
coordinates: [ // Координаты (широта и долгота)
|
||||||
|
#.##, // широта
|
||||||
|
#.## // долгота
|
||||||
|
]
|
||||||
|
type: str // Тип координат
|
||||||
|
}
|
||||||
|
google_place: { // информация из Google
|
||||||
|
meta: { // доп информация
|
||||||
|
formatted_address: str // Адрес Google
|
||||||
|
update_time: str // дата и время последнего обновления
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hotel_rooms: { // наличие типов комнат
|
||||||
|
active: list // список активных комнат
|
||||||
|
dictionaries: list // id словаря который описывает комнаты
|
||||||
|
selected: [ // все доступные комнаты
|
||||||
|
{
|
||||||
|
id: str
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображения
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
import_denied_reason: str // Причина отклонения модератором
|
||||||
|
integration_id: str // id интеграции
|
||||||
|
meals: list // услуги питания
|
||||||
|
parser_source: str // Условный код внешней системы откуда было загружено событие Yandex Afisha – ya/afisha; infotech – INFOTECH; LeClick– leclick/restaurants; LKP Личный кабинет партнёров
|
||||||
|
partner: str // id партнёра
|
||||||
|
partner_is_not_moderated: bool // Партнёр не проходил модерацию
|
||||||
|
partner_system_type: str // Наименование системы партнера
|
||||||
|
phones: list // Контактные телефоны
|
||||||
|
region: str // Регион
|
||||||
|
rooms: [ // Список объектов содержащий информацию о номерах
|
||||||
|
{
|
||||||
|
name: str // Название комнат
|
||||||
|
description: str // Описание комнат
|
||||||
|
images: list // Изображения комнат
|
||||||
|
integration_id: str // id интеграции
|
||||||
|
rate_plans: [ // Тарифы комнаты
|
||||||
|
{
|
||||||
|
integration_id: str // id интеграции
|
||||||
|
name: str // Наименование тарифа
|
||||||
|
description: str // Описание тарифа
|
||||||
|
currency: str // Валюта тарифа
|
||||||
|
services: list // Услуги тарифа
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
russpass_recommendation: bool // Признак значка "RUSSPASS рекомендует" true / false
|
||||||
|
services: [ // Услуги отеля
|
||||||
|
{
|
||||||
|
name: str // Название услуги
|
||||||
|
description: str // Описание услуги
|
||||||
|
kind: str // Тип услуги
|
||||||
|
mealPlanCode: str // Код услуги питания
|
||||||
|
integration_id: str // id интеграции
|
||||||
|
}
|
||||||
|
]
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
stars: str // Звёзд отеля
|
||||||
|
time_zone: str // Временная зона
|
||||||
|
title: str // Название
|
||||||
|
}
|
||||||
|
}
|
1
data/images.json
Normal file
1
data/images.json
Normal file
File diff suppressed because one or more lines are too long
1
data/kino.json
Normal file
1
data/kino.json
Normal file
File diff suppressed because one or more lines are too long
1
data/museum.json
Normal file
1
data/museum.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[{"_id": {"$oid": "64107fe599c33f2daae19203"}, "dictionary_data": {"youtube_videos": [], "russpass_recommendation": false, "aac_images": [], "parser_source": "LKP", "disable_sessionids_autoupdate": false, "information_pages": [], "type_audio_guide": [], "sessionIds": [], "social_buttons": [], "partner": "63ff5a8fd40834b15998dcf9", "images": [], "image_explore_preview": [{"source": {"id": "6422a74bcfc508abfb3fe7e8"}}, {"source": {"id": "6422a750cfc508abfb3fe7f8"}}, {"source": {"id": "6422a758cfc508abfb3fe804"}}], "image_detailed_page_main": [], "tags_main_screen": [], "aac_tags": [], "duration": "60", "ticket_price": "500", "is_can_buy": true, "galereya": [], "recommendation_prime": [], "age": "0+", "timetable_by_place": [], "schedule": [], "without_schedule": false, "description": "\u0412 \u00ab\u041e\u0442\u043a\u0440\u044b\u0442\u043e\u0439 \u043f\u0430\u0440\u0444\u044e\u043c\u0435\u0440\u043d\u043e\u0439 \u043b\u0430\u0431\u043e\u0440\u0430\u0442\u043e\u0440\u0438\u0438\u00bb \u0434\u0443\u0445\u0438 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0438\u0441\u0442\u043e\u0440\u0438\u0438, \u043d\u043e \u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u0430\u043c\u0438 \u0438 \u0433\u0435\u0440\u043e\u044f\u043c\u0438 \u0441\u043f\u0435\u043a\u0442\u0430\u043a\u043b\u0435\u0439. \u0422\u0430\u043a\u0438\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f - \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u0438\u0437\u043c\u0443 \u0430\u0440\u043e\u043c\u0430\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0438\u0440\u0443\u044e\u0442 \u0441\u0430\u043c\u044b\u0435 \u043f\u043e\u0442\u0430\u0451\u043d\u043d\u044b\u0435 \u0437\u043e\u043d\u044b \u0434\u0443\u0448\u0438 \u0438 \u0442\u0435\u043b\u0430, \u043f\u0440\u043e\u0431\u0443\u0436\u0434\u0430\u044f \u0444\u0430\u043d\u0442\u0430\u0437\u0438\u044e, \u043e\u0436\u0438\u0432\u043b\u044f\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0431\u043e\u043d\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0435, \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0435, \u0441\u043b\u0443\u0445\u043e\u0432\u044b\u0435 \u0438 \u0442\u0430\u043a\u0442\u0438\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0437\u044b. \u041f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0430\u0440\u0444\u044e\u043c\u0435\u0440\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0441\u0442\u0432\u0443\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044e \u043a\u0440\u0443\u0433\u043e\u0437\u043e\u0440\u0430 \u0438 \u0441\u043f\u0435\u043a\u0442\u0440\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043c\u0438\u0440\u043e\u043c \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u043c. ", "is_priority": false, "tags": [], "cg_recommendations": [], "place": ["641081162a94483af923a496"], "restaurants": [], "city": "5bbf0e5f729bd4000fe02d5e", "event_type": "63eb49a688c02c9c01208394", "title": "\u0422\u0435\u0430\u0442\u0440 \u0430\u0440\u043e\u043c\u0430\u0442\u043e\u0432", "import_denied": false}}]
|
1
data/only_cords.json
Normal file
1
data/only_cords.json
Normal file
File diff suppressed because one or more lines are too long
15392
data/osm/apartment.json
Normal file
15392
data/osm/apartment.json
Normal file
File diff suppressed because it is too large
Load Diff
113888
data/osm/artwork.json
Normal file
113888
data/osm/artwork.json
Normal file
File diff suppressed because it is too large
Load Diff
803412
data/osm/attraction.json
Normal file
803412
data/osm/attraction.json
Normal file
File diff suppressed because it is too large
Load Diff
100900
data/osm/bars.json
Normal file
100900
data/osm/bars.json
Normal file
File diff suppressed because it is too large
Load Diff
905482
data/osm/cafes.json
Normal file
905482
data/osm/cafes.json
Normal file
File diff suppressed because it is too large
Load Diff
122011
data/osm/chalet.json
Normal file
122011
data/osm/chalet.json
Normal file
File diff suppressed because it is too large
Load Diff
6834
data/osm/gallery.json
Normal file
6834
data/osm/gallery.json
Normal file
File diff suppressed because it is too large
Load Diff
64484
data/osm/hostel.json
Normal file
64484
data/osm/hostel.json
Normal file
File diff suppressed because it is too large
Load Diff
195593
data/osm/hostel_2.json
Normal file
195593
data/osm/hostel_2.json
Normal file
File diff suppressed because it is too large
Load Diff
694300
data/osm/hotels.json
Normal file
694300
data/osm/hotels.json
Normal file
File diff suppressed because it is too large
Load Diff
41589
data/osm/motel.json
Normal file
41589
data/osm/motel.json
Normal file
File diff suppressed because it is too large
Load Diff
264923
data/osm/museum.json
Normal file
264923
data/osm/museum.json
Normal file
File diff suppressed because it is too large
Load Diff
34692
data/osm/other.json
Normal file
34692
data/osm/other.json
Normal file
File diff suppressed because it is too large
Load Diff
316960
data/osm/restaurants.json
Normal file
316960
data/osm/restaurants.json
Normal file
File diff suppressed because it is too large
Load Diff
3067934
data/osm/shops.json
Normal file
3067934
data/osm/shops.json
Normal file
File diff suppressed because it is too large
Load Diff
74340
data/osm/theme_park.json
Normal file
74340
data/osm/theme_park.json
Normal file
File diff suppressed because it is too large
Load Diff
79998
data/osm/viewpoint.json
Normal file
79998
data/osm/viewpoint.json
Normal file
File diff suppressed because it is too large
Load Diff
34176
data/osm/zoo.json
Normal file
34176
data/osm/zoo.json
Normal file
File diff suppressed because it is too large
Load Diff
757844
data/places.json
Normal file
757844
data/places.json
Normal file
File diff suppressed because one or more lines are too long
278
data/places_description.txt
Normal file
278
data/places_description.txt
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
address: str // адрес места
|
||||||
|
cg_recommendations: list // CG: Рекомендации членов клуба Prime
|
||||||
|
chain: list // Сеть
|
||||||
|
city: list // Город
|
||||||
|
country: list // идентификатор страны (дублирует strana)
|
||||||
|
creator: str // Кто создал запись
|
||||||
|
description: str // Описание места
|
||||||
|
district: list // Список районов из выборки
|
||||||
|
emails: list[str] // Контактные email-ы
|
||||||
|
exter_spot_id: int // Идентификатор площадки в ЕБС
|
||||||
|
external_id: str // id места из внешней системы
|
||||||
|
geo_data: {
|
||||||
|
center_distance: float // расстояние до центра города
|
||||||
|
coordinates: [ // Координаты (широта и долгота)
|
||||||
|
#.##, // широта
|
||||||
|
#.## // долгота
|
||||||
|
]
|
||||||
|
type: str // Тип координат
|
||||||
|
}
|
||||||
|
google_place: { // Google Place
|
||||||
|
meta: {
|
||||||
|
formatted_address: str // Адрес Google
|
||||||
|
international_phone_number: str // Номер телефона
|
||||||
|
place_id: str // id места из внешней системы
|
||||||
|
update_time: str // дата и время последнего обновления
|
||||||
|
vicinity: str // "около"
|
||||||
|
website: str // сайт места
|
||||||
|
}
|
||||||
|
schedule_google: { // Расписание google
|
||||||
|
all: {
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
days: { // Объекты дней недели
|
||||||
|
Sun: { // Воскресенье
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Mon: { // Понедельник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Tue: { // Вторник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Wed: { // Среда
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Thu: { // Четверг
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Fri: { // Пятница
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Sat: { // Суббота
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use_manual_schedule: bool // Применять ручное расписание
|
||||||
|
}
|
||||||
|
holding: str // Холдинг
|
||||||
|
hotels_types: list // Тип отеля
|
||||||
|
hp_images: list // HP Изображения
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображения
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
import_denied_reason: str // Причина отклонения модератором
|
||||||
|
is_showed_in_placeslist: bool // Отображать в списке площадок
|
||||||
|
metro: list // список id станций метро
|
||||||
|
parser_source: str // Условный код внешней системы откуда было загружено событие Yandex Afisha – ya/afisha; infotech – INFOTECH; LeClick– leclick/restaurants; LKP Личный кабинет партнёров
|
||||||
|
partner: list // id партнёра
|
||||||
|
partner_subtype: list // Подтип партнера ПП
|
||||||
|
partner_system: list // Партнерская система
|
||||||
|
partner_type: str // Тип партнера
|
||||||
|
phones: list // Контактные телефоны
|
||||||
|
place_note: str // Доп информация
|
||||||
|
post_code: str // Почтовый индекс
|
||||||
|
pravila-soglasovaniya: bool // Правила согласования
|
||||||
|
privileges_prime: list // Привилегии Prime
|
||||||
|
promo: list // Условный код промо слова
|
||||||
|
region: str // Регион
|
||||||
|
remote_place_id: str // нет
|
||||||
|
route_description: str // Описание для пути, проходящего
|
||||||
|
short_title: str // Короткое название
|
||||||
|
sites: list // web сайты
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
source: str // Источник данных
|
||||||
|
strana: list // идентификатор страны (дублирует country)
|
||||||
|
tags: list // Тэги дополнительные
|
||||||
|
tags_main_screen: list // Тэги главного экрана
|
||||||
|
testovaya-ploshadkakf: str // Тестовая площадкаКФ
|
||||||
|
tet: { // Тет
|
||||||
|
coordinates: list
|
||||||
|
}
|
||||||
|
title: str // Название
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
working_time: { // График работы площадки
|
||||||
|
all: {
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
days: { // Объекты дней недели
|
||||||
|
Sun: { // Воскресенье
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Mon: { // Понедельник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Tue: { // Вторник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Wed: { // Среда
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Thu: { // Четверг
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Fri: { // Пятница
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Sat: { // Суббота
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
working_time_description: str // Примечание ко времени работы
|
||||||
|
ya_afisha_id: str // Yandex Afisha ID
|
||||||
|
}
|
||||||
|
}
|
18430
data/regions.json
Normal file
18430
data/regions.json
Normal file
File diff suppressed because it is too large
Load Diff
54
data/regions_description.txt
Normal file
54
data/regions_description.txt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
city: list // Список городов
|
||||||
|
coat_of_arms: list // Герб (аналог: сoat_of_arms)
|
||||||
|
code_ott_avia_from: str // Код региона откуда (города) в onetwotrip для авиабилетов
|
||||||
|
code_ott_avia_to: str // Код региона куда (города) в onetwotrip для авиабилетов
|
||||||
|
code_ott_rzd_from: str // Код региона откуда (города) в onetwotrip жд билетов
|
||||||
|
code_ott_rzd_to: str // Код региона куда (города) в onetwotrip жд билетов
|
||||||
|
country: str // Страна
|
||||||
|
description: str // Детальное описание
|
||||||
|
description_title: str // Заголовок детального описания
|
||||||
|
external_id: str // Внешний ID
|
||||||
|
gallery: list // Галерея изображений
|
||||||
|
icon: [ // Иконка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Основное изображение
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
map_info: str // Информация для карты
|
||||||
|
on_map: [ // Регион на карте
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
price_hotel: int // Цена билета за ночь (отель)
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
showcase_cards: list // Активности на витрине региона
|
||||||
|
showcase_description: str // Описание на витрине региона
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
time_zone: list // Часовой пояс
|
||||||
|
title: str // Заголовок
|
||||||
|
title_accusative: str // Название в винительном падеже
|
||||||
|
title_dative: str // Название в дательном падеже
|
||||||
|
title_genitive: str // Название в родительном падеже
|
||||||
|
travel_line_id: str // TravelLine ID
|
||||||
|
url: str // Официальный адрес сайт региона/ОИВа
|
||||||
|
сoat_of_arms: list // Герб (аналог: coat_of_arms)
|
||||||
|
}
|
||||||
|
}
|
261892
data/restaurants.json
Normal file
261892
data/restaurants.json
Normal file
File diff suppressed because it is too large
Load Diff
288
data/restaurants_description.txt
Normal file
288
data/restaurants_description.txt
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
_id: str // нет
|
||||||
|
address: str // Адрес
|
||||||
|
avg_price_level: str // Уровень цен
|
||||||
|
avg_time_visit: int // Среднее время посещения, мин
|
||||||
|
bill: int // Средний чек
|
||||||
|
can_reserve: bool // Доступен для бронирования
|
||||||
|
cg_recommendations: list // Рекомендации членов клуба Prime
|
||||||
|
chain: list // Сеть
|
||||||
|
children: list // Дети
|
||||||
|
city: list // Город
|
||||||
|
country: list // Страна
|
||||||
|
cuisines: list // Кухни
|
||||||
|
description: str // Описание
|
||||||
|
district: list // Район
|
||||||
|
emails: list // Электронные почты
|
||||||
|
external_id: str // Внешний ID
|
||||||
|
external_status: int // нет
|
||||||
|
geo_data: {
|
||||||
|
center_distance: float // расстояние до центра города
|
||||||
|
coordinates: [ // Координаты (широта и долгота)
|
||||||
|
#.##, // широта
|
||||||
|
#.## // долгота
|
||||||
|
]
|
||||||
|
type: str // Тип координат
|
||||||
|
}
|
||||||
|
google_place: { // Google Place
|
||||||
|
meta: {
|
||||||
|
formatted_address: str // Адрес Google
|
||||||
|
international_phone_number: str // Номер телефона
|
||||||
|
place_id: str // id места из внешней системы
|
||||||
|
update_time: str // дата и время последнего обновления
|
||||||
|
vicinity: str // "около"
|
||||||
|
website: str // сайт места
|
||||||
|
}
|
||||||
|
schedule_google: { // Расписание google
|
||||||
|
all: {
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
days: { // Объекты дней недели
|
||||||
|
Sun: { // Воскресенье
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Mon: { // Понедельник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Tue: { // Вторник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Wed: { // Среда
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Thu: { // Четверг
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Fri: { // Пятница
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Sat: { // Суббота
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use_manual_schedule: bool // Применять ручное расписание
|
||||||
|
}
|
||||||
|
guides: list // Гиды
|
||||||
|
holding: str // Холдинг
|
||||||
|
hp_images: list // Изображения Hotel Prime
|
||||||
|
icons: [ // Тэги ресторана (Особенности)
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображения
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images360: list // 360 изображения
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
menu_type: list // Тип меню
|
||||||
|
metro: list // Метро
|
||||||
|
parser_source: str // Источник импорта
|
||||||
|
partner: str // Партнер
|
||||||
|
partner_subtype: list // Подтип партнера ПП
|
||||||
|
partner_type: list // Тип партнера
|
||||||
|
phones: list // Телефоны
|
||||||
|
post_code: str // Почтовый индекс
|
||||||
|
promo: list // Промоакции
|
||||||
|
rating_prime: str // Рейтинг Prime
|
||||||
|
region: str // Регион
|
||||||
|
remote_id: str // ID Ресторана
|
||||||
|
remote_status: int // нет
|
||||||
|
rest_id: str // нет
|
||||||
|
rest_services: list // Услуги ресторанов
|
||||||
|
route_description: str // Как добраться
|
||||||
|
russpass_recommendation: bool // RUSSPASS рекомендует
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
showcase_filter: list // Фильтрация по витринам
|
||||||
|
sites: list // web сайты
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
source: str // нет
|
||||||
|
subcategories: list // Подкатегория
|
||||||
|
tags: list // Тэги основные (фильтр)
|
||||||
|
tags_main_screen: list // Тэг главного экрана
|
||||||
|
title: str // Название
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
working_time: { // График работы площадки
|
||||||
|
all: {
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
days: { // Объекты дней недели
|
||||||
|
Sun: { // Воскресенье
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Mon: { // Понедельник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Tue: { // Вторник
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Wed: { // Среда
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Thu: { // Четверг
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Fri: { // Пятница
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
Sat: { // Суббота
|
||||||
|
breaks: [ // перерывы
|
||||||
|
{
|
||||||
|
startTime: str // время начала перерыва
|
||||||
|
endTime: str // время конца перерыва
|
||||||
|
}
|
||||||
|
]
|
||||||
|
closed: bool // Закрыта ли на целый день активность или нет; true - закрыто
|
||||||
|
endTime: str // Время окончания работы
|
||||||
|
startTime: str // Время начала работы
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
working_time_description: str // Примечание ко времени работы
|
||||||
|
}
|
||||||
|
}
|
670
data/routes.json
Normal file
670
data/routes.json
Normal file
|
@ -0,0 +1,670 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"_id": {
|
||||||
|
"$oid": "63ce792cb792256419d55e28"
|
||||||
|
},
|
||||||
|
"dictionary_data": {
|
||||||
|
"type_audio_guide": [],
|
||||||
|
"region": "5d08e36dad3a9a001701b95b",
|
||||||
|
"information_pages": [
|
||||||
|
"5ed7a8a9070b700019e2d3cc",
|
||||||
|
"5ed7aaed7437c000194f1972"
|
||||||
|
],
|
||||||
|
"image_explore_preview": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce70b1b792256419d51be2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image_detailed_page_main": [],
|
||||||
|
"tags_main_screen": [
|
||||||
|
"5e4aa3aca7edd0687c4688e1"
|
||||||
|
],
|
||||||
|
"rp_price_id": 39665,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce7095b792256419d51b90"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce7099b792256419d51b94"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce709db792256419d51ba7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce70a2b792256419d51baa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce70a6b792256419d51bbf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce70a9b792256419d51bca"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "63ce70adb792256419d51bd8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"route": {
|
||||||
|
"dictionaries": [
|
||||||
|
"5a673061e97c730010ac6281"
|
||||||
|
],
|
||||||
|
"selected": [
|
||||||
|
{
|
||||||
|
"id": "5f146cfa448801001847422d",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5f2bfb47a0eff200185761cc",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e67af0ea4ba9e001a93240f",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a45826e00001944d0ee",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561770826e00001944d02c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619e8826e00001944d0d0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5617c6826e00001944d042",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619a6826e00001944d0c2",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": [
|
||||||
|
"5f146cfa448801001847422d",
|
||||||
|
"5f2bfb47a0eff200185761cc",
|
||||||
|
"5e67af0ea4ba9e001a93240f",
|
||||||
|
"5e561a45826e00001944d0ee",
|
||||||
|
"5e561770826e00001944d02c",
|
||||||
|
"5e5619e8826e00001944d0d0",
|
||||||
|
"5e5617c6826e00001944d042",
|
||||||
|
"5e5619a6826e00001944d0c2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "Moscow CityPass Express ― это новый формат карты гостя столицы. В ее стоимость включены увлекательные экскурсии и бесплатное посещение интереснейших мест города, а срок действия составляет целых 30 дней. Туристы смогут познакомиться с главными достопримечательностями Москвы во время путешествия на двухэтажном автобусе, побывать на стадионе «Лужники» и прокатиться по канатной дороге, полюбоваться панорамными видами на мегаполис со смотровой площадки в сталинской высотке, прогуляться по «Зарядью» в компании гида и посетить музеи, которые известны на весь мир. Получить карту Moscow CityPass Express можно в Музее советских игровых автоматов.\n",
|
||||||
|
"sort": 60,
|
||||||
|
"time": "90",
|
||||||
|
"packet_price": 5100,
|
||||||
|
"route_tags": [],
|
||||||
|
"city": "5bbe8a8a9e7fcb000f78a92d",
|
||||||
|
"import_denied": false,
|
||||||
|
"title": "Moscow CityPass Express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": {
|
||||||
|
"$oid": "5e58fffb7849960019a7e18a"
|
||||||
|
},
|
||||||
|
"dictionary_data": {
|
||||||
|
"is_priority": false,
|
||||||
|
"is_can_buy": false,
|
||||||
|
"type_audio_guide": [],
|
||||||
|
"region": "5d08e36dad3a9a001701b95b",
|
||||||
|
"information_pages": [
|
||||||
|
"5ed7a8a9070b700019e2d3cc",
|
||||||
|
"5ed7aaed7437c000194f1972"
|
||||||
|
],
|
||||||
|
"short_description": "Пластиковая смарт карта Moscow CityPass откроет двери всех главных достопримечательностей столицы! ",
|
||||||
|
"image_explore_preview": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "623982ee994c9997315b6917"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image_detailed_page_main": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "705a663b-41c4-430f-abf9-814f493c0c52",
|
||||||
|
"avg": "rgba(125, 136, 148, 0.4)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags_main_screen": [
|
||||||
|
"5e4aa3aca7edd0687c4688e1"
|
||||||
|
],
|
||||||
|
"billing_product_id": 3507,
|
||||||
|
"rp_price_id": 6301,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "635fce9512bcf2bb9a7508b9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "635fce9912bcf2bb9a7508d3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"route": {
|
||||||
|
"active": [
|
||||||
|
"5e561615826e00001944cfe2",
|
||||||
|
"5e561627826e00001944cfe6",
|
||||||
|
"5e561760826e00001944d027",
|
||||||
|
"5e561768826e00001944d029",
|
||||||
|
"5e561770826e00001944d02c",
|
||||||
|
"5e561784826e00001944d030",
|
||||||
|
"5e561815826e00001944d067",
|
||||||
|
"5e56181d826e00001944d069",
|
||||||
|
"5e56193a826e00001944d0ac",
|
||||||
|
"5e5617b9826e00001944d03e",
|
||||||
|
"5e5617e5826e00001944d04b",
|
||||||
|
"5e561800826e00001944d05e",
|
||||||
|
"5e5617c6826e00001944d042",
|
||||||
|
"5e561948826e00001944d0b0",
|
||||||
|
"5e561964826e00001944d0b4",
|
||||||
|
"5e56169d826e00001944cfff",
|
||||||
|
"5e5619ad826e00001944d0c4",
|
||||||
|
"5e5619f0826e00001944d0d2",
|
||||||
|
"5e5619a6826e00001944d0c2",
|
||||||
|
"5e56199a826e00001944d0c0",
|
||||||
|
"5e5618b5826e00001944d08b",
|
||||||
|
"5e561729826e00001944d01c",
|
||||||
|
"5e561950826e00001944d0b2",
|
||||||
|
"5e5619e8826e00001944d0d0",
|
||||||
|
"5e5616b4826e00001944d005",
|
||||||
|
"5e5616ac826e00001944d003",
|
||||||
|
"5e5616a5826e00001944d001",
|
||||||
|
"5e561941826e00001944d0ae",
|
||||||
|
"5e5616e6826e00001944d00f",
|
||||||
|
"5e561636826e00001944cfea",
|
||||||
|
"5e5619d9826e00001944d0cc",
|
||||||
|
"5e5619c4826e00001944d0ca",
|
||||||
|
"5e5619bc826e00001944d0c8",
|
||||||
|
"5e5619b4826e00001944d0c6",
|
||||||
|
"5e56196c826e00001944d0b6",
|
||||||
|
"5e561974826e00001944d0b8",
|
||||||
|
"5e56197b826e00001944d0ba",
|
||||||
|
"5e56198a826e00001944d0be",
|
||||||
|
"5e561898826e00001944d087",
|
||||||
|
"5e56171a826e00001944d017",
|
||||||
|
"5e561982826e00001944d0bc",
|
||||||
|
"5e561a38826e00001944d0ec",
|
||||||
|
"5e561a4e826e00001944d0f0",
|
||||||
|
"5e561759826e00001944d025",
|
||||||
|
"5e561a45826e00001944d0ee",
|
||||||
|
"5e561a5e826e00001944d0f4",
|
||||||
|
"5e561a56826e00001944d0f2",
|
||||||
|
"5e561a26826e00001944d0e8"
|
||||||
|
],
|
||||||
|
"dictionaries": [
|
||||||
|
"5bb51b424390424300d06a41",
|
||||||
|
"5a673061e97c730010ac6281"
|
||||||
|
],
|
||||||
|
"selected": [
|
||||||
|
{
|
||||||
|
"id": "5e561615826e00001944cfe2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561627826e00001944cfe6",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561760826e00001944d027",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561768826e00001944d029",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5617a2826e00001944d03a",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561770826e00001944d02c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561784826e00001944d030",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561815826e00001944d067",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56181d826e00001944d069",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56193a826e00001944d0ac",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5617b9826e00001944d03e",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5617e5826e00001944d04b",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561800826e00001944d05e",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5617c6826e00001944d042",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561948826e00001944d0b0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561964826e00001944d0b4",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56169d826e00001944cfff",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619ad826e00001944d0c4",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619f0826e00001944d0d2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619a6826e00001944d0c2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56199a826e00001944d0c0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5618b5826e00001944d08b",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561729826e00001944d01c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561950826e00001944d0b2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619e8826e00001944d0d0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5616b4826e00001944d005",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5616ac826e00001944d003",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5616a5826e00001944d001",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561941826e00001944d0ae",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5616e6826e00001944d00f",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561636826e00001944cfea",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619d9826e00001944d0cc",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619c4826e00001944d0ca",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619bc826e00001944d0c8",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619b4826e00001944d0c6",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56196c826e00001944d0b6",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561974826e00001944d0b8",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56197b826e00001944d0ba",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56198a826e00001944d0be",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561898826e00001944d087",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56171a826e00001944d017",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561982826e00001944d0bc",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5619e1826e00001944d0ce",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a38826e00001944d0ec",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a4e826e00001944d0f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561759826e00001944d025",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a45826e00001944d0ee",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a5e826e00001944d0f4",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a56826e00001944d0f2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561a26826e00001944d0e8",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e56162f826e00001944cfe8",
|
||||||
|
"active": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "Пластиковая смарт карта Moscow CityPass откроет двери всех главных достопримечательностей столицы! Экскурсии по суше и воде, проход в музеи без очередей, скидки в ресторанах и 15 поездок на общественном транспорте — и все это бесплатно! В турпакет включены более 40 достопримечательностей и 6 экскурсионных туров разных форматов. Скучать не придется! \n\nИспользуя карту Moscow CityPass, вы можете посетить каждый объект программы, но не более одного раза, в течение срока действия карты (кроме билетов на City Sightseeing Bus. Они бывают нескольких видов на 24, 48 и 72 часа. Вы можете выходить и заходить обратно на любой из остановок неограниченное количество раз в течение срока действия билета).",
|
||||||
|
"sort": 96,
|
||||||
|
"time": "48",
|
||||||
|
"packet_price": 6500,
|
||||||
|
"route_tags": [
|
||||||
|
"5e4fa298d067450019abf839",
|
||||||
|
"5e4f9f18d067450019abf806",
|
||||||
|
"5e4fa278d067450019abf834",
|
||||||
|
"5e4fa0a4d067450019abf81c",
|
||||||
|
"5ec3ee769bee8c001946a9a7",
|
||||||
|
"5ec3ee40bfdeb400197cbe8e"
|
||||||
|
],
|
||||||
|
"city": "5bbe8a8a9e7fcb000f78a92d",
|
||||||
|
"import_denied": false,
|
||||||
|
"title": "Moscow CityPass на 2 дня"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": {
|
||||||
|
"$oid": "5e5902357849960019a7e25e"
|
||||||
|
},
|
||||||
|
"dictionary_data": {
|
||||||
|
"type_audio_guide": [],
|
||||||
|
"region": "5d08e36dad3a9a001701b94c",
|
||||||
|
"information_pages": [
|
||||||
|
"5ed7a8a9070b700019e2d3cc",
|
||||||
|
"5f132560448801001846dfc0"
|
||||||
|
],
|
||||||
|
"service_count": null,
|
||||||
|
"activity_count": null,
|
||||||
|
"short_description": "Карта гостя Санкт-Петербурга, ключ к главным достопримечательностям города и не только!",
|
||||||
|
"image_explore_preview": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "6351452112bcf2bb9a0c8e7c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image_detailed_page_main": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "5f16ea774488010018484d59"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags_main_screen": [
|
||||||
|
"5e4aa3aca7edd0687c4688e1"
|
||||||
|
],
|
||||||
|
"rp_price_id": 7101,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "635fc95f12bcf2bb9a748e5c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"id": "635fc96412bcf2bb9a748e60"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"route": {
|
||||||
|
"active": [
|
||||||
|
"5c1ce09d514196d20547020e",
|
||||||
|
"5e5615db826e00001944cfd2",
|
||||||
|
"5e5615e9826e00001944cfd6",
|
||||||
|
"5e55a62a826e00001944c86c",
|
||||||
|
"5e5e331d2fc00200193460d3",
|
||||||
|
"5e561606826e00001944cfe0",
|
||||||
|
"5e5615ff826e00001944cfde",
|
||||||
|
"5e5e473d2fc00200193461b6",
|
||||||
|
"5e5e531e2fc00200193461f4",
|
||||||
|
"5e5e49822fc00200193461c2",
|
||||||
|
"5e5e4bf42fc00200193461d1",
|
||||||
|
"5e5e561a2fc002001934620c",
|
||||||
|
"5e5e45052fc00200193461a1",
|
||||||
|
"5e55a346b333bd0019d18d2c",
|
||||||
|
"5e5e29d82fc0020019346078",
|
||||||
|
"5e5615e1826e00001944cfd4",
|
||||||
|
"5e5d676d88e8040019cda132",
|
||||||
|
"5e5e42982fc0020019346184",
|
||||||
|
"5e5615f8826e00001944cfdc",
|
||||||
|
"5e5e6903ccb7de00197f8abd",
|
||||||
|
"5e5e5e45ccb7de00197f8aa3",
|
||||||
|
"5e5e6e30ccb7de00197f8aec",
|
||||||
|
"5e590ac47849960019a7e44b",
|
||||||
|
"5e590a777849960019a7e445",
|
||||||
|
"5e57735d2c129400197af134",
|
||||||
|
"5f130fb1448801001846dec8",
|
||||||
|
"5e5e6fe8ccb7de00197f8af3"
|
||||||
|
],
|
||||||
|
"dictionaries": [
|
||||||
|
"5bb51b424390424300d06a41",
|
||||||
|
"5a673061e97c730010ac6281"
|
||||||
|
],
|
||||||
|
"selected": [
|
||||||
|
{
|
||||||
|
"id": "5c1ce09d514196d20547020e",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e40f22fc0020019346173",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5615db826e00001944cfd2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5615e9826e00001944cfd6",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e55a62a826e00001944c86c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e267b2fc002001934603b",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e2e422fc0020019346090",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e30fc2fc00200193460c8",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e331d2fc00200193460d3",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e561606826e00001944cfe0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5615ff826e00001944cfde",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e473d2fc00200193461b6",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e531e2fc00200193461f4",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e49822fc00200193461c2",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e4bf42fc00200193461d1",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e561a2fc002001934620c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e218d2fc002001934601a",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e45052fc00200193461a1",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e55a346b333bd0019d18d2c",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e29d82fc0020019346078",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5615e1826e00001944cfd4",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5d676d88e8040019cda132",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e42982fc0020019346184",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5615f8826e00001944cfdc",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e6903ccb7de00197f8abd",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e5e45ccb7de00197f8aa3",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e59098f7849960019a7e435",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e6e30ccb7de00197f8aec",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e590ac47849960019a7e44b",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e590a777849960019a7e445",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e57735d2c129400197af134",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5f130fb1448801001846dec8",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e5e6fe8ccb7de00197f8af3",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "St. Petersburg CityPass — это карта гостя Санкт-Петербурга, ключ к главным достопримечательностям города и не только! Комплект состоит из путеводителя с отрывными билетами на двух языках (русский, английский) и карты города. Вас ждет доступ к 23 музеям, 6 увлекательных разноформатных экскурсий и многое другое! Это просто, удобно и экономно! \n\nОбратите внимание, что после приобретения St. Petersburg CityPass, вы получаете путеводитель с отрывными билетами. В течение срока действия CityPass вы можете один раз посетить все музеи и экскурсии (кроме билетов на City Sightseeing Bus. Они бывают нескольких видов на 24, 48 и 72 часа. Вы можете выходить и заходить обратно на любой из остановок неограниченное количество раз в течение срока действия билета). CityPass действует с момента первого прохода в музей, автобус или на борт корабля. Скидками и специальными предложениями можно воспользоваться и после окончания срока действия CityPass!",
|
||||||
|
"sort": 90,
|
||||||
|
"time": "48",
|
||||||
|
"packet_price": 5500,
|
||||||
|
"route_tags": [],
|
||||||
|
"city": "5bbf0e5f729bd4000fe02d5e",
|
||||||
|
"import_denied": false,
|
||||||
|
"pravila-soglasovaniya": false,
|
||||||
|
"title": "St.-Petersburg CityPass на 2 дня"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
56
data/routes_description.txt
Normal file
56
data/routes_description.txt
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
billing_product_id: int // Идентификатор продукта в биллинге
|
||||||
|
city: str // Город
|
||||||
|
description: str // Описание
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображение
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
information_pages: list // Информационные страницы
|
||||||
|
is_can_buy: bool // нет
|
||||||
|
is_priority: bool // нет
|
||||||
|
packet_price: int // Стоимость пакета
|
||||||
|
pravila-soglasovaniya: bool // Правила согласования
|
||||||
|
region: str // Регион
|
||||||
|
route: { // Маршрут
|
||||||
|
active: list // список активностей
|
||||||
|
dictionaries: list // Используемые словари
|
||||||
|
selected: [ // события / рестораны и прочие участвующие
|
||||||
|
{
|
||||||
|
id: str
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
route_tags: list // Тэги маршрута
|
||||||
|
rp_price_id: int // Идентификатор тарифа в биллинге
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
tags_main_screen: list // Тэг главного экрана
|
||||||
|
time: str // Продолжительность, мин
|
||||||
|
title: str // Заголовок
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
}
|
||||||
|
}
|
1
data/spektatli.json
Normal file
1
data/spektatli.json
Normal file
File diff suppressed because one or more lines are too long
1
data/teatr.json
Normal file
1
data/teatr.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[{"_id": {"$oid": "64107fe599c33f2daae19203"}, "dictionary_data": {"youtube_videos": [], "russpass_recommendation": false, "aac_images": [], "parser_source": "LKP", "disable_sessionids_autoupdate": false, "information_pages": [], "type_audio_guide": [], "sessionIds": [], "social_buttons": [], "partner": "63ff5a8fd40834b15998dcf9", "images": [], "image_explore_preview": [{"source": {"id": "6422a74bcfc508abfb3fe7e8"}}, {"source": {"id": "6422a750cfc508abfb3fe7f8"}}, {"source": {"id": "6422a758cfc508abfb3fe804"}}], "image_detailed_page_main": [], "tags_main_screen": [], "aac_tags": [], "duration": "60", "ticket_price": "500", "is_can_buy": true, "galereya": [], "recommendation_prime": [], "age": "0+", "timetable_by_place": [], "schedule": [], "without_schedule": false, "description": "\u0412 \u00ab\u041e\u0442\u043a\u0440\u044b\u0442\u043e\u0439 \u043f\u0430\u0440\u0444\u044e\u043c\u0435\u0440\u043d\u043e\u0439 \u043b\u0430\u0431\u043e\u0440\u0430\u0442\u043e\u0440\u0438\u0438\u00bb \u0434\u0443\u0445\u0438 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0438\u0441\u0442\u043e\u0440\u0438\u0438, \u043d\u043e \u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u0430\u043c\u0438 \u0438 \u0433\u0435\u0440\u043e\u044f\u043c\u0438 \u0441\u043f\u0435\u043a\u0442\u0430\u043a\u043b\u0435\u0439. \u0422\u0430\u043a\u0438\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f - \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u0438\u0437\u043c\u0443 \u0430\u0440\u043e\u043c\u0430\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0438\u0440\u0443\u044e\u0442 \u0441\u0430\u043c\u044b\u0435 \u043f\u043e\u0442\u0430\u0451\u043d\u043d\u044b\u0435 \u0437\u043e\u043d\u044b \u0434\u0443\u0448\u0438 \u0438 \u0442\u0435\u043b\u0430, \u043f\u0440\u043e\u0431\u0443\u0436\u0434\u0430\u044f \u0444\u0430\u043d\u0442\u0430\u0437\u0438\u044e, \u043e\u0436\u0438\u0432\u043b\u044f\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0431\u043e\u043d\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0435, \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0435, \u0441\u043b\u0443\u0445\u043e\u0432\u044b\u0435 \u0438 \u0442\u0430\u043a\u0442\u0438\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0437\u044b. \u041f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0430\u0440\u0444\u044e\u043c\u0435\u0440\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0441\u0442\u0432\u0443\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044e \u043a\u0440\u0443\u0433\u043e\u0437\u043e\u0440\u0430 \u0438 \u0441\u043f\u0435\u043a\u0442\u0440\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043c\u0438\u0440\u043e\u043c \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u043c. ", "is_priority": false, "tags": [], "cg_recommendations": [], "place": ["641081162a94483af923a496"], "restaurants": [], "city": "5bbf0e5f729bd4000fe02d5e", "event_type": "63eb49a688c02c9c01208394", "title": "\u0422\u0435\u0430\u0442\u0440 \u0430\u0440\u043e\u043c\u0430\u0442\u043e\u0432", "import_denied": false}}]
|
20988
data/tours.json
Normal file
20988
data/tours.json
Normal file
File diff suppressed because one or more lines are too long
92
data/tours_description.txt
Normal file
92
data/tours_description.txt
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
city: str // Город
|
||||||
|
complexity: str // Cложность
|
||||||
|
days: int // Количество дней
|
||||||
|
description: str // Описание
|
||||||
|
hotel_stars: str // Звезды отеля
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображения
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
included: str // В стоимость включено
|
||||||
|
is_can_buy: bool // Можно купить
|
||||||
|
language: str // Язык проведения
|
||||||
|
min_age: str // Возрастное ограничение
|
||||||
|
minGroupCount: str // Группа
|
||||||
|
nights: int // Количество ночей
|
||||||
|
paid_separately: str // Оплачивается дополнительно
|
||||||
|
partner: str // Партнер
|
||||||
|
pravila-soglasovaniya: bool // Правила согласования
|
||||||
|
price: str // Стоимость
|
||||||
|
program: str // Программа
|
||||||
|
region: str // Регион
|
||||||
|
route: [ // Программа тура
|
||||||
|
{
|
||||||
|
title: str // заголовок
|
||||||
|
day_contents: { // события, посещения внутри дня
|
||||||
|
dictionaries: list // Используемые словари
|
||||||
|
selected: [ // события / рестораны и прочие участвующие
|
||||||
|
{
|
||||||
|
id: str
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
]
|
||||||
|
active: list // список активностей
|
||||||
|
}
|
||||||
|
events: [ // список событий
|
||||||
|
{
|
||||||
|
title: str
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
geo_data: {
|
||||||
|
coordinates: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
russpass_recommendation: bool // RUSSPASS рекомендует
|
||||||
|
season_end: str // Конец сезона
|
||||||
|
season_start: str // Начало сезона
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
tags: list // Тэги
|
||||||
|
tags_main_screen: list // Тэг главного экрана
|
||||||
|
title: str // Заголовок
|
||||||
|
tour_composition: [ // Состав тура
|
||||||
|
{
|
||||||
|
title: str
|
||||||
|
list: list
|
||||||
|
}
|
||||||
|
]
|
||||||
|
tour_type: str // Тип тура
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
}
|
||||||
|
}
|
29729
data/tracks.json
Normal file
29729
data/tracks.json
Normal file
File diff suppressed because it is too large
Load Diff
78
data/tracks_description.txt
Normal file
78
data/tracks_description.txt
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$oid: str // id объекта коллекции
|
||||||
|
}
|
||||||
|
dictionary_data: {
|
||||||
|
city: str // Город
|
||||||
|
days_count: int // Длительность (дни)
|
||||||
|
description: str // Полное описание
|
||||||
|
duration_hours: int // Общее время прохождения маршрута (часы)
|
||||||
|
image_detailed_page_main: [ // Главная картинка
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
image_explore_preview: [ // Превью для Explore
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
images: [ // Изображения
|
||||||
|
{
|
||||||
|
source: {
|
||||||
|
id: str // id картинки в бд
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
import_denied: bool // Отклонено модератором
|
||||||
|
information_pages: list // Информационные страницы
|
||||||
|
price: int // Стоимость маршрута
|
||||||
|
region: str // Регион
|
||||||
|
route: [ // Маршрут по дням
|
||||||
|
{
|
||||||
|
title: str // заголовок
|
||||||
|
day_contents: { // события, посещения внутри дня
|
||||||
|
dictionaries: list // Используемые словари
|
||||||
|
selected: [ // события / рестораны и прочие участвующие
|
||||||
|
{
|
||||||
|
id: str
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
]
|
||||||
|
active: list // список активностей
|
||||||
|
}
|
||||||
|
recommended_hotels: list
|
||||||
|
descriptions: { // Описания
|
||||||
|
'active_id': { // id из day_contents
|
||||||
|
description: str // Описание для данного элемента маршрута (может быть отлично от данных объекта)
|
||||||
|
duration_custom_for_this_route: int // Продолжительность для данного элемента маршрута (может быть отлична от данных объекта)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
translated_data: {
|
||||||
|
'language': { // 2-х буквенное описание языка
|
||||||
|
descriptions: { // Описания
|
||||||
|
'active_id': { // id из day_contents
|
||||||
|
description: str // Описание для данного элемента маршрута (может быть отлично от данных объекта)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
russpass_recommendation: bool // RUSSPASS рекомендует
|
||||||
|
short_description: str // Краткое описание
|
||||||
|
sort: int // Порядковый номер сортировки
|
||||||
|
tags: list // Тэги
|
||||||
|
tags_main_screen: list // Тэги главного экрана
|
||||||
|
title: str // Заголовок
|
||||||
|
total_activity_price: int // Суммарная цена на активности
|
||||||
|
type: str // Тип маршрута
|
||||||
|
type_audio_guide: list // Тип аудиогида
|
||||||
|
video_link: str // Ссылка на видео
|
||||||
|
video_url_detail: str // Ссылка на видео на странице маршрута
|
||||||
|
}
|
||||||
|
}
|
1
data/yarmarki.json
Normal file
1
data/yarmarki.json
Normal file
File diff suppressed because one or more lines are too long
0
data4.json
Normal file
0
data4.json
Normal file
68
local.yml
Normal file
68
local.yml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
passfinder_local_postgres_data: {}
|
||||||
|
passfinder_local_postgres_data_backups: {}
|
||||||
|
|
||||||
|
services:
|
||||||
|
django: &django
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./compose/local/django/Dockerfile
|
||||||
|
image: passfinder_local_django
|
||||||
|
container_name: passfinder_local_django
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
volumes:
|
||||||
|
- .:/app:z
|
||||||
|
env_file:
|
||||||
|
- ./.envs/.local/.django
|
||||||
|
- ./.envs/.local/.postgres
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
command: /start
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./compose/production/postgres/Dockerfile
|
||||||
|
image: passfinder_production_postgres
|
||||||
|
container_name: passfinder_local_postgres
|
||||||
|
volumes:
|
||||||
|
- passfinder_local_postgres_data:/var/lib/postgresql/data
|
||||||
|
- passfinder_local_postgres_data_backups:/backups
|
||||||
|
env_file:
|
||||||
|
- ./.envs/.local/.postgres
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:6
|
||||||
|
container_name: passfinder_local_redis
|
||||||
|
|
||||||
|
celeryworker:
|
||||||
|
<<: *django
|
||||||
|
image: passfinder_local_celeryworker
|
||||||
|
container_name: passfinder_local_celeryworker
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- postgres
|
||||||
|
ports: []
|
||||||
|
command: /start-celeryworker
|
||||||
|
|
||||||
|
celerybeat:
|
||||||
|
<<: *django
|
||||||
|
image: passfinder_local_celerybeat
|
||||||
|
container_name: passfinder_local_celerybeat
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- postgres
|
||||||
|
ports: []
|
||||||
|
command: /start-celerybeat
|
||||||
|
|
||||||
|
flower:
|
||||||
|
<<: *django
|
||||||
|
image: passfinder_local_flower
|
||||||
|
container_name: passfinder_local_flower
|
||||||
|
ports:
|
||||||
|
- "5555:5555"
|
||||||
|
command: /start-flower
|
6
locale/README.rst
Normal file
6
locale/README.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Translations
|
||||||
|
============
|
||||||
|
|
||||||
|
Translations will be placed in this folder when running::
|
||||||
|
|
||||||
|
python manage.py makemessages
|
31
manage.py
Normal file
31
manage.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError:
|
||||||
|
# The above import may fail for some other reason. Ensure that the
|
||||||
|
# issue is really that Django is missing to avoid masking other
|
||||||
|
# exceptions on Python 2.
|
||||||
|
try:
|
||||||
|
import django # noqa
|
||||||
|
except ImportError:
|
||||||
|
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?"
|
||||||
|
)
|
||||||
|
|
||||||
|
raise
|
||||||
|
|
||||||
|
# This allows easy placement of apps within the interior
|
||||||
|
# passfinder directory.
|
||||||
|
current_path = Path(__file__).parent.resolve()
|
||||||
|
sys.path.append(str(current_path / "passfinder"))
|
||||||
|
|
||||||
|
execute_from_command_line(sys.argv)
|
57
parsers/attractions.py
Normal file
57
parsers/attractions.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from passfinder.events.models import Region, Event
|
||||||
|
|
||||||
|
with open("data/pos-attr.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
reggg = {
|
||||||
|
"г. Санкт-Петербург": "Санкт-Петербург",
|
||||||
|
"г. Москва": "Москва",
|
||||||
|
"г. Севастополь": "Севастополь",
|
||||||
|
"Республика Адыгея (Адыгея)": "Республика Адыгея",
|
||||||
|
"Чувашская Республика - Чувашия": "Чувашская Республика",
|
||||||
|
"Республика Татарстан (Татарстан)": "Республика Татарстан",
|
||||||
|
"Республика Северная Осетия - Алания": "Республика Северная Осетия – Алания",
|
||||||
|
"Ханты-Мансийский автономный округ - Югра": "Ханты-Мансийский автономный округ — Югра",
|
||||||
|
}
|
||||||
|
rett = []
|
||||||
|
ret = []
|
||||||
|
for infff in data:
|
||||||
|
info = infff["general"]
|
||||||
|
if info["address"] and "mapPosition" in info["address"]:
|
||||||
|
r_name = (
|
||||||
|
reggg[info["region"]["value"]]
|
||||||
|
if info["region"]["value"] in reggg
|
||||||
|
else info["region"]["value"]
|
||||||
|
)
|
||||||
|
res = {
|
||||||
|
"title": info["name"],
|
||||||
|
"parser_source": "mkrf.ru",
|
||||||
|
# "region": Region.objects.get(title=r_name),
|
||||||
|
"lat": info["address"]["mapPosition"]["coordinates"][0],
|
||||||
|
"lon": info["address"]["mapPosition"]["coordinates"][1],
|
||||||
|
"address": info["address"]["fullAddress"],
|
||||||
|
"type": "attraction",
|
||||||
|
"extra_kwargs": {"objectType": info["objectType"]["value"]},
|
||||||
|
}
|
||||||
|
if "typologies" in info:
|
||||||
|
res["extra_kwargs"]["typologies"] = [x["value"] for x in info["typologies"]]
|
||||||
|
|
||||||
|
if "securityInfo" in info or "borderInfo" in info:
|
||||||
|
for ev in Event.objects.filter(
|
||||||
|
title=info["name"],
|
||||||
|
address=res["address"],
|
||||||
|
lat=res["lat"],
|
||||||
|
lon=res["lon"],
|
||||||
|
):
|
||||||
|
ev.extra_kwargs = res["extra_kwargs"]
|
||||||
|
ev.save()
|
||||||
|
|
||||||
|
ret.append(res)
|
||||||
|
|
||||||
|
|
||||||
|
def get_att():
|
||||||
|
return ret
|
89
parsers/bulvari.py
Normal file
89
parsers/bulvari.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region, Place
|
||||||
|
|
||||||
|
with open("data/bulvary.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
al = True
|
||||||
|
d = {}
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["type"] = "boulevard"
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["description"] = info["description"]
|
||||||
|
if "parser_source" in info:
|
||||||
|
d["parser_source"] = info["parser_source"]
|
||||||
|
if "sort" in info:
|
||||||
|
d["sort"] = int(info["sort"])
|
||||||
|
if "is_can_buy" in info:
|
||||||
|
d["can_buy"] = info["is_can_buy"]
|
||||||
|
if "priority" in info:
|
||||||
|
d["priority"] = int(info["priority"])
|
||||||
|
if "duration" in info:
|
||||||
|
d["duration"] = int(info["duration"])
|
||||||
|
if "ticket_price" in info:
|
||||||
|
d["ticket_price"] = int(info["ticket_price"])
|
||||||
|
if "schedule" in info:
|
||||||
|
d["schedule"] = info["schedule"]
|
||||||
|
|
||||||
|
if "payment_method" in info:
|
||||||
|
d["payment_method"] = info["payment_method"]
|
||||||
|
elif "purchase_method" in info:
|
||||||
|
d["payment_method"] = info["purchase_method"]
|
||||||
|
if "age" in info:
|
||||||
|
d["age"] = info["age"]
|
||||||
|
if "booking_link" in info:
|
||||||
|
d["booking_link"] = info["booking_link"]
|
||||||
|
if "discover_moscow_link" in info:
|
||||||
|
d["discover_moscow_link"] = info["discover_moscow_link"]
|
||||||
|
if info["city"]:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["region"]:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["place"]:
|
||||||
|
try:
|
||||||
|
d["place"] = Place.objects.get(oid=info["place"][0])
|
||||||
|
except Place.DoesNotExist:
|
||||||
|
...
|
||||||
|
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if "place" in d:
|
||||||
|
d["location"] = d["place"].location
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
|
||||||
|
media = []
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
for m in info["galereya"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
else:
|
||||||
|
print(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_bulvari():
|
||||||
|
return result
|
27
parsers/city.py
Normal file
27
parsers/city.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
with open("data/cities.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
d = {}
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["id"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
if "region" in info:
|
||||||
|
d["region"] = info["region"]
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
|
||||||
|
result.append(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_cities():
|
||||||
|
return result
|
89
parsers/concert.py
Normal file
89
parsers/concert.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region, Place
|
||||||
|
|
||||||
|
with open("data/concerts.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
al = True
|
||||||
|
d = {}
|
||||||
|
d["type"] = "concert"
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["description"] = info["description"]
|
||||||
|
if "parser_source" in info:
|
||||||
|
d["parser_source"] = info["parser_source"]
|
||||||
|
if "sort" in info:
|
||||||
|
d["sort"] = int(info["sort"])
|
||||||
|
if "is_can_buy" in info:
|
||||||
|
d["can_buy"] = info["is_can_buy"]
|
||||||
|
if "priority" in info:
|
||||||
|
d["priority"] = int(info["priority"])
|
||||||
|
if "duration" in info:
|
||||||
|
d["duration"] = int(info["duration"])
|
||||||
|
if "ticket_price" in info:
|
||||||
|
d["ticket_price"] = int(info["ticket_price"])
|
||||||
|
if "schedule" in info:
|
||||||
|
d["schedule"] = info["schedule"]
|
||||||
|
|
||||||
|
if "payment_method" in info:
|
||||||
|
d["payment_method"] = info["payment_method"]
|
||||||
|
elif "purchase_method" in info:
|
||||||
|
d["payment_method"] = info["purchase_method"]
|
||||||
|
if "age" in info:
|
||||||
|
d["age"] = info["age"]
|
||||||
|
if "booking_link" in info:
|
||||||
|
d["booking_link"] = info["booking_link"]
|
||||||
|
if "discover_moscow_link" in info:
|
||||||
|
d["discover_moscow_link"] = info["discover_moscow_link"]
|
||||||
|
if info["city"]:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if "region" in info:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["place"]:
|
||||||
|
try:
|
||||||
|
d["place"] = Place.objects.get(oid=info["place"][0])
|
||||||
|
except Place.DoesNotExist:
|
||||||
|
...
|
||||||
|
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if "place" in d:
|
||||||
|
d["location"] = d["place"].location
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
|
||||||
|
media = []
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
for m in info["galereya"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
else:
|
||||||
|
pprint(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_concert():
|
||||||
|
return result
|
89
parsers/dostoprimechatelnost.py
Normal file
89
parsers/dostoprimechatelnost.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region, Place
|
||||||
|
|
||||||
|
with open("data/dostoprimechatelnosti.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
al = True
|
||||||
|
d = {}
|
||||||
|
d["type"] = "attraction"
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["description"] = info["description"]
|
||||||
|
if "parser_source" in info:
|
||||||
|
d["parser_source"] = info["parser_source"]
|
||||||
|
if "sort" in info:
|
||||||
|
d["sort"] = int(info["sort"])
|
||||||
|
if "is_can_buy" in info:
|
||||||
|
d["can_buy"] = info["is_can_buy"]
|
||||||
|
if "priority" in info:
|
||||||
|
d["priority"] = int(info["priority"])
|
||||||
|
if "duration" in info:
|
||||||
|
d["duration"] = int(info["duration"])
|
||||||
|
if "ticket_price" in info:
|
||||||
|
d["ticket_price"] = int(info["ticket_price"])
|
||||||
|
if "schedule" in info:
|
||||||
|
d["schedule"] = info["schedule"]
|
||||||
|
|
||||||
|
if "payment_method" in info:
|
||||||
|
d["payment_method"] = info["payment_method"]
|
||||||
|
elif "purchase_method" in info:
|
||||||
|
d["payment_method"] = info["purchase_method"]
|
||||||
|
if "age" in info:
|
||||||
|
d["age"] = info["age"]
|
||||||
|
if "booking_link" in info:
|
||||||
|
d["booking_link"] = info["booking_link"]
|
||||||
|
if "discover_moscow_link" in info:
|
||||||
|
d["discover_moscow_link"] = info["discover_moscow_link"]
|
||||||
|
if info["city"]:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["region"]:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["place"]:
|
||||||
|
try:
|
||||||
|
d["place"] = Place.objects.get(oid=info["place"][0])
|
||||||
|
except Place.DoesNotExist:
|
||||||
|
...
|
||||||
|
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if "place" in d:
|
||||||
|
d["location"] = d["place"].location
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
|
||||||
|
media = []
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
for m in info["galereya"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
else:
|
||||||
|
pprint(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dostoprimechatelnost():
|
||||||
|
return result
|
89
parsers/excursii.py
Normal file
89
parsers/excursii.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region, Place
|
||||||
|
|
||||||
|
with open("data/excursii.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
al = True
|
||||||
|
d = {}
|
||||||
|
d["type"] = "excursion"
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["description"] = info["description"]
|
||||||
|
if "parser_source" in info:
|
||||||
|
d["parser_source"] = info["parser_source"]
|
||||||
|
if "sort" in info:
|
||||||
|
d["sort"] = int(info["sort"])
|
||||||
|
if "is_can_buy" in info:
|
||||||
|
d["can_buy"] = info["is_can_buy"]
|
||||||
|
if "priority" in info:
|
||||||
|
d["priority"] = int(info["priority"])
|
||||||
|
if "duration" in info:
|
||||||
|
d["duration"] = int(info["duration"])
|
||||||
|
if "ticket_price" in info:
|
||||||
|
d["ticket_price"] = int(info["ticket_price"])
|
||||||
|
if "schedule" in info:
|
||||||
|
d["schedule"] = info["schedule"]
|
||||||
|
|
||||||
|
if "payment_method" in info:
|
||||||
|
d["payment_method"] = info["payment_method"]
|
||||||
|
elif "purchase_method" in info:
|
||||||
|
d["payment_method"] = info["purchase_method"]
|
||||||
|
if "age" in info:
|
||||||
|
d["age"] = info["age"]
|
||||||
|
if "booking_link" in info:
|
||||||
|
d["booking_link"] = info["booking_link"]
|
||||||
|
if "discover_moscow_link" in info:
|
||||||
|
d["discover_moscow_link"] = info["discover_moscow_link"]
|
||||||
|
if info["city"]:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if "region" in info:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["place"]:
|
||||||
|
try:
|
||||||
|
d["place"] = Place.objects.get(oid=info["place"][0])
|
||||||
|
except Place.DoesNotExist:
|
||||||
|
...
|
||||||
|
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if "place" in d:
|
||||||
|
d["location"] = d["place"].location
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
|
||||||
|
media = []
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
for m in info["galereya"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
else:
|
||||||
|
pprint(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_excursion():
|
||||||
|
return result
|
67
parsers/extract_mus.py
Normal file
67
parsers/extract_mus.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
url = "http://vrm.museum.ru/mus/list.asp?by=alpha"
|
||||||
|
response = requests.get(url)
|
||||||
|
soup = BeautifulSoup(response.text, "html.parser")
|
||||||
|
t = soup.find_all("tr")
|
||||||
|
for j in range(20, len(t)):
|
||||||
|
try:
|
||||||
|
el = t[j]
|
||||||
|
l = str(el.find_all(href=True)[0]).split('"')[1]
|
||||||
|
link = "http://vrm.museum.ru" + l
|
||||||
|
response = requests.get(link)
|
||||||
|
name = BeautifulSoup(
|
||||||
|
[x for x in response.text.splitlines() if f"http://www.museum.ru{l}" in x][
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"html.parser",
|
||||||
|
).text
|
||||||
|
soup2 = BeautifulSoup(response.text, "html.parser")
|
||||||
|
data2 = []
|
||||||
|
for table in soup2.find_all("table"):
|
||||||
|
rows = table.find_all("tr")
|
||||||
|
data = []
|
||||||
|
for row in rows:
|
||||||
|
cols = row.find_all("td")
|
||||||
|
cols = [ele.text.strip() for ele in cols]
|
||||||
|
data.append([ele for ele in cols if ele])
|
||||||
|
data2 += data
|
||||||
|
|
||||||
|
data3 = {}
|
||||||
|
for row in data2:
|
||||||
|
if len(row) > 0:
|
||||||
|
rec = []
|
||||||
|
for el in row:
|
||||||
|
rec += el.split(":")
|
||||||
|
if len(rec) > 1:
|
||||||
|
c_name = " ".join(rec[0].split())
|
||||||
|
desc = " ".join(" ".join(rec[1:]).split())
|
||||||
|
data3[c_name] = desc
|
||||||
|
|
||||||
|
images = []
|
||||||
|
img_tags = soup2.find_all("img")
|
||||||
|
urls = [img["src"] for img in img_tags]
|
||||||
|
add = {
|
||||||
|
"name": name,
|
||||||
|
"urls": [x for x in urls if "asp" in x],
|
||||||
|
"link": link,
|
||||||
|
} | data3
|
||||||
|
result.append(add)
|
||||||
|
print(name)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print(j, "/", len(t))
|
||||||
|
|
||||||
|
with open("ext.json", "w", encoding="utf-16") as f:
|
||||||
|
json.dump({"links": result}, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
with open("ext.json", "w", encoding="utf-16") as f:
|
||||||
|
json.dump({"links": result}, f, ensure_ascii=False, indent=4)
|
63
parsers/hotel.py
Normal file
63
parsers/hotel.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region
|
||||||
|
|
||||||
|
with open("data/hotels.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
d = {}
|
||||||
|
al = True
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["address"] = info["address"]
|
||||||
|
if "stars" in info and info["stars"]:
|
||||||
|
d["stars"] = int(info["stars"])
|
||||||
|
|
||||||
|
if "city" in info:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if "region" in info:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
if "rooms" in info:
|
||||||
|
d["rooms"] = info["rooms"]
|
||||||
|
|
||||||
|
phones = []
|
||||||
|
if "phones" in info:
|
||||||
|
for phone in info["phones"]:
|
||||||
|
phones.append(
|
||||||
|
{"hotel": d["oid"], "name": phone["name"], "number": phone["number"]}
|
||||||
|
)
|
||||||
|
if phones:
|
||||||
|
d["phones"] = phones
|
||||||
|
|
||||||
|
media = []
|
||||||
|
if "images" in info:
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hotel():
|
||||||
|
return result
|
89
parsers/movie.py
Normal file
89
parsers/movie.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
from passfinder.events.models import City, Region, Place
|
||||||
|
|
||||||
|
with open("data/kino.json") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in data:
|
||||||
|
al = True
|
||||||
|
d = {}
|
||||||
|
d["type"] = "movie"
|
||||||
|
info = r["dictionary_data"]
|
||||||
|
d["oid"] = r["_id"]["$oid"]
|
||||||
|
d["title"] = info["title"]
|
||||||
|
d["description"] = info["description"]
|
||||||
|
if "parser_source" in info:
|
||||||
|
d["parser_source"] = info["parser_source"]
|
||||||
|
if "sort" in info:
|
||||||
|
d["sort"] = int(info["sort"])
|
||||||
|
if "is_can_buy" in info:
|
||||||
|
d["can_buy"] = info["is_can_buy"]
|
||||||
|
if "priority" in info:
|
||||||
|
d["priority"] = int(info["priority"])
|
||||||
|
if "duration" in info:
|
||||||
|
d["duration"] = int(info["duration"])
|
||||||
|
if "ticket_price" in info:
|
||||||
|
d["ticket_price"] = int(info["ticket_price"])
|
||||||
|
if "schedule" in info:
|
||||||
|
d["schedule"] = info["schedule"]
|
||||||
|
|
||||||
|
if "payment_method" in info:
|
||||||
|
d["payment_method"] = info["payment_method"]
|
||||||
|
elif "purchase_method" in info:
|
||||||
|
d["payment_method"] = info["purchase_method"]
|
||||||
|
if "age" in info:
|
||||||
|
d["age"] = info["age"]
|
||||||
|
if "booking_link" in info:
|
||||||
|
d["booking_link"] = info["booking_link"]
|
||||||
|
if "discover_moscow_link" in info:
|
||||||
|
d["discover_moscow_link"] = info["discover_moscow_link"]
|
||||||
|
if info["city"]:
|
||||||
|
try:
|
||||||
|
d["city"] = City.objects.get(oid=info["city"])
|
||||||
|
except City.DoesNotExist:
|
||||||
|
...
|
||||||
|
if "region" in info:
|
||||||
|
try:
|
||||||
|
d["region"] = Region.objects.get(oid=info["region"])
|
||||||
|
except Region.DoesNotExist:
|
||||||
|
...
|
||||||
|
if info["place"]:
|
||||||
|
try:
|
||||||
|
d["place"] = Place.objects.get(oid=info["place"][0])
|
||||||
|
except Place.DoesNotExist:
|
||||||
|
...
|
||||||
|
|
||||||
|
if "geo_data" in info and info["geo_data"]["coordinates"]:
|
||||||
|
d["location"] = Point(
|
||||||
|
info["geo_data"]["coordinates"][0], info["geo_data"]["coordinates"][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if "place" in d:
|
||||||
|
d["location"] = d["place"].location
|
||||||
|
else:
|
||||||
|
al = False
|
||||||
|
|
||||||
|
media = []
|
||||||
|
for m in info["images"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
for m in info["galereya"]:
|
||||||
|
media.append({"file": m["source"]["id"], "type": "image"})
|
||||||
|
|
||||||
|
if media:
|
||||||
|
d["media"] = media
|
||||||
|
|
||||||
|
if al:
|
||||||
|
result.append(d)
|
||||||
|
else:
|
||||||
|
pprint(d)
|
||||||
|
|
||||||
|
|
||||||
|
def get_movie():
|
||||||
|
return result
|
58
parsers/mus_load.py
Normal file
58
parsers/mus_load.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
with open("data/ext.json", "r", encoding="utf-16") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
with open("data/only_cords.json", "r") as f:
|
||||||
|
data2 = json.load(f)
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
|
||||||
|
for j in range(len(data2)):
|
||||||
|
info = data["links"][j]
|
||||||
|
pos = data2[j]
|
||||||
|
|
||||||
|
if "cords" in pos:
|
||||||
|
p_name = [x for x in info.keys() if "плата" in x.lower() or "цена" in x.lower()]
|
||||||
|
|
||||||
|
res = {
|
||||||
|
"sort": j,
|
||||||
|
"type": "museum",
|
||||||
|
"parser_source": "museum.ru",
|
||||||
|
"title": info["name"],
|
||||||
|
"lat": pos["cords"][0],
|
||||||
|
"lon": pos["cords"][1],
|
||||||
|
}
|
||||||
|
if p_name and info[p_name[0]] != "См. здесь":
|
||||||
|
for n in p_name:
|
||||||
|
m = []
|
||||||
|
if "руб" in info[n]:
|
||||||
|
ppp = info[n].split()
|
||||||
|
for ind, eee in enumerate(ppp):
|
||||||
|
if "руб" in eee:
|
||||||
|
try:
|
||||||
|
val = int(ppp[ind - 1])
|
||||||
|
m.append(val)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
val = int(ppp[ind + 1])
|
||||||
|
m.append(val)
|
||||||
|
except Exception:
|
||||||
|
...
|
||||||
|
if m:
|
||||||
|
res["ticket_price"] = max(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
if "Режим работы" in info and info["Режим работы"] != "См. здесь":
|
||||||
|
res["schedule"] = {"plain": info["Режим работы"]}
|
||||||
|
|
||||||
|
if "Описание" in info:
|
||||||
|
res["description"] = info["Описание"]
|
||||||
|
|
||||||
|
ret.append(res)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mus():
|
||||||
|
return ret
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user