This commit is contained in:
Andrey Gumirov
2022-05-01 00:57:58 +07:00
parent a968042f6e
commit e1bd7234ed
19 changed files with 329 additions and 39 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*__pycache__* *__pycache__*
tmp

View File

@ -1,2 +1,4 @@
from .menu import menu_router from .menu import menu_router
from .test import test_router from .test import test_router
from .teacher import teacher_router
from .student import student_router

13
blueprints/student.py Normal file
View File

@ -0,0 +1,13 @@
from vkwave.bots import DefaultRouter, SimpleBotEvent, simple_bot_message_handler, PayloadContainsFilter, BotEvent
import locales
student_router = DefaultRouter()
@student_router.registrar.with_decorator(PayloadContainsFilter("student"),)
async def student_menu(event: BotEvent):
sevent = SimpleBotEvent(event)
return await sevent.answer(
message=locales.ST_MENU,
keyboard=locales.ST_KB.get_keyboard(),
)

13
blueprints/teacher.py Normal file
View File

@ -0,0 +1,13 @@
from vkwave.bots import DefaultRouter, SimpleBotEvent, simple_bot_message_handler, PayloadContainsFilter, BotEvent
import locales
teacher_router = DefaultRouter()
@teacher_router.registrar.with_decorator(PayloadContainsFilter("teacher"),)
async def teacher_menu(event: BotEvent):
sevent = SimpleBotEvent(event)
return await sevent.answer(
message=locales.TC_MENU,
keyboard=locales.TC_KB.get_keyboard(),
)

View File

@ -3,13 +3,14 @@ import logging
import random import random
from vkwave.bots import DefaultRouter, SimpleBotEvent, simple_bot_message_handler, PayloadFilter, PayloadContainsFilter, \ from vkwave.bots import DefaultRouter, SimpleBotEvent, simple_bot_message_handler, PayloadFilter, PayloadContainsFilter, \
PhotoUploader PhotoUploader, Storage
from vkwave.bots import Keyboard, ButtonColor from vkwave.bots import Keyboard, ButtonColor
from vkwave.bots import EventTypeFilter, BotEvent from vkwave.bots import EventTypeFilter, BotEvent
from vkwave.types.bot_events import BotEventType from vkwave.types.bot_events import BotEventType
from vkwave.bots.fsm import FiniteStateMachine, StateFilter, ForWhat, State, ANY_STATE from vkwave.bots.fsm import FiniteStateMachine, StateFilter, ForWhat, State, ANY_STATE
import locales import locales
import util
from config import Config from config import Config
from db import DB from db import DB
from db.db import TestResult from db.db import TestResult
@ -21,9 +22,12 @@ from locales import INPUT_NAME_TEXT
# MENU_KB.add_row() # MENU_KB.add_row()
# MENU_KB.add_text_button(text="Бонус", payload={"command": "bonus"}, color=ButtonColor.POSITIVE) # MENU_KB.add_text_button(text="Бонус", payload={"command": "bonus"}, color=ButtonColor.POSITIVE)
# from nft_things.NftSender import NFTSender # from nft_things.NftSender import NFTSender
from util.redis_db import RedisDB
test_router = DefaultRouter() test_router = DefaultRouter()
fsm = FiniteStateMachine()
test_router.registrar.add_default_filter(StateFilter(fsm, ..., ..., always_false=True))
test_router.registrar.add_default_filter( test_router.registrar.add_default_filter(
EventTypeFilter(BotEventType.MESSAGE_NEW.value)) # we don't want to write it in all handlers. EventTypeFilter(BotEventType.MESSAGE_NEW.value)) # we don't want to write it in all handlers.
@ -40,7 +44,7 @@ test_router.registrar.add_default_filter(
# return "You are quited!" # return "You are quited!"
@test_router.registrar.with_decorator( @test_router.registrar.with_decorator(
PayloadContainsFilter("test"),# for state in States.questions[:-1]] PayloadContainsFilter("test"),
) )
async def main_part_handle(event: BotEvent): async def main_part_handle(event: BotEvent):
user_id = event.object.object.message.from_id user_id = event.object.object.message.from_id
@ -51,7 +55,7 @@ async def main_part_handle(event: BotEvent):
logging.debug(f"State index: {state_idx}") logging.debug(f"State index: {state_idx}")
q_res = payload['q'] if 'q' in payload else None q_res = payload['q'] if 'q' in payload else event.object.object.message.text
logging.debug(f"Qres: {q_res}") logging.debug(f"Qres: {q_res}")
# extra_state_data works as fsm.add_data(..., state_data={"name": event.object.object.message.text}) # extra_state_data works as fsm.add_data(..., state_data={"name": event.object.object.message.text})
@ -61,17 +65,28 @@ async def main_part_handle(event: BotEvent):
DB().update_test_result(user_id, question=state_idx, answer=q_res) DB().update_test_result(user_id, question=state_idx, answer=q_res)
if state_idx + 1 < len(locales.questions): if state_idx + 1 < len(locales.questions):
return await botevent.answer( RedisDB().set_state(user_id, state_idx + 1)
message=locales.questions[state_idx + 1][0], kb = locales.questions[state_idx + 1][1]
keyboard=locales.questions[state_idx + 1][1].get_keyboard(), if kb:
) return await botevent.answer(
message=locales.questions[state_idx + 1][0],
keyboard=kb.get_keyboard(),
)
else:
return await botevent.answer(
message=locales.questions[state_idx + 1][0],
payload=json.dumps({"test": state_idx + 1}),
)
else: else:
# todo add task to send user an image here # todo add task to send user an image here
# Config().nft_sender.add_task() # Config().nft_sender.add_task()
RedisDB().del_state(user_id)
logging.warn("Sending attach!") logging.warn("Sending attach!")
big_attachment = await Config().uploader.get_attachments_from_paths( big_attachment = await Config().uploader.get_attachments_from_paths(
peer_id=user_id, peer_id=user_id,
file_paths=["img.jpg"], file_paths=[util.get_image(user_id)],
) )
await Config().api_ctx.messages.send( await Config().api_ctx.messages.send(
user_id=user_id, attachment=big_attachment, random_id=0 user_id=user_id, attachment=big_attachment, random_id=0

View File

@ -1,4 +1,7 @@
import os import os
from vkwave.bots import PhotoUploader
from util import Singleton from util import Singleton
@ -9,8 +12,13 @@ class Config(metaclass=Singleton):
PG_USER = os.environ["USER"] PG_USER = os.environ["USER"]
PG_PASS = os.environ["PASS"] PG_PASS = os.environ["PASS"]
PG_ADDR = os.environ["DB_ADDR"] PG_ADDR = os.environ["DB_ADDR"]
REDIS_ADDR = os.environ["REDIS_ADDR"]
NFT_SVC_ADDR = os.environ["NFT_ADDR"]
TMP_DIR = os.environ["TMP_DIR"]
def __init__(self): def __init__(self):
self.api_ctx = None self.api_ctx = None
self.uploader = None self.uploader: PhotoUploader = None

View File

@ -64,7 +64,14 @@ class DB(metaclass=Singleton):
def update_test_result(self, user_id: int, question: int, answer: str): def update_test_result(self, user_id: int, question: int, answer: str):
user = self._session.query(Candidate).filter(Candidate.id == user_id).first() user = self._session.query(Candidate).filter(Candidate.id == user_id).first()
user.test_result[0].answers.append(QuestionAnswer(question=question, answer=answer)) for i, ans in enumerate(user.test_result[0].answers):
if ans.question == question:
logging.debug(f"Update test result: {question} {answer}")
user.test_result[0].answers[i].answer = answer
break
else:
logging.warn(f"New test result: {question} {answer}")
user.test_result[0].answers.append(QuestionAnswer(question=question, answer=answer))
self._session.commit() self._session.commit()
def add_candidate(self, candidate: Candidate): def add_candidate(self, candidate: Candidate):

View File

@ -1,5 +1,12 @@
version: "2" version: "2"
services: services:
image_gen:
build:
context: ./nft_svc
dockerfile: Dockerfile
ports:
- 5001:5000
pgdb: pgdb:
image: 'postgres:12' image: 'postgres:12'
restart: always restart: always
@ -12,3 +19,10 @@ services:
# - ${PG_MNT}:/var/lib/postgresql/data # - ${PG_MNT}:/var/lib/postgresql/data
ports: ports:
- ${PG_OUTBOUND_PORT}:5432 - ${PG_OUTBOUND_PORT}:5432
redis:
image: "redis:alpine"
command: redis-server --appendonly yes --appendfsync everysec
ports:
- 6379:6379
# volumes:
# - ${FDS_REDIS_MNT}:/data

View File

@ -1,10 +1,57 @@
from vkwave.bots import Keyboard, ButtonColor from vkwave.bots import Keyboard, ButtonColor
# menu # menu
MENU = "Привет!" MENU = "Привет, друг! Это - бот инженерной школы. Инженерная школа - молодой проект на Механико-Математическом факультете НГУ."
MENU_KB = Keyboard() MENU_KB = Keyboard()
MENU_KB.add_text_button(text="Пройти тест!", payload={"test": "-1"}, color=ButtonColor.POSITIVE) MENU_KB.add_text_button(text="Пройти тест!", payload={"test": "-1"}, color=ButtonColor.POSITIVE)
MENU_KB.add_link_button(text="Общий чатик", link="https://vk.me/join/AJQ1dw97/SBEQYIyQdZfG69y") MENU_KB.add_link_button(text="Общий чатик", link="https://vk.me/join/AJQ1dw97/SBEQYIyQdZfG69y")
MENU_KB.add_row()
MENU_KB.add_text_button(text="Пообщаться со студентом", payload={"student": "1"}, color=ButtonColor.PRIMARY)
MENU_KB.add_row()
MENU_KB.add_text_button(text="Пообщаться с преподавателем", payload={"teacher": "1"}, color=ButtonColor.PRIMARY)
# Student menu
ST_MENU = """Вот список студентов, которые могут рассказать тебе про Инженерную Школу
https://vk.com/cleverbitch - Булгакова Лиза, 2 курс
https://vk.com/shestakova__d - Шестакова Даша, 2 курс
https://vk.com/fossa_mar - Пискеева Даша, 2 курс
https://vk.com/brainkiller78 - Тищенко Данил, 1 курс
https://vk.com/skazaniyk - Хамутский Дима, 1 курс
https://vk.com/kirik229 - Шадрина Анжела, 2 курс
https://vk.com/dinazavrrrik - Коновалов Назар, 1 курс
https://vk.com/tenikeev - Еникеев Тимур, 1 курс
"""
ST_KB = Keyboard()
ST_KB.add_link_button(text="Булгакова Лиза", link="https://vk.com/cleverbitch")
ST_KB.add_link_button(text="Шестакова Даша", link="https://vk.com/shestakova__d")
ST_KB.add_row()
ST_KB.add_link_button(text="Пискеева Даша", link="https://vk.com/fossa_mar")
ST_KB.add_link_button(text="Тищенко Данил", link="https://vk.com/brainkiller78")
ST_KB.add_row()
ST_KB.add_link_button(text="Хамутский Дима", link="https://vk.com/skazaniyk")
ST_KB.add_link_button(text="Шадрина Анжела", link="https://vk.com/kirik229")
ST_KB.add_row()
ST_KB.add_link_button(text="Коновалов Назар", link="https://vk.com/dinazavrrrik")
ST_KB.add_link_button(text="Еникеев Тимур", link="https://vk.com/tenikeev")
ST_KB.add_row()
ST_KB.add_text_button(text="ХОЧУ ДОМОЙ!!!!!!!!!!!!!!!", payload={}, color=ButtonColor.NEGATIVE)
# Teacher menu
TC_MENU = """Вот список преподавателей, которые могут рассказать тебе про Инженерную Школу
https://vk.com/anastasia.v.karpenko - Карпенко Анастасия Валерьевна, преподаватель по логике и ОПД
https://vk.com/krkaushan - Насыбуллова Кристина Андреевна,
https://vk.com/id11073597 - Насыбуллов Тимур Ринатович, преподаватель по Алгебре и геометрии и ТФКП
https://vk.com/yury_efremenko - Ефременко Юрий Данилович, преподаватель по Алгебре и геометрии."""
TC_KB = Keyboard()
TC_KB.add_link_button(text="Анастасия Валерьевна", link="https://vk.com/anastasia.v.karpenko")
TC_KB.add_link_button(text="Кристина Андреевна", link="https://vk.com/krkaushan")
TC_KB.add_row()
TC_KB.add_link_button(text="Тимур Ринатович", link="https://vk.com/id11073597")
TC_KB.add_link_button(text="Юрий Данилович", link="https://vk.com/yury_efremenko")
TC_KB.add_row()
TC_KB.add_text_button(text="ХОЧУ ДОМОЙ!!!!!!!!!!!!!!!", payload={}, color=ButtonColor.NEGATIVE)
# TEST Questions # TEST Questions
INPUT_NAME_TEXT = "Пожалуйста, введите имя:" INPUT_NAME_TEXT = "Пожалуйста, введите имя:"
@ -12,44 +59,57 @@ INPUT_NAME_TEXT = "Пожалуйста, введите имя:"
# 1 # 1
WHAT_ENGINEER_ARE_YOU = "Кто ты из инженеров?" WHAT_ENGINEER_ARE_YOU = "Кто ты из инженеров?"
WHAT_ENGINEER_ARE_YOU_KB = Keyboard() WHAT_ENGINEER_ARE_YOU_KB = Keyboard()
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Маск", payload={"q": "Маск", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Маск", payload={"q": "Маск"}, color=ButtonColor.PRIMARY)
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Рогозин", payload={"q": "Рогозин", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Рогозин", payload={"q": "Рогозин"}, color=ButtonColor.PRIMARY)
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Тесла", payload={"q": "Тесла", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Тесла", payload={"q": "Тесла"}, color=ButtonColor.PRIMARY)
WHAT_ENGINEER_ARE_YOU_KB.add_row() WHAT_ENGINEER_ARE_YOU_KB.add_row()
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Кулибин", payload={"q": "Кулибин", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Кулибин", payload={"q": "Кулибин"}, color=ButtonColor.PRIMARY)
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Калашников", payload={"q": "Калашников", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Калашников", payload={"q": "Калашников"}, color=ButtonColor.PRIMARY)
WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Кондратюк", payload={"q": "Кондратюк", "test": "0"}, color=ButtonColor.PRIMARY) WHAT_ENGINEER_ARE_YOU_KB.add_text_button(text="Кондратюк", payload={"q": "Кондратюк"}, color=ButtonColor.PRIMARY)
# 2 # 2
PROG_LANG = "Какой ЯП нравится?" PROG_LANG = "Какой ЯП нравится?"
PROG_LANG_KB = Keyboard() PROG_LANG_KB = Keyboard()
PROG_LANG_KB.add_text_button(text="Python", payload={"q": "Python", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="Python", payload={"q": "Python"}, color=ButtonColor.PRIMARY)
PROG_LANG_KB.add_text_button(text="Pascal", payload={"q": "Pascal", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="Pascal", payload={"q": "Pascal"}, color=ButtonColor.PRIMARY)
PROG_LANG_KB.add_text_button(text="C/C++", payload={"q": "ccpp", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="C/C++", payload={"q": "ccpp"}, color=ButtonColor.PRIMARY)
PROG_LANG_KB.add_row() PROG_LANG_KB.add_row()
PROG_LANG_KB.add_text_button(text="JS", payload={"q": "JS", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="JS", payload={"q": "JS"}, color=ButtonColor.PRIMARY)
PROG_LANG_KB.add_text_button(text="HTML+CSS", payload={"q": "HTMLCSS", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="HTML+CSS", payload={"q": "HTMLCSS"}, color=ButtonColor.PRIMARY)
PROG_LANG_KB.add_text_button(text="Haskel", payload={"q": "Haskel", "test": "1"}, color=ButtonColor.PRIMARY) PROG_LANG_KB.add_text_button(text="Haskel", payload={"q": "Haskel"}, color=ButtonColor.PRIMARY)
# 3 # 3
FAV_THEME = "Какой предмет нравится?" FAV_THEME = "Какой предмет нравится?"
FAV_THEME_KB = Keyboard() FAV_THEME_KB = Keyboard()
FAV_THEME_KB.add_text_button(text="Матеша", payload={"q": "Матеша", "test": "2"}, color=ButtonColor.PRIMARY) FAV_THEME_KB.add_text_button(text="Матеша", payload={"q": "Матеша"}, color=ButtonColor.PRIMARY)
FAV_THEME_KB.add_text_button(text="Русский/Литра", payload={"q": "русскийлитра", "test": "2"}, color=ButtonColor.PRIMARY) FAV_THEME_KB.add_text_button(text="Русский/Литра", payload={"q": "русскийлитра"}, color=ButtonColor.PRIMARY)
FAV_THEME_KB.add_text_button(text="Инфа", payload={"q": "Инфа", "test": "2"}, color=ButtonColor.PRIMARY) FAV_THEME_KB.add_text_button(text="Инфа", payload={"q": "Инфа"}, color=ButtonColor.PRIMARY)
FAV_THEME_KB.add_row() FAV_THEME_KB.add_row()
FAV_THEME_KB.add_text_button(text="Физика", payload={"q": "Физика", "test": "2"}, color=ButtonColor.PRIMARY) FAV_THEME_KB.add_text_button(text="Физика", payload={"q": "Физика"}, color=ButtonColor.PRIMARY)
FAV_THEME_KB.add_text_button(text="другое", payload={"q": "other", "test": "2"}, color=ButtonColor.PRIMARY) FAV_THEME_KB.add_text_button(text="другое", payload={"q": "other"}, color=ButtonColor.PRIMARY)
# 4 # 4
EGE = "Как готовился к ЕГЭ?" EGE = "Как готовился к ЕГЭ?"
EGE_KB = Keyboard() EGE_KB = Keyboard()
EGE_KB.add_text_button(text="В школе", payload={"q": "школа", "test": "3"}, color=ButtonColor.PRIMARY) EGE_KB.add_text_button(text="В школе", payload={"q": "школа"}, color=ButtonColor.PRIMARY)
EGE_KB.add_text_button(text="online", payload={"q": "online", "test": "3"}, color=ButtonColor.PRIMARY) EGE_KB.add_text_button(text="online", payload={"q": "online"}, color=ButtonColor.PRIMARY)
EGE_KB.add_text_button(text="репетитор", payload={"q": "репетитор", "test": "3"}, color=ButtonColor.PRIMARY) EGE_KB.add_text_button(text="репетитор", payload={"q": "репетитор"}, color=ButtonColor.PRIMARY)
EGE_KB.add_row() EGE_KB.add_row()
EGE_KB.add_text_button(text="Сам", payload={"q": "Сам", "test": "3"}, color=ButtonColor.PRIMARY) EGE_KB.add_text_button(text="Сам", payload={"q": "Сам"}, color=ButtonColor.PRIMARY)
EGE_KB.add_text_button(text="wtf?", payload={"q": "wtf", "test": "3"}, color=ButtonColor.PRIMARY) EGE_KB.add_text_button(text="wtf?", payload={"q": "wtf"}, color=ButtonColor.PRIMARY)
# 5
TRANS_ENGINEER_COUNT = "Сколько студентов ИШ надо, чтобы включить трансу?"
TRANS_ENGINEER_COUNT_KB = Keyboard()
TRANS_ENGINEER_COUNT_KB.add_text_button(text="1", payload={"q": "1"}, color=ButtonColor.PRIMARY)
TRANS_ENGINEER_COUNT_KB.add_text_button(text="2", payload={"q": "2"}, color=ButtonColor.PRIMARY)
TRANS_ENGINEER_COUNT_KB.add_text_button(text="5", payload={"q": "5"}, color=ButtonColor.PRIMARY)
# 6
ZODIAC = "Кто вы по знаку зодиака?"
# 7
CONTACT = "Введите почту или ник в телеграмме, по которому мы сможем прислать вам результаты"
# last # last
LAST_MESSAGE = "Спасибо, что прошел тест!" LAST_MESSAGE = "Спасибо, что прошел тест!"
@ -62,4 +122,7 @@ questions = [
(PROG_LANG, PROG_LANG_KB), (PROG_LANG, PROG_LANG_KB),
(FAV_THEME, FAV_THEME_KB), (FAV_THEME, FAV_THEME_KB),
(EGE, EGE_KB), (EGE, EGE_KB),
(TRANS_ENGINEER_COUNT, TRANS_ENGINEER_COUNT_KB),
(ZODIAC, None),
(CONTACT, None),
] ]

View File

@ -3,11 +3,12 @@ import logging
from vkwave.bots import SimpleLongPollBot, PhotoUploader from vkwave.bots import SimpleLongPollBot, PhotoUploader
from blueprints import ( from blueprints import (
menu_router, test_router, menu_router, test_router, student_router, teacher_router,
) )
from config import Config from config import Config
from middlewares import UserMiddleware from middlewares import UserMiddleware
# from nft_things.NftSender import NFTSender # from nft_things.NftSender import NFTSender
from middlewares.test_state_middleware import TestStateMiddleware
logging.basicConfig(level="DEBUG") logging.basicConfig(level="DEBUG")
@ -20,12 +21,12 @@ Config().api_ctx = bot.api_context
Config().uploader = uploader Config().uploader = uploader
bot.middleware_manager.add_middleware(UserMiddleware()) bot.middleware_manager.add_middleware(UserMiddleware())
bot.middleware_manager.add_middleware(TestStateMiddleware())
bot.dispatcher.add_router(test_router) bot.dispatcher.add_router(test_router)
# bot.dispatcher.add_router(games_router) bot.dispatcher.add_router(student_router)
# bot.dispatcher.add_router(coin_flip_router) bot.dispatcher.add_router(teacher_router)
# bot.dispatcher.add_router(bonus_router)
# регаем последним чтобы сначала проверялись все остальные команды # регаем последним чтобы сначала проверялись все остальные команды
bot.dispatcher.add_router(menu_router) bot.dispatcher.add_router(menu_router)

View File

@ -0,0 +1,26 @@
import json
import logging
from vkwave.bots import BaseMiddleware, BotEvent, MiddlewareResult, SimpleBotEvent, Storage
from vkwave.bots.storage.types import Key
from db import DB, Candidate
from util.redis_db import RedisDB
storage = Storage()
class TestStateMiddleware(BaseMiddleware):
async def pre_process_event(self, event: BotEvent) -> MiddlewareResult:
user_id = event.object.object.message.from_id
redis_test_state = RedisDB().get_state(user_id)
logging.debug(event.object.object.message.payload)
payload_json = json.loads(event.object.object.message.payload) if event.object.object.message.payload else {}
if 'test' not in payload_json.keys() and (
redis_test_state != None):
logging.debug(f"User {user_id} is in state: {redis_test_state}")
payload_json["test"] = int(redis_test_state)
event.object.object.message.payload = json.dumps(payload_json)
return MiddlewareResult(True)

14
nft_svc/Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM python:3.9
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY . /app
EXPOSE 5000
ENTRYPOINT [ "flask" ]
CMD ["run", "--host=0.0.0.0", "--port=5000"]

61
nft_svc/app.py Normal file
View File

@ -0,0 +1,61 @@
import base64
import io
from PIL import Image
from flask import Flask, request, jsonify
import matplotlib.image as mpimg
import numpy as np
import numba
app = Flask(__name__)
@numba.njit
def optimized_mandelbrot(n_rows, n_columns, iterations, cx, cy):
x_cor = np.linspace(-2, 2, n_rows)
y_cor = np.linspace(-2, 2, n_columns)
output = np.zeros((n_rows,n_columns))
c = cx + 1j * cy
for i in range(n_rows):
for j in range(n_columns):
z = x_cor[i] + y_cor[j] * 1j
count = 0
for k in range(iterations):
z = (z*z) + c
count += 1
if np.abs(z) > 4:
break
output[i, j] = count
return output.T
def open_image_as_array(path):
return mpimg.imread(path)
def get_encoded_img(arr):
img = Image.fromarray(arr).convert("L")
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='PNG')
encoded_img = base64.encodebytes(img_byte_arr.getvalue()).decode('ascii')
return encoded_img
@app.route('/getImage', methods=['GET'])
def get_image():
name = request.args.get('name')
fractal = optimized_mandelbrot(1000, 1000, np.random.randint(2, 251), np.random.uniform(-1, 1), np.random.uniform(-1, 1))
img = get_encoded_img(fractal)
return jsonify({
"code": 0,
"image": img,
"first_time": 1
})
def start_app():
app.run()
if __name__ == '__main__':
start_app()

View File

@ -0,0 +1,10 @@
# Gunicorn config variables
loglevel = "info"
errorlog = "-" # stderr
accesslog = "-" # stdout
worker_tmp_dir = "/dev/shm"
graceful_timeout = 120
timeout = 120
keepalive = 5
threads = 4
workers = 4

6
nft_svc/requirements.txt Normal file
View File

@ -0,0 +1,6 @@
matplotlib~=3.5.1
numpy~=1.21.6
numba~=0.55.1
Pillow~=9.1.0
Flask~=2.1.1
gunicorn

View File

View File

@ -1 +1,2 @@
from .singleton import Singleton from .singleton import Singleton
from .nft_util import get_image

17
util/nft_util.py Normal file
View File

@ -0,0 +1,17 @@
import base64
import logging
import os
from sphinx.util import requests
from config import Config
def get_image(user_id: int) -> str:
path = os.path.join(Config().TMP_DIR, f"{user_id}.png")
if not os.path.exists(path):
logging.warn(f"Getting new image for {user_id}!")
req = requests.get(f"{Config().NFT_SVC_ADDR}/getImage").json()
with open(path, "wb") as f:
f.write(base64.b64decode(req["image"]))
return path

18
util/redis_db.py Normal file
View File

@ -0,0 +1,18 @@
from config import Config
from util import Singleton
import redis
class RedisDB(metaclass=Singleton):
def __init__(self):
self.r = redis.Redis(Config().REDIS_ADDR)
def get_state(self, user_id: int) -> int:
db_ret = self.r.get(f"{user_id}_test_state")
return int(db_ret.decode()) if db_ret else None
def set_state(self, user_id: int, state: int):
self.r.set(f"{user_id}_test_state", str(state))
def del_state(self, user_id):
self.r.delete(f"{user_id}_test_state")