Commit ef0f5eda authored by gaoming's avatar gaoming

newproject

parents
#article -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import
from enum import Enum
class APP_TYPE(Enum):
'''
APP类型
'''
GENGMEI=('0','gengmei')
LIKE=('1','like')
class CLIENT_TYPE(Enum):
"""
端 的 来源
"""
USER = (0, '用户版客户端')
DOCTOR = (1, '医生版客户端')
SHEQU = (2, '社区版客户端') # for aso
PLATFORM_CHOICES =[
('android', 'ANDROID', 'Android'),
('iPhone', 'IPHONE', 'iPhone'),
('XCX', 'XCX', 'XCX'), #小程序
]
class PLATFORM_CHANNEL(Enum):
UNKNOWN = (0, '未知')
IOS = (1, 'ios客户端')
ANDROID = (2, 'android客户端')
M = (3, 'm站')
PC = (4, 'PC站')
WECHAT_APP = (5, '微信小程序')
MOMO = (6, "陌陌")
DOCTOR = (7, "医生后台")
class CHANNEL_STATUS(Enum):
"""
渠道记录和激活返回的状态信息
ACT_前缀: 激活状态, ADD_前缀: 点击记录状态
"""
ACT_INVALID_PARAMS = '激活失败, 参数不合法'
ACT_FAIL_GENERAL = '激活失败'
ACT_ALREADY_ACTIVATED = '激活失败, 设备已激活'
ACT_NO_PROMOTION = '激活失败, 设备不是指定渠道推广'
ACT_OK = '激活成功'
ADD_INVALID_PARAMS = '记录失败, 参数不合法'
ADD_ALREADY_ACTIVATED = '记录失败, 设备已激活'
ADD_OK = '记录成功'
ADD_FAIL_GENERAL = '记录失败'
class CHANNEL_CALLBACK_TYPE(Enum):
"""
渠道回调类型
"""
NO_CALLBACK = ('0', '无回调')
GET_CALLBACK = ('1', 'GET回调')
POST_CALLBACK = ('2', 'POST回调')
class PROMOTION_CHANNEL_NAME(Enum):
"""
渠道名称列表, 规定枚举值为小写, 格式为promotion_xxxx
"""
MALIOASO = ('promotion_malioaso', 'malioaso')
MALIOASOSHEQU = ('promotion_malioaso-shequ', 'malioaso-shequ')
GDT = ('promotion_gdt', '广点通')
MCAS = ('promotion_mcas', '迈可思')
QUMI = ('promotion_qumi', '趣米')
AISI = ('promotion_aisi', '爱思助手')
SHIKE = ('promotion_shike', '试客')
ASO100 = ('promotion_aso100', 'aso100')
QIANKA = ('promotion_qianka', '钱咖')
DIANRU = ('promotion_dianru', '点入')
GREENKOO = ('promotion_greenkoo', '青稞万维')
WYZM = ('promotion_51zm', '51桌面')
XIAOYU = ('promotion_xiaoyu', '小鱼赚钱')
BAIDU = ('promotion_baidu', '百度')
GOOGLE = ('promotion_google', '谷歌')
MOMO = ('promotion_momo', '陌陌')
TOUTIAO = ('promotion_toutiao', '今日头条')
WANGYIYOUDAO = ('promotion_wangyiyoudao', '网易有道')
QUTOUTIAO = ('promotion_qutoutiao', '趣头条')
KOOMOBI = ('promotion_koomobi', '青稞万维')
BAIDUSHOUZHU = ('promotion_bdshouzhu', '百度手助')
DIANJING = ('promotion_dianjing', '360点睛实效平台')
RUIYING = ('promotion_ruiying', '瑞盈aso')
JULANG = ('promotion_julang', '聚浪aso')
MEIYOU = ('promotion_meiyou', '美柚')
MEITU = ('promotion_meitu', '美图')
KXXXL = ('promotion_kxxxl', '消消乐')
SHOUJIBAIDU = ('promotion_baiduxxl', '手机百度')
\ No newline at end of file
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',)
\ No newline at end of file
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'channels.settings')
app = Celery('channels')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
"""
Django settings for channels project.
Generated by 'django-admin startproject' using Django 2.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'b-56^v12@eex@zzf0$v1lopdbu9#@_khe7^3q_-mcz*(!^1n$)'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
#**********************************Celery Config*************************************************
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' # Broker配置,使用Redis作为消息中间件
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0' # BACKEND配置,这里使用redis
CELERY_RESULT_SERIALIZER = 'json' # 结果序列化方案
#***********************************************************************************
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'like',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'channels.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'channels.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
# -*- coding:utf-8 -*-
from django.urls import include, path,re_path
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('like/', include('like.urls'),name='like'),
]
\ No newline at end of file
"""
WSGI config for channels project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'channels.settings')
application = get_wsgi_application()
File added
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class LikeConfig(AppConfig):
name = 'like'
# Generated by Django 2.2.1 on 2019-06-03 09:21
import channels.HermesEnum
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='PromotionChannel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_name', models.CharField(default=channels.HermesEnum.APP_TYPE(('0', 'gengmei')), max_length=64, verbose_name='app名字')),
('appid', models.CharField(max_length=64, verbose_name='渠道id')),
('idfa', models.CharField(max_length=64, verbose_name='设备id')),
('model', models.CharField(max_length=40, null=True, verbose_name='Model')),
('client_type', models.IntegerField(default=channels.HermesEnum.CLIENT_TYPE((0, '用户版客户端')), verbose_name='客户端类型')),
('callback_type', models.CharField(default=channels.HermesEnum.CHANNEL_CALLBACK_TYPE(('0', '无回调')), max_length=2)),
('callback', models.CharField(blank=True, default='', max_length=1024, verbose_name='渠道回调')),
('request', models.TextField(blank=True, default='', verbose_name='点击上报请求的json')),
('response', models.TextField(blank=True, default='', verbose_name='回调响应的json')),
('active_key', models.CharField(default='', max_length=1024, verbose_name='激活回调需要的额外签名')),
('record_from', models.CharField(blank=True, default='', max_length=128, verbose_name='数据的来源')),
('platform', models.IntegerField(default=channels.HermesEnum.PLATFORM_CHANNEL((0, '未知')), verbose_name='IOS或安卓类型')),
('version', models.CharField(max_length=10, null=True, verbose_name='APP版本')),
('os_version', models.CharField(max_length=20, null=True, verbose_name='系统版本')),
('screen', models.CharField(max_length=40, null=True, verbose_name='屏幕分辨率')),
('channel', models.CharField(default=channels.HermesEnum.CLIENT_TYPE((0, '用户版客户端')), max_length=40, null=True, verbose_name='渠道')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
('active_time', models.DateTimeField(blank=True, null=True, verbose_name='激活时间')),
('last_activity', models.DateTimeField(auto_now=True, verbose_name='最后活跃时间')),
('mac', models.CharField(default='', max_length=100, verbose_name='iOS设备的idfv或Android设备的mac')),
],
options={
'db_table': 'like_promotion_channel',
'unique_together': {('idfa', 'appid')},
'verbose_name': '渠道推广记录',
},
),
]
# Generated by Django 2.2.1 on 2019-06-03 11:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('like', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='promotionchannel',
name='app_name',
field=models.CharField(default='0', max_length=64, verbose_name='app名字'),
),
migrations.AlterField(
model_name='promotionchannel',
name='callback_type',
field=models.CharField(default='0', max_length=2),
),
migrations.AlterField(
model_name='promotionchannel',
name='channel',
field=models.CharField(default=0, max_length=40, null=True, verbose_name='渠道'),
),
migrations.AlterField(
model_name='promotionchannel',
name='client_type',
field=models.IntegerField(default=0, verbose_name='客户端类型'),
),
migrations.AlterField(
model_name='promotionchannel',
name='platform',
field=models.IntegerField(default=0, verbose_name='IOS或安卓类型'),
),
]
# Generated by Django 2.2.1 on 2019-06-04 01:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('like', '0002_auto_20190603_1902'),
]
operations = [
migrations.RenameField(
model_name='promotionchannel',
old_name='idfa',
new_name='device_id',
),
migrations.AlterUniqueTogether(
name='promotionchannel',
unique_together={('device_id', 'appid')},
),
]
# Generated by Django 2.2.1 on 2019-06-04 05:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('like', '0003_auto_20190604_0958'),
]
operations = [
migrations.AddField(
model_name='promotionchannel',
name='idfa',
field=models.CharField(max_length=64, null=True, verbose_name='idfa'),
),
migrations.AddField(
model_name='promotionchannel',
name='idfv',
field=models.CharField(max_length=64, null=True, verbose_name='idfv'),
),
migrations.AddField(
model_name='promotionchannel',
name='imei',
field=models.CharField(max_length=64, null=True, verbose_name='imei'),
),
migrations.AddField(
model_name='promotionchannel',
name='udid',
field=models.CharField(max_length=64, null=True, verbose_name='udid'),
),
migrations.AddField(
model_name='promotionchannel',
name='uuid',
field=models.CharField(max_length=64, null=True, verbose_name='uuid'),
),
migrations.AlterField(
model_name='promotionchannel',
name='mac',
field=models.CharField(default='', max_length=100, null=True, verbose_name='iOS设备的idfv或Android设备的mac'),
),
]
# Create your models here.
# coding=utf-8
from __future__ import unicode_literals
import datetime
import math
import json
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from channels.HermesEnum import(
APP_TYPE,
CHANNEL_CALLBACK_TYPE,
CLIENT_TYPE,
PLATFORM_CHANNEL,
CHANNEL_STATUS
)
class PromotionChannel(models.Model):
class Meta:
unique_together = ('device_id', 'appid')
verbose_name = '渠道推广记录'
db_table = 'like_promotion_channel'
app_label = 'like'
app_name = models.CharField(max_length=64, verbose_name=u'app名字',default=APP_TYPE.GENGMEI.value[0])
appid = models.CharField(max_length=64, verbose_name=u'渠道id')
device_id = models.CharField(max_length=64, verbose_name=u'设备id')
idfa = models.CharField(max_length=64,null=True, verbose_name=u'idfa')
idfv = models.CharField(max_length=64,null=True, verbose_name=u'idfv')
udid=models.CharField(max_length=64,null=True, verbose_name=u'udid')
uuid=models.CharField(max_length=64,null=True, verbose_name=u'uuid')
imei = models.CharField(max_length=64,null=True,verbose_name=u'imei')
#设备物理地址
mac = models.CharField(max_length=100, verbose_name=u'iOS设备的idfv或Android设备的mac', null=True, default="")
platform = models.CharField(max_length=100,verbose_name=u'IOS或安卓类型',default=PLATFORM_CHANNEL.UNKNOWN.value[0])
model = models.CharField(verbose_name=u'Model', null=True, max_length=40)#这是什么
client_type = models.IntegerField(verbose_name=u'客户端类型', default=CLIENT_TYPE.USER.value[0])
callback_type = models.CharField(max_length=2,default=CHANNEL_CALLBACK_TYPE.NO_CALLBACK.value[0])
callback = models.CharField(max_length=1024, verbose_name=u'渠道回调', blank=True, default='')
request = models.TextField(blank=True, verbose_name=u'点击上报请求的json', default='')
response = models.TextField(blank=True, verbose_name=u'回调响应的json', default='')
active_key = models.CharField(max_length=1024, verbose_name=u'激活回调需要的额外签名', default='')
record_from = models.CharField(max_length=128, verbose_name=u'数据的来源', blank=True, default='')
version = models.CharField(verbose_name=u'APP版本', null=True, max_length=10)
os_version = models.CharField(verbose_name=u'系统版本', null=True, max_length=20)
# Android Only
screen = models.CharField(verbose_name=u'屏幕分辨率', null=True, max_length=40)
channel = models.CharField(verbose_name=u'渠道', null=True, max_length=40,default=CLIENT_TYPE.USER.value[0])
# time
create_time = models.DateTimeField(default=timezone.now, verbose_name=u'创建时间')
active_time = models.DateTimeField(null=True, blank=True, verbose_name=u'激活时间')
last_activity = models.DateTimeField(auto_now=True, verbose_name=u'最后活跃时间')
@staticmethod
def record(request,**kwargs):
"""
新增加渠道设备数据
"""
#获取类的全部字段
propertys=PromotionChannel.__dict__.keys()
keys=kwargs.keys()
#查找存在表中的字段
params={key:value for key,value in kwargs.items() if key in propertys}
#获得request的数据
if request:
params.update({"request":json.dumps(request.GET.dict())})
#appid加前缀和子渠道
appid=params.get("appid")
sub_name=kwargs.get('sub_name')
appid = 'promotion_{0}{1}'.format(appid,"_{0}".format(sub_name) if sub_name else "")
params.update({"appid":appid})
print(params)
#保存并记录
try:
promotion= PromotionChannel.objects.create(**params)
except Exception as e:
#字典转字符串
print(str(e))
return False,CHANNEL_STATUS.ADD_FAIL_GENERAL
return True,''
from django.test import TestCase
# Create your tests here.
from django.urls import include, path,re_path
from . import views
app_name="like"
#(?P<sub_name>[a-z0-9]+/)?
urlpatterns =[
re_path(r'^[a-z0-9]+/click-notify$', views.Channel.as_view()),
re_path(r'^statistic$', views.Statistic.as_view())
]
\ No newline at end of file
#!/usr/bin/env python
# encoding=utf-8
from distutils.version import LooseVersion
from django.conf import settings
from channels.HermesEnum import PLATFORM_CHOICES
#!/usr/bin/env python
# encoding=utf-8
#from __future__ import unicode_literals
import abc
from urllib.parse import urlencode,unquote
import logging
import json
# import requests #导致循环引用
import hashlib
import time
from collections import namedtuple, OrderedDict
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseNotAllowed, JsonResponse, HttpResponse, Http404
from django.views.generic import View
from .models import *
from celery import shared_task
from django.db.models import Q
class Statistic(View):
"""
点击统计
"""
def get(self,request):
data = request.GET.dict()
# 迈可思, 趣米, 广点通, 爱思助手激活回调(增加点入)
self.async_active_callback.delay(**data)
return JsonResponse({
"code":0,#0为成功,其他为失败
"failMsg":"",#错误信息
})
def post(self,request):
data=request.POST.dict()
# 迈可思, 趣米, 广点通, 爱思助手激活回调(增加点入)
self.async_active_callback.delay(**data)
print(data)
#self.async_active_callback(**data)
return JsonResponse({
"code":0,#0为成功,其他为失败
"failMsg":"",#错误信息
})
@csrf_exempt
def dispatch(self, *args, **kwargs):
return super(Statistic, self).dispatch(*args, **kwargs)
@shared_task
def async_active_callback(**kwargs):
'''
异步激活和回调
'''
platform=kwargs.get('platform','')
#设备编号相关id
device_id=kwargs.get('device_id')
client_type=kwargs.get("client_type",CLIENT_TYPE.USER.value[0])
md5=hashlib.md5()
md5.update(device_id.encode(encoding='utf-8'))
device_id=md5.hexdigest()
#获取查询子串
sub_query=["{0}='{1}'".format(key,value) for key,value in kwargs.items() if key in ["device_id","idfa","idfv","imei","mac"]]
sql="select * from like_promotion_channel where {0}".format(" or ".join(sub_query))
record = PromotionChannel.objects.raw(sql)
#如果不存在,谷歌推广?
if not record:
kwargs.update({"appid":"google","active_time":datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
record=PromotionChannel.record(None,**kwargs)
return
if record:
record=record[0]
#如果已经激活,不做处理
if record and record.active_time:
return
#无回调,不做处理
if record.callback_type==CHANNEL_CALLBACK_TYPE.NO_CALLBACK:
return
#无回调url,不做处理
if not record.callback:
return
url = unquote(record.callback)
#根据平台不同,调整参数类型
try:
import requests
#GET回调
if record.callback_type == CHANNEL_CALLBACK_TYPE.GET_CALLBACK:
response = requests.get(url, verify=_VERIFIED)
#POST回调
if record.callback_type == CHANNEL_CALLBACK_TYPE.POST_CALLBACK:
response = requests.post(url, verify=_VERIFIED)
except Exception as e:
channel_logger.error(str(e))
#保存更新时间
record.save(active_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
return JsonResponse({'error':0, 'msg':''})
class Channel(View):
'''
处理请求
'''
request=None
def get(self,request):
self.request=request
platform=request.path.split('/')[2]
#获得渠道数据
data=Channel.__dict__.get(platform)(self)
result,msg=PromotionChannel.record(request,**data)
if not result:
return JsonResponse({'code':1,'failMsg':msg})
return JsonResponse({"code":0,"failMsg":""})
def google(self):
'''
谷歌推广
'''
data=self.request.GET.dict()
return data
def zhihu(self):
data=self.request.GET.dict()
data.update({"appid":"zhihu"})
#获得设备类型,和设备号
_os=data.get("os")
if _os.strip() in ("ios","android"):
platform = PLATFORM_CHANNEL.IOS.value[0]if _os.strip()=="ios" else PLATFORM_CHANNEL.ANDROID.value[0]
data.update({"platform":platform})
create_time=time.localtime(int(data.pop('click_time'))/1000)
dt=time.strftime("%Y-%m-%d %H:%M:%S",create_time)
data.update({"create_time":dt})
#增加记录
return data
def xiaomi(self):
#处理小米数据
data=self.request.GET.dict()
return data
\ No newline at end of file
/*
Navicat Premium Data Transfer
Source Server : channel本地
Source Server Type : SQLite
Source Server Version : 3012001
Source Schema : main
Target Server Type : SQLite
Target Server Version : 3012001
File Encoding : 65001
Date: 04/06/2019 10:05:06
*/
PRAGMA foreign_keys = false;
-- ----------------------------
-- Table structure for like_promotion_channel
-- ----------------------------
DROP TABLE IF EXISTS "like_promotion_channel";
CREATE TABLE "like_promotion_channel" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "app_name" varchar(64) NOT NULL, "appid" varchar(64) NOT NULL, "model" varchar(40) NULL, "client_type" integer NOT NULL, "callback_type" varchar(2) NOT NULL, "callback" varchar(1024) NOT NULL, "request" text NOT NULL, "response" text NOT NULL, "active_key" varchar(1024) NOT NULL, "record_from" varchar(128) NOT NULL, "platform" integer NOT NULL, "version" varchar(10) NULL, "os_version" varchar(20) NULL, "screen" varchar(40) NULL, "channel" varchar(40) NULL, "create_time" datetime NOT NULL, "active_time" datetime NULL, "last_activity" datetime NOT NULL, "mac" varchar(100) NOT NULL, "device_id" varchar(64) NOT NULL);
-- ----------------------------
-- Auto increment value for like_promotion_channel
-- ----------------------------
UPDATE "main"."sqlite_sequence" SET seq = 1 WHERE name = 'like_promotion_channel';
-- ----------------------------
-- Indexes structure for table like_promotion_channel
-- ----------------------------
CREATE UNIQUE INDEX "main"."like_promotion_channel_device_id_appid_a7b04688_uniq"
ON "like_promotion_channel" (
"device_id" ASC,
"appid" ASC
);
PRAGMA foreign_keys = true;
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'channels.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
python manage.py runserver 9000
celery worker -A channel_tasks -l debug
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment