mirror of
				https://github.com/spbleadersofdigtal/backend.git
				synced 2025-10-31 23:57:31 +03:00 
			
		
		
		
	updated openai, added pdt to pptx convert
This commit is contained in:
		
							parent
							
								
									983f122a81
								
							
						
					
					
						commit
						1c58a20508
					
				|  | @ -1,4 +1,4 @@ | |||
| exclude: "^docs/|/migrations/" | ||||
| exclude: "^docs/|/migrations/|^ml/" | ||||
| default_stages: [commit] | ||||
| 
 | ||||
| repos: | ||||
|  |  | |||
|  | @ -9,27 +9,29 @@ | |||
| 
 | ||||
| regex = r"({(\n.+)+\n})" | ||||
| 
 | ||||
| description = """🍀 Что такое Pitch-Deck? Pitch-Deck представляет собой презентацию-тизер проекта/компании для | ||||
| инвесторов, партнеров, журналистов и других заинтересованных лиц. Цель презентации - привлечение дополнительного | ||||
| финансирования (инвестиций). Почему это проблема? | ||||
| description = """ | ||||
| 🍀 Кейсодержатель: | ||||
| ООО «Акселератор Возможностей» (https://ac-vo.ru/) при ИНТЦ МГУ «Воробьевы горы». | ||||
| Организация технологических и инвестиционных мероприятий, курирование инновационной деятельности внутри ИНТЦ МГУ «Воробьевы горы» | ||||
| 
 | ||||
| 🍀 Проблема #1. Недостаток средств: Для многих стартапов ограниченные финансы создают преграду при разработке | ||||
| качественного Pitch Deck. Отсутствие достаточных средств для найма профессиональных консультантов, дизайнеров и | ||||
| копирайтеров, а также для проведения исследований рынка, может привести к созданию менее привлекательной и | ||||
| малоинформативной презентации, что затрудняет привлечение инвестиций. | ||||
| Раскроем небольшую тайну венчура — для привлечения денежных средств и защиты своего проекта, стартапу нужен Pitch-Deck. | ||||
| 
 | ||||
| 🍀 Проблема #2. Недостаток экспертизы: Проблемой для стартапов является недостаток экспертизы для проведения | ||||
| необходимых исследований и корректного отражения их результатов в Pitch Deck. Не всегда у стартапов есть нужные | ||||
| знания в области маркетинга, финансов и анализа рынка, что затрудняет создание убедительной и информативной | ||||
| презентации для привлечения инвестиций. | ||||
| 🍀 Что такое Pitch-Deck? | ||||
| Pitch-Deck представляет собой презентацию-тизер проекта/компании для инвесторов, партнеров, журналистов и других заинтересованных лиц. Цель презентации - привлечение дополнительного финансирования (инвестиций). | ||||
| Почему это проблема? | ||||
| 
 | ||||
| 🍀 Проблема #3. Недостаток времени Молодым компаниям для привлечения инвестиций требуется подготовить целый пакет | ||||
| документов, одним из которых является Pitch Deck. Особенностью стартапов является сравнительного молодая и небольшая | ||||
| команда, у которой чисто физически не хватает времени на разработку инвестиционных материалов, ведь они полностью | ||||
| погружены в процесс разработки и улучшения продукта или сервиса. | ||||
| 🍀 Проблема #1. Недостаток средств: | ||||
| Для многих стартапов ограниченные финансы создают преграду при разработке качественного Pitch Deck. Отсутствие достаточных средств для найма профессиональных консультантов, дизайнеров и копирайтеров, а также для проведения исследований рынка, может привести к созданию менее привлекательной и малоинформативной презентации, что затрудняет привлечение инвестиций. | ||||
| 
 | ||||
| 🍀 ИДЕЯ: Основная идея кейса заключается в создании вспомогательного инструмента на основе ИИ, заточенного под | ||||
| создание Pitch-Deck.""" | ||||
| 🍀 Проблема #2. Недостаток экспертизы: | ||||
| Проблемой для стартапов является недостаток экспертизы для проведения необходимых исследований и корректного отражения их результатов в Pitch Deck. Не всегда у стартапов есть нужные знания в области маркетинга, финансов и анализа рынка, что затрудняет создание убедительной и информативной презентации для привлечения инвестиций. | ||||
| 
 | ||||
| 🍀 Проблема #3. Недостаток времени | ||||
| Молодым компаниям для привлечения инвестиций требуется подготовить целый пакет документов, одним из которых является Pitch Deck. Особенностью стартапов является сравнительного молодая и небольшая команда, у которой чисто физически не хватает времени на разработку инвестиционных материалов, ведь они полностью погружены в процесс разработки и улучшения продукта или сервиса. | ||||
| 
 | ||||
| 🍀 ИДЕЯ: | ||||
| Основная идея кейса заключается в создании вспомогательного инструмента на основе ИИ, заточенного под создание Pitch-Deck. | ||||
| """ | ||||
| 
 | ||||
| names_prompt = """ | ||||
| По тексту ответь или предположи ответ на вопросы в следующем формате: | ||||
|  | @ -42,31 +44,32 @@ | |||
|     """ | ||||
|     По тексту ответь или предположи ответ на вопросы в следующем формате: | ||||
|     { | ||||
|       'users': 'Кто будет пользоваться продуктом', | ||||
|       'problems': 'Какие проблемы решает продукт', | ||||
|       'actuality': 'Продолжите предложение: Актуальность проблемы подтверждается тем фактом, что...', | ||||
|       'solve': 'Как решаем эти проблемы', | ||||
|       'works': 'Как работает решение', | ||||
|       'users': 'Кто будет пользоваться продуктом?', | ||||
|       'problems': 'Какие проблемы решает продукт?', | ||||
|       'actuality': 'Каким фактом обуславливается актуальность проблемы?', | ||||
|       'solve': 'Как решаем эти проблемы?', | ||||
|       'works': 'Как работает решение?', | ||||
|     } | ||||
|     """, | ||||
|     """ | ||||
|     По тексту ответь или предположи ответ на вопросы в следующем формате: | ||||
|     { | ||||
| 'awards': 'Ценность продукта для пользователей', | ||||
| 'money': 'На чем проект зарабатывает? сколько и за что ему платят клиенты', | ||||
| 'aims': Напиши 3 цели: на месяц, на полгода и год, формат: {'1': цель на месяц, '2': цель на полгода, '3': цель на год}, | ||||
| 'investments_sold': 'На что потратить инвестиции под проект', | ||||
| 'financial_indicators': 'Напиши финансовые показатели проекта' | ||||
|       'awards': 'Ценность продукта для пользователей', | ||||
|       'money': 'На чем проект зарабатывает? сколько и за что ему платят клиенты', | ||||
|       'aims': Напиши 3 цели: на месяц, на полгода и год, формат: {'1': цель на месяц, '2': цель на полгода, '3': цель на год}, | ||||
|       'investments_sold': 'На что потратить инвестиции под проект', | ||||
|       'financial_indicators': 'Напиши финансовые показатели проекта' | ||||
|     } | ||||
|     """, | ||||
|     """По тексту ответь или предположи ответ на вопросы в следующем формате: { 'achieve': 'Чего добьется команда после | ||||
|     освоения инвестиций', 'competitors_strength': 'Сильные стороны конкурентов', 'competitors_low': 'Слабые стороны | ||||
|     конкурентов', 'advantages': 'Какие могут быть преимущества над конкурентами', 'category': "На каком рынке | ||||
|     находится этот проект? Выбери из вариантов: 'Business Software', 'IndustrialTech', 'E-commerce', 'Advertising & | ||||
|     Marketing', 'Hardware', 'RetailTech', 'ConstructionTech', 'Web3', 'EdTech', 'Business Intelligence', | ||||
|     'Cybersecurity', 'HrTech', 'Telecom & Communication', 'Media & Entertainment', 'FinTech', 'MedTech', 'Transport & | ||||
|     Logistics', 'Gaming', 'FoodTech', 'AI', 'WorkTech', 'Consumer Goods & Services', 'Aero & SpaceTech', | ||||
|     'Legal & RegTech', 'Travel', 'PropTech', 'Energy', 'GreenTech'" } , | ||||
|     """ | ||||
|     По тексту ответь или предположи ответ на вопросы в следующем формате: | ||||
|     { | ||||
|       'achieve': 'Чего добьется команда после освоения инвестиций', | ||||
|       'competitors_strength': 'Сильные стороны конкурентов', | ||||
|       'competitors_low': 'Слабые стороны конкурентов', | ||||
|       'advantages': 'Какие могут быть преимущества над конкурентами', | ||||
|       'category': "На каком рынке находится этот проект? Выбери из вариантов: 'Business Software', 'IndustrialTech', 'E-commerce', 'Advertising & Marketing', 'Hardware', 'RetailTech', 'ConstructionTech', 'Web3', 'EdTech', 'Business Intelligence', 'Cybersecurity', 'HrTech', 'Telecom & Communication', 'Media & Entertainment', 'FinTech', 'MedTech', 'Transport & Logistics', 'Gaming', 'FoodTech', 'AI', 'WorkTech', 'Consumer Goods & Services', 'Aero & SpaceTech', 'Legal & RegTech', 'Travel', 'PropTech', 'Energy', 'GreenTech'" | ||||
|     } | ||||
|     """, | ||||
| ] | ||||
| 
 | ||||
|  | @ -103,9 +106,13 @@ def create_hints(description: str, stage: int): | |||
|         messages=[{"role": "user", "content": description + "\n" + prompts[stage]}], | ||||
|     ) | ||||
|     str_content = chat_completion.choices[0].message.content | ||||
|     print(str_content) | ||||
|     filtered_content = list(re.finditer(regex, str_content, re.MULTILINE))[-1].group() | ||||
|     if not len(filtered_content): | ||||
|     try: | ||||
|         filtered_content = list(re.finditer(regex, str_content, re.MULTILINE))[ | ||||
|             -1 | ||||
|         ].group() | ||||
|         if not len(filtered_content): | ||||
|             raise ValueError(f"answer doesnt pass validation, {filtered_content}") | ||||
|     except: | ||||
|         raise ValueError(f"answer doesnt pass validation, {filtered_content}") | ||||
|     content = literal_eval(filtered_content) | ||||
|     for assertion_statement in assertions[stage]: | ||||
|  | @ -147,7 +154,7 @@ def create_name_hint(description: str): | |||
|     answer = literal_eval(chat_completion.choices[0].message.content)["names"].split( | ||||
|         ", " | ||||
|     ) | ||||
|     assert len(answer) == 5 | ||||
|     print(answer) | ||||
|     return {"type": "names", "value": answer} | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| from rest_framework.generics import get_object_or_404 | ||||
| 
 | ||||
| from pitch_deck_generator.decks.models import ( | ||||
|     PdfToPPTXStorage, | ||||
|     PitchDeck, | ||||
|     Question, | ||||
|     QuestionAnswer, | ||||
|  | @ -40,11 +41,17 @@ class HintSerializer(serializers.Serializer): | |||
| class PresentationAnswerSerializer(serializers.Serializer): | ||||
|     slug = serializers.CharField() | ||||
|     answer = serializers.JSONField() | ||||
|     photos = serializers.ListSerializer(child=serializers.ImageField()) | ||||
| 
 | ||||
| 
 | ||||
| class PitchDeckSlidePresentationSerializer(serializers.Serializer): | ||||
|     slide = serializers.IntegerField() | ||||
|     data = PresentationAnswerSerializer(many=True) | ||||
| 
 | ||||
| 
 | ||||
| class PitchDeckPresentationSerializer(serializers.Serializer): | ||||
|     slide = serializers.IntegerField() | ||||
|     data = PresentationAnswerSerializer(many=True) | ||||
|     deck = BasePitchDeckSerializer() | ||||
|     slide = PitchDeckSlidePresentationSerializer(many=True) | ||||
| 
 | ||||
| 
 | ||||
| class QuestionSerializer(serializers.ModelSerializer): | ||||
|  | @ -100,7 +107,7 @@ class Meta: | |||
|         } | ||||
| 
 | ||||
|     def validate(self, data): | ||||
|         answer = data["answer"] | ||||
|         answer = data["answer"] if "answer" in data else None | ||||
|         question = get_object_or_404( | ||||
|             Question, id=self.context["view"].kwargs["question_id"] | ||||
|         ) | ||||
|  | @ -168,7 +175,7 @@ def validate(self, data): | |||
|                 if answer: | ||||
|                     raise serializers.ValidationError("Answer should be blank") | ||||
|                 for key, value in data.items(): | ||||
|                     if isinstance(value, InMemoryUploadedFile): | ||||
|                     if isinstance(value, (TemporaryUploadedFile, InMemoryUploadedFile)): | ||||
|                         if "_" not in key: | ||||
|                             raise serializers.ValidationError( | ||||
|                                 "You should use file_num for file keys" | ||||
|  | @ -198,7 +205,7 @@ def validate(self, data): | |||
|                 len_f = 0 | ||||
| 
 | ||||
|                 for key, value in data.items(): | ||||
|                     if isinstance(value, TemporaryUploadedFile): | ||||
|                     if isinstance(value, (TemporaryUploadedFile, InMemoryUploadedFile)): | ||||
|                         if "_" not in key: | ||||
|                             raise serializers.ValidationError( | ||||
|                                 "You should use file_num for file keys" | ||||
|  | @ -247,19 +254,27 @@ def create(self, validated_data): | |||
|         q = QuestionAnswer.objects.get_or_create( | ||||
|             deck_id=validated_data["deck_id"], question_id=validated_data["question_id"] | ||||
|         )[0] | ||||
|         q.answer = validated_data["answer"] | ||||
|         q.answer = validated_data["answer"] if "answer" in validated_data else {} | ||||
|         q.save() | ||||
| 
 | ||||
|         s = [ | ||||
|             key | ||||
|             for key, val in validated_data.items() | ||||
|             if isinstance(val, TemporaryUploadedFile) and key != "file" | ||||
|             if isinstance(val, (TemporaryUploadedFile, InMemoryUploadedFile)) | ||||
|             and key != "file" | ||||
|         ] | ||||
|         if "file" in validated_data: | ||||
|             QuestionAnswerPhoto.objects.create(answer=q, file=validated_data["file"]) | ||||
|         elif s: | ||||
|         if s: | ||||
|             s.sort(key=lambda x: int(x.split("_")[1])) | ||||
|             for key in s: | ||||
|                 QuestionAnswerPhoto.objects.create(answer=q, file=validated_data[key]) | ||||
| 
 | ||||
|         return q | ||||
| 
 | ||||
| 
 | ||||
| class PdfToPPTXSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = PdfToPPTXStorage | ||||
|         fields = ["pdf", "pptx"] | ||||
|         extra_kwargs = {"pptx": {"read_only": True}} | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| from django.urls import path | ||||
| 
 | ||||
| from pitch_deck_generator.decks.api.views import ( | ||||
|     ConvertPdfToPPTXApiView, | ||||
|     CreateQuestionAnswerApiView, | ||||
|     GetDeckPresentationDataApiView, | ||||
|     GetDeckQuestionApiView, | ||||
|  | @ -15,6 +16,7 @@ | |||
| urlpatterns = [ | ||||
|     path("", ListDecksApiView.as_view()), | ||||
|     path("<int:id>", RetrievePitchApiView.as_view()), | ||||
|     path("pdf-to-pptx", ConvertPdfToPPTXApiView.as_view()), | ||||
|     path( | ||||
|         "question/<int:deck_id>/presentation", GetDeckPresentationDataApiView.as_view() | ||||
|     ), | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
|     AnswerSerializer, | ||||
|     BasePitchDeckSerializer, | ||||
|     HintSerializer, | ||||
|     PdfToPPTXSerializer, | ||||
|     PitchDeckPresentationSerializer, | ||||
|     PitchDeckSerializer, | ||||
|     QuestionSerializer, | ||||
|  | @ -74,6 +75,7 @@ def get(self, request, *args, **kwargs): | |||
| 
 | ||||
| 
 | ||||
| class GetDeckPresentationDataApiView(generics.GenericAPIView): | ||||
|     queryset = PitchDeck.objects.none() | ||||
|     serializer_class = PitchDeckPresentationSerializer | ||||
| 
 | ||||
|     structure = { | ||||
|  | @ -84,7 +86,7 @@ class GetDeckPresentationDataApiView(generics.GenericAPIView): | |||
|         5: ["market_values", "users"], | ||||
|         6: ["competitors", "competitors_strength", "competitors_low", "advantages"], | ||||
|         7: ["money", "finance_model"], | ||||
|         8: ["how_much_investments", "financial_indicators"], | ||||
|         8: ["how_much_investments", "financial_indicators", "users_metrics"], | ||||
|         9: ["your_role", "your_teammates", "past_investors"], | ||||
|         10: ["how_much_investments", "time_to_spend", "investments_sold"], | ||||
|         11: ["company_value", "future_value", "time_to_spend"], | ||||
|  | @ -97,14 +99,27 @@ def get(self, request, *args, **kwargs): | |||
|             PitchDeck, | ||||
|             id=self.kwargs["deck_id"], | ||||
|         ) | ||||
|         re_data = { | ||||
|             "deck": BasePitchDeckSerializer().to_representation(deck), | ||||
|         } | ||||
|         resp = [] | ||||
|         data = deck.questions | ||||
|         for slide, tags in self.structure.items(): | ||||
|             slide_data = {"slide": slide, "data": []} | ||||
|             for tag in tags: | ||||
|                 slide_data["data"].append( | ||||
|                     {"slug": tag, "answer": data[tag] if tag in data else {}} | ||||
|                 ) | ||||
|             resp.append(slide_data) | ||||
|                 b_data = {} | ||||
|                 if tag in data: | ||||
|                     if "answer" in data[tag]: | ||||
|                         b_data["answer"] = data[tag]["answer"] | ||||
|                     if "photos" in data[tag]: | ||||
|                         b_data["photos"] = data[tag]["photos"] | ||||
| 
 | ||||
|         return Response(resp) | ||||
|                 slide_data["data"].append({"slug": tag, **b_data}) | ||||
|             resp.append(slide_data) | ||||
|         re_data["slides"] = resp | ||||
|         return Response(re_data) | ||||
| 
 | ||||
| 
 | ||||
| class ConvertPdfToPPTXApiView(generics.CreateAPIView): | ||||
|     serializer_class = PdfToPPTXSerializer | ||||
|     parser_classes = [FormParser, MultiPartParser] | ||||
|  |  | |||
|  | @ -0,0 +1,53 @@ | |||
| # Generated by Django 4.2.4 on 2023-08-26 21:07 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ("decks", "0007_alter_question_type_alter_questionanswer_deck"), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name="PdfToPPTXStorage", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.BigAutoField( | ||||
|                         auto_created=True, | ||||
|                         primary_key=True, | ||||
|                         serialize=False, | ||||
|                         verbose_name="ID", | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("pdf", models.FileField(upload_to="pdf/")), | ||||
|                 ("pptx", models.FileField(blank=True, null=True, upload_to="pptx/")), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="question", | ||||
|             name="type", | ||||
|             field=models.CharField( | ||||
|                 choices=[ | ||||
|                     ("text", "Text"), | ||||
|                     ("number", "Number"), | ||||
|                     ("text_array", "text array"), | ||||
|                     ("range", "Range"), | ||||
|                     ("multiple_range", "multiple range"), | ||||
|                     ("select", "Select"), | ||||
|                     ("link", "Link"), | ||||
|                     ("date", "Date"), | ||||
|                     ("multiple_date_description", "multiple date description"), | ||||
|                     ("photo", "Photo"), | ||||
|                     ("multiple_photo", "multiple photo"), | ||||
|                     ("photo_description", "photo description"), | ||||
|                     ("multiple_link_description", "multiple link description"), | ||||
|                     ("multiple_photo_description", "multiple photo description"), | ||||
|                     ("multiple_links", "multiple links"), | ||||
|                 ], | ||||
|                 max_length=26, | ||||
|             ), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -85,3 +85,8 @@ class QuestionAnswerPhoto(models.Model): | |||
|         "QuestionAnswer", related_name="photos", on_delete=models.CASCADE | ||||
|     ) | ||||
|     file = models.ImageField(upload_to="uploads/") | ||||
| 
 | ||||
| 
 | ||||
| class PdfToPPTXStorage(models.Model): | ||||
|     pdf = models.FileField(upload_to="pdf/") | ||||
|     pptx = models.FileField(upload_to="pptx/", blank=True, null=True) | ||||
|  |  | |||
|  | @ -1,8 +1,18 @@ | |||
| from io import BytesIO | ||||
| 
 | ||||
| import requests | ||||
| from django.core.files import File | ||||
| from django.db.models.signals import post_save, pre_save | ||||
| from django.dispatch import receiver | ||||
| 
 | ||||
| from pitch_deck_generator.decks.models import PitchDeck, QuestionAnswer | ||||
| from pitch_deck_generator.decks.models import ( | ||||
|     PdfToPPTXStorage, | ||||
|     PitchDeck, | ||||
|     QuestionAnswer, | ||||
| ) | ||||
| from pitch_deck_generator.decks.tasks import ( | ||||
|     ML_HOST, | ||||
|     create_images_mokups, | ||||
|     generate_numeric_values, | ||||
|     qenerate_answer_qr, | ||||
|     run_pitch_deck_calculation, | ||||
|  | @ -23,9 +33,14 @@ def question_answer_create(sender, instance: QuestionAnswer, created, **kwargs): | |||
|             generate_numeric_values.apply_async( | ||||
|                 kwargs={"pk": instance.deck.pk}, countdown=1 | ||||
|             ) | ||||
|         elif instance.question.inner_tag == "names": | ||||
|             instance.deck.name = instance.answer | ||||
|             instance.deck.save() | ||||
|         elif instance.question.inner_tag in ["finance_model"]: | ||||
|             qenerate_answer_qr.apply_async(kwargs={"pk": instance.pk}, countdown=1) | ||||
|         save_answer_to_deck.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|             qenerate_answer_qr.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|         elif instance.question.inner_tag == "images": | ||||
|             create_images_mokups.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|         save_answer_to_deck.apply_async(kwargs={"pk": instance.pk}, countdown=10) | ||||
| 
 | ||||
| 
 | ||||
| @receiver(pre_save, sender=QuestionAnswer) | ||||
|  | @ -36,5 +51,23 @@ def question_answer_update(sender, instance: QuestionAnswer, **kwargs): | |||
|                 kwargs={"pk": instance.deck.pk}, countdown=1 | ||||
|             ) | ||||
|         elif instance.question.inner_tag in ["finance_model"]: | ||||
|             qenerate_answer_qr.apply_async(kwargs={"pk": instance.pk}, countdown=1) | ||||
|         save_answer_to_deck.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|             qenerate_answer_qr.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|         elif instance.question.inner_tag == "images": | ||||
|             create_images_mokups.apply_async(kwargs={"pk": instance.pk}, countdown=5) | ||||
|         save_answer_to_deck.apply_async(kwargs={"pk": instance.pk}, countdown=10) | ||||
| 
 | ||||
| 
 | ||||
| @receiver(post_save, sender=PdfToPPTXStorage) | ||||
| def pdt_to_pptx_convert(sender, instance: PdfToPPTXStorage, created, **kwargs): | ||||
|     if created: | ||||
|         with open(instance.pdf.path, "rb") as f: | ||||
|             r = requests.post(ML_HOST + "convert-to-pptx", files={"in_file": f}).json() | ||||
|             data = requests.get(ML_HOST + r["file"][1:]).content | ||||
|             instance.pptx.save( | ||||
|                 instance.pdf.path.split("/")[-1].replace("pdf", "pptx"), | ||||
|                 File( | ||||
|                     BytesIO(data), | ||||
|                     instance.pdf.path.split("/")[-1].replace("pdf", "pptx"), | ||||
|                 ), | ||||
|             ) | ||||
|         instance.save() | ||||
|  |  | |||
|  | @ -13,8 +13,9 @@ | |||
|     QuestionAnswerPhoto, | ||||
|     QuestionDeckHint, | ||||
| ) | ||||
| from pitch_deck_generator.decks.services import get_image_mokeup | ||||
| 
 | ||||
| ML_HOST = "https://purple-kids-drive.loca.lt/" | ||||
| ML_HOST = "https://forty-eggs-slide.loca.lt/" | ||||
| 
 | ||||
| data_types = { | ||||
|     "names": ("text", 1), | ||||
|  | @ -166,3 +167,13 @@ def qenerate_answer_qr(pk: int): | |||
|             answer=qa, | ||||
|             file=File(tmp, name="qr.png"), | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| @shared_task | ||||
| def create_images_mokups(pk: int): | ||||
|     qa = QuestionAnswer.objects.get(pk=pk) | ||||
|     for image in qa.photos.all(): | ||||
|         mokup_path = get_image_mokeup(image.file.path) | ||||
|         with open(mokup_path, "rb") as f: | ||||
|             image.file = File(f, name="mokup.png") | ||||
|             image.save() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user