diff --git a/ai/migrations/0011_aimodel_aishow_ai_model.py b/ai/migrations/0011_aimodel_aishow_ai_model.py new file mode 100644 index 0000000..aa01c4c --- /dev/null +++ b/ai/migrations/0011_aimodel_aishow_ai_model.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.7 on 2026-07-03 14:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0010_aishow_target'), + ] + + operations = [ + migrations.CreateModel( + name='AiModel', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('config_name', models.CharField(max_length=50)), + ('base_url', models.CharField(max_length=100)), + ('api_key', models.CharField(max_length=50)), + ('model_name', models.CharField(default='', max_length=500)), + ], + options={ + 'verbose_name': '模型配置', + 'verbose_name_plural': '模型配置', + 'db_table': 'ai_model_configs', + }, + ), + migrations.AddField( + model_name='aishow', + name='ai_model', + field=models.CharField(default='', max_length=100), + ), + ] diff --git a/ai/migrations/0012_aimodel_delete_flag.py b/ai/migrations/0012_aimodel_delete_flag.py new file mode 100644 index 0000000..e9a761e --- /dev/null +++ b/ai/migrations/0012_aimodel_delete_flag.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2026-07-04 13:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0011_aimodel_aishow_ai_model'), + ] + + operations = [ + migrations.AddField( + model_name='aimodel', + name='delete_flag', + field=models.BooleanField(default=False), + ), + ] diff --git a/ai/migrations/0013_aimodel_current_used.py b/ai/migrations/0013_aimodel_current_used.py new file mode 100644 index 0000000..3fe8e96 --- /dev/null +++ b/ai/migrations/0013_aimodel_current_used.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2026-07-04 13:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0012_aimodel_delete_flag'), + ] + + operations = [ + migrations.AddField( + model_name='aimodel', + name='current_used', + field=models.BooleanField(default=False), + ), + ] diff --git a/ai/migrations/0014_airuntime.py b/ai/migrations/0014_airuntime.py new file mode 100644 index 0000000..88be67e --- /dev/null +++ b/ai/migrations/0014_airuntime.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.7 on 2026-07-04 13:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0013_aimodel_current_used'), + ] + + operations = [ + migrations.CreateModel( + name='AiRuntime', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('runtime_key', models.CharField(max_length=50)), + ('runtime_val', models.TextField()), + ], + options={ + 'verbose_name': 'AI运行时', + 'verbose_name_plural': 'AI运行时', + 'db_table': 'ai_runtime', + }, + ), + ] diff --git a/ai/migrations/0015_moviehotinfo.py b/ai/migrations/0015_moviehotinfo.py new file mode 100644 index 0000000..d7d9cf3 --- /dev/null +++ b/ai/migrations/0015_moviehotinfo.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.7 on 2026-07-04 15:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0014_airuntime'), + ] + + operations = [ + migrations.CreateModel( + name='MovieHotInfo', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('movie_zz_code', models.CharField(max_length=50)), + ('movie_name', models.CharField(max_length=50)), + ('req_date', models.DateField()), + ('api_response', models.TextField()), + ('wants', models.CharField(max_length=50)), + ('hots', models.CharField(max_length=50)), + ('day_1', models.CharField(max_length=50)), + ('day_2', models.CharField(max_length=50)), + ('day_3', models.CharField(max_length=50)), + ('day_4', models.CharField(max_length=50)), + ('day_5', models.CharField(max_length=50)), + ], + options={ + 'verbose_name': 'AI影片热度信息', + 'verbose_name_plural': 'AI影片热度信息', + 'db_table': 'ai_movie_hot', + }, + ), + ] diff --git a/ai/migrations/0016_remove_moviehotinfo_day_1_remove_moviehotinfo_day_2_and_more.py b/ai/migrations/0016_remove_moviehotinfo_day_1_remove_moviehotinfo_day_2_and_more.py new file mode 100644 index 0000000..df1deef --- /dev/null +++ b/ai/migrations/0016_remove_moviehotinfo_day_1_remove_moviehotinfo_day_2_and_more.py @@ -0,0 +1,83 @@ +# Generated by Django 4.2.7 on 2026-07-04 15:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0015_moviehotinfo'), + ] + + operations = [ + migrations.RemoveField( + model_name='moviehotinfo', + name='day_1', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_2', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_3', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_4', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_5', + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_1_date', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_1_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_2_date', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_2_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_3_date', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_3_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_4_date', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_4_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_5_date', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_5_val', + field=models.CharField(default='', max_length=50), + ), + ] diff --git a/ai/migrations/0017_remove_moviehotinfo_day_1_date_and_more.py b/ai/migrations/0017_remove_moviehotinfo_day_1_date_and_more.py new file mode 100644 index 0000000..09e0891 --- /dev/null +++ b/ai/migrations/0017_remove_moviehotinfo_day_1_date_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 4.2.7 on 2026-07-04 15:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0016_remove_moviehotinfo_day_1_remove_moviehotinfo_day_2_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='moviehotinfo', + name='day_1_date', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_1_val', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_2_date', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_2_val', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_3_date', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_3_val', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_4_date', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_4_val', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_5_date', + ), + migrations.RemoveField( + model_name='moviehotinfo', + name='day_5_val', + ), + migrations.AddField( + model_name='moviehotinfo', + name='day_data', + field=models.TextField(default=''), + ), + ] diff --git a/ai/migrations/0018_moviehotinfo_day1_val_moviehotinfo_day2_val_and_more.py b/ai/migrations/0018_moviehotinfo_day1_val_moviehotinfo_day2_val_and_more.py new file mode 100644 index 0000000..ab54822 --- /dev/null +++ b/ai/migrations/0018_moviehotinfo_day1_val_moviehotinfo_day2_val_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.7 on 2026-07-04 22:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0017_remove_moviehotinfo_day_1_date_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='moviehotinfo', + name='day1_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day2_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day3_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day4_val', + field=models.CharField(default='', max_length=50), + ), + migrations.AddField( + model_name='moviehotinfo', + name='day5_val', + field=models.CharField(default='', max_length=50), + ), + ] diff --git a/ai/migrations/0019_moviehotinfo_title_alter_moviehotinfo_hots_and_more.py b/ai/migrations/0019_moviehotinfo_title_alter_moviehotinfo_hots_and_more.py new file mode 100644 index 0000000..b9dcfe9 --- /dev/null +++ b/ai/migrations/0019_moviehotinfo_title_alter_moviehotinfo_hots_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.7 on 2026-07-04 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0018_moviehotinfo_day1_val_moviehotinfo_day2_val_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='moviehotinfo', + name='title', + field=models.CharField(default='', max_length=1000), + ), + migrations.AlterField( + model_name='moviehotinfo', + name='hots', + field=models.CharField(default='', max_length=50), + ), + migrations.AlterField( + model_name='moviehotinfo', + name='wants', + field=models.CharField(default='', max_length=50), + ), + ] diff --git a/ai/migrations/0020_moviehotinfo_show_type.py b/ai/migrations/0020_moviehotinfo_show_type.py new file mode 100644 index 0000000..a5e6fec --- /dev/null +++ b/ai/migrations/0020_moviehotinfo_show_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2026-07-05 09:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ai', '0019_moviehotinfo_title_alter_moviehotinfo_hots_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='moviehotinfo', + name='show_type', + field=models.CharField(default='', max_length=500), + ), + ] diff --git a/ai/models.py b/ai/models.py index 61572dd..6331ef2 100644 --- a/ai/models.py +++ b/ai/models.py @@ -3,6 +3,11 @@ from django.db import models # Create your models here. class TestCinema(models.Model): + """ + 用于存储测试影院基本信息、数据库配置信息 + user_config用于模拟用户设置情况 + is_active设置为False时不会自动生成影院的排片,需要手动生成 + """ id = models.AutoField(primary_key=True) name = models.CharField(max_length=50) zz_code = models.CharField(max_length=50) @@ -20,6 +25,10 @@ class TestCinema(models.Model): class PromptTemplate(models.Model): + """ + 分板块的提示词模板,用于动态调整提示词的内容和结构。 + ShowPromptVersion为当前提示词版本 + """ id = models.AutoField(primary_key=True) prompt_type = models.CharField(max_length=50) prompt_key = models.CharField(max_length=50) @@ -36,24 +45,33 @@ class PromptTemplate(models.Model): class AiShow(models.Model): + """ + 用于存储ai生成的结果已经影院真实排片 + temp_date: 模板数据日期 + target: 目标日期已排影片数据,csv格式 + show: 目标日期ai生成的排片数据,csv格式 + sales: ai排片预期收入 + prompt + """ id = models.AutoField(primary_key=True) cinema = models.CharField(max_length=50) zz_code = models.CharField(max_length=50) - show_date = models.DateField() - is_ai_show = models.BooleanField(default=True) - template = models.TextField(default='') - temp_date = models.CharField(max_length=50, default='') - target = models.TextField(default='') - show = models.TextField() - sales = models.CharField(max_length=50) - prompt = models.TextField() - result = models.TextField() - message = models.TextField() - created_at = models.DateTimeField(auto_now_add=True) + show_date = models.DateField() # 排片目标日期 + is_ai_show = models.BooleanField(default=True) # 当is_ai_show为True时,记录为ai生成的排片,否则为影院真实排片 + ai_model = models.CharField(default='', max_length=100) # ai生成排片时使用的模型 + template = models.TextField(default='') # ai生成排片时的模板日期排片数据,csv格式 + temp_date = models.CharField(max_length=50, default='') # 模板数据日期 + target = models.TextField(default='') # 目标日期已排影片数据,csv格式 + show = models.TextField() # 目标日期ai生成的排片数据,csv格式 + sales = models.CharField(max_length=50) # ai排片预期收入 + prompt = models.TextField() # 生成的用户提示词部分 + result = models.TextField() # ai返回结果中最终输出部分 + message = models.TextField() # ai返回结果的完整信息 + created_at = models.DateTimeField(auto_now_add=True) # ai返回结果的时间 updated_at = models.DateTimeField(auto_now=True) - take_times = models.IntegerField(default=0) - take_tokens = models.CharField(default='', max_length=2000) - prompt_version = models.CharField(max_length=50, default='') + take_times = models.IntegerField(default=0) # 从提示词发给ai到取得返回结果的时间,单位秒 + take_tokens = models.CharField(default='', max_length=2000) # ai返回结果中token消耗部分的信息 + prompt_version = models.CharField(max_length=50, default='') # 使用的提示词版本 def __str__(self): return self.cinema @@ -69,7 +87,7 @@ class CinemaHall(models.Model): cinema_code = models.CharField(max_length=50) hall_id = models.CharField(max_length=50) hall_name = models.CharField(max_length=50) - hall_type = models.CharField(max_length=500 , default='') + hall_type = models.CharField(max_length=500, default='') def __str__(self): return self.hall_name @@ -77,4 +95,62 @@ class CinemaHall(models.Model): class Meta: verbose_name = '影厅数据' verbose_name_plural = '影厅数据' - db_table = 'ai_show_hall' \ No newline at end of file + db_table = 'ai_show_hall' + + +class AiModel(models.Model): + id = models.AutoField(primary_key=True) + config_name = models.CharField(max_length=50) + base_url = models.CharField(max_length=100) + api_key = models.CharField(max_length=50) + model_name = models.CharField(max_length=500, default='') + delete_flag = models.BooleanField(default=False) + current_used = models.BooleanField(default=False) + + def __str__(self): + return self.config_name + + class Meta: + verbose_name = '模型配置' + verbose_name_plural = '模型配置' + db_table = 'ai_model_configs' + + +class AiRuntime(models.Model): + id = models.AutoField(primary_key=True) + runtime_key = models.CharField(max_length=50) + runtime_val = models.TextField() + + def __str__(self): + return self.runtime_key + + class Meta: + verbose_name = 'AI运行时' + verbose_name_plural = 'AI运行时' + db_table = 'ai_runtime' + + +class MovieHotInfo(models.Model): + id = models.AutoField(primary_key=True) + movie_zz_code = models.CharField(max_length=50) + movie_name = models.CharField(max_length=50) + req_date = models.DateField() + api_response = models.TextField() + show_type = models.CharField(max_length=500, default='') + title = models.CharField(max_length=1000, default='') + wants = models.CharField(max_length=50, default='') + hots = models.CharField(max_length=50, default='') + day1_val = models.CharField(max_length=50, default='') + day2_val = models.CharField(max_length=50, default='') + day3_val = models.CharField(max_length=50, default='') + day4_val = models.CharField(max_length=50, default='') + day5_val = models.CharField(max_length=50, default='') + day_data = models.TextField(default='') + + def __str__(self): + return self.movie_name + + class Meta: + verbose_name = 'AI影片热度信息' + verbose_name_plural = 'AI影片热度信息' + db_table = 'ai_movie_hot' diff --git a/ai/serializers.py b/ai/serializers.py index 23f6a33..ed0b8ae 100644 --- a/ai/serializers.py +++ b/ai/serializers.py @@ -25,3 +25,21 @@ class CinemaHallSerializer(serializers.ModelSerializer): class Meta: model = CinemaHall fields = '__all__' + + +class AiModelSerializer(serializers.ModelSerializer): + class Meta: + model = AiModel + fields = '__all__' + + +class AiRuntimeSerializer(serializers.ModelSerializer): + class Meta: + model = AiRuntime + fields = '__all__' + + +class MovieHotInfoSerializer(serializers.ModelSerializer): + class Meta: + model = MovieHotInfo + fields = '__all__' \ No newline at end of file diff --git a/ai/utils/basic_func.py b/ai/utils/basic_func.py index d75fd12..4608920 100644 --- a/ai/utils/basic_func.py +++ b/ai/utils/basic_func.py @@ -49,10 +49,11 @@ def format_show_data(cinema_code: str, show_date: str, data_left: dict, data_rig for h in hall: hall_dict = {'hall_name': h.hall_name, 'hall_id': h.hall_id, 'ai': [], 'real': []} for ai in data_left: - if ai['影厅id'] == str(h.hall_id): + if dict_get(ai, ['影厅id', '影厅 id']) == str(h.hall_id): + print(ai) hall_dict['ai'].append({ 'movie_name': ai['影片别名'], - 'movie_id': ai['本地影片id'], + 'movie_id': dict_get(ai, ['本地影片id' '本地影片 id']), 'language': ai['语言'], 'show_date': ai['放映日期'], 'start': ai['开始时间'], @@ -61,10 +62,10 @@ def format_show_data(cinema_code: str, show_date: str, data_left: dict, data_rig 'duration': ai['场间'] }) for real in data_right: - if real['影厅id'] == str(h.hall_id): + if dict_get(real, ['影厅id', '影厅 id']) == str(h.hall_id): hall_dict['real'].append({ 'movie_name': real['影片别名'], - 'movie_id': real['本地影片id'], + 'movie_id': dict_get(real, ['本地影片id' '本地影片 id']), 'language': real['语言'], 'show_date': real['放映日期'], 'start': real['开始时间'], @@ -143,6 +144,7 @@ def get_hall_type(cinema_code): hall_dict[h.hall_name] = h.hall_type return hall_dict + # 获取排片占比 def count_show(show_data): print('count_show', show_data) @@ -158,12 +160,35 @@ def count_show(show_data): show_count.append(f"{show}:{str(round(count / len(show_data) * 100, 2))}%,共排{str(count)}场") return '\n'.join(show_count) + # 处理黑名单 -def get_black_list(cinema_code:str): +def get_black_list(cinema_code: str): black_list = json.loads(TestCinema.objects.filter(zz_code=cinema_code).first().user_config)['movie_black_list'] black_list_bool = False - black_list_str = '\' \'' + black_list_str = "' '" if len(black_list) > 0: black_list_bool = True black_list_str = ','.join(['%s'] * len(black_list)) - return black_list_bool, black_list_str, black_list \ No newline at end of file + return black_list_bool, black_list_str, black_list + + +# 获取运行时参数 +def get_runtime_val(key: str): + return AiRuntime.objects.filter(runtime_key=key).first().runtime_val + +# 获取当前开启的模型 +def get_current_model(): + return AiModel.objects.filter(current_used=True).first() + + +# 通不过不同key获取数据 +def dict_get(_dict: dict, keys: list): + for k in keys: + if k in _dict.keys(): + return _dict[k] + return False + + +# AI返回数据清洗 +def data_cleansing(data:str): + return data.replace(',', ',').replace('影厅 id', '影厅id').replace('本地影片 id', '本地影片id').replace(', ', ',') diff --git a/ai/utils/datetime_format.py b/ai/utils/datetime_format.py index 0fb5681..51cee4f 100644 --- a/ai/utils/datetime_format.py +++ b/ai/utils/datetime_format.py @@ -14,6 +14,19 @@ def get_data_datetime(show_date): target_end = datetime.datetime.strftime(target_end_datetime_obj, '%Y-%m-%d %H:%M:%S') return history_start, history_end, target_start, target_end +def get_data_datetime_template(show_date, template_date): + show_date_obj = datetime.datetime.strptime(show_date, '%Y-%m-%d') + target_start_datetime_obj = show_date_obj + datetime.timedelta(hours=6) + target_end_datetime_obj = show_date_obj + datetime.timedelta(hours=29, minutes=59, seconds=59) + template_date_obj = datetime.datetime.strptime(template_date, '%Y-%m-%d') + data_start_datetime_obj = template_date_obj + datetime.timedelta(hours=6) + data_end_datetime_obj = template_date_obj + datetime.timedelta(hours=29, minutes=59, seconds=59) + history_start = datetime.datetime.strftime(data_start_datetime_obj, '%Y-%m-%d %H:%M:%S') + history_end = datetime.datetime.strftime(data_end_datetime_obj, '%Y-%m-%d %H:%M:%S') + target_start = datetime.datetime.strftime(target_start_datetime_obj, '%Y-%m-%d %H:%M:%S') + target_end = datetime.datetime.strftime(target_end_datetime_obj, '%Y-%m-%d %H:%M:%S') + return history_start, history_end, target_start, target_end + def get_date_type(show_date, template_date): show_date_obj = datetime.datetime.strptime(show_date, '%Y-%m-%d') diff --git a/ai/utils/movie_data.py b/ai/utils/movie_data.py index d7598d6..fdfcc83 100644 --- a/ai/utils/movie_data.py +++ b/ai/utils/movie_data.py @@ -1,45 +1,91 @@ from hashlib import md5 import requests +from ai.models import * import json -from datetime import datetime as dt +from django.db.models import Q +import datetime # 从BI的日常影片数据接口中获取信息 class MovieData: def __init__(self): self.salt = '53b22eda5938706a34fd9d44f097bec2' - self.ts = str(int(dt.timestamp(dt.now()))) + self.ts = str(int(datetime.datetime.timestamp(datetime.datetime.now()))) self.url = 'https://nation.bi.piao51.cn/nationDataApi/restful/boxoffice/nation/movieSupportData' - def get_data(self, movie_code): - params = { - 'movieCode': movie_code, - 'timeStamp': self.ts, - 'sign': self.sign(movie_code) - } - req = requests.get(self.url, params=params) - # print(json.dumps(req.json(), indent=4)) - r = '' - req = req.json() - if (req['success'] is True) and req['data']['movieInfo'] != '': - info = req['data']['movieInfo'] - hot = f"《{info['movie_name']}》 - 想看:{info['wish_num']},热度:小红书{info['xiaohongshu']}、视频号{info['shipinhao']}、抖音{info['douyin']}、微博{info['weibo']};" - # print(hot) - daily = req['data']['movieDailyBox'] - sales = [f"{daily[n]['businessDate']} {daily[n]['boxRate']}%" for n in range(0, 6)] - # print(sales) - pre_sales = [f"{daily[n]['businessDate']} {daily[n]['boxRate']}%" for n in range(7, len(daily))] - # print(pre_sales) - r = f"\t{hot}过去六天的票房占比:{'、'.join(sales)};今天到当前时间的票房占比:{daily[6]['boxRate']}%;未来7天的预售票房占比:{'、'.join(pre_sales)};" - print('MovieData.get_data', movie_code, r) - return r + # 从BI获取热度数据 + def get_data_from_bi(self, movie_code, show_type): + # 基本变量定义 + today_str = datetime.datetime.today().strftime('%Y-%m-%d') # 当天日期字符串 + movie_name = '' # 返回的影片名称,以BI返回为准 + hot_title = '' # CSV表头 + hot_data = '' # 数据部分 + # 如果本地有则读取本地数据 + today_obj = datetime.datetime.strptime(today_str, '%Y-%m-%d') + if not MovieHotInfo.objects.filter(Q(movie_zz_code=movie_code) & Q(req_date=today_obj)).exists(): + params = { + 'movieCode': movie_code, + 'timeStamp': self.ts, + 'sign': self.sign(movie_code) + } + req = requests.get(self.url, params=params) + # print(json.dumps(req.json(), indent=4)) + + req = req.json() + + if (req['success'] is True) and req['data']['movieInfo'] != '': + # 基础数据 + info = req['data']['movieInfo'] + movie_name = info['movie_name'] + daily = req['data']['movieDailyBox'] + if MovieHotInfo.objects.filter(Q(movie_name=movie_name) & Q(req_date=today_str)).exists(): + return '', '', '' + # 生成标题 + hot_info_list = ['影片名称', '想看数量', '鼎新热度指数'] + hot_info_list.extend([f"{daily[n]['businessDate']}(销售)" for n in range(4, 6)]) + hot_info_list.append(f"{daily[6]['businessDate']}(今日)") + hot_info_list.extend([f"{daily[n]['businessDate']}(预售)" for n in range(7, 9)]) + hot_title = f"{','.join(hot_info_list)}" + # 生成热度指数 + hot = int((self.to_number(info['xiaohongshu']) + self.to_number(info['shipinhao']) + self.to_number( + info['douyin']) + self.to_number(info['weibo'])) / 10000) + # 准备数据部分 + data_list = [] + for n in range(4, 6): + data_list.append({'data': f"{daily[n]['businessDate']}(销售)", 'value': f"{daily[n]['boxRate']}%"}) + data_list.append({'data': f"{daily[6]['businessDate']}(今日)", 'value': f"{daily[6]['boxRate']}%"}) + for n in range(7, 9): + data_list.append({'date': f"{daily[n]['businessDate']}(预售)", 'value': f"{daily[n]['boxRate']}%"}) + # 写入数据 + MovieHotInfo.objects.create( + movie_zz_code=movie_code, + movie_name=info['movie_name'], + req_date=today_str, + api_response=req, + show_type=show_type, + title=hot_title, + wants=info['wish_num'], + hots=hot, + day1_val=daily[4]['boxRate'], + day2_val=daily[5]['boxRate'], + day3_val=daily[6]['boxRate'], + day4_val=daily[7]['boxRate'], + day5_val=daily[8]['boxRate'], + day_data=json.dumps(data_list), + ) + return movie_name, hot_title, hot_data def sign(self, code): _pre = md5((self.salt + f"movieCode={code}&timeStamp={self.ts}").encode()).hexdigest() _sign = md5((_pre + self.salt).encode()).hexdigest() return _sign - -if __name__ == '__main__': - md = MovieData() - md.get_data('082000852026') + @staticmethod + def to_number(num_str): + if num_str == '--': + return 0 + if '万' in num_str: + return int(float(num_str.replace('万', '')) * 10000) + if '亿' in num_str: + return int(float(num_str.replace('亿', '')) * 100000000) + return 0 diff --git a/ai/utils/show_database.py b/ai/utils/show_database.py index 81d28e7..749e292 100644 --- a/ai/utils/show_database.py +++ b/ai/utils/show_database.py @@ -43,32 +43,6 @@ class GetData: } return self.format_to_csv(show_data, show_data_mapping) - # 获取指定范围的影片销售数据 - def get_sell_data(self, start_datetime, end_datetime): - # print(self.cur.mogrify(GET_SELL_DATA, (start_datetime, end_datetime))) - self.cur.execute(GET_SELL_DATA, (start_datetime, end_datetime, start_datetime, end_datetime)) - sell_data = self.cur.fetchall() - sell_data_mapping = { - 'movie_name': '影片别名', - 'movie_id': '本地影片id', - 'show_date': '放映日期', - 'start_time': '开始时间', - 'hall_name': '影厅别名', - 'hall_id': '影厅id', - 'total_count': '场次观影人数', - 'total_income': '场次收入', - 'seat_num': '影厅座位数', - 'average_price': '平均价', - 'rate': '上座率(单位%)', - } - return self.format_to_csv(sell_data, sell_data_mapping) - - # 获取指定范围的影片销售数据 - def get_total_income(self, start_datetime, end_datetime): - print(self.cur.mogrify(GET_TOTAL_INCOME, (start_datetime, end_datetime,start_datetime, end_datetime))) - self.cur.execute(GET_TOTAL_INCOME, (start_datetime, end_datetime, start_datetime, end_datetime)) - return self.cur.fetchone()['total_income'] - # 获取指定日期的可放映影片 def get_available_movie(self, date): start_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59, @@ -77,7 +51,7 @@ class GetData: self.cur.execute(GET_AVAILABLE_MOVIE, (start_datetime, end_datetime)) available_movie = self.cur.fetchall() available_movie_list = [ - f"\t《{m['cinema_movie_alias']}》 - 影片id:{m['cinema_movie_id']},影片时长:{m['cinema_movie_time']},语言:{m['language']},制式:{m['media_type']}, 最早排片开始时间:{m['cinema_movie_start_datetime']}, 最晚排片截止时间:{m['cinema_movie_end_datetime']};" + f"\t《{m['cinema_movie_alias']}》 - 影片id:{m['cinema_movie_id']};" for m in available_movie] return '\n'.join(available_movie_list) @@ -111,6 +85,86 @@ class GetData: new_list = [f"《{m['cinema_movie_alias']}》" for m in new] return new_list + # 为获取影片热度功能提供影院数据 + def get_movie_info(self, date, show_type='all'): + start_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59, + seconds=59) + end_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=6) + bl_bool, bl_str, bl_list = get_black_list(self.zz_code) + if bl_bool: + if show_type == "new": + sql = GET_NEW_MOVIE_INFO + bl_str + GET_MOVIE_INFO_END + self.cur.execute(sql, (start_datetime, end_datetime, *bl_list)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime, *bl_list))) + elif show_type == "old": + sql = GET_OLD_MOVIE_INFO + bl_str + GET_MOVIE_INFO_END + self.cur.execute(sql, (end_datetime, start_datetime, *bl_list)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime, *bl_list))) + else: + sql = GET_MOVIE_INFO + bl_str + GET_MOVIE_INFO_END + self.cur.execute(sql, (start_datetime, end_datetime, *bl_list)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime, *bl_list))) + + else: + if show_type == "new": + sql = GET_NEW_MOVIE_INFO + bl_str + GET_MOVIE_INFO_END + self.cur.execute(sql, (start_datetime, end_datetime)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime))) + elif show_type == "old": + sql = GET_OLD_MOVIE_INFO + bl_str + GET_MOVIE_INFO_END + self.cur.execute(sql, (end_datetime, start_datetime)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime))) + else: + sql = GET_MOVIE_INFO + bl_str + GET_MOVIE_INFO + self.cur.execute(sql, (start_datetime, end_datetime)) + print(self.cur.mogrify(sql, (start_datetime, end_datetime))) + return self.cur.fetchall() + + # 按照场次获取销售数据 + def get_sell_data(self, start_datetime, end_datetime): + # print(self.cur.mogrify(GET_SELL_DATA, (start_datetime, end_datetime))) + self.cur.execute(GET_SELL_DATA, (start_datetime, end_datetime, start_datetime, end_datetime)) + sell_data = self.cur.fetchall() + sell_data_mapping = { + 'movie_name': '影片别名', + 'movie_id': '本地影片id', + 'show_date': '放映日期', + 'start_time': '开始时间', + 'hall_name': '影厅别名', + 'hall_id': '影厅id', + 'total_count': '场次观影人数', + 'total_income': '场次收入', + 'seat_num': '影厅座位数', + 'average_price': '平均价', + 'rate': '上座率(单位%)', + } + return self.format_to_csv(sell_data, sell_data_mapping) + + # 按照影片获取销售数据 + def get_sell_data_by_movie(self, start_datetime, end_datetime): + self.cur.execute(GET_SELL_DATA_BY_MOVIE, (start_datetime, end_datetime, start_datetime, end_datetime)) + sell_data = self.cur.fetchall() + print('get_sell_data_by_movie', sell_data) + sell_data_mapping = { + 'movie_alias_name': '影片别名', + 'movie_id': '本地影片id', + 'total_count': '观影总人数', + 'average_show_count': '场均人次', + 'average_seat_rate': '平均上座率(单位%)', + 'total_income': '票房总收入', + 'average_ticket_price': '平均票价', + 'average_show_ticket_price': '场均收入', + 'show_count': '总场次数', + 'show_rate': '排片占比(单位%)', + } + return self.format_to_csv(sell_data, sell_data_mapping) + + # 获取指定范围的影片销售总额 + def get_total_income(self, start_datetime, end_datetime): + print(self.cur.mogrify(GET_TOTAL_INCOME, (start_datetime, end_datetime, start_datetime, end_datetime))) + self.cur.execute(GET_TOTAL_INCOME, (start_datetime, end_datetime, start_datetime, end_datetime)) + return self.cur.fetchone()['total_income'] + # 获取影厅支持的制式 def get_hall_media_type(self): self.cur.execute(GET_HALL_MEDIA_TYPE) @@ -149,19 +203,3 @@ class GetData: def get_media_type(self): self.cur.execute(GET_HALL_MEDIA_TYPE) - - def get_movie_info(self, date): - start_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59, - seconds=59) - end_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=6) - bl_bool, bl_str, bl_list = get_black_list(self.zz_code) - if bl_bool: - sql = GET_MOVIE_INFO_1 + bl_str + GET_MOVIE_INFO_2 - self.cur.execute(sql, (start_datetime, end_datetime, *bl_list)) - print(self.cur.mogrify(sql, (start_datetime, end_datetime, *bl_list))) - else: - sql = GET_MOVIE_INFO_1 + bl_str + GET_MOVIE_INFO_2 - self.cur.execute(sql, (start_datetime, end_datetime)) - print(self.cur.mogrify(sql, (start_datetime, end_datetime))) - self.cur.execute(sql, (start_datetime, end_datetime, *bl_list)) - return self.cur.fetchall() diff --git a/ai/utils/show_func.py b/ai/utils/show_func.py index 607bf05..a8006e1 100644 --- a/ai/utils/show_func.py +++ b/ai/utils/show_func.py @@ -4,10 +4,10 @@ from ai.models import * from ai.utils.show_database import GetData from ai.utils.datetime_format import * from ai.utils.movie_data import * +from ai.utils.basic_func import * def get_cinema_show_result_func(_zz_code, _show_date): - print("get_cinema_show_result_func") cinema = TestCinema.objects.filter(zz_code=_zz_code).first() start = datetime.datetime.strftime(datetime.datetime.strptime(_show_date, '%Y-%m-%d') + datetime.timedelta(hours=6), '%Y-%m-%d %H:%M:%S') @@ -53,6 +53,7 @@ def get_cinema_show_result_func(_zz_code, _show_date): print(e) return show, income + # 获取模板日期排片数据 def get_template_show(cinema, show_date): history_start, history_end, target_start, target_end = get_data_datetime(show_date) @@ -63,10 +64,28 @@ def get_template_show(cinema, show_date): temp_end = datetime.datetime.strftime(temp_date_obj + datetime.timedelta(hours=29, minutes=59, seconds=59), '%Y-%m-%d %H:%M:%S') temp_show = data.get_show_data(temp_start, temp_end) - print('get_template_show', temp_date, temp_show) return temp_show, temp_date +# 获取模板日期排片数据, 从昨天开始找一天有排片的数据做为模板,如果没有则返回None +def get_template_show_tomorrow(cinema): + i = -1 + temp_show = False + while temp_show is False or i >= -3: + tomorrow = datetime.datetime.combine(datetime.date.today() + datetime.timedelta(days=i), datetime.time()) + tomorrow_start = datetime.datetime.strftime(tomorrow + datetime.timedelta(hours=6), '%Y-%m-%d %H:%M:%S') + tomorrow_end = datetime.datetime.strftime(tomorrow + datetime.timedelta(hours=29, minutes=59, seconds=59), + '%Y-%m-%d %H:%M:%S') + tomorrow_str = datetime.datetime.strftime(tomorrow, '%Y-%m-%d') + data = GetData(cinema) + temp_show = data.get_show_data(tomorrow_start, tomorrow_end) + if temp_show is not False: + return temp_show, tomorrow_str + else: + i -= 1 + return temp_show, '' + + # 获取目标日期排片数据 def get_target_show(cinema, show_date): history_start, history_end, target_start, target_end = get_data_datetime(show_date) @@ -76,15 +95,40 @@ def get_target_show(cinema, show_date): target_date_show = '影厅别名,影厅id,影片别名,本地影片id,语言,放映日期,开始时间,结束时间,片长,场间' return target_date_show -def get_all_movie_hot_info(cinema, show_date): +# 从BI接口获取影片热度信息 +def get_movie_hot_info_from_bi(cinema, show_date): data = GetData(cinema) - movie = data.get_movie_info(show_date) + movie_old = data.get_movie_info(show_date, 'old') + movie_new = data.get_movie_info(show_date, 'new') + movie_data = MovieData() - movie_info_list = [] - for m in movie: - print(m) - if (hot:=movie_data.get_data(m['cinema_movie_num'])) != '': - # print('hot', hot) - movie_info_list.append(hot) - # print(movie_info_list) - return movie_info_list + movie_name_list = [] + for m in movie_old: + name, title, hot = movie_data.get_data_from_bi(m['cinema_movie_num'], 'old') + print('name, title, hot', name, title, hot) + if hot != '' and name not in movie_name_list: + movie_name_list.append(name) + for m in movie_new: + name, title, hot = movie_data.get_data_from_bi(m['cinema_movie_num'], 'new') + print('name, title, hot', name, title, hot) + if hot != '' and name not in movie_name_list: + movie_name_list.append(name) + return True + +# 从本地数据库中获取影片热度信息 +def get_all_movie_hot_info(req_date: str = 'today', show_type:str='old', get_num: int = 10): + if req_date == 'today': + date_sr = datetime.datetime.today().strftime('%Y-%m-%d') + else: + date_sr = req_date + hot_order_day = get_runtime_val('hot_order_day') + hot_data = MovieHotInfo.objects.filter(Q(req_date=date_sr) & Q(show_type=show_type)).order_by(hot_order_day).all()[:get_num] + if hot_data: + title = f"\t{hot_data[0].title}" + data_list = [title] + else: + return [] + for hot in hot_data: + data_list.append( + f"\t《{hot.movie_name}》,{hot.wants},{hot.hots},{','.join([d['value'] for d in json.loads(hot.day_data)])}") + return data_list \ No newline at end of file diff --git a/ai/utils/show_process.py b/ai/utils/show_process.py index 840cb2c..6523157 100644 --- a/ai/utils/show_process.py +++ b/ai/utils/show_process.py @@ -1,8 +1,8 @@ import json - from ai.models import * from ai.utils.show_prompt import * from ai.utils.show_func import * +from ai.utils.basic_func import * import datetime from django_redis import get_redis_connection from django.db.models import Q @@ -10,7 +10,7 @@ import re # ai排片主流程 -def show_main_process(zz_code=None, day_delta=3): +def show_main_process(zz_code=None, day_delta=None): # 查看状态 redis_conn = get_redis_connection() redis_key = f'ai_show{datetime.date.today().strftime("%Y%m%d")}' @@ -22,18 +22,26 @@ def show_main_process(zz_code=None, day_delta=3): test_cinema_list = TestCinema.objects.filter(is_active=True).all() else: test_cinema_list = TestCinema.objects.filter(Q(zz_code=zz_code) & Q(is_active=True)).all() - day_delta = 3 if day_delta is None else int(day_delta) + day_delta = int(get_runtime_val('day_delta')) if day_delta is None else int(day_delta) # 获取提示词版本 prompt_ver = PromptTemplate.objects.filter( Q(del_flag=False) & Q(prompt_type='version') & Q(prompt_key='ShowPromptVersion')).first().prompt_val # 开始处理提示词 # 生成目标日期 show_date = datetime.date.strftime(datetime.date.today() + datetime.timedelta(days=day_delta), '%Y-%m-%d') + # 获取当前模型 + model = AiModel.objects.filter(current_used=True).first() + + # 按照影院生成排片 for cinema in test_cinema_list: print(cinema.name) show_ai = ShowAI(cinema, show_date) - template_show, template_date = get_template_show(cinema, show_date) - target_show = get_target_show(cinema, show_date) + # template_show, template_date = get_template_show(cinema, show_date) + template_show, template_date = get_template_show_tomorrow(cinema) + if get_runtime_val('real_target') == 1: + target_show = get_target_show(cinema, show_date) + else: + target_show = '影厅别名,影厅id,影片别名,本地影片id,语言,放映日期,开始时间,结束时间,片长,场间' prompt = show_ai.general_prompt() start = datetime.datetime.now() # 开始计时 result, message, tokens = show_ai.get_show_result_ai() @@ -42,12 +50,17 @@ def show_main_process(zz_code=None, day_delta=3): print('result:', result) print('message:', message) print('tokens:', tokens) + # 获取排片数据 print('result:', result) - result_obj = json.loads(result.strip()) - print('result_obj:', result_obj, type(result_obj)) + if model.config_name in ['lmstudio_qwen3.6_35b','company_qwen3.5_35b']: + result_obj = json.loads(result.replace('```json', '').replace('}\n```', '}').strip()) + else: + result_obj = json.loads(result.strip()) + # 预测排片数据 _show = result_obj['show'].replace('```', '').replace('csv', '').strip() + # 预测销售数据 _sales = str(result_obj['income']).strip() @@ -58,10 +71,11 @@ def show_main_process(zz_code=None, day_delta=3): zz_code=cinema.zz_code, show_date=show_date, is_ai_show=True, + ai_model=model.config_name, template=template_show, temp_date=template_date, target=target_show, - show=_show, + show=data_cleansing(_show), sales=_sales, prompt=prompt, result=result, diff --git a/ai/utils/show_prompt.py b/ai/utils/show_prompt.py index 85b7078..47c8be5 100644 --- a/ai/utils/show_prompt.py +++ b/ai/utils/show_prompt.py @@ -1,8 +1,9 @@ from django.db.models import Q from ai.models import PromptTemplate from ai.utils.show_database import GetData -from ai.utils.datetime_format import (get_data_datetime, get_date_type) -from ai.utils.show_func import get_all_movie_hot_info +from ai.utils.datetime_format import * +from ai.utils.show_func import * +from ai.utils.basic_func import * import datetime import random import json @@ -12,25 +13,128 @@ from openai import OpenAI class ShowAI: def __init__(self, cinema_info, show_date): self.cinema = cinema_info - # self.cinema_name = cinema_info.name - # self.cinema_zz_code = cinema_info.zz_code - # self.cinema_db_config = json.loads(cinema_info.db_config) self.user_config = json.loads(cinema_info.user_config) self.show_date = show_date self.prompt = "" + self.model = get_current_model() + self.ai_model_name = self.model.model_name self.client = OpenAI( - api_key='sk-b7993bf4c8844e79bf84f08f07ff86d9', - base_url='https://api.deepseek.com', + api_key=self.model.api_key, + base_url=self.model.base_url, ) # 生成提示词 + # def general_prompt(self): + # data = GetData(self.cinema) + # # 准备各种日期时间 + # history_start, history_end, target_start, target_end = get_data_datetime(self.show_date) + # temp = datetime.datetime.strftime(data.get_template_date(history_start, history_end), '%Y-%m-%d') + # history_date, target_date, target_date_is_holiday, template_date, template_date_is_holiday = get_date_type( + # self.show_date, temp) + # # print(history_start, history_end, target_start, target_end, history_date, show_date, template_date) + # + # # 开始处理提示词 + # prompt_list = [] + # # 处理排片数据 + # pre_data = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='PreData')).first().prompt_val # 获取提示词模板 + # pre_data = pre_data.replace('{history_show}', data.get_show_data(history_start, history_end)) # 处理排片数据 + # pre_data = pre_data.replace('{history_sales}', data.get_sell_data(history_start, history_end)) # 处理销售数据 + # pre_data = pre_data.replace('{target_already_show}', + # '\t无' if data.get_show_data(target_start, + # target_end) is False else data.get_show_data( + # target_start, target_end)) # 处理目标日期已排场次数据 + # pre_data = pre_data.replace('{reference_date}', history_date) # 处理参考日期 + # pre_data = pre_data.replace('{template_date}', template_date) # 处理模板日期 + # pre_data = pre_data.replace('{target_date}', target_date) # 处理目标日期 + # prompt_list.append(pre_data) + # + # # 处理角色说明 + # role = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Role')).first().prompt_val # 获取提示词模板 + # role = role.replace('{template_date}', template_date) + # prompt_list.append(role) + # + # # 处理影片和影厅信息 + # movie_hall = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='MovieHall')).first().prompt_val # 获取提示词模板 + # movie_hall = movie_hall.replace('{available_movie}', data.get_available_movie(self.show_date)) # 处理上映影片 + # movie_hall = movie_hall.replace('{handle_movie}', data.get_handle_movie(self.show_date)) # 处理可排影片 + # movie_hall = movie_hall.replace('{hall_info}', data.get_hall_media_type()) # 处理影厅信息 + # free_hall = self.user_config['holiday_date_free_hall'] if target_date_is_holiday else self.user_config[ + # 'holiday_date_free_hall'] + # movie_hall = movie_hall.replace('{free_hall}', + # '\t无' if len(free_hall) == 0 else '\n'.join( + # f'\t{h};' for h in free_hall)) # 处理不可排片影厅 + # prompt_list.append(movie_hall) + # + # # 影片热度和排片占比 + # movie_hot = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Hot')).first().prompt_val # 获取提示词模板 + # movie_hot = movie_hot.replace('{movie_hot}', '\n'.join(get_all_movie_hot_info(self.cinema, self.show_date))) + # prompt_list.append(movie_hot) + # + # # 处理具体要求 + # rules = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Rules')).first().prompt_val # 获取提示词模板 + # rules = rules.replace('{template_date}', template_date) # 处理模板日期 + # # 获取新片 + # # new_movie_str_list = [] + # # new_movie = data.get_new_movie(self.show_date) + # # for new in new_movie: + # # new_movie_str_list.append( + # # f"\t增加{new}的场次,建议排{str(random.randint(2, 4))}场,可根据影片热度和口碑适当增加;") + # # rules = rules.replace('{new_movie}', + # # '\t无新片上映' if len(new_movie_str_list) == 0 else '\n'.join( + # # new_movie_str_list)) # 处理新片数据 + # prompt_list.append(rules) + # + # # 处理基本规则 + # basic = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Basic')).first().prompt_val # 获取提示词模板 + # start = self.user_config['holiday_date_start'] if target_date_is_holiday else self.user_config[ + # 'work_date_start'] + # end = self.user_config['holiday_date_end'] if target_date_is_holiday else self.user_config['work_date_end'] + # basic = basic.replace('{start}', start) + # basic = basic.replace('{end}', end) + # prompt_list.append(basic) + # + # # 处理增减场次规则 + # add_reduce = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='AddReduce')).first().prompt_val # 获取提示词模板 + # prompt_list.append(add_reduce) + # + # # 处理黄金时段 + # prime_time = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='PrimeTime')).first().prompt_val # 获取提示词模板 + # prompt_list.append(prime_time) + # + # # 处理暑期档 + # summer = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Summer')).first().prompt_val # 获取提示词模板 + # prompt_list.append(summer) + # + # # 处理输出目标 + # output = PromptTemplate.objects.filter( + # Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Output')).first().prompt_val # 获取提示词模板 + # prompt_list.append(output) + # + # # 打印提示词 + # self.prompt = '\n'.join(prompt_list) + # print(self.prompt) + # # 关闭数据库连接 + # data.exit() + # return self.prompt + + # 生成提示词(对应V1.2版本) def general_prompt(self): data = GetData(self.cinema) # 准备各种日期时间 - history_start, history_end, target_start, target_end = get_data_datetime(self.show_date) - temp = datetime.datetime.strftime(data.get_template_date(history_start, history_end), '%Y-%m-%d') + template_show, template_date = get_template_show_tomorrow(self.cinema) + history_start, history_end, target_start, target_end = get_data_datetime_template(self.show_date, template_date) + # temp = datetime.datetime.strftime(data.get_template_date(history_start, history_end), '%Y-%m-%d') history_date, target_date, target_date_is_holiday, template_date, template_date_is_holiday = get_date_type( - self.show_date, temp) + self.show_date, template_date) # print(history_start, history_end, target_start, target_end, history_date, show_date, template_date) # 开始处理提示词 @@ -38,13 +142,25 @@ class ShowAI: # 处理排片数据 pre_data = PromptTemplate.objects.filter( Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='PreData')).first().prompt_val # 获取提示词模板 - pre_data = pre_data.replace('{history_show}', data.get_show_data(history_start, history_end)) # 处理排片数据 - pre_data = pre_data.replace('{history_sales}', data.get_sell_data(history_start, history_end)) # 处理销售数据 - pre_data = pre_data.replace('{target_already_show}', - '\t无' if data.get_show_data(target_start, - target_end) is False else data.get_show_data( - target_start, target_end)) # 处理目标日期已排场次数据 - pre_data = pre_data.replace('{reference_date}', history_date) # 处理参考日期 + pre_data = pre_data.replace('{template_show}', template_show) # 处理排片数据 + pre_data = pre_data.replace('{history_sales}', + data.get_sell_data_by_movie(history_start, history_end)) # 处理销售数据 + get_movie_hot_info_from_bi(self.cinema, self.show_date) # 获取全部影片的热度信息 + movie_data_old = get_all_movie_hot_info('today', 'old') + movie_data_new = get_all_movie_hot_info('today', 'new') + + pre_data = pre_data.replace('{hot_movie_old}', + '\n'.join(movie_data_old) if len(movie_data_old) == 0 else '\t无') + pre_data = pre_data.replace('{hot_movie_new}', + '\n'.join(movie_data_new) if len(movie_data_new) == 0 else '\t无') + if get_runtime_val('real_target') == '1': + pre_data = pre_data.replace('{target_already_show}', + '\t无' if data.get_show_data(target_start, + target_end) is False else data.get_show_data( + target_start, target_end)) # 处理目标日期已排场次数据 + else: + pre_data = pre_data.replace('{target_already_show}', '无') # 处理目标日期已排场次数据 + # pre_data = pre_data.replace('{reference_date}', history_date) # 处理参考日期 pre_data = pre_data.replace('{template_date}', template_date) # 处理模板日期 pre_data = pre_data.replace('{target_date}', target_date) # 处理目标日期 prompt_list.append(pre_data) @@ -58,7 +174,7 @@ class ShowAI: # 处理影片和影厅信息 movie_hall = PromptTemplate.objects.filter( Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='MovieHall')).first().prompt_val # 获取提示词模板 - movie_hall = movie_hall.replace('{available_movie}', data.get_available_movie(self.show_date)) # 处理上映影片 + # movie_hall = movie_hall.replace('{available_movie}', data.get_available_movie(self.show_date)) # 处理上映影片 movie_hall = movie_hall.replace('{handle_movie}', data.get_handle_movie(self.show_date)) # 处理可排影片 movie_hall = movie_hall.replace('{hall_info}', data.get_hall_media_type()) # 处理影厅信息 free_hall = self.user_config['holiday_date_free_hall'] if target_date_is_holiday else self.user_config[ @@ -68,25 +184,11 @@ class ShowAI: f'\t{h};' for h in free_hall)) # 处理不可排片影厅 prompt_list.append(movie_hall) - # 影片热度和排片占比 - movie_hot = PromptTemplate.objects.filter( - Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Hot')).first().prompt_val # 获取提示词模板 - movie_hot = movie_hot.replace('{movie_hot}', '\n'.join(get_all_movie_hot_info(self.cinema, self.show_date))) - prompt_list.append(movie_hot) - # 处理具体要求 rules = PromptTemplate.objects.filter( Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Rules')).first().prompt_val # 获取提示词模板 rules = rules.replace('{template_date}', template_date) # 处理模板日期 - # 获取新片 - # new_movie_str_list = [] - # new_movie = data.get_new_movie(self.show_date) - # for new in new_movie: - # new_movie_str_list.append( - # f"\t增加{new}的场次,建议排{str(random.randint(2, 4))}场,可根据影片热度和口碑适当增加;") - # rules = rules.replace('{new_movie}', - # '\t无新片上映' if len(new_movie_str_list) == 0 else '\n'.join( - # new_movie_str_list)) # 处理新片数据 + rules = rules.replace('{available_movie}', data.get_available_movie(self.show_date)) # 处理上映影片 prompt_list.append(rules) # 处理基本规则 @@ -121,7 +223,7 @@ class ShowAI: # 打印提示词 self.prompt = '\n'.join(prompt_list) - print(self.prompt) + print('开始打印提示词\n', self.prompt) # 关闭数据库连接 data.exit() return self.prompt @@ -130,20 +232,18 @@ class ShowAI: def get_show_result_ai(self): # 生成提示词 # self.general_prompt() - # 与DeepSeek开始对话 - print("与DeepSeek开始对话") + # 与AI开始对话 + print(f"与{self.ai_model_name}开始对话") response = self.client.chat.completions.create( - model='deepseek-v4-pro', + model=self.ai_model_name, messages=[ - {'role': 'system', 'content': 'You are a helpful assistant'}, + {'role': 'system', 'content': '你是中文AI助手。要求:1.所有思考过程必须使用中文。2.所有tool reasoning必须使用中文。3.所有规划必须使用中文。4.即使用户使用英文提问,也必须中文推理。5.最终输出使用中文。'}, {'role': 'user', 'content': self.prompt}, ], stream=False, reasoning_effort="high", extra_body={"thinking": {"type": "enabled"}} ) - # print(dict(response)) - # print(response.choices[0].message.content) res_json = response.model_dump_json() res_obj = json.loads(res_json) print(res_obj) diff --git a/ai/utils/sql.py b/ai/utils/sql.py index 15601d2..27b7984 100644 --- a/ai/utils/sql.py +++ b/ai/utils/sql.py @@ -91,6 +91,50 @@ GROUP BY md.cinema_movie_show_id, CAST(md.cinema_movie_show_id AS CHAR) ORDER BY md.show_date, md.hall_id, md.start_time; """ +GET_SELL_DATA_BY_MOVIE = """ +SELECT sd.movie_alias_name movie_alias_name, + sd.movie_id movie_id, + SUM(sd.total_count) total_count, + ROUND(IF(SUM(sd.total_count) = 0, 0.0, SUM(sd.total_count) / COUNT(sd.cinema_movie_show_id)), + 2) average_show_count, + ROUND(IF(SUM(sd.total_count) = 0, 0.0, SUM(sd.total_count) / SUM(sd.total_seat)) * 100, + 2) average_seat_rate, + SUM(sd.total_income) total_income, + ROUND(IF(SUM(sd.total_income) = 0, 0.0, SUM(sd.total_income) / SUM(sd.total_count)), + 2) average_ticket_price, + ROUND(IF(SUM(sd.total_income) = 0, 0.0, SUM(sd.total_income) / COUNT(sd.cinema_movie_show_id)), + 2) average_show_ticket_price, + COUNT(sd.cinema_movie_show_id) show_count, + ROUND(IF(COUNT(sd.cinema_movie_show_id) = 0, 0.0, + COUNT(sd.cinema_movie_show_id) / (SELECT COUNT(ms.cinema_movie_show_id) + FROM cinema_movie_show ms + WHERE ms.cinema_movie_show_joinflg = 0 + AND ms.cinema_movie_show_start_time >= % s + AND ms.cinema_movie_show_start_time <= % s + AND ms.cinema_del_flag = 1 + AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1))) * + 100, + 2) show_rate +FROM (SELECT CAST(ms.cinema_movie_show_id AS CHAR) cinema_movie_show_id, + mi.cinema_movie_id movie_id, + mi.cinema_movie_cn_name cinema_movie_name, + mi.cinema_movie_alias movie_alias_name, + SUM(IF(sl.cinema_ticket_status <> 3, 1, 0)) total_count, + SUM(IF(sl.cinema_ticket_status <> 3, sl.cinema_ticket_income, 0)) total_income, + ms.cinema_hall_seat_num total_seat + FROM cinema_movie_show ms + LEFT JOIN cinema_sell_log sl ON ms.cinema_movie_show_id = sl.cinema_movie_show_id + JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id + JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id + WHERE ms.cinema_movie_show_joinflg = 0 + AND ms.cinema_movie_show_start_time >= % s + AND ms.cinema_movie_show_start_time <= % s + AND ms.cinema_del_flag = 1 + AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1) + GROUP BY ms.cinema_movie_show_id, CAST(ms.cinema_movie_show_id AS CHAR)) sd +GROUP BY sd.movie_id ORDER BY average_show_ticket_price DESC; +""" + # 获取指定日期的可放映影片 GET_AVAILABLE_MOVIE = """ SELECT cmi.cinema_movie_alias, @@ -202,7 +246,7 @@ FROM ( # 获取指定日期的可用于排片的影片 -GET_MOVIE_INFO_1 = """ +GET_MOVIE_INFO = """ SELECT cmi.cinema_movie_cn_name, cmi.cinema_movie_num FROM cinema_movie_info cmi @@ -210,6 +254,23 @@ WHERE cmi.cinema_movie_start_datetime <= %s AND cmi.cinema_movie_end_datetime > %s AND cmi.cinema_movie_num NOT IN (""" -GET_MOVIE_INFO_2 = """) +GET_MOVIE_INFO_END = """) GROUP BY cmi.cinema_movie_num; -""" \ No newline at end of file +""" + +# 获取指定日期的可用于排片的影片 +GET_NEW_MOVIE_INFO = """ +SELECT cmi.cinema_movie_cn_name, + cmi.cinema_movie_num +FROM cinema_movie_info cmi +WHERE cmi.cinema_movie_start_datetime <= %s + AND cmi.cinema_movie_start_datetime > %s + AND cmi.cinema_movie_num NOT IN (""" + +GET_OLD_MOVIE_INFO = """ +SELECT cmi.cinema_movie_cn_name, + cmi.cinema_movie_num +FROM cinema_movie_info cmi +WHERE cmi.cinema_movie_start_datetime <= %s +AND cmi.cinema_movie_end_datetime > %s + AND cmi.cinema_movie_num NOT IN (""" diff --git a/ai/views.py b/ai/views.py index ee409de..4ba0c8b 100644 --- a/ai/views.py +++ b/ai/views.py @@ -76,7 +76,7 @@ def report(request): last_real_data = AiShow.objects.filter(Q(is_ai_show=False) & Q(zz_code=zz_code) & Q(show_date=show_date)).order_by('-id').first() - ai_dict = csv_to_obj(last_ai_data.show) + ai_dict = csv_to_obj(data_cleansing(last_ai_data.show)) real_dict = csv_to_obj(last_real_data.show) temp_dict = csv_to_obj(last_ai_data.template) target_dict = csv_to_obj(last_ai_data.target) @@ -92,6 +92,10 @@ def report(request): # 生成时间 create_time = datetime.datetime.strftime((last_ai_data.created_at + datetime.timedelta(hours=8)), '%Y-%m-%d %H:%M:%S') + if last_ai_data.ai_model in ['lmstudio_qwen3.6_35b', 'company_qwen3.5_35b']: + think = json.loads(last_ai_data.message)['choices'][0]['message']['reasoning'] + else: + think = json.loads(last_ai_data.message)['choices'][0]['message']['reasoning_content'] # 返回 result_dict = { 'status': 'success', @@ -113,6 +117,8 @@ def report(request): 'ai_count': ai_count, 'real_count': real_count, 'result': last_ai_data.result, + 'think': think, + 'ai_model': last_ai_data.ai_model, 'create_time': create_time } }