diff --git a/BaseModels/base_models.py b/BaseModels/base_models.py index 23bdef7..bd139d2 100644 --- a/BaseModels/base_models.py +++ b/BaseModels/base_models.py @@ -28,7 +28,10 @@ class BaseModel(models.Model): json_data = models.JSONField(verbose_name=_('Дополнительные данные'), default=dict, blank=True) def __str__(self): - return self.name + if self.name: + return self.name + else: + return str(self.id) def get_node_by_name(self, node_name): if not self.json_data or not node_name in self.json_data: diff --git a/BaseModels/openAI/openAI_funcs.py b/BaseModels/openAI/openAI_funcs.py new file mode 100644 index 0000000..e4095fc --- /dev/null +++ b/BaseModels/openAI/openAI_funcs.py @@ -0,0 +1,56 @@ +import time + +import requests +def send_request(msg): + # url = 'https://api.openai.com/v1/chat/completions' + # headers = { + # 'Content-Type': 'application/json', + # 'Authorization': 'Bearer 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' + # } + # data = { + # "model": "gpt-3.5-turbo", + # "messages": [{ + # "role": "user", + # "content": msg + # }] + # } + # res = requests.post(url=url, headers=headers, data=data) + + import os + import openai + openai.api_key = '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' + + res = None + + while not res: + + s = f'send request >>> {msg}' + print(s) + + try: + + res = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[ + { + "role": "user", + "content": msg + } + ] + ) + except Exception as e: + msg = f'!!! --- OpenAI send_request fail = {str(e)} > sleep 60sec' + print(msg) + time.sleep(60) + # send_request(msg) + + if res and 'OpenAI account' in res['choices'][0]['message']['content']: + msg = f"!!! --- OpenAI send_request fail = {str(res['choices'][0]['message']['content'])} > sleep 60sec" + print(msg) + res = None + time.sleep(60) + + + + + return res \ No newline at end of file diff --git a/FirePlayProj/settings.py b/FirePlayProj/settings.py index a853698..7318324 100644 --- a/FirePlayProj/settings.py +++ b/FirePlayProj/settings.py @@ -121,7 +121,7 @@ TIME_ZONE = 'Europe/Minsk' USE_I18N = True -USE_TZ = True +USE_TZ = False # Static files (CSS, JavaScript, Images) diff --git a/FirePlayProj/urls.py b/FirePlayProj/urls.py index 8566b3c..47205c5 100644 --- a/FirePlayProj/urls.py +++ b/FirePlayProj/urls.py @@ -23,5 +23,6 @@ from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), path('ckeditor/', include('ckeditor_uploader.urls')), + path('', include('QuestionsApp.urls')) ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/GPTgeneratorAPI/__init__.py b/GPTgeneratorAPI/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/GPTgeneratorAPI/admin.py b/GPTgeneratorAPI/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/GPTgeneratorAPI/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/GPTgeneratorAPI/apps.py b/GPTgeneratorAPI/apps.py new file mode 100644 index 0000000..70eacb4 --- /dev/null +++ b/GPTgeneratorAPI/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GptgeneratorapiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'GPTgeneratorAPI' diff --git a/GPTgeneratorAPI/migrations/__init__.py b/GPTgeneratorAPI/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/GPTgeneratorAPI/models.py b/GPTgeneratorAPI/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/GPTgeneratorAPI/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/GPTgeneratorAPI/tests.py b/GPTgeneratorAPI/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/GPTgeneratorAPI/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/GPTgeneratorAPI/views.py b/GPTgeneratorAPI/views.py new file mode 100644 index 0000000..7c758fa --- /dev/null +++ b/GPTgeneratorAPI/views.py @@ -0,0 +1,195 @@ +import time + +from django.shortcuts import render +from BaseModels.openAI.openAI_funcs import send_request + +def get_questions_for_category(category, quest_count, level): + # if level == 1: + name_level = 'простой' + if level == 2: + name_level = 'средний' + if level == 3: + name_level = 'сложный' + + + # msg = f'создай список (в одну строку, без нумерации) из {quest_count} вопросов уровня сложности "{name_level}" по 3 ответа на каждый из них по теме ' \ + # f'"{category.name}", ответы выведи в одну строку с разделителем "|", перед правильным ответом поставь символ "~", перед строкой каждого вопроса ставь символ "#"' + msg = f'Cоздай список (в одну строку, без нумерации) из {quest_count} вопросов по теме "{category.name}", уровень сложности вопросов - "{name_level}", к каждому вопросу подготовь 2 неверных ответа и 1 верный (без нумерации, с разделителем "|"), перед верным ответом поставь символ "~", перед каждым вопросом поставь символ "#"' + # print(msg) + res = send_request(msg) + return res + +def get_questions(): + from QuestionsApp.models import Question, QuestionCategory, Answer + + answers_count = 3 + request_questions_count = 2 + + from django.db.models import Count, F + cats = QuestionCategory.objects.filter(enable=True).exclude(parent_category=None).annotate( + quest_count=Count('rel_questions_for_category') + ).order_by('quest_count') + for cat in cats: + for level in [1, 2, 3]: + res = get_questions_for_category(cat, request_questions_count, level) + + data = res['choices'][0]['message']['content'] + print(data) + res_quests = data.split('#') + for res_quest in res_quests: + if not res_quest: + continue + + check_right_answer_count = 0 + + try: + + res_quest_struct_list = res_quest.split('|') + if len(res_quest_struct_list) < 2: + continue + + quest_for_add = prepare_phrase(res_quest_struct_list[0]) + if not quest_for_add: + continue + + print(quest_for_add) + + quest_dublicate = Question.objects.filter(name=quest_for_add).count() + if quest_dublicate: + msg = f'! - дубликат вопроса "{quest_for_add}" > пропускаем' + print(msg) + continue + + quest_obj = Question.objects.create( + name=quest_for_add, + category=cat, + game_level=level, + ) + + answer_objs_list = [] + + answers_list = res_quest_struct_list[1:]#.split('|') + + for answer in answers_list: + if not answer: + continue + + right_answer = False + + answer = answer.strip() + if answer[0] == '~': + right_answer = True + check_right_answer_count += 1 + + answer_for_add = prepare_phrase(answer) + if not answer_for_add: + continue + + answer_obj = Answer( + name=answer_for_add, + right_answer=right_answer, + question=quest_obj + ) + answer_objs_list.append(answer_obj) + print(f' - {answer_for_add} > {right_answer}') + + if len(answer_objs_list) != answers_count or check_right_answer_count != 1: + Question.objects.filter(id=quest_obj.id).delete() + msg = f'!!! - неверное количество ответов или правильных ответов' + print(msg) + continue + + Answer.objects.bulk_create(answer_objs_list) + + + except Exception as e: + msg = f'!!! --- get_questions question {res_quest} > processed ERROR = {str(e)}' + print(msg) + + # максимум 3 запроса в минуту + time.sleep(30) + + return True + + +def get_sub_categories_for_category(category): + msg = f'создай 500 подтематик для тематики {category.name} и выведи с разделителем ^ в одну строку без нумерации' + print(msg) + res = send_request(msg) + return res + +def prepare_phrase(phrase): + + try: + name = phrase.replace('\n', '') + while name and name[-1] in (',', '.', ':', ';', '-', ' '): + name = name[:-1] + + while name and name[0] in (',', '.', ':', ';', '-', ' ', '~'): + name = name[1:] + + if len(name) > 3 and name[1] == '.': + name = name[2:] + + if len(name) > 3 and name[1] == ' ': + try: + i = int(name[0]) + name = name[2:] + except: + pass + + name = name[2:] + + # убираем маркировку подпунктов + if name and name[1] == ')': + name = name[2:] + + name = name.strip() + except Exception as e: + msg = f'prepare_phrase "{phrase}" error = {str(e)}' + print(msg) + name = '' + + return name + + +def get_subcategories(): + from QuestionsApp.models import QuestionCategory + categories = QuestionCategory.objects.all() + + cats_names_list = list(categories.values_list('name', flat=True)) + + + for cat in categories: + sub_cats_objs = [] + + res = get_sub_categories_for_category(cat) + + data = res['choices'][0]['message']['content'] + # data = data.replace('-', '') + + sub_cats_list = data.split('^') + for sub_cat in sub_cats_list: + name = prepare_phrase(sub_cat) + if not name: + msg = f'! - {sub_cat} > проблема в строке, пропускаем' + print(msg) + continue + + if name in cats_names_list: + msg = f'! - {name} > пропускаем, дубликат' + print(msg) + continue + + print(name) + sub_cat_obj = QuestionCategory( + name=name, + parent_category=cat + ) + sub_cats_objs.append(sub_cat_obj) + cats_names_list.append(name) + + QuestionCategory.objects.bulk_create(sub_cats_objs) + # cat.parsed = True + + return True diff --git a/QuestionsApp/admin.py b/QuestionsApp/admin.py index 1f124ec..e8d9cb0 100644 --- a/QuestionsApp/admin.py +++ b/QuestionsApp/admin.py @@ -1,9 +1,18 @@ from django.contrib import admin -from BaseModels.admin_utils import Admin_BaseIconModel +from BaseModels.admin_utils import * from .models import * -class Admin_Question(Admin_BaseIconModel): +class Admin_QuestionCategory(Admin_BaseIconModel): pass +admin.site.register(QuestionCategory, Admin_QuestionCategory) + +class Admin_AnswerTabularInline(Admin_BaseIconTabularModel): + fields = ['name', 'right_answer', 'order'] + model = Answer + extra = 0 + +class Admin_Question(Admin_BaseIconModel): + inlines = [Admin_AnswerTabularInline] admin.site.register(Question, Admin_Question) class Admin_Answer(Admin_BaseIconModel): diff --git a/QuestionsApp/migrations/0002_questioncategory_question_category.py b/QuestionsApp/migrations/0002_questioncategory_question_category.py new file mode 100644 index 0000000..b64d6a7 --- /dev/null +++ b/QuestionsApp/migrations/0002_questioncategory_question_category.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.1 on 2023-05-19 12:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('QuestionsApp', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='QuestionCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')), + ('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')), + ('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')), + ('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')), + ('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')), + ('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')), + ('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')), + ('parsed', models.BooleanField(default=False, verbose_name='Использован')), + ('parent_category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='categories_for_parentCategory', to='QuestionsApp.questioncategory', verbose_name='Родительская категория')), + ], + options={ + 'verbose_name': 'Категория вопроса', + 'verbose_name_plural': 'Категории вопросов', + }, + ), + migrations.AddField( + model_name='question', + name='category', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_questions_for_category', to='QuestionsApp.questioncategory', verbose_name='Категория'), + ), + ] diff --git a/QuestionsApp/migrations/0003_alter_questioncategory_parent_category.py b/QuestionsApp/migrations/0003_alter_questioncategory_parent_category.py new file mode 100644 index 0000000..4964ab7 --- /dev/null +++ b/QuestionsApp/migrations/0003_alter_questioncategory_parent_category.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-05-19 13:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('QuestionsApp', '0002_questioncategory_question_category'), + ] + + operations = [ + migrations.AlterField( + model_name='questioncategory', + name='parent_category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='categories_for_parentCategory', to='QuestionsApp.questioncategory', verbose_name='Родительская категория'), + ), + ] diff --git a/QuestionsApp/models.py b/QuestionsApp/models.py index 8a326a6..0eeb228 100644 --- a/QuestionsApp/models.py +++ b/QuestionsApp/models.py @@ -2,8 +2,33 @@ from django.db import models from BaseModels.base_models import BaseModel from django.utils.translation import gettext_lazy as _ +class QuestionCategory(BaseModel): + parent_category = models.ForeignKey( + 'QuestionCategory', verbose_name=_('Родительская категория'), related_name='categories_for_parentCategory', + on_delete=models.CASCADE, null=True, blank=True + ) + parsed = models.BooleanField(verbose_name='Использован', default=False) + + def __str__(self): + if self.name: + return f'{self.name}' + else: + return str(self.id) + + + class Meta: + verbose_name = _('Категория вопроса') + verbose_name_plural = _('Категории вопросов') + + class Question(BaseModel): + category = models.ForeignKey( + QuestionCategory, verbose_name='Категория', related_name='rel_questions_for_category', + on_delete=models.SET_NULL, null=True + + ) + game_level = models.IntegerField(verbose_name=_('Уровень игры'), default=1) time_for_answer = models.IntegerField(verbose_name=_('Время на ответ (сек)'), default=7) used_count = models.IntegerField(verbose_name=_('Количество использования'), default=0) diff --git a/QuestionsApp/urls.py b/QuestionsApp/urls.py new file mode 100644 index 0000000..4bdc664 --- /dev/null +++ b/QuestionsApp/urls.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.urls import path, include +from .views import * + +urlpatterns = [ + path('test_code', test_code, name='test_code') +] \ No newline at end of file diff --git a/QuestionsApp/views.py b/QuestionsApp/views.py index 91ea44a..f326023 100644 --- a/QuestionsApp/views.py +++ b/QuestionsApp/views.py @@ -1,3 +1,9 @@ from django.shortcuts import render +from django.http import HttpResponse -# Create your views here. +def test_code(request): + from GPTgeneratorAPI.views import get_subcategories, get_questions + # res = get_subcategories() + res = get_questions() + + return HttpResponse(str(res)) \ No newline at end of file diff --git a/requirements.pip b/requirements.pip index 6c624c8..6a30a00 100644 --- a/requirements.pip +++ b/requirements.pip @@ -2,3 +2,5 @@ Django==4.2.1 django-ckeditor==6.5.1 django-modeltranslation==0.18.9 psycopg2-binary==2.9.6 +openai +requests \ No newline at end of file