├── .gitignore
├── README.md
├── apps
├── __init__.py
├── blog
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── assets.py
│ ├── context_processor.py
│ ├── forms.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_post_category.py
│ │ ├── 0003_auto_20170417_1637.py
│ │ ├── 0004_auto_20170419_0231.py
│ │ ├── 0005_emailsubscription.py
│ │ ├── 0006_auto_20170501_2337.py
│ │ ├── 0007_auto_20170501_2339.py
│ │ └── __init__.py
│ ├── models.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ ├── adsense_tags.py
│ │ └── blog_tags.py
│ ├── tests.py
│ ├── urls.py
│ ├── utils.py
│ └── views.py
└── notes
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── assets
└── robots.txt
├── locale
├── en
│ └── LC_MESSAGES
│ │ └── django.po
└── ru
│ └── LC_MESSAGES
│ └── django.po
├── logs
└── .keep
├── manage.py
├── notion
├── .env-template
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
├── requirements.txt
├── staticfiles
├── courses
│ └── images
│ │ ├── adil.jpg
│ │ ├── bgimage.jpg
│ │ ├── logo_aws.png
│ │ ├── logo_bootstrap.png
│ │ ├── logo_circle_ci.png
│ │ ├── logo_django.png
│ │ ├── logo_do.png
│ │ ├── logo_docker.png
│ │ ├── logo_es6.png
│ │ ├── logo_falcon.png
│ │ ├── logo_jest.png
│ │ ├── logo_jwt.png
│ │ ├── logo_postman.png
│ │ ├── logo_python.png
│ │ ├── logo_vue.png
│ │ └── luigi-logo.png
├── css
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.css.map
│ ├── bootstrap-theme.min.css
│ ├── bootstrap-theme.min.css.map
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── monokai-sublime.min.css
│ ├── normalize.css
│ └── style.css
├── font-awesome
│ ├── css
│ │ ├── font-awesome.css
│ │ └── font-awesome.min.css
│ └── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
├── images
│ ├── DE_770x193.jpg
│ ├── Python900x120.png
│ ├── arrow.jpg
│ ├── author.jpg
│ ├── devbrain.jpg
│ ├── devbrain.png
│ ├── icon.png
│ ├── like.png
│ ├── machine-learning-course.jpg
│ ├── ml-agenda.jpg
│ ├── ml-poster2.jpg
│ ├── pydata-analysis.jpg
│ ├── ru-flag.png
│ ├── social.jpg
│ ├── telegram-logo.png
│ └── us-flag.png
├── js
│ ├── app.js
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ ├── highlight.min.js
│ ├── jquery-3.2.1.min.js
│ └── npm.js
└── redactor
│ └── plugins
│ └── source.js
└── templates
├── base.html
├── blog
├── _sidebar.html
├── adsense.html
├── archives.html
├── category_detail.html
├── index.html
├── page.html
├── post_detail.html
└── search.html
├── courses
└── index.html
├── notes
├── note_detail.html
└── notes.html
└── tags
└── jobs.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Python template
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 | *$py.class
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 | .hypothesis/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 |
58 | # Flask stuff:
59 | instance/
60 | .webassets-cache
61 |
62 | # Scrapy stuff:
63 | .scrapy
64 |
65 | # Sphinx documentation
66 | docs/_build/
67 |
68 | # PyBuilder
69 | target/
70 |
71 | # IPython Notebook
72 | .ipynb_checkpoints
73 |
74 | # pyenv
75 | .python-version
76 |
77 | # celery beat schedule file
78 | celerybeat-schedule
79 |
80 | # dotenv
81 | .env
82 |
83 | # virtualenv
84 | venv/
85 | ENV/
86 |
87 | # Spyder project settings
88 | .spyderproject
89 |
90 | # Rope project settings
91 | .ropeproject
92 | ### VirtualEnv template
93 | # Virtualenv
94 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
95 | .Python
96 | [Bb]in
97 | [Ii]nclude
98 | [Ll]ib
99 | [Ll]ib64
100 | [Ll]ocal
101 | [Ss]cripts
102 | pyvenv.cfg
103 | .venv
104 | pip-selfcheck.json
105 | ### JetBrains template
106 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
107 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
108 |
109 | # User-specific stuff:
110 | .idea/workspace.xml
111 | .idea/tasks.xml
112 | .idea/dictionaries
113 | .idea/vcs.xml
114 | .idea/jsLibraryMappings.xml
115 |
116 | # Sensitive or high-churn files:
117 | .idea/dataSources.ids
118 | .idea/dataSources.xml
119 | .idea/dataSources.local.xml
120 | .idea/sqlDataSources.xml
121 | .idea/dynamic.xml
122 | .idea/uiDesigner.xml
123 |
124 | # Gradle:
125 | .idea/gradle.xml
126 | .idea/libraries
127 |
128 | # Mongo Explorer plugin:
129 | .idea/mongoSettings.xml
130 |
131 | ## File-based project format:
132 | *.iws
133 |
134 | ## Plugin-specific files:
135 |
136 | # IntelliJ
137 | /out/
138 |
139 | # mpeltonen/sbt-idea plugin
140 | .idea_modules/
141 |
142 | # JIRA plugin
143 | atlassian-ide-plugin.xml
144 |
145 | # Crashlytics plugin (for Android Studio and IntelliJ)
146 | com_crashlytics_export_strings.xml
147 | crashlytics.properties
148 | crashlytics-build.properties
149 | fabric.properties
150 |
151 | .idea/
152 | static/
153 | logs/
154 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # notion
2 | Simple blog engine which powers https://khashtamov.com/
3 |
4 | ## Features
5 | * As simple as possible
6 | * Multiple language support
7 |
--------------------------------------------------------------------------------
/apps/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/apps/__init__.py
--------------------------------------------------------------------------------
/apps/blog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/apps/blog/__init__.py
--------------------------------------------------------------------------------
/apps/blog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django import forms
3 | from redactor.widgets import RedactorEditor
4 |
5 | from apps.blog.models import Category, Post, Page, EmailSubscription, AlternateURL
6 |
7 |
8 | class PostAdminForm(forms.ModelForm):
9 | class Meta:
10 | model = Post
11 | fields = ['title', 'slug', 'text', 'lang', 'status', 'category']
12 | widgets = {
13 | 'text': RedactorEditor(redactor_options={
14 | 'plugins': ['table'],
15 |
16 | }),
17 | }
18 |
19 |
20 | class PageAdminForm(forms.ModelForm):
21 | class Meta:
22 | model = Page
23 | fields = ['title', 'slug', 'text', 'lang', 'visible']
24 | widgets = {
25 | 'text': RedactorEditor(redactor_options={'plugins': ['source']}),
26 | }
27 |
28 |
29 | class CategoryAdmin(admin.ModelAdmin):
30 | list_display = ['title', 'slug', 'created']
31 | search_fields = ['title']
32 |
33 |
34 | class PostAdmin(admin.ModelAdmin):
35 | list_display = ['title', 'page_views', 'lang', 'slug', 'status', 'created']
36 | search_fields = ['title']
37 | ordering = ['-created']
38 | form = PostAdminForm
39 |
40 |
41 | class PageAdmin(admin.ModelAdmin):
42 | list_display = ['title', 'lang', 'slug', 'visible', 'created']
43 | form = PageAdminForm
44 |
45 |
46 | class EmailSubscriptionAdmin(admin.ModelAdmin):
47 | list_display = ['email', 'lang', 'created']
48 |
49 |
50 | class AlternateURLAdmin(admin.ModelAdmin):
51 | list_display = ['post', 'lang', 'alternate_post', 'created']
52 |
53 |
54 | admin.site.register(Post, PostAdmin)
55 | admin.site.register(Category, CategoryAdmin)
56 | admin.site.register(Page, PageAdmin)
57 | admin.site.register(EmailSubscription, EmailSubscriptionAdmin)
58 | admin.site.register(AlternateURL, AlternateURLAdmin)
59 |
--------------------------------------------------------------------------------
/apps/blog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class BlogConfig(AppConfig):
5 | name = 'apps.blog'
6 |
--------------------------------------------------------------------------------
/apps/blog/assets.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django_assets import Bundle, register
4 |
5 | css = Bundle(
6 | os.path.join('css', 'normalize.css'),
7 | os.path.join('css', 'bootstrap.min.css'),
8 | os.path.join('css', 'bootstrap-theme.min.css'),
9 | os.path.join('css', 'style.css'),
10 | os.path.join('css', 'monokai-sublime.min.css'),
11 | filters='cssmin', output='css/build.css'
12 | )
13 |
14 | js = Bundle(
15 | os.path.join('js', 'jquery-3.2.1.min.js'),
16 | os.path.join('js', 'highlight.min.js'),
17 | os.path.join('js', 'bootstrap.min.js'),
18 | filters='jsmin', output='js/build.js'
19 | )
20 | register('css_all', css)
21 | register('js_all', js)
22 |
--------------------------------------------------------------------------------
/apps/blog/context_processor.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import django
3 | from django.conf import settings
4 |
5 |
6 | def generic(request):
7 | return {
8 | 'DEBUG': settings.DEBUG,
9 | 'DJANGO_VERSION': django.__version__,
10 | 'PYTHON_VERSION': sys.version[:6]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/blog/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 |
4 | class EmailForm(forms.Form):
5 | email = forms.EmailField()
6 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-04-17 09:42
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Category',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('title', models.CharField(max_length=30, verbose_name='Title')),
21 | ('slug', models.SlugField()),
22 | ('created', models.DateTimeField(auto_now_add=True)),
23 | ],
24 | options={
25 | 'verbose_name': 'category',
26 | 'verbose_name_plural': 'categories',
27 | },
28 | ),
29 | migrations.CreateModel(
30 | name='Post',
31 | fields=[
32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33 | ('title', models.CharField(max_length=254)),
34 | ('slug', models.SlugField(max_length=100)),
35 | ('text', models.TextField(verbose_name='Text')),
36 | ('lang', models.CharField(choices=[('ru', 'Russian'), ('en', 'English')], max_length=2, verbose_name='Language')),
37 | ('status', models.SmallIntegerField(choices=[(0, 'Draft'), (1, 'Published')], verbose_name='Status')),
38 | ('created', models.DateTimeField(auto_now_add=True)),
39 | ('updated', models.DateTimeField(auto_now=True)),
40 | ],
41 | options={
42 | 'verbose_name': 'post',
43 | 'verbose_name_plural': 'Posts',
44 | },
45 | ),
46 | ]
47 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0002_post_category.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-04-17 10:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('blog', '0001_initial'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='post',
18 | name='category',
19 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='blog.Category'),
20 | preserve_default=False,
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0003_auto_20170417_1637.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-04-17 10:37
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('blog', '0002_post_category'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='post',
18 | name='category',
19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.Category'),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0004_auto_20170419_0231.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-04-18 20:31
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('blog', '0003_auto_20170417_1637'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Page',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('title', models.CharField(max_length=30)),
20 | ('slug', models.SlugField(max_length=30)),
21 | ('text', models.TextField(verbose_name='Text')),
22 | ('lang', models.CharField(choices=[('ru', 'Russian'), ('en', 'English')], max_length=2, verbose_name='Language')),
23 | ('visible', models.BooleanField(default=False)),
24 | ('created', models.DateTimeField(auto_now_add=True)),
25 | ],
26 | options={
27 | 'verbose_name': 'page',
28 | 'verbose_name_plural': 'Pages',
29 | },
30 | ),
31 | migrations.AddField(
32 | model_name='post',
33 | name='page_views',
34 | field=models.PositiveIntegerField(default=0, verbose_name='Page views'),
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0005_emailsubscription.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-04-28 17:32
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import uuid
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('blog', '0004_auto_20170419_0231'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='EmailSubscription',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')),
21 | ('lang', models.CharField(max_length=2, verbose_name='Language')),
22 | ('activated', models.BooleanField(default=False, verbose_name='Activated')),
23 | ('activation_code', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Activation code')),
24 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
25 | ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
26 | ],
27 | options={
28 | 'verbose_name': 'subscription',
29 | 'verbose_name_plural': 'Subscriptions',
30 | },
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0006_auto_20170501_2337.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-05-01 17:37
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('blog', '0005_emailsubscription'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='AlternateURL',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('lang', models.CharField(choices=[('ru', 'Russian'), ('en', 'English')], max_length=2, verbose_name='Language')),
21 | ('url', models.URLField(max_length=254, verbose_name='URL')),
22 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
23 | ],
24 | options={
25 | 'verbose_name': 'alternate URL',
26 | 'verbose_name_plural': 'Alternate URLs',
27 | },
28 | ),
29 | migrations.AlterField(
30 | model_name='post',
31 | name='slug',
32 | field=models.SlugField(max_length=100, verbose_name='Slug'),
33 | ),
34 | migrations.AlterField(
35 | model_name='post',
36 | name='title',
37 | field=models.CharField(max_length=254, verbose_name='Title'),
38 | ),
39 | migrations.AddField(
40 | model_name='alternateurl',
41 | name='post',
42 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.Post'),
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/apps/blog/migrations/0007_auto_20170501_2339.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11 on 2017-05-01 17:39
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('blog', '0006_auto_20170501_2337'),
13 | ]
14 |
15 | operations = [
16 | migrations.RemoveField(
17 | model_name='alternateurl',
18 | name='url',
19 | ),
20 | migrations.AddField(
21 | model_name='alternateurl',
22 | name='alternate_post',
23 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='alternate_post', to='blog.Post', verbose_name='Alternate post'),
24 | preserve_default=False,
25 | ),
26 | migrations.AlterField(
27 | model_name='alternateurl',
28 | name='post',
29 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.Post', verbose_name='Original post'),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/apps/blog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/apps/blog/migrations/__init__.py
--------------------------------------------------------------------------------
/apps/blog/models.py:
--------------------------------------------------------------------------------
1 | import uuid
2 |
3 | from django.db import models
4 | from django.utils.translation import gettext_lazy as _
5 | from django.conf import settings
6 | from django.urls import reverse
7 | from django.contrib.contenttypes.models import ContentType
8 |
9 |
10 | class Category(models.Model):
11 | title = models.CharField(_('Title'), max_length=30)
12 | slug = models.SlugField()
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | verbose_name = _('category')
17 | verbose_name_plural = _('categories')
18 |
19 | def __str__(self):
20 | return self.title
21 |
22 |
23 | class Post(models.Model):
24 | DRAFT, PUBLISHED = range(2)
25 |
26 | STATUSES = (
27 | (DRAFT, _('Draft')),
28 | (PUBLISHED, _('Published'))
29 | )
30 |
31 | title = models.CharField(_('Title'), max_length=254)
32 | slug = models.SlugField(_('Slug'), max_length=100)
33 | text = models.TextField(_('Text'))
34 | lang = models.CharField(_('Language'), max_length=2, choices=settings.LANGUAGES)
35 | category = models.ForeignKey('Category', null=True, on_delete=models.CASCADE)
36 | status = models.SmallIntegerField(_('Status'), choices=STATUSES)
37 | page_views = models.PositiveIntegerField(_('Page views'), default=0)
38 | created = models.DateTimeField(auto_now_add=True)
39 | updated = models.DateTimeField(auto_now=True)
40 |
41 | class Meta:
42 | verbose_name = _('post')
43 | verbose_name_plural = _('Posts')
44 |
45 | def __str__(self):
46 | return self.title
47 |
48 | def get_absolute_url(self):
49 | return '/{}/{}/'.format(self.lang, self.slug)
50 |
51 | def month_year(self) -> str:
52 | return _(self.created.strftime('%B')) + ', ' + self.created.strftime('%Y')
53 |
54 | def get_admin_url(self):
55 | content_type = ContentType.objects.get_for_model(self.__class__)
56 | return reverse('admin:%s_%s_change' % (content_type.app_label, content_type.model),
57 | args=(self.id,))
58 |
59 |
60 | class Page(models.Model):
61 | title = models.CharField(max_length=30)
62 | slug = models.SlugField(max_length=30)
63 | text = models.TextField(_('Text'))
64 | lang = models.CharField(_('Language'), max_length=2, choices=settings.LANGUAGES)
65 | visible = models.BooleanField(default=False)
66 | created = models.DateTimeField(auto_now_add=True)
67 |
68 | class Meta:
69 | verbose_name = _('page')
70 | verbose_name_plural = _('Pages')
71 |
72 | def __str__(self):
73 | return self.title
74 |
75 | def get_absolute_url(self):
76 | return '/{}/p/{}/'.format(self.lang, self.slug)
77 |
78 |
79 | class EmailSubscription(models.Model):
80 | email = models.EmailField(_('Email'), unique=True)
81 | lang = models.CharField(_('Language'), max_length=2)
82 | activated = models.BooleanField(_('Activated'), default=False)
83 | activation_code = models.UUIDField(_('Activation code'), default=uuid.uuid4, editable=False)
84 | created = models.DateTimeField(_('Created'), auto_now_add=True)
85 | updated = models.DateTimeField(_('Updated'), auto_now=True)
86 |
87 | class Meta:
88 | verbose_name = _('subscription')
89 | verbose_name_plural = _('Subscriptions')
90 |
91 | def __str__(self):
92 | return self.email
93 |
94 | def activate(self):
95 | self.activated = True
96 | self.save()
97 |
98 | def send_activation_code(self):
99 | pass
100 |
101 |
102 | class AlternateURL(models.Model):
103 | post = models.ForeignKey('Post', verbose_name=_('Original post'), on_delete=models.CASCADE)
104 | lang = models.CharField(_('Language'), max_length=2, choices=settings.LANGUAGES)
105 | alternate_post = models.ForeignKey('Post', related_name='alternate_post',
106 | verbose_name=_('Alternate post'), on_delete=models.CASCADE)
107 | created = models.DateTimeField(_('Created'), auto_now_add=True)
108 |
109 | class Meta:
110 | verbose_name = _('alternate URL')
111 | verbose_name_plural = _('Alternate URLs')
112 |
113 | def __str__(self):
114 | return self.post.slug
115 |
--------------------------------------------------------------------------------
/apps/blog/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Adylzhan'
2 |
--------------------------------------------------------------------------------
/apps/blog/templatetags/adsense_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.template.loader import render_to_string
3 |
4 | register = template.Library()
5 |
6 |
7 | @register.filter
8 | def inject_adsense_after_n_paragraph(value, arg):
9 | ad_code = render_to_string('blog/adsense.html')
10 | paragraphs = value.split('
')
11 |
12 | if arg < len(paragraphs):
13 | paragraphs[arg] = ad_code + paragraphs[arg]
14 |
15 | value = ''.join(paragraphs)
16 | return value
17 |
--------------------------------------------------------------------------------
/apps/blog/templatetags/blog_tags.py:
--------------------------------------------------------------------------------
1 | from django.template.defaulttags import register
2 | from django.template.loader import render_to_string
3 | from django.utils.safestring import mark_safe
4 |
5 | from apps.blog.utils import get_latest_jobs
6 |
7 |
8 | @register.filter
9 | def text_preview(text):
10 | more = text.find('')
11 | if more != -1:
12 | return text[:more]
13 | else:
14 | return text
15 |
16 |
17 | @register.filter
18 | def erase_breaks(text):
19 | return text.replace('\n', '').replace('\r\n', '').replace('\r', '')
20 |
21 |
22 | @register.simple_tag
23 | def display_jobs(limit=5):
24 | jobs = get_latest_jobs(limit, text='python')
25 | return mark_safe(render_to_string('tags/jobs.html', {'jobs': jobs}))
26 |
--------------------------------------------------------------------------------
/apps/blog/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/apps/blog/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from apps.blog.views import (
4 | HomePageView,
5 | PostDetailView,
6 | CategoryDetailView,
7 | PageDetailView,
8 | LastestPostFeed,
9 | SearchView,
10 | )
11 |
12 | app_name = 'blog'
13 | urlpatterns = [
14 | path('', HomePageView.as_view(), name='posts'),
15 | path('p//', PageDetailView.as_view(), name='page'),
16 | path('category//', CategoryDetailView.as_view(), name='category'),
17 | path('feed/', LastestPostFeed(), name='feed'),
18 | path('search/', SearchView.as_view(), name='search'),
19 | path('/', PostDetailView.as_view(), name='post'),
20 | ]
21 |
--------------------------------------------------------------------------------
/apps/blog/utils.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 |
4 | def get_latest_jobs(limit=5, text=''):
5 | try:
6 | response = requests.get(
7 | 'https://remotelist.ru/api/jobs',
8 | params={'limit': limit, 'text': text},
9 | timeout=5
10 | )
11 | except Exception:
12 | return []
13 | else:
14 | return response.json()
15 |
--------------------------------------------------------------------------------
/apps/blog/views.py:
--------------------------------------------------------------------------------
1 | from django.views.generic import ListView, DetailView, View
2 | from django.utils import translation
3 | from django.utils.translation import gettext as _
4 | from django.urls import reverse, reverse_lazy
5 | from django.contrib.syndication.views import Feed
6 | from django.http import JsonResponse, HttpResponseRedirect, HttpResponseForbidden
7 | from django.db.models import F
8 | from django.contrib.postgres.search import SearchQuery, SearchVector
9 | from django.shortcuts import render
10 |
11 | from apps.blog.models import Post, Category, Page, EmailSubscription, AlternateURL
12 | from apps.notes.models import Note
13 | from apps.blog.forms import EmailForm
14 |
15 |
16 | class LastestPostFeed(Feed):
17 | title = _("Adil Khashtamov's personal blog")
18 | description = _('pragmatic programmer')
19 | link = reverse_lazy('blog:posts')
20 |
21 | def items(self):
22 | return Post.objects.filter(
23 | lang=translation.get_language(), status=Post.PUBLISHED
24 | ).order_by('-created')[:5]
25 |
26 | def item_title(self, item):
27 | return item.title
28 |
29 | def item_description(self, item):
30 | return item.text[: item.text.find('')]
31 |
32 | def item_link(self, item):
33 | return reverse('blog:post', kwargs={'slug': item.slug})
34 |
35 |
36 | class HomePageView(ListView):
37 | template_name = 'blog/index.html'
38 | model = Post
39 |
40 | def get_queryset(self):
41 | if self.request.user.is_superuser:
42 | return self.model.objects.filter(lang=translation.get_language()).order_by(
43 | '-id'
44 | )
45 | else:
46 | return self.model.objects.filter(
47 | lang=translation.get_language(), status=Post.PUBLISHED
48 | ).order_by('-id')
49 |
50 | def get_context_data(self, *, object_list=None, **kwargs):
51 | context = super().get_context_data(object_list=object_list, **kwargs)
52 | context['popular_posts'] = self.model.objects.filter(
53 | lang=translation.get_language(), status=Post.PUBLISHED
54 | ).order_by('-page_views')[:10]
55 | context['recent_posts'] = self.model.objects.filter(
56 | lang=translation.get_language(), status=Post.PUBLISHED
57 | ).order_by('-id')[:10]
58 | return context
59 |
60 |
61 | class PostDetailView(DetailView):
62 | template_name = 'blog/post_detail.html'
63 | model = Post
64 |
65 | def get_queryset(self):
66 | if self.request.user.is_superuser:
67 | return super().get_queryset().filter(lang=translation.get_language())
68 | else:
69 | return (
70 | super()
71 | .get_queryset()
72 | .filter(lang=translation.get_language(), status=Post.PUBLISHED)
73 | )
74 |
75 | def get_context_data(self, **kwargs):
76 | context = super().get_context_data(**kwargs)
77 | article = context['object']
78 | article.page_views = F('page_views') + 1
79 | article.save()
80 | context['object'] = self.model.objects.get(id=article.id)
81 |
82 | context['lastest_notes'] = (
83 | Note.objects.filter(status=Note.PUBLISHED, lang=translation.get_language())
84 | .select_related('theme')
85 | .order_by('-created')[:5]
86 | )
87 | context['related_articles'] = Post.objects.filter(
88 | status=Post.PUBLISHED,
89 | lang=translation.get_language(),
90 | category=article.category,
91 | ).exclude(id=article.id)
92 | context['alternate_posts'] = AlternateURL.objects.filter(post=self.object)
93 | context['popular_posts'] = self.model.objects.filter(
94 | lang=translation.get_language(), status=Post.PUBLISHED
95 | ).order_by('-page_views')[:10]
96 | context['recent_posts'] = self.model.objects.filter(
97 | lang=translation.get_language(), status=Post.PUBLISHED
98 | ).order_by('-id')[:10]
99 | return context
100 |
101 |
102 | class CategoryDetailView(DetailView):
103 | template_name = 'blog/category_detail.html'
104 | model = Category
105 |
106 | def get_context_data(self, **kwargs):
107 | context = super().get_context_data(**kwargs)
108 | context['posts'] = Post.objects.filter(
109 | lang=translation.get_language(),
110 | status=Post.PUBLISHED,
111 | category=self.get_object(),
112 | ).order_by('-created')
113 | return context
114 |
115 |
116 | class PageDetailView(DetailView):
117 | template_name = 'blog/page.html'
118 | model = Page
119 |
120 | def get_queryset(self):
121 | return (
122 | super().get_queryset().filter(lang=translation.get_language(), visible=True)
123 | )
124 |
125 |
126 | class SubscriptionView(View):
127 | def post(self, request):
128 | form = EmailForm(request.POST)
129 | if form.is_valid():
130 | try:
131 | EmailSubscription.objects.create(
132 | email=form.cleaned_data['email'],
133 | lang=translation.get_language()
134 | )
135 | except: # duplicate email
136 | pass
137 | return HttpResponseRedirect(reverse('blog:posts'))
138 |
139 |
140 | class SearchView(View):
141 | def get(self, request):
142 | lang_mapping = {'en': 'english', 'ru': 'russian'}
143 | query = request.GET.get('query')
144 | lang = translation.get_language()
145 | lang_config = lang_mapping.get(lang, 'russian')
146 | search_query = SearchQuery(query, config=lang_config)
147 | vector = SearchVector('text', config=lang_config)
148 | results = (
149 | Post.objects.annotate(search=vector)
150 | .filter(lang=lang, search=search_query)
151 | .order_by('-created')
152 | )
153 |
154 | return render(request, 'blog/search.html', {'results': results, 'query': query})
155 |
--------------------------------------------------------------------------------
/apps/notes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/apps/notes/__init__.py
--------------------------------------------------------------------------------
/apps/notes/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django import forms
3 | from redactor.widgets import RedactorEditor
4 |
5 | from apps.notes.models import Note, Theme
6 |
7 |
8 | class NoteAdminForm(forms.ModelForm):
9 | class Meta:
10 | model = Note
11 | fields = ['title', 'slug', 'text', 'lang', 'status', 'theme']
12 | widgets = {
13 | 'text': RedactorEditor(redactor_options={
14 | 'plugins': ['table'],
15 |
16 | }),
17 | }
18 |
19 |
20 | class NoteAdmin(admin.ModelAdmin):
21 | list_display = ['title', 'lang', 'slug', 'status', 'created']
22 | search_fields = ['title']
23 | ordering = ['-created']
24 | form = NoteAdminForm
25 |
26 |
27 | class ThemeAdmin(admin.ModelAdmin):
28 | list_display = ['title', 'slug']
29 | search_fields = ['title']
30 |
31 |
32 | admin.site.register(Note, NoteAdmin)
33 | admin.site.register(Theme, ThemeAdmin)
34 |
--------------------------------------------------------------------------------
/apps/notes/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class NotesConfig(AppConfig):
5 | name = 'apps.notes'
6 |
--------------------------------------------------------------------------------
/apps/notes/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0 on 2020-04-20 03:30
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Theme',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('title', models.CharField(max_length=30, verbose_name='Title')),
20 | ('slug', models.SlugField(max_length=64, verbose_name='Slug')),
21 | ],
22 | options={
23 | 'verbose_name': 'theme',
24 | 'verbose_name_plural': 'Themes',
25 | },
26 | ),
27 | migrations.CreateModel(
28 | name='Note',
29 | fields=[
30 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
31 | ('slug', models.SlugField(max_length=100, verbose_name='Slug')),
32 | ('title', models.CharField(max_length=254, verbose_name='Title')),
33 | ('text', models.TextField(verbose_name='Text')),
34 | ('created', models.DateTimeField(auto_now_add=True)),
35 | ('lang', models.CharField(choices=[('ru', 'Russian'), ('en', 'English')], max_length=2, verbose_name='Language')),
36 | ('status', models.SmallIntegerField(choices=[(0, 'Draft'), (1, 'Published')], verbose_name='Status')),
37 | ('theme', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notes.Theme')),
38 | ],
39 | options={
40 | 'verbose_name': 'note',
41 | 'verbose_name_plural': 'Notes',
42 | },
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/apps/notes/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/apps/notes/migrations/__init__.py
--------------------------------------------------------------------------------
/apps/notes/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.translation import gettext_lazy as _
3 | from django.conf import settings
4 | from django.contrib.contenttypes.models import ContentType
5 | from django.urls import reverse
6 |
7 |
8 |
9 |
10 | class Theme(models.Model):
11 | title = models.CharField(_('Title'), max_length=30)
12 | slug = models.SlugField(_('Slug'), max_length=64)
13 |
14 | class Meta:
15 | verbose_name = _('theme')
16 | verbose_name_plural = _('Themes')
17 |
18 | def __str__(self):
19 | return self.title
20 |
21 |
22 | class Note(models.Model):
23 | DRAFT, PUBLISHED = range(2)
24 |
25 | STATUSES = ((DRAFT, _('Draft')), (PUBLISHED, _('Published')))
26 |
27 | slug = models.SlugField(_('Slug'), max_length=100)
28 | title = models.CharField(_('Title'), max_length=254)
29 | text = models.TextField(_('Text'))
30 | created = models.DateTimeField(auto_now_add=True)
31 | lang = models.CharField(_('Language'), max_length=2, choices=settings.LANGUAGES)
32 | status = models.SmallIntegerField(_('Status'), choices=STATUSES)
33 | theme = models.ForeignKey('Theme', on_delete=models.CASCADE)
34 |
35 | class Meta:
36 | verbose_name = _('note')
37 | verbose_name_plural = _('Notes')
38 |
39 | def get_absolute_url(self):
40 | return '/{}/{}/'.format(self.lang, self.slug)
41 |
42 | def get_admin_url(self):
43 | content_type = ContentType.objects.get_for_model(self.__class__)
44 | return reverse(
45 | 'admin:%s_%s_change' % (content_type.app_label, content_type.model),
46 | args=(self.id,),
47 | )
48 |
49 | def __str__(self):
50 | return self.title
51 |
--------------------------------------------------------------------------------
/apps/notes/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/apps/notes/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from apps.notes.views import (
4 | NoteDetailView,
5 | NotesListView,
6 | )
7 |
8 | app_name = 'notes'
9 | urlpatterns = [
10 | path('', NotesListView.as_view(), name='notes-list'),
11 | path('//', NoteDetailView.as_view(), name='note-detail'),
12 | ]
13 |
--------------------------------------------------------------------------------
/apps/notes/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, get_object_or_404
2 | from django.views.generic import ListView, View
3 | from django.utils import translation
4 |
5 | from apps.notes.models import Note, Theme
6 |
7 |
8 | class NotesListView(ListView):
9 | model = Note
10 | template_name = 'notes/notes.html'
11 |
12 | def get_queryset(self):
13 | return (
14 | super()
15 | .get_queryset()
16 | .filter(lang=translation.get_language(), status=Note.PUBLISHED)
17 | .order_by('theme', '-created')
18 | .select_related('theme')
19 | )
20 |
21 |
22 | class NoteDetailView(View):
23 | def get(self, request, theme_name: str, slug: str):
24 | theme = get_object_or_404(Theme, slug=theme_name)
25 | note = get_object_or_404(Note, slug=slug, status=Note.PUBLISHED)
26 | return render(request, 'notes/note_detail.html', {
27 | 'theme': theme,
28 | 'object': note
29 | })
30 |
--------------------------------------------------------------------------------
/assets/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /ru/search/*
3 | Disallow: /en/search/*
4 | Host: https://khashtamov.com
5 | Sitemap: https://khashtamov.com/sitemap.xml
6 |
--------------------------------------------------------------------------------
/locale/en/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2021-01-11 18:34+0600\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: .\apps\blog\models.py:11 .\apps\blog\models.py:31 .\apps\notes\models.py:9
21 | #: .\apps\notes\models.py:26
22 | msgid "Title"
23 | msgstr ""
24 |
25 | #: .\apps\blog\models.py:16 .\templates\blog\category_detail.html:3
26 | #: .\templates\blog\category_detail.html:9
27 | msgid "category"
28 | msgstr ""
29 |
30 | #: .\apps\blog\models.py:17
31 | msgid "categories"
32 | msgstr ""
33 |
34 | #: .\apps\blog\models.py:27 .\apps\notes\models.py:23
35 | #: .\templates\blog\index.html:26 .\templates\blog\post_detail.html:36
36 | #: .\templates\notes\note_detail.html:22
37 | msgid "Draft"
38 | msgstr ""
39 |
40 | #: .\apps\blog\models.py:28 .\apps\notes\models.py:23
41 | msgid "Published"
42 | msgstr ""
43 |
44 | #: .\apps\blog\models.py:32 .\apps\notes\models.py:10 .\apps\notes\models.py:25
45 | msgid "Slug"
46 | msgstr ""
47 |
48 | #: .\apps\blog\models.py:33 .\apps\blog\models.py:60 .\apps\notes\models.py:27
49 | msgid "Text"
50 | msgstr ""
51 |
52 | #: .\apps\blog\models.py:34 .\apps\blog\models.py:61 .\apps\blog\models.py:78
53 | #: .\apps\blog\models.py:101 .\apps\notes\models.py:29
54 | msgid "Language"
55 | msgstr ""
56 |
57 | #: .\apps\blog\models.py:36 .\apps\notes\models.py:30
58 | msgid "Status"
59 | msgstr ""
60 |
61 | #: .\apps\blog\models.py:37
62 | msgid "Page views"
63 | msgstr ""
64 |
65 | #: .\apps\blog\models.py:42
66 | msgid "post"
67 | msgstr ""
68 |
69 | #: .\apps\blog\models.py:43
70 | msgid "Posts"
71 | msgstr ""
72 |
73 | #: .\apps\blog\models.py:66
74 | msgid "page"
75 | msgstr ""
76 |
77 | #: .\apps\blog\models.py:67
78 | msgid "Pages"
79 | msgstr ""
80 |
81 | #: .\apps\blog\models.py:77
82 | msgid "Email"
83 | msgstr ""
84 |
85 | #: .\apps\blog\models.py:79
86 | msgid "Activated"
87 | msgstr ""
88 |
89 | #: .\apps\blog\models.py:80
90 | msgid "Activation code"
91 | msgstr ""
92 |
93 | #: .\apps\blog\models.py:81 .\apps\blog\models.py:104
94 | msgid "Created"
95 | msgstr ""
96 |
97 | #: .\apps\blog\models.py:82
98 | msgid "Updated"
99 | msgstr ""
100 |
101 | #: .\apps\blog\models.py:85
102 | msgid "subscription"
103 | msgstr ""
104 |
105 | #: .\apps\blog\models.py:86
106 | msgid "Subscriptions"
107 | msgstr ""
108 |
109 | #: .\apps\blog\models.py:100
110 | msgid "Original post"
111 | msgstr ""
112 |
113 | #: .\apps\blog\models.py:103
114 | msgid "Alternate post"
115 | msgstr ""
116 |
117 | #: .\apps\blog\models.py:107
118 | msgid "alternate URL"
119 | msgstr ""
120 |
121 | #: .\apps\blog\models.py:108
122 | msgid "Alternate URLs"
123 | msgstr ""
124 |
125 | #: .\apps\blog\views.py:20
126 | msgid "Adil Khashtamov's personal blog"
127 | msgstr ""
128 |
129 | #: .\apps\blog\views.py:21
130 | msgid "pragmatic programmer"
131 | msgstr ""
132 |
133 | #: .\apps\notes\models.py:13
134 | msgid "theme"
135 | msgstr ""
136 |
137 | #: .\apps\notes\models.py:14
138 | msgid "Themes"
139 | msgstr ""
140 |
141 | #: .\apps\notes\models.py:34
142 | msgid "note"
143 | msgstr ""
144 |
145 | #: .\apps\notes\models.py:35 .\templates\base.html:40
146 | msgid "Notes"
147 | msgstr ""
148 |
149 | #: .\notion\settings.py:118
150 | msgid "Russian"
151 | msgstr ""
152 |
153 | #: .\notion\settings.py:119
154 | msgid "English"
155 | msgstr ""
156 |
157 | #: .\templates\base.html:11
158 | msgid ""
159 | "Software engineer personal blog. Usually write about building web apps, "
160 | "machine learning and data analysis"
161 | msgstr ""
162 |
163 | #: .\templates\base.html:13
164 | msgid "Adil Khashtamov's personal blog — pragmatic programmer"
165 | msgstr ""
166 |
167 | #: .\templates\base.html:39
168 | msgid "Blog"
169 | msgstr ""
170 |
171 | #: .\templates\base.html:41
172 | msgid "Archives"
173 | msgstr ""
174 |
175 | #: .\templates\base.html:42
176 | msgid "Contacts"
177 | msgstr ""
178 |
179 | #: .\templates\base.html:51
180 | msgid "Admin"
181 | msgstr ""
182 |
183 | #: .\templates\base.html:67
184 | msgid "Powered by"
185 | msgstr ""
186 |
187 | #: .\templates\blog\_sidebar.html:9
188 | msgid "Subscription"
189 | msgstr ""
190 |
191 | #: .\templates\blog\_sidebar.html:14 .\templates\blog\post_detail.html:53
192 | msgid "e-mail"
193 | msgstr ""
194 |
195 | #: .\templates\blog\_sidebar.html:16 .\templates\blog\post_detail.html:55
196 | msgid "Subscribe"
197 | msgstr ""
198 |
199 | #: .\templates\blog\_sidebar.html:21
200 | msgid "Search"
201 | msgstr ""
202 |
203 | #: .\templates\blog\_sidebar.html:25
204 | msgid "search term"
205 | msgstr ""
206 |
207 | #: .\templates\blog\_sidebar.html:28
208 | msgid "Find"
209 | msgstr ""
210 |
211 | #: .\templates\blog\_sidebar.html:34
212 | msgid "Recent notes"
213 | msgstr ""
214 |
215 | #: .\templates\blog\_sidebar.html:43
216 | msgid "Recent posts"
217 | msgstr ""
218 |
219 | #: .\templates\blog\_sidebar.html:55
220 | msgid "Ad"
221 | msgstr ""
222 |
223 | #: .\templates\blog\archives.html:3 .\templates\blog\archives.html:9
224 | msgid "Archive of posts"
225 | msgstr ""
226 |
227 | #: .\templates\blog\archives.html:11
228 | msgid "By category"
229 | msgstr ""
230 |
231 | #: .\templates\blog\archives.html:23
232 | msgid "By year"
233 | msgstr ""
234 |
235 | #: .\templates\blog\index.html:24 .\templates\blog\post_detail.html:34
236 | #: .\templates\notes\note_detail.html:20
237 | msgid "Edit"
238 | msgstr ""
239 |
240 | #: .\templates\blog\index.html:30
241 | msgid "read more"
242 | msgstr ""
243 |
244 | #: .\templates\blog\post_detail.html:31 .\templates\blog\search.html:26
245 | msgid "views"
246 | msgstr ""
247 |
248 | #: .\templates\blog\post_detail.html:45
249 | msgid "Join the mailing list"
250 | msgstr ""
251 |
252 | #: .\templates\blog\post_detail.html:46
253 | msgid ""
254 | "If you like the content I produce, please join my mailing list to stay tuned."
255 | msgstr ""
256 |
257 | #: .\templates\blog\post_detail.html:65
258 | msgid "Related posts"
259 | msgstr ""
260 |
261 | #: .\templates\blog\search.html:8
262 | #, python-format
263 | msgid "Results for «%(query)s»"
264 | msgstr ""
265 |
266 | #: .\templates\blog\search.html:16
267 | msgid "No articles found"
268 | msgstr ""
269 |
270 | #: .\templates\blog\search.html:18
271 | msgid "Articles found"
272 | msgstr ""
273 |
274 | #: .\templates\notes\notes.html:5 .\templates\notes\notes.html:11
275 | msgid "Useful notes"
276 | msgstr ""
277 |
278 | #: .\templates\tags\jobs.html:7
279 | msgid "Remote jobs"
280 | msgstr ""
281 |
--------------------------------------------------------------------------------
/locale/ru/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: \n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2021-03-04 22:11+0600\n"
11 | "PO-Revision-Date: 2021-03-04 22:12+0600\n"
12 | "Last-Translator: \n"
13 | "Language-Team: \n"
14 | "Language: ru\n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
20 | "%100>=11 && n%100<=14)? 2 : 3);\n"
21 | "X-Generator: Poedit 2.3\n"
22 |
23 | #: .\apps\blog\models.py:11 .\apps\blog\models.py:31 .\apps\notes\models.py:9
24 | #: .\apps\notes\models.py:26
25 | msgid "Title"
26 | msgstr "Заголовок"
27 |
28 | #: .\apps\blog\models.py:16 .\templates\blog\category_detail.html:3
29 | #: .\templates\blog\category_detail.html:9
30 | msgid "category"
31 | msgstr "категория"
32 |
33 | #: .\apps\blog\models.py:17
34 | msgid "categories"
35 | msgstr "категории"
36 |
37 | #: .\apps\blog\models.py:27 .\apps\notes\models.py:23
38 | #: .\templates\blog\index.html:26 .\templates\blog\post_detail.html:37
39 | #: .\templates\notes\note_detail.html:22
40 | msgid "Draft"
41 | msgstr "Черновик"
42 |
43 | #: .\apps\blog\models.py:28 .\apps\notes\models.py:23
44 | msgid "Published"
45 | msgstr "Опубликован"
46 |
47 | #: .\apps\blog\models.py:32 .\apps\notes\models.py:10 .\apps\notes\models.py:25
48 | msgid "Slug"
49 | msgstr "Ссылка"
50 |
51 | #: .\apps\blog\models.py:33 .\apps\blog\models.py:60 .\apps\notes\models.py:27
52 | msgid "Text"
53 | msgstr "Текст"
54 |
55 | #: .\apps\blog\models.py:34 .\apps\blog\models.py:61 .\apps\blog\models.py:78
56 | #: .\apps\blog\models.py:101 .\apps\notes\models.py:29
57 | msgid "Language"
58 | msgstr "Язык"
59 |
60 | #: .\apps\blog\models.py:36 .\apps\notes\models.py:30
61 | msgid "Status"
62 | msgstr "Статус"
63 |
64 | #: .\apps\blog\models.py:37
65 | msgid "Page views"
66 | msgstr "Просмотры"
67 |
68 | #: .\apps\blog\models.py:42
69 | msgid "post"
70 | msgstr "запись"
71 |
72 | #: .\apps\blog\models.py:43
73 | msgid "Posts"
74 | msgstr "Записи"
75 |
76 | #: .\apps\blog\models.py:66
77 | msgid "page"
78 | msgstr "траница"
79 |
80 | #: .\apps\blog\models.py:67
81 | msgid "Pages"
82 | msgstr "Страницы"
83 |
84 | #: .\apps\blog\models.py:77
85 | msgid "Email"
86 | msgstr "Эл.адрес"
87 |
88 | #: .\apps\blog\models.py:79
89 | msgid "Activated"
90 | msgstr "Активирован"
91 |
92 | #: .\apps\blog\models.py:80
93 | msgid "Activation code"
94 | msgstr "Код активации"
95 |
96 | #: .\apps\blog\models.py:81 .\apps\blog\models.py:104
97 | msgid "Created"
98 | msgstr "Создан"
99 |
100 | #: .\apps\blog\models.py:82
101 | msgid "Updated"
102 | msgstr "Обновлён"
103 |
104 | #: .\apps\blog\models.py:85
105 | msgid "subscription"
106 | msgstr "подписка"
107 |
108 | #: .\apps\blog\models.py:86
109 | msgid "Subscriptions"
110 | msgstr "Подписки"
111 |
112 | #: .\apps\blog\models.py:100
113 | msgid "Original post"
114 | msgstr "Оригинальный пост"
115 |
116 | #: .\apps\blog\models.py:103
117 | msgid "Alternate post"
118 | msgstr "Альтернативные посты"
119 |
120 | #: .\apps\blog\models.py:107
121 | msgid "alternate URL"
122 | msgstr "альтернативный URL"
123 |
124 | #: .\apps\blog\models.py:108
125 | msgid "Alternate URLs"
126 | msgstr "Альтернативные URL"
127 |
128 | #: .\apps\blog\views.py:21
129 | msgid "Adil Khashtamov's personal blog"
130 | msgstr "Персональный блог Адиля Хаштамова"
131 |
132 | #: .\apps\blog\views.py:22
133 | msgid "pragmatic programmer"
134 | msgstr "программист-прагматик"
135 |
136 | #: .\apps\notes\models.py:13
137 | msgid "theme"
138 | msgstr "тема"
139 |
140 | #: .\apps\notes\models.py:14
141 | msgid "Themes"
142 | msgstr "Темы"
143 |
144 | #: .\apps\notes\models.py:34
145 | msgid "note"
146 | msgstr "заметка"
147 |
148 | #: .\apps\notes\models.py:35 .\templates\base.html:40
149 | msgid "Notes"
150 | msgstr "Заметки"
151 |
152 | #: .\notion\settings.py:121
153 | msgid "Russian"
154 | msgstr "Русский"
155 |
156 | #: .\notion\settings.py:122
157 | msgid "English"
158 | msgstr "Английский"
159 |
160 | #: .\templates\base.html:11
161 | msgid ""
162 | "Software engineer personal blog. Usually write about building web apps, "
163 | "machine learning and data analysis"
164 | msgstr ""
165 | "Персональный блог программиста-прагматика. Пишу о разработке веб-приложений, "
166 | "машинном обучении и анализе данных"
167 |
168 | #: .\templates\base.html:13
169 | msgid "Adil Khashtamov's personal blog — pragmatic programmer"
170 | msgstr "Персональный блог Адиля Хаштамова — программист-прагматик"
171 |
172 | #: .\templates\base.html:39
173 | msgid "Blog"
174 | msgstr "Блог"
175 |
176 | #: .\templates\base.html:41
177 | msgid "Archives"
178 | msgstr "Архивы"
179 |
180 | #: .\templates\base.html:42
181 | msgid "Contacts"
182 | msgstr "Контакты"
183 |
184 | #: .\templates\base.html:51
185 | msgid "Admin"
186 | msgstr "Админка"
187 |
188 | #: .\templates\base.html:67
189 | msgid "Powered by"
190 | msgstr "Работает на"
191 |
192 | #: .\templates\blog\_sidebar.html:9
193 | msgid "Subscription"
194 | msgstr "Подписка"
195 |
196 | #: .\templates\blog\_sidebar.html:14 .\templates\blog\post_detail.html:63
197 | msgid "e-mail"
198 | msgstr "эл.адрес"
199 |
200 | #: .\templates\blog\_sidebar.html:16 .\templates\blog\post_detail.html:65
201 | msgid "Subscribe"
202 | msgstr "Подписаться"
203 |
204 | #: .\templates\blog\_sidebar.html:21
205 | msgid "Search"
206 | msgstr "Поиск"
207 |
208 | #: .\templates\blog\_sidebar.html:25
209 | msgid "search term"
210 | msgstr "поиск"
211 |
212 | #: .\templates\blog\_sidebar.html:28
213 | msgid "Find"
214 | msgstr "Найти"
215 |
216 | #: .\templates\blog\_sidebar.html:34
217 | msgid "Recent notes"
218 | msgstr "Последние заметки"
219 |
220 | #: .\templates\blog\_sidebar.html:43
221 | msgid "Recent posts"
222 | msgstr "Свежие записи"
223 |
224 | #: .\templates\blog\_sidebar.html:55
225 | msgid "Ad"
226 | msgstr "Реклама"
227 |
228 | #: .\templates\blog\archives.html:3 .\templates\blog\archives.html:9
229 | msgid "Archive of posts"
230 | msgstr "Архив записей"
231 |
232 | #: .\templates\blog\archives.html:11
233 | msgid "By category"
234 | msgstr "По категории"
235 |
236 | #: .\templates\blog\archives.html:23
237 | msgid "By year"
238 | msgstr "По годам"
239 |
240 | #: .\templates\blog\index.html:24 .\templates\blog\post_detail.html:34
241 | #: .\templates\notes\note_detail.html:20
242 | msgid "Edit"
243 | msgstr "Редактировать"
244 |
245 | #: .\templates\blog\index.html:30
246 | msgid "read more"
247 | msgstr "читать дальше"
248 |
249 | #: .\templates\blog\post_detail.html:31 .\templates\blog\search.html:26
250 | msgid "views"
251 | msgstr "просмотров"
252 |
253 | #: .\templates\blog\post_detail.html:45
254 | msgid "Translation in "
255 | msgstr "Перевод на"
256 |
257 | #: .\templates\blog\post_detail.html:55
258 | msgid "Join the mailing list"
259 | msgstr "Присоединяйтесь к рассылке"
260 |
261 | #: .\templates\blog\post_detail.html:56
262 | msgid ""
263 | "If you like the content I produce, please join my mailing list to stay tuned."
264 | msgstr "Понравился контент? Пожалуйста, подпишись на рассылку."
265 |
266 | #: .\templates\blog\post_detail.html:73
267 | msgid "Related posts"
268 | msgstr "Интересные записи"
269 |
270 | #: .\templates\blog\search.html:8
271 | #, python-format
272 | msgid "Results for «%(query)s»"
273 | msgstr "Результаты поиска по запросу «%(query)s»"
274 |
275 | #: .\templates\blog\search.html:16
276 | msgid "No articles found"
277 | msgstr "Ничего не найдено"
278 |
279 | #: .\templates\blog\search.html:18
280 | msgid "Articles found"
281 | msgstr "Найдено статей"
282 |
283 | #: .\templates\notes\notes.html:5 .\templates\notes\notes.html:11
284 | msgid "Useful notes"
285 | msgstr "Полезные заметки"
286 |
287 | #: .\templates\tags\jobs.html:7
288 | msgid "Remote jobs"
289 | msgstr "Удаленная работа"
290 |
291 | #~ msgid "Invalid email"
292 | #~ msgstr "Неверный эл.адрес"
293 |
294 | #~ msgid "Email required"
295 | #~ msgstr "Необходимо указать эл.адрес"
296 |
297 | #~ msgid "Adil Khashtamov"
298 | #~ msgstr "Адиль Хаштамов"
299 |
300 | #~ msgid ""
301 | #~ "\n"
302 | #~ "Hi. My name is Adil. I am passionate about building software. This blog "
303 | #~ "is a notebook of my thoughts and ideas regarding software engineering and "
304 | #~ "life around it. Currently I am interested in engineering of modern web "
305 | #~ "apps, machine learning and data analysis.\n"
306 | #~ msgstr ""
307 | #~ "\n"
308 | #~ "Привет! Меня зовут Адиль. Всю свою сознательную жизнь я занимаюсь "
309 | #~ "разработкой программного обеспечения. Этот блог моя записная книжка, где "
310 | #~ "я стараюсь почаще делать заметки на тему программирования, разработки и "
311 | #~ "проектирования ПО. Сейчас особенно заинтересован в современных веб "
312 | #~ "приложениях, машинном обучении и анализе данных.\n"
313 |
314 | #~ msgid "Telegram"
315 | #~ msgstr "Telegram"
316 |
317 | #~ msgid "Most popular"
318 | #~ msgstr "Самые популярные"
319 |
320 | #~ msgid "Telegram channel"
321 | #~ msgstr "Telegram канал"
322 |
323 | #~ msgid ""
324 | #~ "Recently I opened my own Telegram channel related to software\n"
325 | #~ " development called "
326 | #~ "DevBrain where I share my thoughts and\n"
327 | #~ " external resources (articles, "
328 | #~ "videos, presentations, book recommendations)\n"
329 | #~ " regarding software "
330 | #~ "craftsmanship."
331 | #~ msgstr ""
332 | #~ "Не так давно я открыл канал в Telegram на тему разработки программного "
333 | #~ "обеспечения, называется он DevBrain . На канале я делюсь "
334 | #~ "своими мыслями , а также ресурсами (статьями, видео, презентациями, "
335 | #~ "книжными рекомендациями и т.д.) о кодинге, методологиях разработки и "
336 | #~ "многом другом с чем нам, разработчикам, приходится сталкиваться каждый "
337 | #~ "день в работе."
338 |
339 | #~ msgid ""
340 | #~ "There are more than 2000 members already, so please join us —\n"
341 | #~ " DevBrain "
343 | #~ msgstr ""
344 | #~ "На канале уже более 3000 участников, поэтому присоединяйтесь и Вы — DevBrain "
347 |
348 | #~ msgid ""
349 | #~ "If you like my articles, please subscribe to stay up to date with new "
350 | #~ "material"
351 | #~ msgstr ""
352 | #~ "Если вам нравится материал на сайте — подпишитесь, чтобы не пропустить "
353 | #~ "обновления"
354 |
355 | #~ msgid "Put your email here"
356 | #~ msgstr "Введите электронный адрес"
357 |
358 | #~ msgid "Thank you! You have been successfuly subscribed."
359 | #~ msgstr "Спасибо! Вы успешно подписались."
360 |
361 | #~ msgid "Powered by Django 1.11 & Python 3.5"
362 | #~ msgstr "Работает на Django 1.11 & Python 3.5"
363 |
364 | #~ msgid "In English"
365 | #~ msgstr "На английской"
366 |
367 | #~ msgid "In Russian"
368 | #~ msgstr "На русском"
369 |
--------------------------------------------------------------------------------
/logs/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/logs/.keep
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "notion.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/notion/.env-template:
--------------------------------------------------------------------------------
1 | DEBUG=True
2 | SECRET_KEY='very-secret-key'
3 | ALLOWED_HOSTS=127.0.0.1
4 | DATABASE_URL=psql://notion:password@127.0.0.1:5432/notion
5 |
--------------------------------------------------------------------------------
/notion/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/notion/__init__.py
--------------------------------------------------------------------------------
/notion/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django.utils.translation import gettext_lazy as _
4 | import environ
5 |
6 | env = environ.Env(DEBUG=(bool, False))
7 | environ.Env.read_env()
8 | root = environ.Path(__file__) - 2
9 |
10 | BASE_DIR = str(root)
11 |
12 | SECRET_KEY = env.str('SECRET_KEY', 'my-santa-claus')
13 |
14 | DEBUG = env.bool('DEBUG', True)
15 |
16 | ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
17 |
18 |
19 | INSTALLED_APPS = [
20 | 'django.contrib.admin',
21 | 'django.contrib.auth',
22 | 'django.contrib.contenttypes',
23 | 'django.contrib.sessions',
24 | 'django.contrib.messages',
25 | 'django.contrib.staticfiles',
26 | 'django.contrib.sites',
27 | 'django.contrib.redirects',
28 | 'django.contrib.sitemaps',
29 |
30 | # project apps
31 | 'apps.blog.apps.BlogConfig',
32 | 'apps.notes.apps.NotesConfig',
33 |
34 | # 3rd party apps
35 | 'redactor',
36 | 'bootstrap_pagination',
37 | 'django_assets',
38 | ]
39 | MIDDLEWARE = [
40 | 'django.middleware.security.SecurityMiddleware',
41 | 'django.contrib.sessions.middleware.SessionMiddleware',
42 | 'django.middleware.locale.LocaleMiddleware',
43 | 'django.middleware.common.CommonMiddleware',
44 | 'django.middleware.csrf.CsrfViewMiddleware',
45 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
46 | 'django.contrib.messages.middleware.MessageMiddleware',
47 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
48 | 'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
49 |
50 | 'htmlmin.middleware.HtmlMinifyMiddleware',
51 | 'htmlmin.middleware.MarkRequestMiddleware',
52 | ]
53 |
54 | HTML_MINIFY = True
55 |
56 | ROOT_URLCONF = 'notion.urls'
57 |
58 | TEMPLATES = [
59 | {
60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
61 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
62 | 'APP_DIRS': True,
63 | 'OPTIONS': {
64 | 'context_processors': [
65 | 'django.template.context_processors.debug',
66 | 'django.template.context_processors.request',
67 | 'django.contrib.auth.context_processors.auth',
68 | 'django.contrib.messages.context_processors.messages',
69 | 'apps.blog.context_processor.generic',
70 | ],
71 | },
72 | },
73 | ]
74 |
75 | WSGI_APPLICATION = 'notion.wsgi.application'
76 |
77 | DATABASES = {
78 | 'default': env.db(),
79 | }
80 |
81 | AUTH_PASSWORD_VALIDATORS = [
82 | {
83 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
84 | },
85 | {
86 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
87 | },
88 | {
89 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
90 | },
91 | {
92 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
93 | },
94 | ]
95 |
96 | LANGUAGE_CODE = 'ru'
97 |
98 | TIME_ZONE = 'UTC'
99 |
100 | USE_I18N = True
101 |
102 | USE_L10N = True
103 |
104 | USE_TZ = True
105 |
106 | STATIC_URL = '/static/'
107 |
108 | STATIC_ROOT = os.path.join(BASE_DIR, 'static')
109 |
110 | STATICFILES_DIRS = [
111 | os.path.join(BASE_DIR, 'staticfiles')
112 | ]
113 |
114 | STATICFILES_FINDERS = (
115 | "django.contrib.staticfiles.finders.FileSystemFinder",
116 | "django.contrib.staticfiles.finders.AppDirectoriesFinder",
117 | "django_assets.finders.AssetsFinder"
118 | )
119 |
120 | LANGUAGES = (
121 | ('ru', _('Russian')),
122 | ('en', _('English')),
123 | )
124 |
125 | LOCALE_PATHS = (
126 | os.path.join(BASE_DIR, 'locale'),
127 | )
128 |
129 | MAX_LOG_FILE_SIZE = 1024 * 1024 * 50 # 50 megabytes
130 |
131 | LOGGING = {
132 | 'version': 1,
133 | 'disable_existing_loggers': True,
134 | 'filters': {
135 | 'require_debug_false': {
136 | '()': 'django.utils.log.RequireDebugFalse'
137 | }
138 | },
139 | 'formatters': {
140 | 'verbose': {
141 | 'format': '[%(asctime)s]: %(levelname)s: %(message)s'
142 | },
143 | },
144 | 'handlers': {
145 | 'mail_admins': {
146 | 'level': 'ERROR',
147 | 'filters': ['require_debug_false'],
148 | 'class': 'django.utils.log.AdminEmailHandler'
149 | },
150 | 'console': {
151 | 'level': 'DEBUG',
152 | 'formatter': 'verbose',
153 | 'class': 'logging.StreamHandler'
154 | },
155 | 'file_handler': {
156 | 'filename': os.path.join(BASE_DIR, 'logs', 'notion.log'),
157 | 'class': 'logging.handlers.RotatingFileHandler',
158 | 'encoding': 'utf-8',
159 | 'formatter': 'verbose',
160 | 'maxBytes': MAX_LOG_FILE_SIZE,
161 | 'backupCount': 20,
162 | },
163 | 'null': {
164 | 'class': 'logging.NullHandler',
165 | }
166 | },
167 | 'loggers': {
168 | 'django': {
169 | 'handlers': ['file_handler'],
170 | 'level': 'ERROR',
171 | 'propagate': True,
172 | },
173 | }
174 | }
175 |
176 | MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
177 |
178 | REDACTOR_UPLOAD = 'redactor/'
179 | REDACTOR_OPTIONS = {'lang': 'ru', 'removeComments': False}
180 |
181 | MEDIA_URL = '/uploads/'
182 |
183 | SECURE_REFERRER_POLICY = 'no-referrer-when-downgrade'
184 |
--------------------------------------------------------------------------------
/notion/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include
2 | from django.contrib import admin
3 | from django.urls import path
4 | from django.conf.urls.i18n import i18n_patterns
5 | from django.contrib.sitemaps.views import sitemap
6 | from django.contrib.sitemaps import GenericSitemap
7 | from django.conf.urls.static import static
8 | from django.conf import settings
9 |
10 | from apps.blog.models import Post, Page
11 | from apps.blog.views import SubscriptionView
12 |
13 | blog_dict = {
14 | 'queryset': Post.objects.filter(status=Post.PUBLISHED),
15 | 'date_field': 'created',
16 | }
17 |
18 | page_dict = {
19 | 'queryset': Page.objects.filter(visible=True),
20 | 'date_field': 'created'
21 | }
22 |
23 | urlpatterns = [
24 | path(
25 | 'sitemap.xml',
26 | sitemap, {
27 | 'sitemaps': {
28 | 'blog': GenericSitemap(blog_dict, priority=1, protocol='https'),
29 | 'page': GenericSitemap(page_dict, priority=0.5, protocol='https')
30 | }
31 | }, name='django.contrib.sitemaps.views.sitemap'
32 | ),
33 | path('redactor/', include('redactor.urls')),
34 | path('cpadmin/', admin.site.urls),
35 | path('subscribe/', SubscriptionView.as_view(), name='subscription'),
36 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
37 |
38 | urlpatterns += i18n_patterns(
39 | path('notes/', include('apps.notes.urls', namespace='notes')),
40 | path('', include('apps.blog.urls', namespace='blog')),
41 | )
42 |
--------------------------------------------------------------------------------
/notion/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for notion project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "notion.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==3.2.18
2 | psycopg2==2.9.5
3 | django-environ==0.11.2
4 | https://github.com/adilkhash/django-redactor3/archive/master.zip
5 | redis==4.4.2
6 | https://github.com/adilkhash/django-bootstrap-pagination/archive/master.zip
7 | requests==2.28.2
8 | Pillow==9.4.0
9 | django-assets==2.0
10 | cssmin==0.2.0
11 | django-htmlmin==0.11.0
12 |
--------------------------------------------------------------------------------
/staticfiles/courses/images/adil.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/adil.jpg
--------------------------------------------------------------------------------
/staticfiles/courses/images/bgimage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/bgimage.jpg
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_aws.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_aws.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_bootstrap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_bootstrap.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_circle_ci.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_circle_ci.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_django.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_django.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_do.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_do.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_docker.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_es6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_es6.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_falcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_falcon.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_jest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_jest.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_jwt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_jwt.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_postman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_postman.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_python.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/logo_vue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/logo_vue.png
--------------------------------------------------------------------------------
/staticfiles/courses/images/luigi-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/courses/images/luigi-logo.png
--------------------------------------------------------------------------------
/staticfiles/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | .btn-default,
7 | .btn-primary,
8 | .btn-success,
9 | .btn-info,
10 | .btn-warning,
11 | .btn-danger {
12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
15 | }
16 | .btn-default:active,
17 | .btn-primary:active,
18 | .btn-success:active,
19 | .btn-info:active,
20 | .btn-warning:active,
21 | .btn-danger:active,
22 | .btn-default.active,
23 | .btn-primary.active,
24 | .btn-success.active,
25 | .btn-info.active,
26 | .btn-warning.active,
27 | .btn-danger.active {
28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
30 | }
31 | .btn-default.disabled,
32 | .btn-primary.disabled,
33 | .btn-success.disabled,
34 | .btn-info.disabled,
35 | .btn-warning.disabled,
36 | .btn-danger.disabled,
37 | .btn-default[disabled],
38 | .btn-primary[disabled],
39 | .btn-success[disabled],
40 | .btn-info[disabled],
41 | .btn-warning[disabled],
42 | .btn-danger[disabled],
43 | fieldset[disabled] .btn-default,
44 | fieldset[disabled] .btn-primary,
45 | fieldset[disabled] .btn-success,
46 | fieldset[disabled] .btn-info,
47 | fieldset[disabled] .btn-warning,
48 | fieldset[disabled] .btn-danger {
49 | -webkit-box-shadow: none;
50 | box-shadow: none;
51 | }
52 | .btn-default .badge,
53 | .btn-primary .badge,
54 | .btn-success .badge,
55 | .btn-info .badge,
56 | .btn-warning .badge,
57 | .btn-danger .badge {
58 | text-shadow: none;
59 | }
60 | .btn:active,
61 | .btn.active {
62 | background-image: none;
63 | }
64 | .btn-default {
65 | text-shadow: 0 1px 0 #fff;
66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
72 | background-repeat: repeat-x;
73 | border-color: #dbdbdb;
74 | border-color: #ccc;
75 | }
76 | .btn-default:hover,
77 | .btn-default:focus {
78 | background-color: #e0e0e0;
79 | background-position: 0 -15px;
80 | }
81 | .btn-default:active,
82 | .btn-default.active {
83 | background-color: #e0e0e0;
84 | border-color: #dbdbdb;
85 | }
86 | .btn-default.disabled,
87 | .btn-default[disabled],
88 | fieldset[disabled] .btn-default,
89 | .btn-default.disabled:hover,
90 | .btn-default[disabled]:hover,
91 | fieldset[disabled] .btn-default:hover,
92 | .btn-default.disabled:focus,
93 | .btn-default[disabled]:focus,
94 | fieldset[disabled] .btn-default:focus,
95 | .btn-default.disabled.focus,
96 | .btn-default[disabled].focus,
97 | fieldset[disabled] .btn-default.focus,
98 | .btn-default.disabled:active,
99 | .btn-default[disabled]:active,
100 | fieldset[disabled] .btn-default:active,
101 | .btn-default.disabled.active,
102 | .btn-default[disabled].active,
103 | fieldset[disabled] .btn-default.active {
104 | background-color: #e0e0e0;
105 | background-image: none;
106 | }
107 | .btn-primary {
108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
114 | background-repeat: repeat-x;
115 | border-color: #245580;
116 | }
117 | .btn-primary:hover,
118 | .btn-primary:focus {
119 | background-color: #265a88;
120 | background-position: 0 -15px;
121 | }
122 | .btn-primary:active,
123 | .btn-primary.active {
124 | background-color: #265a88;
125 | border-color: #245580;
126 | }
127 | .btn-primary.disabled,
128 | .btn-primary[disabled],
129 | fieldset[disabled] .btn-primary,
130 | .btn-primary.disabled:hover,
131 | .btn-primary[disabled]:hover,
132 | fieldset[disabled] .btn-primary:hover,
133 | .btn-primary.disabled:focus,
134 | .btn-primary[disabled]:focus,
135 | fieldset[disabled] .btn-primary:focus,
136 | .btn-primary.disabled.focus,
137 | .btn-primary[disabled].focus,
138 | fieldset[disabled] .btn-primary.focus,
139 | .btn-primary.disabled:active,
140 | .btn-primary[disabled]:active,
141 | fieldset[disabled] .btn-primary:active,
142 | .btn-primary.disabled.active,
143 | .btn-primary[disabled].active,
144 | fieldset[disabled] .btn-primary.active {
145 | background-color: #265a88;
146 | background-image: none;
147 | }
148 | .btn-success {
149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
155 | background-repeat: repeat-x;
156 | border-color: #3e8f3e;
157 | }
158 | .btn-success:hover,
159 | .btn-success:focus {
160 | background-color: #419641;
161 | background-position: 0 -15px;
162 | }
163 | .btn-success:active,
164 | .btn-success.active {
165 | background-color: #419641;
166 | border-color: #3e8f3e;
167 | }
168 | .btn-success.disabled,
169 | .btn-success[disabled],
170 | fieldset[disabled] .btn-success,
171 | .btn-success.disabled:hover,
172 | .btn-success[disabled]:hover,
173 | fieldset[disabled] .btn-success:hover,
174 | .btn-success.disabled:focus,
175 | .btn-success[disabled]:focus,
176 | fieldset[disabled] .btn-success:focus,
177 | .btn-success.disabled.focus,
178 | .btn-success[disabled].focus,
179 | fieldset[disabled] .btn-success.focus,
180 | .btn-success.disabled:active,
181 | .btn-success[disabled]:active,
182 | fieldset[disabled] .btn-success:active,
183 | .btn-success.disabled.active,
184 | .btn-success[disabled].active,
185 | fieldset[disabled] .btn-success.active {
186 | background-color: #419641;
187 | background-image: none;
188 | }
189 | .btn-info {
190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
196 | background-repeat: repeat-x;
197 | border-color: #28a4c9;
198 | }
199 | .btn-info:hover,
200 | .btn-info:focus {
201 | background-color: #2aabd2;
202 | background-position: 0 -15px;
203 | }
204 | .btn-info:active,
205 | .btn-info.active {
206 | background-color: #2aabd2;
207 | border-color: #28a4c9;
208 | }
209 | .btn-info.disabled,
210 | .btn-info[disabled],
211 | fieldset[disabled] .btn-info,
212 | .btn-info.disabled:hover,
213 | .btn-info[disabled]:hover,
214 | fieldset[disabled] .btn-info:hover,
215 | .btn-info.disabled:focus,
216 | .btn-info[disabled]:focus,
217 | fieldset[disabled] .btn-info:focus,
218 | .btn-info.disabled.focus,
219 | .btn-info[disabled].focus,
220 | fieldset[disabled] .btn-info.focus,
221 | .btn-info.disabled:active,
222 | .btn-info[disabled]:active,
223 | fieldset[disabled] .btn-info:active,
224 | .btn-info.disabled.active,
225 | .btn-info[disabled].active,
226 | fieldset[disabled] .btn-info.active {
227 | background-color: #2aabd2;
228 | background-image: none;
229 | }
230 | .btn-warning {
231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
237 | background-repeat: repeat-x;
238 | border-color: #e38d13;
239 | }
240 | .btn-warning:hover,
241 | .btn-warning:focus {
242 | background-color: #eb9316;
243 | background-position: 0 -15px;
244 | }
245 | .btn-warning:active,
246 | .btn-warning.active {
247 | background-color: #eb9316;
248 | border-color: #e38d13;
249 | }
250 | .btn-warning.disabled,
251 | .btn-warning[disabled],
252 | fieldset[disabled] .btn-warning,
253 | .btn-warning.disabled:hover,
254 | .btn-warning[disabled]:hover,
255 | fieldset[disabled] .btn-warning:hover,
256 | .btn-warning.disabled:focus,
257 | .btn-warning[disabled]:focus,
258 | fieldset[disabled] .btn-warning:focus,
259 | .btn-warning.disabled.focus,
260 | .btn-warning[disabled].focus,
261 | fieldset[disabled] .btn-warning.focus,
262 | .btn-warning.disabled:active,
263 | .btn-warning[disabled]:active,
264 | fieldset[disabled] .btn-warning:active,
265 | .btn-warning.disabled.active,
266 | .btn-warning[disabled].active,
267 | fieldset[disabled] .btn-warning.active {
268 | background-color: #eb9316;
269 | background-image: none;
270 | }
271 | .btn-danger {
272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
278 | background-repeat: repeat-x;
279 | border-color: #b92c28;
280 | }
281 | .btn-danger:hover,
282 | .btn-danger:focus {
283 | background-color: #c12e2a;
284 | background-position: 0 -15px;
285 | }
286 | .btn-danger:active,
287 | .btn-danger.active {
288 | background-color: #c12e2a;
289 | border-color: #b92c28;
290 | }
291 | .btn-danger.disabled,
292 | .btn-danger[disabled],
293 | fieldset[disabled] .btn-danger,
294 | .btn-danger.disabled:hover,
295 | .btn-danger[disabled]:hover,
296 | fieldset[disabled] .btn-danger:hover,
297 | .btn-danger.disabled:focus,
298 | .btn-danger[disabled]:focus,
299 | fieldset[disabled] .btn-danger:focus,
300 | .btn-danger.disabled.focus,
301 | .btn-danger[disabled].focus,
302 | fieldset[disabled] .btn-danger.focus,
303 | .btn-danger.disabled:active,
304 | .btn-danger[disabled]:active,
305 | fieldset[disabled] .btn-danger:active,
306 | .btn-danger.disabled.active,
307 | .btn-danger[disabled].active,
308 | fieldset[disabled] .btn-danger.active {
309 | background-color: #c12e2a;
310 | background-image: none;
311 | }
312 | .thumbnail,
313 | .img-thumbnail {
314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
316 | }
317 | .dropdown-menu > li > a:hover,
318 | .dropdown-menu > li > a:focus {
319 | background-color: #e8e8e8;
320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
325 | background-repeat: repeat-x;
326 | }
327 | .dropdown-menu > .active > a,
328 | .dropdown-menu > .active > a:hover,
329 | .dropdown-menu > .active > a:focus {
330 | background-color: #2e6da4;
331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
336 | background-repeat: repeat-x;
337 | }
338 | .navbar-default {
339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
345 | background-repeat: repeat-x;
346 | border-radius: 4px;
347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
349 | }
350 | .navbar-default .navbar-nav > .open > a,
351 | .navbar-default .navbar-nav > .active > a {
352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
357 | background-repeat: repeat-x;
358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
360 | }
361 | .navbar-brand,
362 | .navbar-nav > li > a {
363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
364 | }
365 | .navbar-inverse {
366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
372 | background-repeat: repeat-x;
373 | border-radius: 4px;
374 | }
375 | .navbar-inverse .navbar-nav > .open > a,
376 | .navbar-inverse .navbar-nav > .active > a {
377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
382 | background-repeat: repeat-x;
383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
385 | }
386 | .navbar-inverse .navbar-brand,
387 | .navbar-inverse .navbar-nav > li > a {
388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
389 | }
390 | .navbar-static-top,
391 | .navbar-fixed-top,
392 | .navbar-fixed-bottom {
393 | border-radius: 0;
394 | }
395 | @media (max-width: 767px) {
396 | .navbar .navbar-nav .open .dropdown-menu > .active > a,
397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
399 | color: #fff;
400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
405 | background-repeat: repeat-x;
406 | }
407 | }
408 | .alert {
409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
412 | }
413 | .alert-success {
414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
419 | background-repeat: repeat-x;
420 | border-color: #b2dba1;
421 | }
422 | .alert-info {
423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
428 | background-repeat: repeat-x;
429 | border-color: #9acfea;
430 | }
431 | .alert-warning {
432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
437 | background-repeat: repeat-x;
438 | border-color: #f5e79e;
439 | }
440 | .alert-danger {
441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
446 | background-repeat: repeat-x;
447 | border-color: #dca7a7;
448 | }
449 | .progress {
450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
455 | background-repeat: repeat-x;
456 | }
457 | .progress-bar {
458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
463 | background-repeat: repeat-x;
464 | }
465 | .progress-bar-success {
466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
471 | background-repeat: repeat-x;
472 | }
473 | .progress-bar-info {
474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
479 | background-repeat: repeat-x;
480 | }
481 | .progress-bar-warning {
482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
487 | background-repeat: repeat-x;
488 | }
489 | .progress-bar-danger {
490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
495 | background-repeat: repeat-x;
496 | }
497 | .progress-bar-striped {
498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
501 | }
502 | .list-group {
503 | border-radius: 4px;
504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
506 | }
507 | .list-group-item.active,
508 | .list-group-item.active:hover,
509 | .list-group-item.active:focus {
510 | text-shadow: 0 -1px 0 #286090;
511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
516 | background-repeat: repeat-x;
517 | border-color: #2b669a;
518 | }
519 | .list-group-item.active .badge,
520 | .list-group-item.active:hover .badge,
521 | .list-group-item.active:focus .badge {
522 | text-shadow: none;
523 | }
524 | .panel {
525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
527 | }
528 | .panel-default > .panel-heading {
529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
534 | background-repeat: repeat-x;
535 | }
536 | .panel-primary > .panel-heading {
537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
542 | background-repeat: repeat-x;
543 | }
544 | .panel-success > .panel-heading {
545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
550 | background-repeat: repeat-x;
551 | }
552 | .panel-info > .panel-heading {
553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
558 | background-repeat: repeat-x;
559 | }
560 | .panel-warning > .panel-heading {
561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
566 | background-repeat: repeat-x;
567 | }
568 | .panel-danger > .panel-heading {
569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
574 | background-repeat: repeat-x;
575 | }
576 | .well {
577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
582 | background-repeat: repeat-x;
583 | border-color: #dcdcdc;
584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
586 | }
587 | /*# sourceMappingURL=bootstrap-theme.css.map */
588 |
--------------------------------------------------------------------------------
/staticfiles/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */
--------------------------------------------------------------------------------
/staticfiles/css/bootstrap-theme.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
--------------------------------------------------------------------------------
/staticfiles/css/monokai-sublime.min.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#23241f}.hljs,.hljs-tag,.hljs-subst{color:#f8f8f2}.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-quote,.hljs-number,.hljs-regexp,.hljs-literal,.hljs-link{color:#ae81ff}.hljs-code,.hljs-title,.hljs-section,.hljs-selector-class{color:#a6e22e}.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-name,.hljs-attr{color:#f92672}.hljs-symbol,.hljs-attribute{color:#66d9ef}.hljs-params,.hljs-class .hljs-title{color:#f8f8f2}.hljs-string,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-selector-id,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-addition,.hljs-variable,.hljs-template-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e}
--------------------------------------------------------------------------------
/staticfiles/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Change the default font family in all browsers (opinionated).
5 | * 2. Correct the line height in all browsers.
6 | * 3. Prevent adjustments of font size after orientation changes in
7 | * IE on Windows Phone and in iOS.
8 | */
9 |
10 | /* Document
11 | ========================================================================== */
12 |
13 | html {
14 | font-family: sans-serif; /* 1 */
15 | line-height: 1.15; /* 2 */
16 | -ms-text-size-adjust: 100%; /* 3 */
17 | -webkit-text-size-adjust: 100%; /* 3 */
18 | }
19 |
20 | /* Sections
21 | ========================================================================== */
22 |
23 | /**
24 | * Remove the margin in all browsers (opinionated).
25 | */
26 |
27 | body {
28 | margin: 0;
29 | }
30 |
31 | /**
32 | * Add the correct display in IE 9-.
33 | */
34 |
35 | article,
36 | aside,
37 | footer,
38 | header,
39 | nav,
40 | section {
41 | display: block;
42 | }
43 |
44 | /**
45 | * Correct the font size and margin on `h1` elements within `section` and
46 | * `article` contexts in Chrome, Firefox, and Safari.
47 | */
48 |
49 | h1 {
50 | font-size: 2em;
51 | margin: 0.67em 0;
52 | }
53 |
54 | /* Grouping content
55 | ========================================================================== */
56 |
57 | /**
58 | * Add the correct display in IE 9-.
59 | * 1. Add the correct display in IE.
60 | */
61 |
62 | figcaption,
63 | figure,
64 | main { /* 1 */
65 | display: block;
66 | }
67 |
68 | /**
69 | * Add the correct margin in IE 8.
70 | */
71 |
72 | figure {
73 | margin: 1em 40px;
74 | }
75 |
76 | /**
77 | * 1. Add the correct box sizing in Firefox.
78 | * 2. Show the overflow in Edge and IE.
79 | */
80 |
81 | hr {
82 | box-sizing: content-box; /* 1 */
83 | height: 0; /* 1 */
84 | overflow: visible; /* 2 */
85 | }
86 |
87 | /**
88 | * 1. Correct the inheritance and scaling of font size in all browsers.
89 | * 2. Correct the odd `em` font sizing in all browsers.
90 | */
91 |
92 | pre {
93 | font-family: monospace, monospace; /* 1 */
94 | font-size: 1em; /* 2 */
95 | }
96 |
97 | /* Text-level semantics
98 | ========================================================================== */
99 |
100 | /**
101 | * 1. Remove the gray background on active links in IE 10.
102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
103 | */
104 |
105 | a {
106 | background-color: transparent; /* 1 */
107 | -webkit-text-decoration-skip: objects; /* 2 */
108 | }
109 |
110 | /**
111 | * Remove the outline on focused links when they are also active or hovered
112 | * in all browsers (opinionated).
113 | */
114 |
115 | a:active,
116 | a:hover {
117 | outline-width: 0;
118 | }
119 |
120 | /**
121 | * 1. Remove the bottom border in Firefox 39-.
122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
123 | */
124 |
125 | abbr[title] {
126 | border-bottom: none; /* 1 */
127 | text-decoration: underline; /* 2 */
128 | text-decoration: underline dotted; /* 2 */
129 | }
130 |
131 | /**
132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: inherit;
138 | }
139 |
140 | /**
141 | * Add the correct font weight in Chrome, Edge, and Safari.
142 | */
143 |
144 | b,
145 | strong {
146 | font-weight: bolder;
147 | }
148 |
149 | /**
150 | * 1. Correct the inheritance and scaling of font size in all browsers.
151 | * 2. Correct the odd `em` font sizing in all browsers.
152 | */
153 |
154 | code,
155 | kbd,
156 | samp {
157 | font-family: monospace, monospace; /* 1 */
158 | font-size: 1em; /* 2 */
159 | }
160 |
161 | /**
162 | * Add the correct font style in Android 4.3-.
163 | */
164 |
165 | dfn {
166 | font-style: italic;
167 | }
168 |
169 | /**
170 | * Add the correct background and color in IE 9-.
171 | */
172 |
173 | mark {
174 | background-color: #ff0;
175 | color: #000;
176 | }
177 |
178 | /**
179 | * Add the correct font size in all browsers.
180 | */
181 |
182 | small {
183 | font-size: 80%;
184 | }
185 |
186 | /**
187 | * Prevent `sub` and `sup` elements from affecting the line height in
188 | * all browsers.
189 | */
190 |
191 | sub,
192 | sup {
193 | font-size: 75%;
194 | line-height: 0;
195 | position: relative;
196 | vertical-align: baseline;
197 | }
198 |
199 | sub {
200 | bottom: -0.25em;
201 | }
202 |
203 | sup {
204 | top: -0.5em;
205 | }
206 |
207 | /* Embedded content
208 | ========================================================================== */
209 |
210 | /**
211 | * Add the correct display in IE 9-.
212 | */
213 |
214 | audio,
215 | video {
216 | display: inline-block;
217 | }
218 |
219 | /**
220 | * Add the correct display in iOS 4-7.
221 | */
222 |
223 | audio:not([controls]) {
224 | display: none;
225 | height: 0;
226 | }
227 |
228 | /**
229 | * Remove the border on images inside links in IE 10-.
230 | */
231 |
232 | img {
233 | border-style: none;
234 | }
235 |
236 | /**
237 | * Hide the overflow in IE.
238 | */
239 |
240 | svg:not(:root) {
241 | overflow: hidden;
242 | }
243 |
244 | /* Forms
245 | ========================================================================== */
246 |
247 | /**
248 | * 1. Change the font styles in all browsers (opinionated).
249 | * 2. Remove the margin in Firefox and Safari.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | font-family: sans-serif; /* 1 */
258 | font-size: 100%; /* 1 */
259 | line-height: 1.15; /* 1 */
260 | margin: 0; /* 2 */
261 | }
262 |
263 | /**
264 | * Show the overflow in IE.
265 | * 1. Show the overflow in Edge.
266 | */
267 |
268 | button,
269 | input { /* 1 */
270 | overflow: visible;
271 | }
272 |
273 | /**
274 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
275 | * 1. Remove the inheritance of text transform in Firefox.
276 | */
277 |
278 | button,
279 | select { /* 1 */
280 | text-transform: none;
281 | }
282 |
283 | /**
284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
285 | * controls in Android 4.
286 | * 2. Correct the inability to style clickable types in iOS and Safari.
287 | */
288 |
289 | button,
290 | html [type="button"], /* 1 */
291 | [type="reset"],
292 | [type="submit"] {
293 | -webkit-appearance: button; /* 2 */
294 | }
295 |
296 | /**
297 | * Remove the inner border and padding in Firefox.
298 | */
299 |
300 | button::-moz-focus-inner,
301 | [type="button"]::-moz-focus-inner,
302 | [type="reset"]::-moz-focus-inner,
303 | [type="submit"]::-moz-focus-inner {
304 | border-style: none;
305 | padding: 0;
306 | }
307 |
308 | /**
309 | * Restore the focus styles unset by the previous rule.
310 | */
311 |
312 | button:-moz-focusring,
313 | [type="button"]:-moz-focusring,
314 | [type="reset"]:-moz-focusring,
315 | [type="submit"]:-moz-focusring {
316 | outline: 1px dotted ButtonText;
317 | }
318 |
319 | /**
320 | * Change the border, margin, and padding in all browsers (opinionated).
321 | */
322 |
323 | fieldset {
324 | border: 1px solid #c0c0c0;
325 | margin: 0 2px;
326 | padding: 0.35em 0.625em 0.75em;
327 | }
328 |
329 | /**
330 | * 1. Correct the text wrapping in Edge and IE.
331 | * 2. Correct the color inheritance from `fieldset` elements in IE.
332 | * 3. Remove the padding so developers are not caught out when they zero out
333 | * `fieldset` elements in all browsers.
334 | */
335 |
336 | legend {
337 | box-sizing: border-box; /* 1 */
338 | color: inherit; /* 2 */
339 | display: table; /* 1 */
340 | max-width: 100%; /* 1 */
341 | padding: 0; /* 3 */
342 | white-space: normal; /* 1 */
343 | }
344 |
345 | /**
346 | * 1. Add the correct display in IE 9-.
347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
348 | */
349 |
350 | progress {
351 | display: inline-block; /* 1 */
352 | vertical-align: baseline; /* 2 */
353 | }
354 |
355 | /**
356 | * Remove the default vertical scrollbar in IE.
357 | */
358 |
359 | textarea {
360 | overflow: auto;
361 | }
362 |
363 | /**
364 | * 1. Add the correct box sizing in IE 10-.
365 | * 2. Remove the padding in IE 10-.
366 | */
367 |
368 | [type="checkbox"],
369 | [type="radio"] {
370 | box-sizing: border-box; /* 1 */
371 | padding: 0; /* 2 */
372 | }
373 |
374 | /**
375 | * Correct the cursor style of increment and decrement buttons in Chrome.
376 | */
377 |
378 | [type="number"]::-webkit-inner-spin-button,
379 | [type="number"]::-webkit-outer-spin-button {
380 | height: auto;
381 | }
382 |
383 | /**
384 | * 1. Correct the odd appearance in Chrome and Safari.
385 | * 2. Correct the outline style in Safari.
386 | */
387 |
388 | [type="search"] {
389 | -webkit-appearance: textfield; /* 1 */
390 | outline-offset: -2px; /* 2 */
391 | }
392 |
393 | /**
394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
395 | */
396 |
397 | [type="search"]::-webkit-search-cancel-button,
398 | [type="search"]::-webkit-search-decoration {
399 | -webkit-appearance: none;
400 | }
401 |
402 | /**
403 | * 1. Correct the inability to style clickable types in iOS and Safari.
404 | * 2. Change font properties to `inherit` in Safari.
405 | */
406 |
407 | ::-webkit-file-upload-button {
408 | -webkit-appearance: button; /* 1 */
409 | font: inherit; /* 2 */
410 | }
411 |
412 | /* Interactive
413 | ========================================================================== */
414 |
415 | /*
416 | * Add the correct display in IE 9-.
417 | * 1. Add the correct display in Edge, IE, and Firefox.
418 | */
419 |
420 | details, /* 1 */
421 | menu {
422 | display: block;
423 | }
424 |
425 | /*
426 | * Add the correct display in all browsers.
427 | */
428 |
429 | summary {
430 | display: list-item;
431 | }
432 |
433 | /* Scripting
434 | ========================================================================== */
435 |
436 | /**
437 | * Add the correct display in IE 9-.
438 | */
439 |
440 | canvas {
441 | display: inline-block;
442 | }
443 |
444 | /**
445 | * Add the correct display in IE.
446 | */
447 |
448 | template {
449 | display: none;
450 | }
451 |
452 | /* Hidden
453 | ========================================================================== */
454 |
455 | /**
456 | * Add the correct display in IE 10-.
457 | */
458 |
459 | [hidden] {
460 | display: none;
461 | }
462 |
--------------------------------------------------------------------------------
/staticfiles/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "PT Serif", serif;
3 | background-color: #fff;
4 | color: #000;
5 | line-height: 2em;
6 | }
7 |
8 | p {
9 | font-size: 1.25em;
10 | }
11 |
12 | .blog-masthead {
13 | background-color: #000;
14 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1);
15 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1);
16 | }
17 |
18 | /* Nav links */
19 | .blog-nav-item {
20 | position: relative;
21 | display: inline-block;
22 | padding-top: 15px;
23 | padding-right: 30px;
24 | padding-bottom: 15px;
25 | font-weight: 500;
26 | color: #cdddeb;
27 | font-size: 1.3em;
28 | }
29 | .blog-nav-item:hover,
30 | .blog-nav-item:focus {
31 | color: #fff;
32 | text-decoration: none;
33 | }
34 |
35 | /* Active state gets a caret at the bottom */
36 | .blog-nav .active {
37 | color: #fff;
38 | }
39 | .blog-nav .active:after {
40 | position: absolute;
41 | bottom: 0;
42 | left: 50%;
43 | width: 0;
44 | height: 0;
45 | margin-left: -5px;
46 | vertical-align: middle;
47 | content: " ";
48 | border-right: 5px solid transparent;
49 | border-bottom: 5px solid;
50 | border-left: 5px solid transparent;
51 | }
52 |
53 | div.article-meta {
54 | margin-bottom: 10px;
55 | }
56 |
57 | .star-ratings-rating-stars-container {
58 | vertical-align: bottom;
59 | }
60 |
61 | .article-meta span {
62 | color: #9b9b9b;
63 | }
64 |
65 | div.hello-msg {
66 | background-color: #f6f6f6;
67 | padding: 15px;
68 | margin-top: -29px;
69 | }
70 |
71 | img.arrow-msg {
72 | position: relative;
73 | top: 100px;
74 | left: -14px;
75 | }
76 |
77 |
78 | div.social-btns a{
79 | display: inline-block;
80 | margin-right: 5px;
81 | text-decoration: none;
82 | color:#000;
83 | }
84 |
85 | article.post-list {
86 | margin-bottom: 90px;
87 | }
88 | div.articles {
89 | margin-top: 40px;
90 | }
91 |
92 | @media (min-width: 1200px) {
93 | .container {
94 | width: 970px;
95 | }
96 | }
97 |
98 | div.subscription {
99 | background-color: #f5f5f1;
100 | border: 1px #b9baba solid;
101 | border-radius: 2px;
102 | padding: 10px;
103 | }
104 |
105 | div.tchannel-info {
106 | background-color: #f6f6f6;
107 | padding: 15px;
108 | padding-left: 25px;
109 | padding-right: 25px;
110 | }
111 |
112 |
113 | button.search-btn {
114 | border: none;
115 | background: transparent;
116 | position: absolute;
117 | margin-left: 150px;
118 | padding-top: 4px;
119 | /*top: 23%;*/
120 | /*right: 30px;*/
121 | /*padding: 0;*/
122 | height: 16px;
123 | width: 16px;
124 | font-size: 16px;
125 | outline: none;
126 | }
127 |
128 | ul.menu__navigation {
129 | padding: 0;
130 | }
131 |
132 | ul.menu__navigation li {
133 | list-style: none;
134 | font-size: 1.05em;
135 | }
136 |
137 | ul.menu__navigation li a {
138 | color: #f5f5f1;
139 | }
140 | @media (min-width: 768px) {
141 | ul.menu__navigation li {
142 | display: inline-block;
143 | padding-right: 10px;
144 | }
145 | }
146 |
147 | .navbar-toggle .icon-bar {
148 | background-color: #fff;
149 | }
150 |
151 | .sidebar-widget {
152 | border: 1px solid #ebebeb;
153 | padding: 25px;
154 | margin-bottom: 20px;
155 | }
156 |
157 | h4.sidebar-title {
158 | font-weight: bold;
159 | font-size: 1.5em;
160 | border-bottom: 1px solid #ebebeb;
161 | margin-bottom: 21px;
162 | padding-bottom: 7px;
163 | }
164 |
165 | @media (min-width: 1200px) {
166 | .container-wrap {
167 | width: 1200px;
168 | }
169 | }
170 |
171 | .tlgr-info {
172 | padding: 10px;
173 | border: 1px #b9baba solid;
174 | background-color: #f9ffc6;
175 | }
176 |
177 | a.job {
178 | color: #499402;
179 | }
180 |
181 | span.job-company {
182 | font-size: 1.1em;
183 | font-weight: bold;
184 | }
185 |
186 | hr {
187 | display: block;
188 | unicode-bidi: isolate;
189 | margin-block-start: 0.5em;
190 | margin-block-end: 0.5em;
191 | margin-inline-start: auto;
192 | margin-inline-end: auto;
193 | overflow: hidden;
194 | }
195 |
196 | figure img {
197 | /*width:100%;*/
198 | }
199 |
--------------------------------------------------------------------------------
/staticfiles/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/staticfiles/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/staticfiles/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/staticfiles/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/staticfiles/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/staticfiles/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/staticfiles/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/staticfiles/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/staticfiles/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/staticfiles/images/DE_770x193.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/DE_770x193.jpg
--------------------------------------------------------------------------------
/staticfiles/images/Python900x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/Python900x120.png
--------------------------------------------------------------------------------
/staticfiles/images/arrow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/arrow.jpg
--------------------------------------------------------------------------------
/staticfiles/images/author.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/author.jpg
--------------------------------------------------------------------------------
/staticfiles/images/devbrain.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/devbrain.jpg
--------------------------------------------------------------------------------
/staticfiles/images/devbrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/devbrain.png
--------------------------------------------------------------------------------
/staticfiles/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/icon.png
--------------------------------------------------------------------------------
/staticfiles/images/like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/like.png
--------------------------------------------------------------------------------
/staticfiles/images/machine-learning-course.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/machine-learning-course.jpg
--------------------------------------------------------------------------------
/staticfiles/images/ml-agenda.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/ml-agenda.jpg
--------------------------------------------------------------------------------
/staticfiles/images/ml-poster2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/ml-poster2.jpg
--------------------------------------------------------------------------------
/staticfiles/images/pydata-analysis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/pydata-analysis.jpg
--------------------------------------------------------------------------------
/staticfiles/images/ru-flag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/ru-flag.png
--------------------------------------------------------------------------------
/staticfiles/images/social.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/social.jpg
--------------------------------------------------------------------------------
/staticfiles/images/telegram-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/telegram-logo.png
--------------------------------------------------------------------------------
/staticfiles/images/us-flag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adilkhash/notion/5bb5af9464ce580b72b4151527876017ecca2e38/staticfiles/images/us-flag.png
--------------------------------------------------------------------------------
/staticfiles/js/app.js:
--------------------------------------------------------------------------------
1 | var app = new Vue({
2 | el: '#app',
3 | data: {
4 | email: '',
5 | subscribed: false,
6 | invalidSubscription: false,
7 | validationError: ''
8 | },
9 | methods: {
10 | subscribeBtn: function (e) {
11 | this.invalidSubscription = false;
12 | this.subscribed = false;
13 | this.validationError = '';
14 |
15 | axios.post('/subscribe/', Qs.stringify({'email': this.email}))
16 | .then(function (response) {
17 | if (response.data.status == 'ok') {
18 | app.subscribed = true;
19 | app.email = '';
20 | } else {
21 | app.invalidSubscription = true;
22 | app.validationError = response.data.msg;
23 | }
24 | })
25 | .catch(function (error) {
26 | app.invalidSubscription = true;
27 | })
28 | }
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/staticfiles/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/staticfiles/redactor/plugins/source.js:
--------------------------------------------------------------------------------
1 | (function($)
2 | {
3 | $.Redactor.prototype.source = function()
4 | {
5 | return {
6 | init: function()
7 | {
8 | var button = this.button.addFirst('html', 'HTML');
9 | this.button.setIcon(button, ' ');
10 | this.button.addCallback(button, this.source.toggle);
11 |
12 | var style = {
13 | 'width': '100%',
14 | 'margin': '0',
15 | 'background': '#1d1d1d',
16 | 'box-sizing': 'border-box',
17 | 'color': '#ccc',
18 | 'font-size': '15px',
19 | 'outline': 'none',
20 | 'padding': '20px',
21 | 'line-height': '24px',
22 | 'font-family': 'Consolas, Menlo, Monaco, "Courier New", monospace'
23 | };
24 |
25 | this.source.$textarea = $('');
26 | this.source.$textarea.css(style).hide();
27 |
28 | if (this.opts.type === 'textarea')
29 | {
30 | this.core.box().append(this.source.$textarea);
31 | }
32 | else
33 | {
34 | this.core.box().after(this.source.$textarea);
35 | }
36 |
37 | this.core.element().on('destroy.callback.redactor', $.proxy(function()
38 | {
39 | this.source.$textarea.remove();
40 |
41 | }, this));
42 |
43 | },
44 | toggle: function()
45 | {
46 | return (this.source.$textarea.hasClass('open')) ? this.source.hide() : this.source.show();
47 | },
48 | setCaretOnShow: function()
49 | {
50 | this.source.offset = this.offset.get();
51 | var scroll = $(window).scrollTop();
52 |
53 | var width = this.core.editor().innerWidth();
54 | var height = this.core.editor().innerHeight();
55 |
56 | // caret position sync
57 | this.source.start = 0;
58 | this.source.end = 0;
59 | var $editorDiv = $("
").append($.parseHTML(this.core.editor().html(), document, true));
60 | var $selectionMarkers = $editorDiv.find("span.redactor-selection-marker");
61 |
62 | if ($selectionMarkers.length > 0)
63 | {
64 | var editorHtml = $editorDiv.html().replace(/&/g, '&');
65 |
66 | if ($selectionMarkers.length === 1)
67 | {
68 | this.source.start = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-1").prop("outerHTML"));
69 | this.source.end = this.source.start;
70 | }
71 | else if ($selectionMarkers.length === 2)
72 | {
73 | this.source.start = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-1").prop("outerHTML"));
74 | this.source.end = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-2").prop("outerHTML")) - $editorDiv.find("#selection-marker-1").prop("outerHTML").toString().length;
75 | }
76 | }
77 | },
78 | setCaretOnHide: function(html)
79 | {
80 | this.source.start = this.source.$textarea.get(0).selectionStart;
81 | this.source.end = this.source.$textarea.get(0).selectionEnd;
82 |
83 | // if selection starts from end
84 | if (this.source.start > this.source.end && this.source.end > 0)
85 | {
86 | var tempStart = this.source.end;
87 | var tempEnd = this.source.start;
88 |
89 | this.source.start = tempStart;
90 | this.source.end = tempEnd;
91 | }
92 |
93 | this.source.start = this.source.enlargeOffset(html, this.source.start);
94 | this.source.end = this.source.enlargeOffset(html, this.source.end);
95 |
96 | html = html.substr(0, this.source.start) + this.marker.html(1) + html.substr(this.source.start);
97 |
98 | if (this.source.end > this.source.start)
99 | {
100 | var markerLength = this.marker.html(1).toString().length;
101 |
102 | html = html.substr(0, this.source.end + markerLength) + this.marker.html(2) + html.substr(this.source.end + markerLength);
103 | }
104 |
105 |
106 | return html;
107 |
108 | },
109 | hide: function()
110 | {
111 | this.source.$textarea.removeClass('open').hide();
112 | this.source.$textarea.off('.redactor-source');
113 |
114 | var code = this.source.$textarea.val();
115 |
116 | code = this.paragraphize.load(code);
117 | code = this.source.setCaretOnHide(code);
118 |
119 | this.code.start(code);
120 | this.button.enableAll();
121 | this.core.editor().show().focus();
122 | this.selection.restore();
123 | this.placeholder.enable();
124 |
125 | this.core.callback('visual');
126 | },
127 | show: function()
128 | {
129 | this.selection.save();
130 | this.source.setCaretOnShow();
131 |
132 | var height = this.core.editor().height();
133 | var code = this.code.get();
134 |
135 | // callback
136 | code = this.core.callback('source', code);
137 |
138 | this.core.editor().hide();
139 | this.button.disableAll('html');
140 |
141 | this.source.$textarea.val(code).height(height).addClass('open').show();
142 | this.source.$textarea.on('keyup.redactor-source', $.proxy(function()
143 | {
144 | if (this.opts.type === 'textarea')
145 | {
146 | this.core.textarea().val(this.source.$textarea.val());
147 | }
148 |
149 | }, this));
150 |
151 | this.marker.remove();
152 |
153 | $(window).scrollTop(scroll);
154 |
155 | if (this.source.$textarea[0].setSelectionRange)
156 | {
157 | this.source.$textarea[0].setSelectionRange(this.source.start, this.source.end);
158 | }
159 |
160 | this.source.$textarea[0].scrollTop = 0;
161 |
162 | setTimeout($.proxy(function()
163 | {
164 | this.source.$textarea.focus();
165 |
166 | }, this), 0);
167 | },
168 | enlargeOffset: function(html, offset)
169 | {
170 | var htmlLength = html.length;
171 | var c = 0;
172 |
173 | if (html[offset] === '>')
174 | {
175 | c++;
176 | }
177 | else
178 | {
179 | for(var i = offset; i <= htmlLength; i++)
180 | {
181 | c++;
182 |
183 | if (html[i] === '>')
184 | {
185 | break;
186 | }
187 | else if (html[i] === '<' || i === htmlLength)
188 | {
189 | c = 0;
190 | break;
191 | }
192 | }
193 | }
194 |
195 | return offset + c;
196 | }
197 | };
198 | };
199 | })(jQuery);
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | {% load static %}
3 | {% load assets %}
4 | {% get_current_language as LANGUAGE_CODE %}
5 |
6 |
7 |
8 | {% if not DEBUG %}
9 |
10 |
11 |
17 | {% endif %}
18 |
19 |
20 |
21 |
22 |
23 | {% block title %}{% trans "Adil Khashtamov's personal blog — pragmatic programmer" %}{% endblock %}
24 | {% assets "css_all" %}
25 |
26 | {% endassets %}
27 |
28 |
29 | {% block _head %}
30 | {% endblock %}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
47 |
63 |
64 |
65 |
66 |
67 | {% block content %}
68 | {% endblock %}
69 |
70 |
71 |
80 |
81 | {% block _bottom %}{% endblock %}
82 | {% if not DEBUG %}
83 |
84 |
109 |
110 |
111 | {% endif %}
112 | {% assets "js_all" %}
113 |
114 | {% endassets %}
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/templates/blog/_sidebar.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | {% load static %}
3 | {% get_current_language as LANGUAGE_CODE %}
4 | {% load blog_tags %}
5 |
6 |
7 |
8 |
19 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/templates/blog/adsense.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
9 |
12 |
--------------------------------------------------------------------------------
/templates/blog/archives.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% block title %}{% trans 'Archive of posts' %}{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 |
{% trans 'Archive of posts' %}
10 |
11 |
{% trans 'By category'%}
12 | {% regroup object_list by category.title as groups %}
13 |
14 | {% for group in groups %}
15 |
{{ group.grouper }}
16 |
21 | {% endfor %}
22 |
23 |
{% trans 'By year'%}
24 | {% regroup object_list_by_year by created.year as groups %}
25 |
26 | {% for group in groups %}
27 |
{{ group.grouper }}
28 |
33 | {% endfor %}
34 |
35 |
36 |
37 |
38 | {% endblock %}
39 |
--------------------------------------------------------------------------------
/templates/blog/category_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% block title %}{{ object.title }} {% trans 'category' %}{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 |
{{ object.title }} {% trans 'category' %}
10 |
11 | {% regroup posts by created.year as groups %}
12 |
13 | {% for group in groups %}
14 |
{{ group.grouper }}
15 |
20 | {% endfor %}
21 |
22 |
23 |
24 |
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/templates/blog/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% load static %}
4 | {% load bootstrap_pagination %}
5 | {% load blog_tags %}
6 |
7 | {% block _head %}
8 | {# #}
9 | {# #}
10 | {% endblock %}
11 |
12 | {% block content %}
13 |
14 |
15 |
16 |
17 | {% regroup object_list by month_year as articles %}
18 | {% for article in articles %}
19 |
20 | {{ article.grouper }}
21 |
22 |
23 | {% for post in article.list %}
24 |
25 | {{ post.title }}
26 | {% if post.status == post.DRAFT %}
27 | ({% trans 'Draft' %}) {% endif %}
28 |
29 | {% endfor %}
30 |
31 | {% endfor %}
32 |
33 |
34 | {% include 'blog/_sidebar.html' %}
35 |
36 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/templates/blog/page.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 |
4 | {% block title %}{{ object.title }}{% endblock %}
5 |
6 | {% block meta_description %}{{ object.text|striptags|truncatechars:200 }}{% endblock %}
7 |
8 | {% block content %}
9 |
10 |
11 |
12 |
13 | {{ object.title }}
14 |
15 | {{ object.created|date:"j N o" }}
16 |
17 | {{ object.text|safe }}
18 |
19 |
20 |
21 |
22 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/templates/blog/post_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% load static %}
4 | {% block title %}{{ object.title }}{% endblock %}
5 | {% load blog_tags %}
6 | {% load adsense_tags %}
7 | {% block meta_description %}
8 | {{ object.text|striptags|truncatechars:200|erase_breaks }}{% endblock %}
9 | {% get_current_language as LANGUAGE_CODE %}
10 |
11 | {% block _head %}
12 | {% if alternate_posts %}
13 | {% for post in alternate_posts %}
14 |
17 | {% endfor %}
18 | {% endif %}
19 | {% endblock %}
20 |
21 | {% block content %}
22 |
23 |
24 |
25 |
26 | {{ object.title }}
27 |
28 |
29 |
{{ object.created|date:"j N o" }} ,
{{ object.category.title }} ,
31 |
{{ object.page_views }} {% trans 'views' %},
32 |
33 | {% if alternate_posts %}
34 | {% for post in alternate_posts %}
35 |
36 |
37 |
38 | {{ post.alternate_post }}
39 |
40 |
41 | {% endfor %}
42 | {% endif %}
43 |
44 | {% if user.is_superuser %}
45 |
46 | {% trans 'Edit' %}
47 |
48 | {% if post.status == post.DRAFT %}
49 |
{% trans 'Draft' %}
50 | {% endif %}
51 | {% endif %}
52 |
53 | {{ object.text|safe }}
54 |
55 |
56 |
57 |
69 |
Please enable JavaScript to view the comments
70 | powered by Disqus.
71 |
72 | {% if related_articles %}
73 |
74 |
75 |
{% trans 'Related posts' %}:
76 |
83 |
84 |
85 | {% endif %}
86 |
87 | {% include 'blog/_sidebar.html' %}
88 |
89 |
90 | {% endblock %}
91 |
--------------------------------------------------------------------------------
/templates/blog/search.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% load static %}
4 | {% load blog_tags %}
5 | {% get_current_language as LANGUAGE_CODE %}
6 |
7 | {% block title %}
8 | {% blocktrans %}Results for «{{ query }}»{% endblocktrans %}
9 | {% endblock %}
10 |
11 | {% block content %}
12 |
13 |
14 |
15 | {% if not results %}
16 |
{% trans 'No articles found' %}
17 | {% else %}
18 |
{% trans 'Articles found' %}: {{ results|length }}
19 | {% for post in results %}
20 |
21 |
22 |
28 |
29 | {% endfor %}
30 | {% endif %}
31 |
32 |
33 |
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/templates/courses/index.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | Курс по Data Engineering: построение дата-пайплайнов на Luigi и Python
15 |
16 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
Строим дата-пайплайны на Luigi и Python
69 |
70 | Хватит изобретать свои велосипеды и сражаться с ветряными мельницами. Пора изучать
71 | лучшие практики
72 | индустрии в построении надёжных и гибких дата-пайплайнов в экосистеме языка Python.
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
Дата-пайплайны
87 |
Если ваши задачи так или иначе связаны с данными, то наверняка вам приходилось писать
88 | множество скриптов на коленке для выгрузки данных, их обработки и последующей укладки в
89 | хранилище. Например, перед вами стоит задача скачать датасет из внешнего сервиса,
90 | почистить его и обогатить данные, а полученный результат переложить в базу, например,
91 | PostgreSQL.
92 |
Со временем формируются регулярные задачи, которые необходимо запускать периодически,
93 | между ними появляются зависимости, и вероятность выхода из строя любой из них только
94 | увеличивается. Задачи растут, и если не менять подход, то проблемы будут напоминать
95 | снежный ком, катящийся с горы.
96 |
К счастью, ваши проблемы не уникальны и есть готовые и проверенные решения для построения
97 | эффективных дата-пайплайнов. В этом курсе я расскажу о тонкостях работы с таким
98 | инструментом как Luigi.
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
Что такое Luigi
112 |
113 | Luigi это небольшой фреймворк, написанный на Python для построения процесса выполнения
114 | зависимых задач. Если перед вами стоит задача, которую можно разделить на этапы или шаги
115 | и между ними есть зависимость, то Luigi поможет. Представим ситуацию, что вам необходимо
116 | скачать изображение, добавить водяной знак и загрузить обратно. В реалиях Luigi это
117 | бьётся на 3 таска:
118 |
119 |
120 |
121 |
122 | Скачивание изображения с внешнего источника
123 |
124 |
125 | Добавление водяного знака
126 |
127 |
128 | Загрузка изображения
129 |
130 |
131 |
132 |
133 |
134 | Выполнение третьего этапа невозможно без успешного выполнения двух предыдущих. А что
135 | если на втором или третьем этапе произошла ошибка? Нужно ли запускать процесс сначала?
136 | Если вы используете Luigi, то ответ “нет”.
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
Для кого предназначен курс
151 |
152 | Программа курса предназначена для backend и data инженеров в обязанности которых входят
153 | задачи по работе с данными. Для понимания программы студент должен иметь опыт
154 | программирования на Python.
155 | Опыт работы с реляционными базами данных приветствуется (MySQL, PostgreSQL), но
156 | необязателен.
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
Что изучают на курсе
171 |
172 | Это один из первых курсов из планируемой в будущем специализации по data engineering. В
173 | этом курсе я расскажу как можно эффективно строить пайплайны данных, используя Luigi.
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
Технологии курса
188 |
189 |
190 |
191 |
192 |
Python 3
193 |
194 |
195 |
196 |
Docker
197 |
198 |
199 |
200 |
DigitalOcean
201 |
202 |
203 |
205 |
Luigi
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
Курс находится на стадии разработки, поэтому есть уникальная возможность
224 | сделать его предзаказ всего за 690 рублей . У участников предзаказа будет возможность получать доступ
225 | к урокам по мере их выхода. Оповещения будут приходить на почту.
226 |
227 |
228 |
Предзаказ открыт для первых 100 оплаченных заказов.
229 |
230 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
Об авторе
258 |
259 |
260 |
261 |
262 |
263 |
264 | Меня зовут Адиль Хаштамов, и я пишу код вот уже 17 лет. Веб-разработкой на
265 | Python
266 | активно занимаюсь с 2011 года. Был техническим со-основателем успешного travel
267 | стартапа,
268 | веду блог khashtamov.com .
269 | Сейчас работаю техлидом в компании Playrix Games, веду ряд
270 | своих мелких сайд-проектов, включая агрегатор
271 | удалённых ваканий remotelist.ru
272 | и телеграм-канал @devbrain .
273 |
274 | Последние 3 года активно решаю задачи, связанные с обработкой и анализом данных, построением
275 | дата-инфраструктуры.
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
Программа курса
294 |
295 |
296 |
Что такое дата-пайплайн
297 |
298 | Какие существуют подходы к построению дата-пайплайнов
299 | В чем недостатки “скриптов на коленке”
300 |
301 |
302 |
303 |
304 |
Обзор Luigi
305 |
306 |
307 | Как Luigi решает проблему построения дата-пайплайнов
308 |
309 |
310 | Установка Luigi в виртуальное окружение
311 |
312 |
313 | Пишем первые задачи для запуска
314 |
315 |
316 | Обзор шедулера luigid
317 |
318 |
319 |
320 |
321 |
322 |
323 |
Продвинутое использование Luigi
324 |
325 |
326 | Разбираем тонкости конфига планировщика и воркеров
327 |
328 |
329 |
330 | Настраиваем стандартный мониторинг выполнения задач
331 |
332 |
333 |
334 | Пишем отправку уведомлений в Telegram и Slack
335 |
336 |
337 |
338 |
339 |
340 |
341 |
Деплой на продакшен
342 |
343 |
344 | Luigi в Docker
345 |
346 |
347 | Запуск Luigi в Amazon Web Services на Fargate
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
--------------------------------------------------------------------------------
/templates/notes/note_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% load static %}
4 | {% block title %}{{ object.title }}{% endblock %}
5 | {% load blog_tags %}
6 | {% load adsense_tags %}
7 | {% block meta_description %}{{ object.text|striptags|truncatechars:200|erase_breaks }}{% endblock %}
8 | {% get_current_language as LANGUAGE_CODE %}
9 |
10 | {% block content %}
11 |
12 |
13 |
14 |
15 | {{ object.title }}
16 |
17 |
18 |
{{ object.created|date:"j N o" }} ,
19 | {% if user.is_superuser %}
20 |
{% trans 'Edit' %}
21 | {% if object.status == object.DRAFT %}
22 |
{% trans 'Draft' %}
23 | {% endif %}
24 | {% endif %}
25 |
26 | {{ object.text|safe }}
27 |
28 |
29 |
30 |
31 |
32 | {% endblock %}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/templates/notes/notes.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% load static %}
4 | {% get_current_language as LANGUAGE_CODE %}
5 | {% block title %}{% trans 'Useful notes' %}{% endblock %}
6 |
7 | {% block content %}
8 |
9 |
10 |
11 |
{% trans 'Useful notes' %}
12 |
13 | {% regroup object_list by theme.title as groups %}
14 | {% for group in groups %}
15 |
16 |
17 |
{{ group.grouper }}
18 |
19 |
20 | {% for note in group.list %}
21 |
22 | {{ note.title }}
23 |
24 | {% endfor %}
25 |
26 |
27 | {% endfor %}
28 |
29 |
30 |
31 |
32 |
33 | {% endblock %}
--------------------------------------------------------------------------------
/templates/tags/jobs.html:
--------------------------------------------------------------------------------
1 | {% load cache %}
2 | {% load i18n %}
3 |
4 | {% cache 43200 latest_job_list %}
5 | {% if jobs %}
6 |
18 | {% endif %}
19 | {% endcache %}
20 |
--------------------------------------------------------------------------------