diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a73a41..6b8fa15 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,13 @@ { -} \ No newline at end of file + "python.formatting.provider": "black", + "python.formatting.blackArgs": [ + "-S", + "-C", + "-l 120" + ], + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.rulers": [ + 120 + ] +} diff --git a/core/pagination.py b/core/pagination.py index 0b4dc50..a386b0d 100644 --- a/core/pagination.py +++ b/core/pagination.py @@ -2,12 +2,11 @@ from rest_framework import pagination from rest_framework.response import Response + class PagePaginationWithPageCount(pagination.PageNumberPagination): - page_size_query_param = 'page_size' - - def get_paginated_response(self, data): - response = super().get_paginated_response(data) - response.data['page_count'] = self.page.paginator.num_pages - return response - + page_size_query_param = 'page_size' + def get_paginated_response(self, data): + response = super().get_paginated_response(data) + response.data['page_count'] = self.page.paginator.num_pages + return response diff --git a/dsite/settings_default.py b/dsite/settings_default.py index 825719b..072c38d 100644 --- a/dsite/settings_default.py +++ b/dsite/settings_default.py @@ -37,14 +37,11 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - - # third party 'corsheaders', 'rest_framework', 'rest_framework.authtoken', 'django_filters', - # user apps 'timer', 'recipe', @@ -74,9 +71,9 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', - ], + ] }, - }, + } ] WSGI_APPLICATION = 'dsite.wsgi.application' @@ -85,30 +82,17 @@ WSGI_APPLICATION = 'dsite.wsgi.application' # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} +DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3')}} # Password validation # https://docs.djangoproject.com/en/1.11/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', - }, + {'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'}, ] @@ -140,11 +124,10 @@ REST_FRAMEWORK = { # 'DEFAULT_PERMISSION_CLASSES': [ # 'rest_framework.permissions.IsAuthenticatedOrReadOnly' # ], - # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_PAGINATION_CLASS': 'core.pagination.PagePaginationWithPageCount', 'PAGE_SIZE': 10, - 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] + 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], } # CORS @@ -153,7 +136,7 @@ REST_FRAMEWORK = { # 'http://localhost:8080', # ) CORS_ALLOWED_ORIGINS = [] -CORS_ALLOW_CREDENTIALS = True # 允许携带cookie +CORS_ALLOW_CREDENTIALS = True # 允许携带cookie # CORS_ALLOW_ALL_ORIGINS = False # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect diff --git a/recipe/migrations/0001_initial.py b/recipe/migrations/0001_initial.py index 4b5deac..fee59da 100644 --- a/recipe/migrations/0001_initial.py +++ b/recipe/migrations/0001_initial.py @@ -7,8 +7,7 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( @@ -21,5 +20,5 @@ class Migration(migrations.Migration): ('rate', models.IntegerField(default=0)), ('difficulty', models.IntegerField(default=0)), ], - ), + ) ] diff --git a/recipe/migrations/0002_auto_20211002_1926.py b/recipe/migrations/0002_auto_20211002_1926.py index 82d9966..5d34051 100644 --- a/recipe/migrations/0002_auto_20211002_1926.py +++ b/recipe/migrations/0002_auto_20211002_1926.py @@ -5,15 +5,11 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('recipe', '0001_initial'), - ] + dependencies = [('recipe', '0001_initial')] operations = [ migrations.AlterField( - model_name='recipe', - name='recipe_type', - field=models.CharField(default='meat', max_length=32), + model_name='recipe', name='recipe_type', field=models.CharField(default='meat', max_length=32) ), migrations.CreateModel( name='DailyRecipe', diff --git a/recipe/models.py b/recipe/models.py index d6aed4e..2cc9982 100644 --- a/recipe/models.py +++ b/recipe/models.py @@ -5,226 +5,178 @@ import datetime from utils import const import utils + class Recipe(models.Model): - name = models.CharField(max_length=128) - recipe_type = models.CharField(max_length=32, - default=const.RECIPE_TYPE_MEAT) - note = models.TextField(null=True) - rate = models.IntegerField(default=0) - difficulty = models.IntegerField(default=0) + name = models.CharField(max_length=128) + recipe_type = models.CharField(max_length=32, default=const.RECIPE_TYPE_MEAT) + note = models.TextField(null=True) + rate = models.IntegerField(default=0) + difficulty = models.IntegerField(default=0) - def serialize(self, verbose=False): - data = { - 'id': self.id, - 'name': self.name, - 'recipe_type': self.recipe_type, - } - if verbose: - data.update({ - 'difficulty': self.difficulty, - 'rate': self.rate, - 'note': self.note, - }) + def serialize(self, verbose=False): + data = {'id': self.id, 'name': self.name, 'recipe_type': self.recipe_type} + if verbose: + data.update({'difficulty': self.difficulty, 'rate': self.rate, 'note': self.note}) - return data + return data - @classmethod - def create_from_str(cls, content): - content = content.strip() - if not content: - return - name, *data = content.split(' ') - recipe_type = rate = difficulty = None - keys = [recipe_type, rate, difficulty] - for x in range(len(data)): - keys[x] = data[x] - recipe_type, rate, difficulty = keys + @classmethod + def create_from_str(cls, content): + content = content.strip() + if not content: + return + name, *data = content.split(' ') + recipe_type = rate = difficulty = None + keys = [recipe_type, rate, difficulty] + for x in range(len(data)): + keys[x] = data[x] + recipe_type, rate, difficulty = keys - if recipe_type == '肉': - recipe_type = const.RECIPE_TYPE_MEAT - elif recipe_type == '菜': - recipe_type = const.RECIPE_TYPE_VEGETABLE - elif recipe_type == '汤': - recipe_type = const.RECIPE_TYPE_SOUP - else: - recipe_type = const.RECIPE_TYPE_MEAT - if rate: - try: - rate = int(rate) - except: - rate = 0 - else: - rate = 0 - if difficulty: - try: - difficulty = int(difficulty) - except: - difficulty = 0 - else: - difficulty = 0 - recipe = cls.objects.create( - name=name, - recipe_type=recipe_type, - rate=rate, - difficulty=difficulty - ) - return recipe + if recipe_type == '肉': + recipe_type = const.RECIPE_TYPE_MEAT + elif recipe_type == '菜': + recipe_type = const.RECIPE_TYPE_VEGETABLE + elif recipe_type == '汤': + recipe_type = const.RECIPE_TYPE_SOUP + else: + recipe_type = const.RECIPE_TYPE_MEAT + if rate: + try: + rate = int(rate) + except: + rate = 0 + else: + rate = 0 + if difficulty: + try: + difficulty = int(difficulty) + except: + difficulty = 0 + else: + difficulty = 0 + recipe = cls.objects.create(name=name, recipe_type=recipe_type, rate=rate, difficulty=difficulty) + return recipe - @property - def display_recipe_type(self): - if self.recipe_type == const.RECIPE_TYPE_VEGETABLE: - return '菜' - elif self.recipe_type == const.RECIPE_TYPE_MEAT: - return '肉' - elif self.recipe_type == const.RECIPE_TYPE_SOUP: - return '汤' - return '肉' + @property + def display_recipe_type(self): + if self.recipe_type == const.RECIPE_TYPE_VEGETABLE: + return '菜' + elif self.recipe_type == const.RECIPE_TYPE_MEAT: + return '肉' + elif self.recipe_type == const.RECIPE_TYPE_SOUP: + return '汤' + return '肉' - @property - def display_rate(self): - return '🍚' * self.rate + @property + def display_rate(self): + return '🍚' * self.rate - @property - def display_difficult(self): - return '⭐️' * self.difficulty + @property + def display_difficult(self): + return '⭐️' * self.difficulty - def construct_lart_card(self): + def construct_lart_card(self): - data = { - "config": { - "wide_screen_mode": True - }, - "header": { - "title": { - "tag": "plain_text", - "content": self.name, - }, - "template": "blue" - }, - "elements": [ - { - "tag": "markdown", - "content": "**类型**:%s\n**评分**:%s\n**难度**:%s" % ( - self.display_recipe_type, self.display_rate, self.display_difficult) - }, - { - "tag": "action", - "actions": [ - { - "tag": "button", - "text": { - "tag": "plain_text", - "content": "查看" - }, - "multi_url": { - "url": "https://recipe.tunpok.com/recipe/%s" % self.id, - "android_url": "https://recipe.tunpok.com/recipe-mobile/recipe/%s" % self.id, - "ios_url": "https://recipe.tunpok.com/recipe-mobile/recipe/%s" % self.id, - "pc_url": "https://recipe.tunpok.com/recipe/%s" % self.id - }, - "type": "primary" - }, - { - "tag": "button", - "text": { - "tag": "plain_text", - "content": "删除" - }, - "type": "danger" - } - ] + data = { + "config": {"wide_screen_mode": True}, + "header": {"title": {"tag": "plain_text", "content": self.name}, "template": "blue"}, + "elements": [ + { + "tag": "markdown", + "content": "**类型**:%s\n**评分**:%s\n**难度**:%s" + % (self.display_recipe_type, self.display_rate, self.display_difficult), + }, + { + "tag": "action", + "actions": [ + { + "tag": "button", + "text": {"tag": "plain_text", "content": "查看"}, + "multi_url": { + "url": "https://recipe.tunpok.com/recipe/%s" % self.id, + "android_url": "https://recipe.tunpok.com/recipe-mobile/recipe/%s" % self.id, + "ios_url": "https://recipe.tunpok.com/recipe-mobile/recipe/%s" % self.id, + "pc_url": "https://recipe.tunpok.com/recipe/%s" % self.id, + }, + "type": "primary", + }, + {"tag": "button", "text": {"tag": "plain_text", "content": "删除"}, "type": "danger"}, + ], + }, + ], } - ] - } - return data + return data class DailyRecipe(models.Model): - recipes = models.ManyToManyField(Recipe) - date = models.DateField() - meal_type = models.CharField( - max_length=32, - default=const.DAILY_RECIPE_MEAL_TYPE_SUPPER,) + recipes = models.ManyToManyField(Recipe) + date = models.DateField() + meal_type = models.CharField(max_length=32, default=const.DAILY_RECIPE_MEAL_TYPE_SUPPER) - def generate_recipe(self, prev_recipes=None, ignore_prev=False): - if not prev_recipes: - prev_recipes = [] - if ignore_prev: - prev_recipes = [] + def generate_recipe(self, prev_recipes=None, ignore_prev=False): + if not prev_recipes: + prev_recipes = [] + if ignore_prev: + prev_recipes = [] - recipes = [] - retry = 5 + recipes = [] + retry = 5 - # meat - for x in range(0,2): - while True: - recipe = Recipe.objects.filter( - recipe_type=const.RECIPE_TYPE_MEAT, - ).order_by('?').first() - if recipe and recipe.id not in recipes and recipe.id not in prev_recipes: - recipes.append(recipe.id) - break - else: - retry -= 1 - if retry <= 0: - retry = 5 - break + # meat + for x in range(0, 2): + while True: + recipe = Recipe.objects.filter(recipe_type=const.RECIPE_TYPE_MEAT).order_by('?').first() + if recipe and recipe.id not in recipes and recipe.id not in prev_recipes: + recipes.append(recipe.id) + break + else: + retry -= 1 + if retry <= 0: + retry = 5 + break - # vegetable - for x in range(0, 1): - while True: - recipe = Recipe.objects.filter( - recipe_type=const.RECIPE_TYPE_VEGETABLE, - ).order_by('?').first() - if recipe and recipe.id not in recipes and recipe.id not in prev_recipes: - recipes.append(recipe.id) - break - else: - retry -= 1 - if retry <= 0: - retry = 5 - break + # vegetable + for x in range(0, 1): + while True: + recipe = Recipe.objects.filter(recipe_type=const.RECIPE_TYPE_VEGETABLE).order_by('?').first() + if recipe and recipe.id not in recipes and recipe.id not in prev_recipes: + recipes.append(recipe.id) + break + else: + retry -= 1 + if retry <= 0: + retry = 5 + break - # soup - if random.randint(0,2): - recipe = Recipe.objects.filter( - recipe_type=const.RECIPE_TYPE_SOUP, - ).order_by('?').first() - # if recipe not in recipes and recipe not in prev_recipes: - if recipe: - recipes.append(recipe.id) + # soup + if random.randint(0, 2): + recipe = Recipe.objects.filter(recipe_type=const.RECIPE_TYPE_SOUP).order_by('?').first() + # if recipe not in recipes and recipe not in prev_recipes: + if recipe: + recipes.append(recipe.id) - self.recipes.set(Recipe.objects.filter(id__in=recipes)) + self.recipes.set(Recipe.objects.filter(id__in=recipes)) - def serialize(self): - data = { - const.RECIPE_TYPE_MEAT: [], - const.RECIPE_TYPE_VEGETABLE: [], - const.RECIPE_TYPE_SOUP: [], - } - for key_, value_ in data.items(): - for recipe in self.recipes.filter( - recipe_type=key_).order_by('id'): - value_.append(recipe.serialize()) + def serialize(self): + data = {const.RECIPE_TYPE_MEAT: [], const.RECIPE_TYPE_VEGETABLE: [], const.RECIPE_TYPE_SOUP: []} + for key_, value_ in data.items(): + for recipe in self.recipes.filter(recipe_type=key_).order_by('id'): + value_.append(recipe.serialize()) - date = now() - date = date.replace(year=self.date.year, month=self.date.month, - day=self.date.day, hour=0, minute=0) - data['date'] = utils.timestamp_of(utils.day_start(date)) - data['id'] = self.id - return data + date = now() + date = date.replace(year=self.date.year, month=self.date.month, day=self.date.day, hour=0, minute=0) + data['date'] = utils.timestamp_of(utils.day_start(date)) + data['id'] = self.id + return data - @classmethod - def get_week_recipe_data(cls): - today = localtime() - week_start = (today - datetime.timedelta(days=today.weekday())).date() - week_end = week_start + datetime.timedelta(days=6) - daily_recipes = cls.objects.filter( - date__gte=week_start, - date__lte=week_end, - ).order_by('date') - data = [{}] * (7 - len(daily_recipes)) + @classmethod + def get_week_recipe_data(cls): + today = localtime() + week_start = (today - datetime.timedelta(days=today.weekday())).date() + week_end = week_start + datetime.timedelta(days=6) + daily_recipes = cls.objects.filter(date__gte=week_start, date__lte=week_end).order_by('date') + data = [{}] * (7 - len(daily_recipes)) - for daily_recipe in daily_recipes: - data.append(daily_recipe.serialize()) - return data + for daily_recipe in daily_recipes: + data.append(daily_recipe.serialize()) + return data diff --git a/recipe/serializers.py b/recipe/serializers.py index eedc7dc..d5c8050 100644 --- a/recipe/serializers.py +++ b/recipe/serializers.py @@ -4,23 +4,25 @@ from rest_framework import serializers import recipe.models + class RecipeSerializer(serializers.ModelSerializer): - id = serializers.IntegerField(read_only=True) - class Meta: - model = recipe.models.Recipe - fields = '__all__' + id = serializers.IntegerField(read_only=True) + + class Meta: + model = recipe.models.Recipe + fields = '__all__' class WeekRecipeSerializer(serializers.ModelSerializer): - - class Meta: - model = recipe.models.DailyRecipe - fields = '__all__' + class Meta: + model = recipe.models.DailyRecipe + fields = '__all__' class DailyRecipeSerializer(serializers.ModelSerializer): - id = serializers.IntegerField(read_only=True) - recipes = RecipeSerializer(many=True) - class Meta: - model = recipe.models.DailyRecipe - fields = '__all__' + id = serializers.IntegerField(read_only=True) + recipes = RecipeSerializer(many=True) + + class Meta: + model = recipe.models.DailyRecipe + fields = '__all__' diff --git a/recipe/urls.py b/recipe/urls.py index 20ceeb2..4c3c1b1 100644 --- a/recipe/urls.py +++ b/recipe/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import include, url + # from django.core.urlresolvers import reverse from django.urls import path from rest_framework import routers @@ -11,6 +12,5 @@ urlpatterns = [ url(r'^recipe/(?P\d+)$', views.RecipeAPI.as_view(), name='recipe-detail'), url(r'^recipe/$', views.RecipeListAPI.as_view()), url(r'^week-recipe/$', views.WeekRecipeListAPI.as_view()), - url(r'^daily-recipe/(?P\d+)$', views.DailyRecipeAPI.as_view(), - name='dailyrecipe-detail'), + url(r'^daily-recipe/(?P\d+)$', views.DailyRecipeAPI.as_view(), name='dailyrecipe-detail'), ] diff --git a/recipe/views.py b/recipe/views.py index ea64459..175c057 100644 --- a/recipe/views.py +++ b/recipe/views.py @@ -11,73 +11,66 @@ import recipe.models import recipe.serializers from utils import const + class RecipeAPI(rest_framework.generics.RetrieveUpdateAPIView): - # authentication_classes = (authentication.TokenAuthentication, - # authentication.SessionAuthentication, - # authentication.BasicAuthentication) - # permission_classes = (permissions.IsAuthenticated,) - queryset = recipe.models.Recipe.objects.all() - serializer_class = recipe.serializers.RecipeSerializer + # authentication_classes = (authentication.TokenAuthentication, + # authentication.SessionAuthentication, + # authentication.BasicAuthentication) + # permission_classes = (permissions.IsAuthenticated,) + queryset = recipe.models.Recipe.objects.all() + serializer_class = recipe.serializers.RecipeSerializer -class RecipeListAPI(rest_framework.generics.ListAPIView, - rest_framework.generics.CreateAPIView): +class RecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): - # authentication_classes = (authentication.TokenAuthentication, - # authentication.SessionAuthentication, - # authentication.BasicAuthentication) - # permission_classes = (permissions.IsAuthenticated,) - queryset = recipe.models.Recipe.objects.all() - serializer_class = recipe.serializers.RecipeSerializer - filterset_fields = { - 'recipe_type': const.FILTER_EXACT, - 'difficulty': const.FILTER_GTE_LTE, - 'rate': const.FILTER_GTE_LTE, - } + # authentication_classes = (authentication.TokenAuthentication, + # authentication.SessionAuthentication, + # authentication.BasicAuthentication) + # permission_classes = (permissions.IsAuthenticated,) + queryset = recipe.models.Recipe.objects.all() + serializer_class = recipe.serializers.RecipeSerializer + filterset_fields = { + 'recipe_type': const.FILTER_EXACT, + 'difficulty': const.FILTER_GTE_LTE, + 'rate': const.FILTER_GTE_LTE, + } -class WeekRecipeListAPI(rest_framework.generics.ListAPIView, - rest_framework.generics.CreateAPIView): +class WeekRecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): - queryset = recipe.models.DailyRecipe.objects.all() - serializer_class = recipe.serializers.WeekRecipeSerializer + queryset = recipe.models.DailyRecipe.objects.all() + serializer_class = recipe.serializers.WeekRecipeSerializer - def post(self, request, *args, **kwargs): - # Monday == 0 ... Sunday == 6 - today = localtime() - recipes = [] - for x in range(0, 7-today.weekday()): - daily_recipe, __ = recipe.models.DailyRecipe.objects.get_or_create( - date=today + datetime.timedelta(days=x) - ) - daily_recipe.generate_recipe(recipes) - recipes.extend(daily_recipe.recipes.values_list('id', flat=True)) - return Response(recipe.models.DailyRecipe.get_week_recipe_data(), - status=status.HTTP_201_CREATED, headers={}) + def post(self, request, *args, **kwargs): + # Monday == 0 ... Sunday == 6 + today = localtime() + recipes = [] + for x in range(0, 7 - today.weekday()): + daily_recipe, __ = recipe.models.DailyRecipe.objects.get_or_create(date=today + datetime.timedelta(days=x)) + daily_recipe.generate_recipe(recipes) + recipes.extend(daily_recipe.recipes.values_list('id', flat=True)) + return Response(recipe.models.DailyRecipe.get_week_recipe_data(), status=status.HTTP_201_CREATED, headers={}) - def get(self, request, *args, **kwargs): - data = recipe.models.DailyRecipe.get_week_recipe_data() - return Response(data) + def get(self, request, *args, **kwargs): + data = recipe.models.DailyRecipe.get_week_recipe_data() + return Response(data) class DailyRecipeAPI(rest_framework.generics.RetrieveUpdateAPIView): - queryset = recipe.models.DailyRecipe.objects.all() - serializer_class = recipe.serializers.DailyRecipeSerializer + queryset = recipe.models.DailyRecipe.objects.all() + serializer_class = recipe.serializers.DailyRecipeSerializer - def post(self, request, *args, **kwargs): - daily_recipe = recipe.models.DailyRecipe.objects.get(id=kwargs['pk']) - daily_recipe.generate_recipe() - return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, - headers={}) + def post(self, request, *args, **kwargs): + daily_recipe = recipe.models.DailyRecipe.objects.get(id=kwargs['pk']) + daily_recipe.generate_recipe() + return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, headers={}) - def put(self, request, *args, **kwargs): - daily_recipe = recipe.models.DailyRecipe.objects.get(id=kwargs['pk']) - recipes = request.data.get('meat',[]) - recipes.extend(request.data.get('vegetable', [])) - recipes.extend(request.data.get('soup', [])) - daily_recipe.recipes.set(recipe.models.Recipe.objects.filter( - id__in=recipes)) - return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, - headers={}) + def put(self, request, *args, **kwargs): + daily_recipe = recipe.models.DailyRecipe.objects.get(id=kwargs['pk']) + recipes = request.data.get('meat', []) + recipes.extend(request.data.get('vegetable', [])) + recipes.extend(request.data.get('soup', [])) + daily_recipe.recipes.set(recipe.models.Recipe.objects.filter(id__in=recipes)) + return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, headers={}) diff --git a/scripts/dodo.py b/scripts/dodo.py index dd3f81f..b3628a5 100644 --- a/scripts/dodo.py +++ b/scripts/dodo.py @@ -2,10 +2,12 @@ # --coding:utf-8-- import os import sys + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dsite.settings") sys.path.insert(0, '../') sys.path.insert(0, './') from django.core.wsgi import get_wsgi_application + get_wsgi_application() from http.server import BaseHTTPRequestHandler, HTTPServer @@ -36,48 +38,46 @@ KEDAI_ID = '107263380636355825' logging.basicConfig(filename='/root/develop/log/dodo.log', level=logging.INFO) logger = logging.getLogger('/root/develop/log/dodo.log') -mastodon_cli = Mastodon( - access_token = 'Ug_bUMWCk3RLamOnqYIytmeB0nO6aNfjdmf06mAj2bE', - api_base_url = 'https://nofan.xyz' -) +mastodon_cli = Mastodon(access_token='Ug_bUMWCk3RLamOnqYIytmeB0nO6aNfjdmf06mAj2bE', api_base_url='https://nofan.xyz') pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) redis_cli = redis.Redis(host='localhost', port=6379, decode_responses=True) -class AESCipher(object): + +class AESCipher(object): def __init__(self, key): self.bs = AES.block_size - self.key=hashlib.sha256(AESCipher.str_to_bytes(key)).digest() + self.key = hashlib.sha256(AESCipher.str_to_bytes(key)).digest() + @staticmethod def str_to_bytes(data): u_type = type(b"".decode('utf8')) if isinstance(data, u_type): return data.encode('utf8') return data + @staticmethod def _unpad(s): - return s[:-ord(s[len(s) - 1:])] + return s[: -ord(s[len(s) - 1 :])] + def decrypt(self, enc): - iv = enc[:AES.block_size] + iv = enc[: AES.block_size] cipher = AES.new(self.key, AES.MODE_CBC, iv) - return self._unpad(cipher.decrypt(enc[AES.block_size:])) + return self._unpad(cipher.decrypt(enc[AES.block_size :])) + def decrypt_string(self, enc): enc = base64.b64decode(enc) - return self.decrypt(enc).decode('utf8') + return self.decrypt(enc).decode('utf8') + def get_tenant_access_token(): # 获取token token = redis_cli.get('tenant_access_token_%s' % APP_ID) if token: - return token + return token url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/" - headers = { - "Content-Type": "application/json" - } - req_body = { - "app_id": APP_ID, - "app_secret": APP_SECRET - } + headers = {"Content-Type": "application/json"} + req_body = {"app_id": APP_ID, "app_secret": APP_SECRET} data = bytes(json.dumps(req_body), encoding='utf8') req = request.Request(url=url, data=data, headers=headers, method='POST') @@ -94,33 +94,27 @@ def get_tenant_access_token(): # 获取token logger.error("get tenant_access_token error, code =%s", code) return "" token = rsp_dict.get("tenant_access_token", "") - redis_cli.set('tenant_access_token_%s' % APP_ID, - rsp_dict.get("tenant_access_token", ""), - ex=60*30) + redis_cli.set('tenant_access_token_%s' % APP_ID, rsp_dict.get("tenant_access_token", ""), ex=60 * 30) return token + def get_group_name(chat_id): - group_name = redis_cli.get('group_name_%s' % chat_id) - if not group_name: - url = "https://open.feishu.cn/open-apis/im/v1/chats/" - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer " + get_tenant_access_token() - } - try: - resp = requests.get(url+chat_id, headers=headers) - resp_data = resp.json() - code = resp_data.get("code", -1) - if code == 0: - group_name = resp_data.get('data', {}).get('name') - redis_cli.set('group_name_%s' % chat_id, - group_name, - ex=60*60*24) - except: - # todo: log - return - return group_name + group_name = redis_cli.get('group_name_%s' % chat_id) + if not group_name: + url = "https://open.feishu.cn/open-apis/im/v1/chats/" + headers = {"Content-Type": "application/json", "Authorization": "Bearer " + get_tenant_access_token()} + try: + resp = requests.get(url + chat_id, headers=headers) + resp_data = resp.json() + code = resp_data.get("code", -1) + if code == 0: + group_name = resp_data.get('data', {}).get('name') + redis_cli.set('group_name_%s' % chat_id, group_name, ex=60 * 60 * 24) + except: + # todo: log + return + return group_name class RequestHandler(BaseHTTPRequestHandler): @@ -156,12 +150,12 @@ class RequestHandler(BaseHTTPRequestHandler): event_id = obj.get('header', {}).get('event_id', '') # 重复收到的事件不处理 if event_id and redis_cli.get(event_id): - self.response("") - return + self.response("") + return event = obj.get("event") if event.get("message"): - self.handle_message(event, event_id) - return + self.handle_message(event, event_id) + return return def handle_request_url_verify(self, post_obj): @@ -192,71 +186,77 @@ class RequestHandler(BaseHTTPRequestHandler): text = text.lstrip() orig_text = text if ADD_GROUP_NAME: - group_name = get_group_name(msg.get("chat_id")) - text = '%s #%s' % (text, group_name) + group_name = get_group_name(msg.get("chat_id")) + text = '%s #%s' % (text, group_name) else: - open_id = {"open_id": event.get("sender", {}).get( - 'sender_id', {}).get('open_id')} + open_id = {"open_id": event.get("sender", {}).get('sender_id', {}).get('open_id')} self.response("") if orig_text.startswith('/'): - redis_cli.set(event_id, int(time.time()), ex=60*60*7) - if orig_text not in ['/last', '/del']: - flag = False - for action_ in ['/deploy ', '/菜谱 ']: - if orig_text.startswith(action_): - flag = True - break - if not flag: - self.msg_compoment(access_token, open_id, '指令错误') - return - if orig_text == '/last': - try: - statuses = mastodon_cli.account_statuses(KEDAI_ID, limit=1) - s_text = BeautifulSoup(statuses[0]['content'], 'html.parser') - self.msg_compoment(access_token, open_id, - s_text.get_text('')) - except Exception as exc: - logger.error('operation error: %s', str(exc)) - elif orig_text == '/del': - try: - statuses = mastodon_cli.account_statuses(KEDAI_ID, limit=1) - mastodon_cli.status_delete(statuses[0]['id']) - s_text = BeautifulSoup(statuses[0]['content'], 'html.parser') - self.msg_compoment(access_token, open_id, - '已删除: ' + s_text.get_text('')) - except Exception as exc: - logger.error('operation error: %s', str(exc)) - elif orig_text.startswith('/deploy '): - site_ = orig_text.split('/deploy ')[1] - if site_ == 'dsite': - self.msg_compoment(access_token, open_id, '🚧 %s 开始部署 🚧' % site_) - subprocess.call("/root/deploy/dsite_prepare.sh") - subprocess.run(["supervisorctl", "restart", "dsite"]) - self.msg_compoment(access_token, open_id, '🎉 %s 部署成功 🎉' % site_) - else: - self.msg_compoment(access_token, open_id, '⚠️ %s 不存在 ⚠️' % site_) - elif orig_text.startswith('/菜谱 '): - content = orig_text.split('/菜谱 ')[1] - recipe_ = recipe.models.Recipe.create_from_str(content) - if recipe_: - self.msg_compoment(access_token, open_id, None, - const.LARK_WEBHOOK_MSG_TYPE_INTERACTIVE, - recipe_.construct_lart_card()) - else: - self.msg_compoment(access_token, open_id, '⚠️ 创建失败 ⚠️') - return + redis_cli.set(event_id, int(time.time()), ex=60 * 60 * 7) + if orig_text not in ['/last', '/del']: + flag = False + for action_ in ['/deploy ', '/菜谱 ']: + if orig_text.startswith(action_): + flag = True + break + if not flag: + self.msg_compoment(access_token, open_id, '指令错误') + return + if orig_text == '/last': + try: + statuses = mastodon_cli.account_statuses(KEDAI_ID, limit=1) + s_text = BeautifulSoup(statuses[0]['content'], 'html.parser') + self.msg_compoment(access_token, open_id, s_text.get_text('')) + except Exception as exc: + logger.error('operation error: %s', str(exc)) + elif orig_text == '/del': + try: + statuses = mastodon_cli.account_statuses(KEDAI_ID, limit=1) + mastodon_cli.status_delete(statuses[0]['id']) + s_text = BeautifulSoup(statuses[0]['content'], 'html.parser') + self.msg_compoment(access_token, open_id, '已删除: ' + s_text.get_text('')) + except Exception as exc: + logger.error('operation error: %s', str(exc)) + elif orig_text.startswith('/deploy '): + site_ = orig_text.split('/deploy ')[1] + if site_ == 'dsite': + self.msg_compoment(access_token, open_id, '🚧 %s 开始部署 🚧' % site_) + subprocess.call("/root/deploy/dsite_prepare.sh") + subprocess.run(["supervisorctl", "restart", "dsite"]) + self.msg_compoment(access_token, open_id, '🎉 %s 部署成功 🎉' % site_) + else: + self.msg_compoment(access_token, open_id, '⚠️ %s 不存在 ⚠️' % site_) + elif orig_text.startswith('/菜谱 '): + content = orig_text.split('/菜谱 ')[1] + recipe_ = recipe.models.Recipe.create_from_str(content) + if recipe_: + self.msg_compoment( + access_token, + open_id, + None, + const.LARK_WEBHOOK_MSG_TYPE_INTERACTIVE, + recipe_.construct_lart_card(), + ) + else: + self.msg_compoment(access_token, open_id, '⚠️ 创建失败 ⚠️') + return try: - toot_resp = mastodon_cli.status_post(text) - if toot_resp.get('id'): - self.msg_compoment(access_token, open_id, '📟 dodo 📟') - redis_cli.set(event_id, int(time.time()), ex=60*60*7) - else: - self.msg_compoment(access_token, open_id, """⚠️ didi ⚠️ + toot_resp = mastodon_cli.status_post(text) + if toot_resp.get('id'): + self.msg_compoment(access_token, open_id, '📟 dodo 📟') + redis_cli.set(event_id, int(time.time()), ex=60 * 60 * 7) + else: + self.msg_compoment( + access_token, + open_id, + """⚠️ didi ⚠️ %s - """ % json.loads(toot_resp)) + """ + % json.loads(toot_resp), + ) except Exception as exc: - logger.error('send toot error: %s', str(exc)) + logger.error('send toot error: %s', str(exc)) return elif msg_type == "image": @@ -271,15 +271,10 @@ class RequestHandler(BaseHTTPRequestHandler): def send_message(self, token, open_id, text, msg_type=None, content=None): url = "https://open.feishu.cn/open-apis/message/v4/send/" - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer " + token - } + headers = {"Content-Type": "application/json", "Authorization": "Bearer " + token} if not msg_type: msg_type = const.LARK_WEBHOOK_MSG_TYPE_TEXT - req_body = { - "msg_type": msg_type - } + req_body = {"msg_type": msg_type} if msg_type == const.LARK_WEBHOOK_MSG_TYPE_TEXT: req_body['content'] = {'text': text} elif msg_type == const.LARK_WEBHOOK_MSG_TYPE_INTERACTIVE: @@ -299,13 +294,12 @@ class RequestHandler(BaseHTTPRequestHandler): rsp_dict = json.loads(rsp_body) code = rsp_dict.get("code", -1) if code != 0: - logger.error("send message error, code = %s, msg =%s", - code, - rsp_dict.get("msg", "")) + logger.error("send message error, code = %s, msg =%s", code, rsp_dict.get("msg", "")) def msg_compoment(self, token, open_id, text, msg_type=None, content=None): self.send_message(token, open_id, text, msg_type, content) + def run(): port = 5000 server_address = ('', port) diff --git a/timer/forms.py b/timer/forms.py index 8886a0e..e95a4ba 100644 --- a/timer/forms.py +++ b/timer/forms.py @@ -4,27 +4,23 @@ import timer.models class OfficeHoursForm(forms.ModelForm): - class Meta: - model = timer.models.OfficeHours - fields = ['user', 'begins_at', 'ends_at'] + class Meta: + model = timer.models.OfficeHours + fields = ['user', 'begins_at', 'ends_at'] - def __init__(self, *args, **kwargs): - self.user = kwargs.pop('user') - return super().__init__(*args, **kwargs) - - - def full_clean(self): - if not self.user.is_authenticated: - raise forms.ValidationError('Invalid User.') - return - - def save(self): - begins_at = timer.models.OfficeHours.parse_time_str(self.data.get('begins_at')) - user = self.user - obj = timer.models.OfficeHours.objects.create( - user=user, - begins_at=begins_at, - ends_at=timer.models.OfficeHours.get_ends_at(begins_at)) - return obj + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user') + return super().__init__(*args, **kwargs) + def full_clean(self): + if not self.user.is_authenticated: + raise forms.ValidationError('Invalid User.') + return + def save(self): + begins_at = timer.models.OfficeHours.parse_time_str(self.data.get('begins_at')) + user = self.user + obj = timer.models.OfficeHours.objects.create( + user=user, begins_at=begins_at, ends_at=timer.models.OfficeHours.get_ends_at(begins_at) + ) + return obj diff --git a/timer/migrations/0001_initial.py b/timer/migrations/0001_initial.py index c49660d..5bf1bbc 100644 --- a/timer/migrations/0001_initial.py +++ b/timer/migrations/0001_initial.py @@ -9,9 +9,7 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] operations = [ migrations.CreateModel( @@ -22,5 +20,5 @@ class Migration(migrations.Migration): ('ends_at', models.DateTimeField()), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], - ), + ) ] diff --git a/timer/models.py b/timer/models.py index 9a3a91a..f03b5d9 100644 --- a/timer/models.py +++ b/timer/models.py @@ -6,22 +6,22 @@ import datetime class OfficeHours(models.Model): - begins_at = models.DateTimeField() - ends_at = models.DateTimeField() - user = models.ForeignKey(User, on_delete=django.db.models.deletion.CASCADE) + begins_at = models.DateTimeField() + ends_at = models.DateTimeField() + user = models.ForeignKey(User, on_delete=django.db.models.deletion.CASCADE) - @classmethod - def parse_time_str(cls, time_srt): - return datetime.datetime.strptime(time_srt, '%Y-%m-%d %H:%M') + @classmethod + def parse_time_str(cls, time_srt): + return datetime.datetime.strptime(time_srt, '%Y-%m-%d %H:%M') - @classmethod - def get_ends_at(cls, begins_at): - return begins_at + datetime.timedelta(hours=9.5) + @classmethod + def get_ends_at(cls, begins_at): + return begins_at + datetime.timedelta(hours=9.5) - @property - def get_begins_at_str(self): - return localtime(self.begins_at).strftime('%Y-%m-%d %H:%M') + @property + def get_begins_at_str(self): + return localtime(self.begins_at).strftime('%Y-%m-%d %H:%M') - @property - def get_ends_at_str(self): - return localtime(self.ends_at).strftime('%H:%M') + @property + def get_ends_at_str(self): + return localtime(self.ends_at).strftime('%H:%M') diff --git a/timer/serializers.py b/timer/serializers.py index 8789ba1..30d87c4 100644 --- a/timer/serializers.py +++ b/timer/serializers.py @@ -5,17 +5,17 @@ import timer.models class OfficeHoursSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = timer.models.OfficeHours + class Meta: + model = timer.models.OfficeHours class UserSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = User - fields = ('url', 'username', 'email', 'groups') + class Meta: + model = User + fields = ('url', 'username', 'email', 'groups') class GroupSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = Group - fields = ('url', 'name') + class Meta: + model = Group + fields = ('url', 'name') diff --git a/timer/urls.py b/timer/urls.py index 4f658e4..9707b1e 100644 --- a/timer/urls.py +++ b/timer/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import include, url + # from django.core.urlresolvers import reverse from django.urls import path from rest_framework import routers diff --git a/timer/views.py b/timer/views.py index 172f262..a984a6f 100644 --- a/timer/views.py +++ b/timer/views.py @@ -19,59 +19,57 @@ from django.urls import reverse class OfficeHoursAPI(CreateAPIView): - authentication_classes = (authentication.TokenAuthentication, - authentication.SessionAuthentication, - authentication.BasicAuthentication) - permission_classes = (permissions.IsAuthenticated,) + authentication_classes = ( + authentication.TokenAuthentication, + authentication.SessionAuthentication, + authentication.BasicAuthentication, + ) + permission_classes = (permissions.IsAuthenticated,) - queryset = timer.models.OfficeHours.objects.all() + queryset = timer.models.OfficeHours.objects.all() - def create(self, request, *args, **kwargs): - begins_at = request.data.get('begins_at') - if not begins_at: - raise - try: - begins_at = timer.models.OfficeHours.parse_time_str(begins_at) - ends_at = timer.models.OfficeHours.get_ends_at(begins_at) - oh, __ = timer.models.OfficeHours.objects.get_or_create( - begins_at=begins_at, - ends_at=ends_at, - user=request.user) - except ValueError: - raise - oh.refresh_from_db() - resp_data = { - 'begins_at': oh.get_begins_at_str, - 'ends_at': oh.get_ends_at_str, - } + def create(self, request, *args, **kwargs): + begins_at = request.data.get('begins_at') + if not begins_at: + raise + try: + begins_at = timer.models.OfficeHours.parse_time_str(begins_at) + ends_at = timer.models.OfficeHours.get_ends_at(begins_at) + oh, __ = timer.models.OfficeHours.objects.get_or_create( + begins_at=begins_at, ends_at=ends_at, user=request.user + ) + except ValueError: + raise + oh.refresh_from_db() + resp_data = {'begins_at': oh.get_begins_at_str, 'ends_at': oh.get_ends_at_str} - return Response(resp_data, status=status.HTTP_201_CREATED) + return Response(resp_data, status=status.HTTP_201_CREATED) class OfficeHoursFormView(django.views.generic.FormView): - template_name = 'index.html' - form_class = timer.forms.OfficeHoursForm + template_name = 'index.html' + form_class = timer.forms.OfficeHoursForm - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs['user'] = self.request.user - return kwargs + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['user'] = self.request.user + return kwargs - def post(self, request, *args, **kwargs): - form = self.form_class(**self.get_form_kwargs()) - try: - form.is_valid() - form.save() - except django.forms.ValidationError as e: - return JsonResponse({'error': e.message}, status=status.HTTP_400_BAD_REQUEST) + def post(self, request, *args, **kwargs): + form = self.form_class(**self.get_form_kwargs()) + try: + form.is_valid() + form.save() + except django.forms.ValidationError as e: + return JsonResponse({'error': e.message}, status=status.HTTP_400_BAD_REQUEST) - return super().post(request, *args, **kwargs) + return super().post(request, *args, **kwargs) - def get_success_url(self): - return reverse('office-hours-page') + def get_success_url(self): + return reverse('office-hours-page') - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - today_oh = timer.models.OfficeHours.objects.filter(begins_at__date=localtime().date()).last() - context['today_oh'] = today_oh - return context + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + today_oh = timer.models.OfficeHours.objects.filter(begins_at__date=localtime().date()).last() + context['today_oh'] = today_oh + return context diff --git a/utils/__init__.py b/utils/__init__.py index 047b34a..f24a19d 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -6,113 +6,113 @@ from django.utils import timezone def timestamp_of(d): - if hasattr(d, 'isoformat'): - return calendar.timegm(d.utctimetuple()) - return None + if hasattr(d, 'isoformat'): + return calendar.timegm(d.utctimetuple()) + return None def now(): - return timezone.localtime(timezone.now()) + return timezone.localtime(timezone.now()) def midnight(): - return now().replace(hour=0, minute=0, second=0, microsecond=0) + return now().replace(hour=0, minute=0, second=0, microsecond=0) def is_today(tme): - if day_of(tme) != day_of(midnight()): - return False - return True + if day_of(tme) != day_of(midnight()): + return False + return True def current_time_n(n): - return now() - timedelta(days=n) + return now() - timedelta(days=n) def day_n(n): - """Midnight of N days ago.""" - return midnight() - timedelta(days=n) + """Midnight of N days ago.""" + return midnight() - timedelta(days=n) def day_of(d): - """Returns date part.""" - return str(d.date()) + """Returns date part.""" + return str(d.date()) def midnight_of(d): - """ - Args: - d : datetime object - Return: - Midnight of datetime. - """ - d = timezone.localtime(d) - return d.replace(hour=0, minute=0, second=0, microsecond=0) + """ + Args: + d : datetime object + Return: + Midnight of datetime. + """ + d = timezone.localtime(d) + return d.replace(hour=0, minute=0, second=0, microsecond=0) def day_start(date): - """ 返回 date 这天的开始时间 + """返回 date 这天的开始时间 - Args: - date: `Datetime` 对象 + Args: + date: `Datetime` 对象 - Returns: - 返回 date 这天的开始时间(0 点),`Datetime` 对象 - """ - return timezone.localtime(date).replace(hour=0, minute=0, second=0, - microsecond=0) + Returns: + 返回 date 这天的开始时间(0 点),`Datetime` 对象 + """ + return timezone.localtime(date).replace(hour=0, minute=0, second=0, microsecond=0) def week_start(date): - """ 返回 date 这天所在周的开始时间 + """返回 date 这天所在周的开始时间 - Args: - date: `Datetime` 对象 + Args: + date: `Datetime` 对象 - Returns: - 返回 date 这天所在周开始时间(周一),`Datetime` 对象 - """ - return timezone.localtime(date) + dateutil.relativedelta.relativedelta( - weekday=dateutil.relativedelta.MO(-1), hour=0, minute=0, second=0, - microsecond=0) + Returns: + 返回 date 这天所在周开始时间(周一),`Datetime` 对象 + """ + return timezone.localtime(date) + dateutil.relativedelta.relativedelta( + weekday=dateutil.relativedelta.MO(-1), hour=0, minute=0, second=0, microsecond=0 + ) def month_start(d): - """ 返回 date 这天所在月的开始时间 + """返回 date 这天所在月的开始时间 - Args: - date: `Datetime` 对象 + Args: + date: `Datetime` 对象 - Returns: - 返回 date 这天所在月的开始时间(1号),`Datetime` 对象 - """ - return timezone.localtime(d) + dateutil.relativedelta.relativedelta( - day=1, hour=0, minute=0, second=0, microsecond=0) + Returns: + 返回 date 这天所在月的开始时间(1号),`Datetime` 对象 + """ + return timezone.localtime(d) + dateutil.relativedelta.relativedelta( + day=1, hour=0, minute=0, second=0, microsecond=0 + ) def month_end(d): - """返回 date 这天所在月的结束时间,即最后一天的 23:59:59 + """返回 date 这天所在月的结束时间,即最后一天的 23:59:59 - Args: - data: `Datetime` 对象 + Args: + data: `Datetime` 对象 - Returns: - 返回 date 这天所在月的最后一天的 23:59:59 的时间 - """ - start = month_start(d) - month_days = calendar.monthrange(start.year, start.month)[1] - return start + timedelta(days=month_days, seconds=-1) + Returns: + 返回 date 这天所在月的最后一天的 23:59:59 的时间 + """ + start = month_start(d) + month_days = calendar.monthrange(start.year, start.month)[1] + return start + timedelta(days=month_days, seconds=-1) def year_start(d): - """ 返回 date 这天所在年的开始时间 + """返回 date 这天所在年的开始时间 - Args: - date: `Datetime` 对象 - - Returns: - 返回 date 这天所在年的开始时间(1月 1号),`Datetime` 对象 - """ - return timezone.localtime(d) + dateutil.relativedelta.relativedelta( - month=1, day=1, hour=0, minute=0, second=0, microsecond=0) + Args: + date: `Datetime` 对象 + Returns: + 返回 date 这天所在年的开始时间(1月 1号),`Datetime` 对象 + """ + return timezone.localtime(d) + dateutil.relativedelta.relativedelta( + month=1, day=1, hour=0, minute=0, second=0, microsecond=0 + ) diff --git a/utils/const.py b/utils/const.py index 11c90ad..bdfc767 100644 --- a/utils/const.py +++ b/utils/const.py @@ -2,20 +2,16 @@ DAILY_RECIPE_MEAL_TYPE_BREAKFAST = 'breakfast' DAILY_RECIPE_MEAL_TYPE_LUNCH = 'lunch' DAILY_RECIPE_MEAL_TYPE_SUPPER = 'supper' DAILY_RECIPE_MEAL_TYPE_CHOICE = [ - DAILY_RECIPE_MEAL_TYPE_BREAKFAST, - DAILY_RECIPE_MEAL_TYPE_LUNCH, - DAILY_RECIPE_MEAL_TYPE_SUPPER, + DAILY_RECIPE_MEAL_TYPE_BREAKFAST, + DAILY_RECIPE_MEAL_TYPE_LUNCH, + DAILY_RECIPE_MEAL_TYPE_SUPPER, ] RECIPE_TYPE_MEAT = 'meat' RECIPE_TYPE_VEGETABLE = 'vegetable' RECIPE_TYPE_SOUP = 'soup' -RECIPE_TYPE_CHOICE = [ - RECIPE_TYPE_MEAT, - RECIPE_TYPE_VEGETABLE, - RECIPE_TYPE_SOUP, -] +RECIPE_TYPE_CHOICE = [RECIPE_TYPE_MEAT, RECIPE_TYPE_VEGETABLE, RECIPE_TYPE_SOUP] FILTER_EXACT = ['exact'] FILTER_GTE_LTE = ['exact', 'gte', 'gt', 'lte', 'lt'] diff --git a/utils/depoly_notify.py b/utils/depoly_notify.py index 51ec521..deda3ac 100755 --- a/utils/depoly_notify.py +++ b/utils/depoly_notify.py @@ -2,6 +2,7 @@ import sys import os from imp import reload + sys.path.insert(0, os.path.abspath('..')) sys.path.append('/Users/ching/develop/dsite') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "../dsite.settings") @@ -12,14 +13,9 @@ import git import ipdb if __name__ == '__main__': - # def notify(): + # def notify(): repo = git.Repo(search_parent_directories=True) commit = repo.head.commit rev, branch = commit.name_rev.split(' ') - msg = 'rev: %s\n\nauther: %s\n\nbranch: %s\n\nmessage: %s' % ( - rev, - commit.author.name, - branch, - commit.summary - ) + msg = 'rev: %s\n\nauther: %s\n\nbranch: %s\n\nmessage: %s' % (rev, commit.author.name, branch, commit.summary) print(utils.lark.request({'text': msg})) diff --git a/utils/lark.py b/utils/lark.py index f9f3c92..42804f6 100644 --- a/utils/lark.py +++ b/utils/lark.py @@ -13,25 +13,26 @@ from django.utils.timezone import now def gen_sign(timestamp, secret): - # 拼接timestamp和secret - string_to_sign = '{}\n{}'.format(timestamp, secret) - hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest() + # 拼接timestamp和secret + string_to_sign = '{}\n{}'.format(timestamp, secret) + hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest() - # 对结果进行base64处理 - sign = base64.b64encode(hmac_code).decode('utf-8') + # 对结果进行base64处理 + sign = base64.b64encode(hmac_code).decode('utf-8') + + return sign - return sign def request(content, msg_type=const.LARK_WEBHOOK_MSG_TYPE_TEXT): - """ content: {'text': 'xxxxx} - """ - timestamp = utils.timestamp_of(now()) - data = { - "timestamp": timestamp, - "sign": gen_sign(timestamp, settings.LARK_WEBHOOK_SECRET), - "msg_type": msg_type, - "content": content} - resp = requests.post(settings.LARK_WEBHOOK_URL, data=json.dumps(data)) - if resp.status_code == 200 and resp.json().get('StatusCode') == 0: - return True - return False + """content: {'text': 'xxxxx}""" + timestamp = utils.timestamp_of(now()) + data = { + "timestamp": timestamp, + "sign": gen_sign(timestamp, settings.LARK_WEBHOOK_SECRET), + "msg_type": msg_type, + "content": content, + } + resp = requests.post(settings.LARK_WEBHOOK_URL, data=json.dumps(data)) + if resp.status_code == 200 and resp.json().get('StatusCode') == 0: + return True + return False