add celery and check_subscriptions task

master
alice 4 years ago
parent 1dcf462376
commit 09ac0996b8

@ -0,0 +1,7 @@
from __future__ import absolute_import, unicode_literals
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
__all__ = ('celery_app',)

@ -0,0 +1,36 @@
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from celery.schedules import crontab
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ccvpn.settings')
app = Celery('ccvpn')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
app.conf.beat_schedule = {
'payments__check_subscriptions': {
'task': 'payments.tasks.check_subscriptions',
'schedule': crontab(hour=1),
},
'lambdainst__resync': {
'task': 'lambdainst.tasks.push_all_users',
'schedule': crontab(day_of_week=1),
},
}
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))

@ -43,6 +43,8 @@ INSTALLED_APPS = (
'django.contrib.humanize',
'django_countries',
'django_lcore',
'django_celery_results',
'django_celery_beat',
'lambdainst',
'payments',
'tickets',
@ -193,6 +195,13 @@ ROOT_URL = ''
# Forwarded for header name, if any (None will use remote_addr)
REAL_IP_HEADER_NAME = None
# Celery defaults
#CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'
CELERY_RESULT_BACKEND = 'django-db'
CELERY_CACHE_BACKEND = 'django-cache'
CELERY_BROKER_URL = 'redis://localhost/1'
CELERY_TIMEZONE = 'UTC'
# reCAPTCHA API details. If empty, no captcha is displayed.
RECAPTCHA_API = 'https://www.google.com/recaptcha/api/siteverify'
RECAPTCHA_SITE_KEY = ''

@ -6,7 +6,7 @@ from django.utils.translation import ugettext as _
from django.db.models.signals import post_save
from django.dispatch import receiver
from constance import config as site_config
from django_lcore.core import LcoreUserProfileMethods, setup_sync_hooks
from django_lcore.core import LcoreUserProfileMethods, setup_sync_hooks, VPN_AUTH_STORAGE
from ccvpn.common import get_trial_period_duration
from payments.models import Subscription
@ -84,6 +84,12 @@ class VPNUser(models.Model, LcoreUserProfileMethods):
.first()
return s
def lcore_sync(self):
if VPN_AUTH_STORAGE == 'inst':
return
from lambdainst.tasks import push_user
push_user.delay(user_id=self.id)
def __str__(self):
return self.user.username

@ -0,0 +1,25 @@
import logging
import django_lcore
from lambdainst.models import User
logger = logging.getLogger(__name__)
from celery import task
@task(autoretry_for=(Exception,), default_retry_delay=60*60)
def push_all_users():
for u in User.objects.all():
# skip 'empty' accounts
if u.vpnuser.expiration is None:
continue
logger.debug("pushing user %r", u)
django_lcore.sync_user(u.vpnuser, fail_silently=False)
@task(autoretry_for=(Exception,), max_retries=10, retry_backoff=True)
def push_user(user_id):
user = User.objects.get(id=user_id)
logger.debug("pushing user %r", user)
django_lcore.sync_user(user.vpnuser, fail_silently=False)

@ -87,7 +87,7 @@ def signup(request):
user.vpnuser.campaign = request.session.get('campaign')
user.vpnuser.save()
django_lcore.sync_user(user.vpnuser, True)
user.vpnuser.lcore_sync()
user.backend = 'django.contrib.auth.backends.ModelBackend'
auth.login(request, user)

@ -125,6 +125,26 @@ class StripeBackend(BackendBase):
subscr.save()
return True
def refresh_subscription(self, subscr):
if subscr.backend_extid.startswith('cus_'):
customer = self.stripe.Customer.retrieve(subscr.backend_extid)
for s in customer['subscriptions']['data']:
if s['status'] == 'active':
sub = s
break
else:
return
elif subscr.backend_extid.startswith('sub_'):
sub = self.stripe.Subscription.retrieve(subscr.backend_extid)
else:
print("unhandled subscription backend extid: {}".format(subscr.backend_extid))
return
if sub['status'] == 'canceled':
subscr.status = 'cancelled'
if sub['status'] == 'past_due':
subscr.status = 'error'
def webhook_session_completed(self, event):
session = event['data']['object']
@ -213,6 +233,20 @@ class StripeBackend(BackendBase):
payment.user.vpnuser.save()
payment.save()
def webhook_subscr_update(self, event):
from payments.models import Subscription
stripe_sub = event['data']['object']
sub = Subscription.objects.get(backend_id='stripe', backend_extid=stripe_sub['id'])
if not sub:
return
if stripe_sub['status'] == 'canceled':
sub.status = 'cancelled'
if stripe_sub['status'] == 'past_due':
sub.status = 'error'
sub.save()
def webhook(self, request):
payload = request.body
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
@ -228,6 +262,8 @@ class StripeBackend(BackendBase):
self.webhook_payment_succeeded(event)
if event['type'] == 'checkout.session.completed':
self.webhook_session_completed(event)
if event['type'] == 'customer.subscription.deleted':
self.webhook_subscr_update(event)
return True
def get_ext_url(self, payment):

@ -0,0 +1,16 @@
import logging
from .models import Subscription, ACTIVE_BACKENDS
logger = logging.getLogger(__name__)
from celery import task
@task
def check_subscriptions():
logger.debug("checking subscriptions")
subs = Subscription.objects.filter(status='active', backend_id='stripe').all()
for sub in subs:
logger.debug("checking subscription #%s on %s", sub.id, sub.backend_id)
sub.refresh_from_db()
ACTIVE_BACKENDS['stripe'].refresh_subscription(sub)
sub.save()

309
poetry.lock generated

@ -1,3 +1,14 @@
[[package]]
category = "main"
description = "Low-level AMQP client for Python (fork of amqplib)."
name = "amqp"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.6.1"
[package.dependencies]
vine = ">=1.1.3,<5.0.0a1"
[[package]]
category = "main"
description = "ASGI specs, helper code, and adapters"
@ -26,6 +37,62 @@ wrapt = ">=1.11,<2.0"
python = "<3.8"
version = ">=1.4.0,<1.5"
[[package]]
category = "main"
description = "Python multiprocessing fork with improvements and bugfixes"
name = "billiard"
optional = false
python-versions = "*"
version = "3.6.3.0"
[[package]]
category = "main"
description = "Distributed Task Queue."
name = "celery"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "4.4.7"
[package.dependencies]
billiard = ">=3.6.3.0,<4.0"
kombu = ">=4.6.10,<4.7"
pytz = ">0.0-dev"
vine = "1.3.0"
[package.extras]
arangodb = ["pyArango (>=1.3.2)"]
auth = ["cryptography"]
azureblockblob = ["azure-storage (0.36.0)", "azure-common (1.1.5)", "azure-storage-common (1.1.0)"]
brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"]
cassandra = ["cassandra-driver (<3.21.0)"]
consul = ["python-consul"]
cosmosdbsql = ["pydocumentdb (2.3.2)"]
couchbase = ["couchbase-cffi (<3.0.0)", "couchbase (<3.0.0)"]
couchdb = ["pycouchdb"]
django = ["Django (>=1.11)"]
dynamodb = ["boto3 (>=1.9.178)"]
elasticsearch = ["elasticsearch"]
eventlet = ["eventlet (>=0.24.1)"]
gevent = ["gevent"]
librabbitmq = ["librabbitmq (>=1.5.0)"]
lzma = ["backports.lzma"]
memcache = ["pylibmc"]
mongodb = ["pymongo (>=3.3.0)"]
msgpack = ["msgpack"]
pymemcache = ["python-memcached"]
pyro = ["pyro4"]
redis = ["redis (>=3.2.0)"]
riak = ["riak (>=2.0)"]
s3 = ["boto3 (>=1.9.125)"]
slmq = ["softlayer-messaging (>=1.0.3)"]
solar = ["ephem"]
sqlalchemy = ["sqlalchemy"]
sqs = ["boto3 (>=1.9.125)", "pycurl (7.43.0.5)"]
tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=1.3.1)"]
zstd = ["zstandard"]
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
@ -76,6 +143,31 @@ optional = false
python-versions = "*"
version = "1.0.3"
[[package]]
category = "main"
description = "Database-backed Periodic Tasks."
name = "django-celery-beat"
optional = false
python-versions = "*"
version = "2.0.0"
[package.dependencies]
Django = ">=1.11.17"
celery = "*"
django-timezone-field = ">=4.0,<5.0"
python-crontab = ">=2.3.4"
[[package]]
category = "main"
description = "Celery result backends for Django."
name = "django-celery-results"
optional = false
python-versions = "*"
version = "1.2.1"
[package.dependencies]
celery = ">=4.4,<5.0"
[[package]]
category = "main"
description = "Django live settings with pluggable backends, including Redis."
@ -134,6 +226,7 @@ lcoreapi = "*"
reference = "e186aa5719e2e7e09cb1bed13ae729d2623698cf"
type = "git"
url = "https://git.packetimpact.net/lvpn/django-lcore.git"
[[package]]
category = "main"
description = "Pickled object field for Django"
@ -148,6 +241,18 @@ Django = ">=2.2"
[package.extras]
tests = ["tox"]
[[package]]
category = "main"
description = "A Django app providing database and form fields for pytz timezone objects."
name = "django-timezone-field"
optional = false
python-versions = ">=3.5"
version = "4.0"
[package.dependencies]
django = ">=2.2"
pytz = "*"
[[package]]
category = "main"
description = "A Django application that provides a fully functional TinyMCE 4 editor widget for models and forms."
@ -160,6 +265,41 @@ version = "1.8.0"
Django = ">=1.11"
jsmin = "*"
[[package]]
category = "main"
description = "Celery Flower"
name = "flower"
optional = false
python-versions = "*"
version = "0.9.5"
[package.dependencies]
humanize = "*"
prometheus-client = "0.8.0"
pytz = "*"
[[package.dependencies.celery]]
python = "<3.7"
version = ">=3.1.0"
[[package.dependencies.celery]]
python = ">=3.7"
version = ">=4.3.0"
[package.dependencies.tornado]
python = ">=3.5.2"
version = ">=5.0.0,<7.0.0"
[[package]]
category = "main"
description = "Python humanize utilities"
name = "humanize"
optional = false
python-versions = ">=3.5"
version = "2.6.0"
[package.extras]
tests = ["freezegun", "pytest", "pytest-cov"]
[[package]]
category = "main"
description = "Internationalized Domain Names in Applications (IDNA)"
@ -171,7 +311,7 @@ version = "2.10"
[[package]]
category = "main"
description = "Read metadata from Python packages"
marker = "python_version < \"3.8\""
marker = "python_version < \"3.8\" or python_version >= \"3.7\" and python_version < \"3.8\""
name = "importlib-metadata"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
@ -206,6 +346,37 @@ optional = false
python-versions = "*"
version = "2.2.2"
[[package]]
category = "main"
description = "Messaging library for Python."
name = "kombu"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "4.6.11"
[package.dependencies]
amqp = ">=2.6.0,<2.7"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.18"
[package.extras]
azureservicebus = ["azure-servicebus (>=0.21.1)"]
azurestoragequeues = ["azure-storage-queue"]
consul = ["python-consul (>=0.6.0)"]
librabbitmq = ["librabbitmq (>=1.5.2)"]
mongodb = ["pymongo (>=3.3.0)"]
msgpack = ["msgpack"]
pyro = ["pyro4"]
qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
redis = ["redis (>=3.3.11)"]
slmq = ["softlayer-messaging (>=1.0.3)"]
sqlalchemy = ["sqlalchemy"]
sqs = ["boto3 (>=1.4.4)", "pycurl (7.43.0.2)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=1.3.1)"]
[[package]]
category = "dev"
description = "A fast and thorough lazy object proxy."
@ -229,6 +400,7 @@ requests = "*"
reference = "df771a7dbd1a2166a9a873539f976865f6f6a630"
type = "git"
url = "https://git.packetimpact.net/lvpn/lcoreapi.git"
[[package]]
category = "main"
description = "Python implementation of Markdown."
@ -253,6 +425,17 @@ optional = false
python-versions = "*"
version = "0.6.1"
[[package]]
category = "main"
description = "Python client for the Prometheus monitoring system."
name = "prometheus-client"
optional = false
python-versions = "*"
version = "0.8.0"
[package.extras]
twisted = ["twisted"]
[[package]]
category = "main"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
@ -333,6 +516,32 @@ optional = false
python-versions = "*"
version = "0.11.0"
[[package]]
category = "main"
description = "Python Crontab API"
name = "python-crontab"
optional = false
python-versions = "*"
version = "2.5.1"
[package.dependencies]
python-dateutil = "*"
[package.extras]
cron-description = ["cron-descriptor"]
cron-schedule = ["croniter"]
[[package]]
category = "main"
description = "Extensions to the standard Python datetime module"
name = "python-dateutil"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
version = "2.8.1"
[package.dependencies]
six = ">=1.5"
[[package]]
category = "main"
description = "Parse and manage posts with YAML (or other) frontmatter"
@ -361,6 +570,17 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "5.3.1"
[[package]]
category = "main"
description = "Python client for Redis key-value store"
name = "redis"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "3.5.3"
[package.extras]
hiredis = ["hiredis (>=0.1.3)"]
[[package]]
category = "main"
description = "Python HTTP for Humans."
@ -416,6 +636,15 @@ optional = false
python-versions = "*"
version = "0.10.1"
[[package]]
category = "main"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
marker = "python_version >= \"3.5.2\""
name = "tornado"
optional = false
python-versions = ">= 3.5"
version = "6.0.4"
[[package]]
category = "dev"
description = "a fork of Python 2 and 3 ast modules with type comment support"
@ -438,6 +667,14 @@ brotli = ["brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
[[package]]
category = "main"
description = "Promises, promises, promises."
name = "vine"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.0"
[[package]]
category = "dev"
description = "Module for decorators, wrappers and monkey patching."
@ -449,7 +686,7 @@ version = "1.12.1"
[[package]]
category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version < \"3.8\""
marker = "python_version < \"3.8\" or python_version >= \"3.7\" and python_version < \"3.8\""
name = "zipp"
optional = false
python-versions = ">=3.6"
@ -460,10 +697,14 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata]
content-hash = "22e6b3c34d0d34295899ba2aca1f916916b922321a021a28a5e519ef05c02aa4"
content-hash = "158e7cac2e0a448cae8dd391c82c452dcd1ff937e14b67bb7d2904e5388d5fd6"
python-versions = "^3.6"
[metadata.files]
amqp = [
{file = "amqp-2.6.1-py2.py3-none-any.whl", hash = "sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"},
{file = "amqp-2.6.1.tar.gz", hash = "sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21"},
]
asgiref = [
{file = "asgiref-3.2.10-py3-none-any.whl", hash = "sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed"},
{file = "asgiref-3.2.10.tar.gz", hash = "sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a"},
@ -472,6 +713,14 @@ astroid = [
{file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"},
{file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"},
]
billiard = [
{file = "billiard-3.6.3.0-py3-none-any.whl", hash = "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede"},
{file = "billiard-3.6.3.0.tar.gz", hash = "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"},
]
celery = [
{file = "celery-4.4.7-py2.py3-none-any.whl", hash = "sha256:a92e1d56e650781fb747032a3997d16236d037c8199eacd5217d1a72893bca45"},
{file = "celery-4.4.7.tar.gz", hash = "sha256:d220b13a8ed57c78149acf82c006785356071844afe0b27012a4991d44026f9f"},
]
certifi = [
{file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
{file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
@ -492,6 +741,14 @@ django-admin-list-filter-dropdown = [
{file = "django-admin-list-filter-dropdown-1.0.3.tar.gz", hash = "sha256:07cd37b6a9be1b08f11d4a92957c69b67bc70b1f87a2a7d4ae886c93ea51eb53"},
{file = "django_admin_list_filter_dropdown-1.0.3-py3-none-any.whl", hash = "sha256:bf1b48bab9772dad79db71efef17e78782d4f2421444d5e49bb10e0da71cd6bb"},
]
django-celery-beat = [
{file = "django-celery-beat-2.0.0.tar.gz", hash = "sha256:fdf1255eecfbeb770c6521fe3e69989dfc6373cd5a7f0fe62038d37f80f47e48"},
{file = "django_celery_beat-2.0.0-py2.py3-none-any.whl", hash = "sha256:fe0b2a1b31d4a6234fea4b31986ddfd4644a48fab216ce1843f3ed0ddd2e9097"},
]
django-celery-results = [
{file = "django_celery_results-1.2.1-py2.py3-none-any.whl", hash = "sha256:a29ab580f0e38c66c39f51cc426bbdbb2a391b8cc0867df9dea748db2c961db2"},
{file = "django_celery_results-1.2.1.tar.gz", hash = "sha256:e390f70cc43bbc2cd7c8e4607dc29ab6211a2ab968f93677583f0160921f670c"},
]
django-constance = [
{file = "django-constance-2.7.0.tar.gz", hash = "sha256:62bdb1a3aef20d80e18d832b30ffcc6626762c538817a5a3571bcefa5a55c849"},
{file = "django_constance-2.7.0-py3-none-any.whl", hash = "sha256:866a7356d3f8ee08374285e97c1edae70edaa3df6eaf7b2e9699f7cde8a88f3b"},
@ -509,10 +766,22 @@ django-picklefield = [
{file = "django-picklefield-3.0.1.tar.gz", hash = "sha256:15ccba592ca953b9edf9532e64640329cd47b136b7f8f10f2939caa5f9ce4287"},
{file = "django_picklefield-3.0.1-py3-none-any.whl", hash = "sha256:3c702a54fde2d322fe5b2f39b8f78d9f655b8f77944ab26f703be6c0ed335a35"},
]
django-timezone-field = [
{file = "django-timezone-field-4.0.tar.gz", hash = "sha256:7e3620fe2211c2d372fad54db8f86ff884098d018d56fda4dca5e64929e05ffc"},
{file = "django_timezone_field-4.0-py3-none-any.whl", hash = "sha256:758b7d41084e9ea2e89e59eb616e9b6326e6fbbf9d14b6ef062d624fe8cc6246"},
]
django-tinymce4-lite = [
{file = "django-tinymce4-lite-1.8.0.tar.gz", hash = "sha256:eb0ee7eda19970d06484f9e121871de01287b5345c4007ea2582d6f80ec3edb3"},
{file = "django_tinymce4_lite-1.8.0-py3-none-any.whl", hash = "sha256:2d53510ddb5fe20f25e525d4eaf7c8f8a567b85c9cc29f8ab2964419d9352d88"},
]
flower = [
{file = "flower-0.9.5-py2.py3-none-any.whl", hash = "sha256:71be02bff7b2f56b0a07bd947fb3c748acba7f44f80ae88125d8954ce1a89697"},
{file = "flower-0.9.5.tar.gz", hash = "sha256:56916d1d2892e25453d6023437427fc04706a1308e0bd4822321da34e1643f9c"},
]
humanize = [
{file = "humanize-2.6.0-py3-none-any.whl", hash = "sha256:fd5b32945687443d5b8bc1e02fad027da1d293a9e963b3450122ad98ef534f21"},
{file = "humanize-2.6.0.tar.gz", hash = "sha256:8ee358ea6c23de896b9d1925ebe6a8504bb2ba7e98d5ccf4d07ab7f3b28f3819"},
]
idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
@ -528,6 +797,10 @@ isort = [
jsmin = [
{file = "jsmin-2.2.2.tar.gz", hash = "sha256:b6df99b2cd1c75d9d342e4335b535789b8da9107ec748212706ef7bbe5c2553b"},
]
kombu = [
{file = "kombu-4.6.11-py2.py3-none-any.whl", hash = "sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a"},
{file = "kombu-4.6.11.tar.gz", hash = "sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"},
{file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"},
@ -560,6 +833,10 @@ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
prometheus-client = [
{file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"},
{file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"},
]
psycopg2-binary = [
{file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"},
{file = "psycopg2_binary-2.8.5-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"},
@ -616,6 +893,13 @@ python-bitcoinlib = [
{file = "python-bitcoinlib-0.11.0.tar.gz", hash = "sha256:3daafd63cb755f6e2067b7c9c514053856034c9f9363c80c37007744d54a2e06"},
{file = "python_bitcoinlib-0.11.0-py3-none-any.whl", hash = "sha256:6e7982734637135599e2136d3c88d622f147e3b29201636665f799365784cd9e"},
]
python-crontab = [
{file = "python-crontab-2.5.1.tar.gz", hash = "sha256:4bbe7e720753a132ca4ca9d4094915f40e9d9dc8a807a4564007651018ce8c31"},
]
python-dateutil = [
{file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
{file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
]
python-frontmatter = [
{file = "python-frontmatter-0.5.0.tar.gz", hash = "sha256:a9c2e90fc38e9f0c68d8b82299040f331ca3b8525ac7fa5f6beffef52b26c426"},
{file = "python_frontmatter-0.5.0-py3-none-any.whl", hash = "sha256:a7dcdfdaf498d488dce98bfa9452f8b70f803a923760ceab1ebd99291d98d28a"},
@ -637,6 +921,10 @@ pyyaml = [
{file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
{file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
]
redis = [
{file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
{file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
]
requests = [
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
@ -657,6 +945,17 @@ toml = [
{file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
]
tornado = [
{file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"},
{file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"},
{file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"},
{file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"},
{file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"},
{file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"},
{file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"},
{file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"},
{file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"},
]
typed-ast = [
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
@ -684,6 +983,10 @@ urllib3 = [
{file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"},
{file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
]
vine = [
{file = "vine-1.3.0-py2.py3-none-any.whl", hash = "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"},
{file = "vine-1.3.0.tar.gz", hash = "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87"},
]
wrapt = [
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
]

@ -23,6 +23,11 @@ python-frontmatter = "^0.5.0"
django-tinymce4-lite = "^1.7"
django-admin-list-filter-dropdown = "^1.0"
django-lcore = {git = "https://git.packetimpact.net/lvpn/django-lcore.git", tag = "v1.4.1"}
celery = "^4.4.7"
django-celery-beat = "^2.0.0"
django-celery-results = "^1.2.1"
redis = "^3.5.3"
flower = "^0.9.5"
[tool.poetry.dev-dependencies]
pylint = "^2.3"

Loading…
Cancel
Save