Merge branch 'master' of https://e.coding.net/ctcdevteam/cma_search
This commit is contained in:
commit
3509d73487
|
@ -16,5 +16,6 @@ class ExamFilter(filters.FilterSet):
|
||||||
fields = {
|
fields = {
|
||||||
'close_time': ['exact', 'gte', 'lte'],
|
'close_time': ['exact', 'gte', 'lte'],
|
||||||
'paper': ['exact'],
|
'paper': ['exact'],
|
||||||
'code': ['exact']
|
'code': ['exact'],
|
||||||
|
'is_open': ['exact']
|
||||||
}
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.0.5 on 2022-11-22 02:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('exam', '0004_auto_20221114_1108'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='exam',
|
||||||
|
name='is_open',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='是否公开'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -87,6 +87,7 @@ class Exam(CommonAModel):
|
||||||
proctor_phone = models.CharField('监考人联系方式', max_length=100, null=True, blank=True)
|
proctor_phone = models.CharField('监考人联系方式', max_length=100, null=True, blank=True)
|
||||||
chance = models.IntegerField('考试机会', default=3)
|
chance = models.IntegerField('考试机会', default=3)
|
||||||
paper = models.ForeignKey(Paper, verbose_name='使用的试卷', on_delete=models.CASCADE, null=True, blank=True)
|
paper = models.ForeignKey(Paper, verbose_name='使用的试卷', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
is_open = models.BooleanField('是否公开', default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -136,7 +136,7 @@ class ExamRecordDetailSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def get_questions_(self, obj):
|
def get_questions_(self, obj):
|
||||||
objs = AnswerDetail.objects.select_related('question').filter(
|
objs = AnswerDetail.objects.select_related('question').filter(
|
||||||
examrecord=obj).order_by('question__type')
|
examrecord=obj).order_by('id')
|
||||||
return AnswerDetailSerializer(instance=objs, many=True).data
|
return AnswerDetailSerializer(instance=objs, many=True).data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
# Generated by Django 3.0.5 on 2022-11-16 06:46
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('vod', '0006_viewrecord_total_seconds'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='video',
|
||||||
|
name='views',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='总观看次数'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='video',
|
||||||
|
name='viewsp',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='总观看人数'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='viewrecord',
|
||||||
|
name='video',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='viewrecord_video', to='vod.Video', verbose_name='点播视频'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='viewrecord',
|
||||||
|
name='views',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='总观看次数'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ViewItem',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||||
|
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||||
|
('current', models.IntegerField(default=0, verbose_name='当前观看进度(秒)')),
|
||||||
|
('total_seconds', models.PositiveIntegerField(default=0, verbose_name='本次总观看秒数')),
|
||||||
|
('create_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='viewitem_user', to=settings.AUTH_USER_MODEL, verbose_name='观看人')),
|
||||||
|
('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='viewitem_video', to='vod.Video', verbose_name='点播视频')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='View2',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||||
|
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||||
|
('current', models.IntegerField(default=0, verbose_name='当前观看进度(秒)')),
|
||||||
|
('views', models.IntegerField(default=0, verbose_name='总观看次数')),
|
||||||
|
('total_seconds', models.PositiveIntegerField(default=0, verbose_name='总观看秒数')),
|
||||||
|
('is_completed', models.BooleanField(default=False)),
|
||||||
|
('create_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='view2_user', to=settings.AUTH_USER_MODEL, verbose_name='观看人')),
|
||||||
|
('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='view2_video', to='vod.Video', verbose_name='点播视频')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='View1',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||||
|
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||||
|
('views', models.IntegerField(default=0, verbose_name='总观看次数')),
|
||||||
|
('viewsp', models.IntegerField(default=0, verbose_name='总观看人数')),
|
||||||
|
('video', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='view1_video', to='vod.Video')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.0.5 on 2022-11-16 06:53
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('vod', '0007_auto_20221116_1446'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='view2',
|
||||||
|
unique_together={('create_by', 'video')},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.0.5 on 2022-11-17 01:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('system', '0022_delete_historicaldict'),
|
||||||
|
('vod', '0008_auto_20221116_1453'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='video',
|
||||||
|
name='category_big',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='video_catebig', to='system.Dict', verbose_name='视频大类'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='video',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='video_cate', to='system.Dict', verbose_name='视频分类'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,14 +7,15 @@ from apps.system.models import User, CommonAModel, Dict
|
||||||
class Video(CommonAModel):
|
class Video(CommonAModel):
|
||||||
|
|
||||||
name = models.CharField(verbose_name='视频名称', max_length=100)
|
name = models.CharField(verbose_name='视频名称', max_length=100)
|
||||||
category = models.ForeignKey(Dict, verbose_name='视频分类', on_delete=models.DO_NOTHING)
|
category_big = models.ForeignKey(Dict, verbose_name='视频大类', on_delete=models.SET_NULL, null=True, blank=True, related_name='video_catebig')
|
||||||
|
category = models.ForeignKey(Dict, verbose_name='视频分类', on_delete=models.SET_NULL, null=True, blank=True, related_name='video_cate')
|
||||||
description = models.TextField(verbose_name='视频描述', default='')
|
description = models.TextField(verbose_name='视频描述', default='')
|
||||||
fileid = models.CharField(verbose_name='云点播视频id', unique=True, max_length=200)
|
fileid = models.CharField(verbose_name='云点播视频id', unique=True, max_length=200)
|
||||||
mediaurl = models.CharField(verbose_name='视频地址', max_length=200)
|
mediaurl = models.CharField(verbose_name='视频地址', max_length=200)
|
||||||
coverurl = models.CharField(verbose_name='封面地址', max_length=200)
|
coverurl = models.CharField(verbose_name='封面地址', max_length=200)
|
||||||
duration = models.IntegerField(verbose_name='时长(秒)', default=0)
|
duration = models.IntegerField(verbose_name='时长(秒)', default=0)
|
||||||
views = models.IntegerField(verbose_name='观看次数', default=0)
|
views = models.IntegerField(verbose_name='总观看次数', default=0)
|
||||||
viewsp = models.IntegerField(verbose_name='观看人数', default=0)
|
viewsp = models.IntegerField(verbose_name='总观看人数', default=0)
|
||||||
sort_str = models.CharField('排序字符', max_length=10, null=True, blank=True)
|
sort_str = models.CharField('排序字符', max_length=10, null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -25,16 +26,51 @@ class Video(CommonAModel):
|
||||||
|
|
||||||
|
|
||||||
class ViewRecord(BaseModel):
|
class ViewRecord(BaseModel):
|
||||||
|
"""
|
||||||
# 观看记录
|
某视频-某人的观看记录统计
|
||||||
|
"""
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人', related_name='viewrecord_user')
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人', related_name='viewrecord_user')
|
||||||
views = models.IntegerField(verbose_name='观看次数', default=0)
|
|
||||||
current = models.IntegerField(verbose_name='当前观看进度(秒)', default=0)
|
current = models.IntegerField(verbose_name='当前观看进度(秒)', default=0)
|
||||||
video = models.ForeignKey(Video, verbose_name='点播视频', on_delete=models.CASCADE, related_name='record_video')
|
video = models.ForeignKey(Video, verbose_name='点播视频', on_delete=models.CASCADE, related_name='viewrecord_video')
|
||||||
|
views = models.IntegerField(verbose_name='总观看次数', default=0)
|
||||||
total_seconds = models.PositiveIntegerField(verbose_name='总观看秒数', default=0)
|
total_seconds = models.PositiveIntegerField(verbose_name='总观看秒数', default=0)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '点播观看记录'
|
verbose_name = '点播观看记录'
|
||||||
verbose_name_plural = verbose_name
|
verbose_name_plural = verbose_name
|
||||||
|
|
||||||
|
|
||||||
|
class ViewItem(BaseModel):
|
||||||
|
"""
|
||||||
|
单次观看记录
|
||||||
|
"""
|
||||||
|
create_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人', related_name='viewitem_user')
|
||||||
|
video = models.ForeignKey(Video, verbose_name='点播视频', on_delete=models.CASCADE, related_name='viewitem_video')
|
||||||
|
current = models.IntegerField(verbose_name='当前观看进度(秒)', default=0)
|
||||||
|
total_seconds = models.PositiveIntegerField(verbose_name='本次总观看秒数', default=0)
|
||||||
|
|
||||||
|
|
||||||
|
class View1(BaseModel):
|
||||||
|
"""
|
||||||
|
视频播放统计
|
||||||
|
"""
|
||||||
|
video = models.OneToOneField(Video, on_delete=models.CASCADE, related_name='view1_video')
|
||||||
|
views = models.IntegerField(verbose_name='总观看次数', default=0)
|
||||||
|
viewsp = models.IntegerField(verbose_name='总观看人数', default=0)
|
||||||
|
|
||||||
|
|
||||||
|
class View2(BaseModel):
|
||||||
|
"""
|
||||||
|
某视频-某人的观看记录统计
|
||||||
|
"""
|
||||||
|
create_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人', related_name='view2_user')
|
||||||
|
current = models.IntegerField(verbose_name='当前观看进度(秒)', default=0)
|
||||||
|
video = models.ForeignKey(Video, verbose_name='点播视频', on_delete=models.CASCADE, related_name='view2_video')
|
||||||
|
views = models.IntegerField(verbose_name='总观看次数', default=0)
|
||||||
|
total_seconds = models.PositiveIntegerField(verbose_name='总观看秒数', default=0)
|
||||||
|
is_completed = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (
|
||||||
|
('create_by','video'), # 联合唯一
|
||||||
|
)
|
|
@ -1,5 +1,5 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Video, ViewRecord
|
from .models import Video, ViewRecord, ViewItem, View1, View2
|
||||||
from apps.system.serializers import UserSimpleSerializer
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
|
|
||||||
class VideoSerializer(serializers.ModelSerializer):
|
class VideoSerializer(serializers.ModelSerializer):
|
||||||
|
@ -7,13 +7,34 @@ class VideoSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Video
|
model = Video
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
video = super().create(validated_data)
|
||||||
|
cate = video.category
|
||||||
|
video.category_big = cate
|
||||||
|
if cate.pid:
|
||||||
|
video.category_big = cate.pid
|
||||||
|
video.save()
|
||||||
|
View1.objects.get_or_create(video=video, defaults={'video': video})
|
||||||
|
return video
|
||||||
|
|
||||||
class VideoUpdateSerializer(serializers.ModelSerializer):
|
class VideoUpdateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Video
|
model = Video
|
||||||
fields = ['name', 'category', 'description', 'sort_str']
|
fields = ['name', 'category', 'description', 'sort_str']
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
video = super().update(instance, validated_data)
|
||||||
|
cate = video.category
|
||||||
|
video.category_big = cate
|
||||||
|
if cate.pid:
|
||||||
|
video.category_big = cate.pid
|
||||||
|
video.save()
|
||||||
|
return video
|
||||||
|
|
||||||
class VideoListDetailSerializer(serializers.ModelSerializer):
|
class VideoListDetailSerializer(serializers.ModelSerializer):
|
||||||
|
views_n = serializers.IntegerField(source='view1_video.views', read_only=True, label="总观看次数")
|
||||||
|
viewsp_n = serializers.IntegerField(source='view1_video.viewsp', read_only=True, label="总观看秒数")
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Video
|
model = Video
|
||||||
exclude = ['mediaurl']
|
exclude = ['mediaurl']
|
||||||
|
@ -38,4 +59,29 @@ class VRecordSerializer(serializers.ModelSerializer):
|
||||||
class VRecordUpdateSerializer(serializers.ModelSerializer):
|
class VRecordUpdateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ViewRecord
|
model = ViewRecord
|
||||||
fields=['num', 'current']
|
fields=['num', 'current']
|
||||||
|
|
||||||
|
|
||||||
|
class ViewItemSerializer(serializers.ModelSerializer):
|
||||||
|
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
|
||||||
|
video_ = VideoSimpleSerializer(source='video', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = ViewItem
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class ViewItemUpdateSerializer(serializers.Serializer):
|
||||||
|
current = serializers.IntegerField(min_value=1)
|
||||||
|
seconds = serializers.IntegerField(min_value=0)
|
||||||
|
|
||||||
|
|
||||||
|
class View2Serializer(serializers.Serializer):
|
||||||
|
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
|
||||||
|
video_ = VideoSimpleSerializer(source='video', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = View2
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class DatetimeSerializer(serializers.Serializer):
|
||||||
|
start_time = serializers.DateTimeField(label="开始时间")
|
||||||
|
end_time = serializers.DateTimeField(label="结束时间")
|
||||||
|
limit = serializers.IntegerField(min_value=10, label="展示数量")
|
|
@ -1,11 +1,15 @@
|
||||||
from django.db.models import base
|
from django.db.models import base
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import ClassView, PlayCodeAPIView, SignatureAPIView, VideoView, VideoViewSet, VRecordViewSet, MyViewRecordAPIView
|
from .views import ClassView, PlayCodeAPIView, SignatureAPIView, ViewItemViewSet, VideoViewSet, VRecordViewSet, MyViewRecordAPIView, View2ViewSet
|
||||||
|
from .views2 import AnalyseViewSet
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register('video', VideoViewSet, basename="video")
|
router.register('video', VideoViewSet, basename="video")
|
||||||
router.register('viewrecord', VRecordViewSet, basename='viewrecord')
|
router.register('viewrecord', VRecordViewSet, basename='viewrecord')
|
||||||
|
router.register('viewitem', ViewItemViewSet, basename='viewitem')
|
||||||
|
router.register('view2', View2ViewSet, basename='view2')
|
||||||
|
router.register('analyse', AnalyseViewSet, basename='analyse')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include(router.urls)),
|
path('', include(router.urls)),
|
||||||
path('video/<int:id>/myview/', MyViewRecordAPIView.as_view()),
|
path('video/<int:id>/myview/', MyViewRecordAPIView.as_view()),
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from time import timezone
|
from time import timezone
|
||||||
from apps.system.models import Dict
|
from apps.system.models import Dict
|
||||||
from rest_framework.mixins import ListModelMixin
|
from rest_framework.mixins import ListModelMixin, UpdateModelMixin
|
||||||
from apps.vod.serializers import VRecordSerializer, VRecordUpdateSerializer, VideoListDetailSerializer, VideoSerializer, VideoUpdateSerializer
|
from apps.vod.serializers import VRecordSerializer, VRecordUpdateSerializer, VideoListDetailSerializer, VideoSerializer, VideoUpdateSerializer, ViewItemSerializer, ViewItemUpdateSerializer, View2Serializer, DatetimeSerializer
|
||||||
from apps.vod.models import Video, ViewRecord
|
from apps.vod.models import Video, ViewRecord, ViewItem, View1, View2
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from .vodclient import getAllClass, getPlayCode, searchMedia, getSignature
|
from .vodclient import getAllClass, getPlayCode, searchMedia, getSignature
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
@ -17,6 +17,8 @@ from rest_framework.status import HTTP_400_BAD_REQUEST
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from utils.queryset import get_child_queryset2
|
from utils.queryset import get_child_queryset2
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.db import connection
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
class ClassView(APIView):
|
class ClassView(APIView):
|
||||||
|
@ -69,6 +71,8 @@ class VideoViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
|
||||||
@action(methods=['get'], detail=False, perms_map={'get':'video_view'})
|
@action(methods=['get'], detail=False, perms_map={'get':'video_view'})
|
||||||
def myview(self, request, *args, **kwargs):
|
def myview(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
废弃接口
|
||||||
|
|
||||||
个人观看记录
|
个人观看记录
|
||||||
"""
|
"""
|
||||||
queryset = ViewRecord.objects.filter(user=request.user).order_by('-id')
|
queryset = ViewRecord.objects.filter(user=request.user).order_by('-id')
|
||||||
|
@ -78,10 +82,72 @@ class VideoViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
|
||||||
return self.get_paginated_response(serializer.data)
|
return self.get_paginated_response(serializer.data)
|
||||||
serializer = VRecordSerializer(queryset, many=True)
|
serializer = VRecordSerializer(queryset, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True, perms_map={'get':'*'})
|
||||||
|
def my(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
本视频的我的观看统计
|
||||||
|
|
||||||
|
本视频的我的观看统计
|
||||||
|
"""
|
||||||
|
video = self.get_object()
|
||||||
|
user = request.user
|
||||||
|
ins, _ = View2.objects.get_or_create(create_by=user, video=video, defaults={'video': video, 'create_by': user})
|
||||||
|
return Response(View2Serializer(instance=ins).data)
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True, perms_map={'get':'*'})
|
||||||
|
def start(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
开始播放
|
||||||
|
|
||||||
|
本次播放开始返回记录ID,后续计时使用
|
||||||
|
"""
|
||||||
|
video = self.get_object()
|
||||||
|
user = request.user
|
||||||
|
vi = ViewItem.objects.create(create_by=user, video=video)
|
||||||
|
cal_view1(vi)
|
||||||
|
return Response({"vi": vi.id})
|
||||||
|
|
||||||
|
# @action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||||
|
# def correct_cate(self, request, *args, **kwargs):
|
||||||
|
# for video in Video.objects.get_queryset(all=True).all():
|
||||||
|
# cate = video.category
|
||||||
|
# video.category_big = cate
|
||||||
|
# if cate.pid:
|
||||||
|
# video.category_big = cate.pid
|
||||||
|
# video.save()
|
||||||
|
# return Response()
|
||||||
|
|
||||||
|
def cal_view1(vi: ViewItem):
|
||||||
|
"""
|
||||||
|
统计视频播放数量
|
||||||
|
|
||||||
|
统计视频播放数量
|
||||||
|
"""
|
||||||
|
v1, _ = View1.objects.get_or_create(video=vi.video, defaults={"video": vi.video})
|
||||||
|
v2, _ = View2.objects.get_or_create(video=vi.video, create_by=vi.create_by, defaults={"video": vi.video, "create_by": vi.create_by})
|
||||||
|
v1.views = ViewItem.objects.filter(video=vi.video).count()
|
||||||
|
v1.viewp = View2.objects.filter(video=vi.video).count()
|
||||||
|
v1.save()
|
||||||
|
|
||||||
|
def cal_view2(vi: ViewItem):
|
||||||
|
"""
|
||||||
|
统计个人播放记录
|
||||||
|
|
||||||
|
统计个人播放记录
|
||||||
|
"""
|
||||||
|
v2, _ = View2.objects.get_or_create(video=vi.video, create_by=vi.create_by, defaults={"video": vi.video, "create_by": vi.create_by})
|
||||||
|
v2.views = ViewItem.objects.filter(video=vi.video).count()
|
||||||
|
v2.total_seconds = ViewItem.objects.filter(video=vi.video).aggregate(total=Sum('total_seconds'))['total']
|
||||||
|
v2.save()
|
||||||
|
|
||||||
|
|
||||||
class VRecordViewSet(ListModelMixin, GenericViewSet):
|
class VRecordViewSet(ListModelMixin, GenericViewSet):
|
||||||
|
"""
|
||||||
|
废弃接口
|
||||||
|
|
||||||
|
废弃接口
|
||||||
|
"""
|
||||||
perms_map = {'get':'viewrecord_view'}
|
perms_map = {'get':'viewrecord_view'}
|
||||||
queryset = ViewRecord.objects.all()
|
queryset = ViewRecord.objects.all()
|
||||||
search_fields = ['user__name', 'video__name']
|
search_fields = ['user__name', 'video__name']
|
||||||
|
@ -89,12 +155,93 @@ class VRecordViewSet(ListModelMixin, GenericViewSet):
|
||||||
ordering = ['-update_time']
|
ordering = ['-update_time']
|
||||||
|
|
||||||
|
|
||||||
|
class ViewItemViewSet(ListModelMixin, UpdateModelMixin, GenericViewSet):
|
||||||
|
perms_map = {'get': 'viewitem_view', 'put': '*'}
|
||||||
|
queryset = ViewItem.objects.select_related('create_by', 'video').all()
|
||||||
|
search_fields = ['create_by__name', 'video__name']
|
||||||
|
serializer_class = ViewItemSerializer
|
||||||
|
ordering = ['-id']
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.action == "update":
|
||||||
|
return ViewItemUpdateSerializer
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||||
|
def my(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
我的观看记录
|
||||||
|
|
||||||
|
我的观看记录
|
||||||
|
"""
|
||||||
|
queryset = ViewItem.objects.filter(create_by=request.user).order_by('-id')
|
||||||
|
page = self.paginate_queryset(queryset)
|
||||||
|
if page is not None:
|
||||||
|
serializer = ViewItemSerializer(page, many=True)
|
||||||
|
return self.get_paginated_response(serializer.data)
|
||||||
|
serializer = ViewItemSerializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
user = request.user
|
||||||
|
data = request.data
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.create_by != user:
|
||||||
|
raise ParseError('非创建人')
|
||||||
|
if ViewItem.objects.filter(video=obj.video, create_by=user).order_by('-id').first() != obj:
|
||||||
|
raise ParseError('存在新播放记录')
|
||||||
|
obj.current = data['current']
|
||||||
|
obj.total_seconds = obj.total_seconds + data['seconds']
|
||||||
|
obj.save()
|
||||||
|
cal_view2(obj)
|
||||||
|
return Response({'id': obj.id, 'current': obj.current, 'total_seconds': obj.total_seconds})
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True, perms_map={'get':'*'})
|
||||||
|
def complete(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
完成播放
|
||||||
|
|
||||||
|
完成播放
|
||||||
|
"""
|
||||||
|
obj = self.get_object()
|
||||||
|
obj.current = 0
|
||||||
|
v2 = View2.objects.get(video=obj.video, create_by=obj.create_by)
|
||||||
|
if v2.total_seconds >= obj.video.duration * 0.6:
|
||||||
|
v2.is_completed = True
|
||||||
|
v2.save()
|
||||||
|
return Response()
|
||||||
|
raise ParseError('观看时长不足')
|
||||||
|
|
||||||
|
class View2ViewSet(ListModelMixin, GenericViewSet):
|
||||||
|
perms_map = {'get': 'view2_view', 'put': '*'}
|
||||||
|
queryset = View2.objects.select_related('create_by', 'video').all()
|
||||||
|
search_fields = ['create_by__name', 'video__name']
|
||||||
|
serializer_class = View2Serializer
|
||||||
|
ordering = ['-id']
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||||
|
def my(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
我的观看统计
|
||||||
|
|
||||||
|
我的观看统计
|
||||||
|
"""
|
||||||
|
queryset = View2.objects.filter(create_by=request.user).order_by('-id')
|
||||||
|
page = self.paginate_queryset(queryset)
|
||||||
|
if page is not None:
|
||||||
|
serializer = View2Serializer(page, many=True)
|
||||||
|
return self.get_paginated_response(serializer.data)
|
||||||
|
serializer = View2Serializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class MyViewRecordAPIView(APIView):
|
class MyViewRecordAPIView(APIView):
|
||||||
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
def get(self, request, id , format=None):
|
def get(self, request, id , format=None):
|
||||||
"""
|
"""
|
||||||
|
废弃接口
|
||||||
|
|
||||||
该视频的本人观看信息
|
该视频的本人观看信息
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -111,6 +258,8 @@ class MyViewRecordAPIView(APIView):
|
||||||
|
|
||||||
def put(self, request, id, format=None):
|
def put(self, request, id, format=None):
|
||||||
"""
|
"""
|
||||||
|
废弃接口
|
||||||
|
|
||||||
更新该视频本人的观看信息
|
更新该视频本人的观看信息
|
||||||
params: {current:int}
|
params: {current:int}
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
from apps.vod.serializers import DatetimeSerializer
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from utils.sql import query_all_dict
|
||||||
|
|
||||||
|
class AnalyseViewSet(GenericViewSet):
|
||||||
|
perms_map = {'post': '*'}
|
||||||
|
serializer_class = DatetimeSerializer
|
||||||
|
|
||||||
|
def is_valid(self, request):
|
||||||
|
data = request.data
|
||||||
|
sr = self.get_serializer(data=data)
|
||||||
|
sr.is_valid(raise_exception=True)
|
||||||
|
vdata = sr.validated_data
|
||||||
|
return vdata
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=False)
|
||||||
|
def group_by_video_category_big(self, request):
|
||||||
|
"""
|
||||||
|
视频大类播放量统计
|
||||||
|
|
||||||
|
视频大类播放量统计
|
||||||
|
"""
|
||||||
|
vdata = self.is_valid(request)
|
||||||
|
sql_str = """select
|
||||||
|
d.name as "视频大类",
|
||||||
|
count(v.id) as "视频数量",
|
||||||
|
count(vi.id) as "观看总次数",
|
||||||
|
count(distinct vi.create_by_id) as "观看总人数"
|
||||||
|
from vod_video v
|
||||||
|
left join system_dict d on d.id = v.category_big_id
|
||||||
|
left join vod_viewitem vi on vi.video_id = v.id
|
||||||
|
and vi.create_time >= %s
|
||||||
|
and vi.create_time <= %s
|
||||||
|
group by d.id
|
||||||
|
order by "视频数量" desc, d.sort
|
||||||
|
limit %s
|
||||||
|
"""
|
||||||
|
return Response(query_all_dict(sql_str, [vdata['start_time'], vdata['end_time'], vdata['limit']]))
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=False)
|
||||||
|
def group_by_user_view(self, request):
|
||||||
|
"""
|
||||||
|
个人观看量统计
|
||||||
|
|
||||||
|
个人观看量统计
|
||||||
|
"""
|
||||||
|
vdata = self.is_valid(request)
|
||||||
|
sql_str = """select
|
||||||
|
u.name as "姓名",
|
||||||
|
u.username as "账号",
|
||||||
|
count(v2.is_completed is true) as "观看完成视频总数",
|
||||||
|
count(distinct vi.video_id) as "观看视频总数",
|
||||||
|
count(vi.id) as "观看总次数" ,
|
||||||
|
sum(vi.total_seconds/60) as "观看总时间"
|
||||||
|
from vod_viewitem vi
|
||||||
|
left join vod_video v on v.id = vi.video_id
|
||||||
|
left join system_user u on u.id = vi.create_by_id
|
||||||
|
left join vod_view2 v2 on v2.create_by_id = vi.create_by_id and v2.video_id = vi.video_id
|
||||||
|
where vi.create_time >= %s and vi.create_time <= %s
|
||||||
|
group by u.id
|
||||||
|
order by "观看完成视频总数" desc, "账号"
|
||||||
|
limit %s
|
||||||
|
"""
|
||||||
|
return Response(query_all_dict(sql_str, [vdata['start_time'], vdata['end_time'], vdata['limit']]))
|
||||||
|
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=False)
|
||||||
|
def group_by_org_view(self, request):
|
||||||
|
"""
|
||||||
|
单位观看量统计
|
||||||
|
|
||||||
|
单位观看量统计
|
||||||
|
"""
|
||||||
|
vdata = self.is_valid(request)
|
||||||
|
sql_str = """select
|
||||||
|
o.name as "单位名称",
|
||||||
|
count(v2.is_completed is true) as "观看完成视频总数",
|
||||||
|
count(distinct vi.video_id) as "观看视频总数",
|
||||||
|
count(vi.id) as "观看总次数" ,
|
||||||
|
sum(vi.total_seconds/60) as "观看总时间"
|
||||||
|
from vod_viewitem vi
|
||||||
|
left join vod_video v on v.id = vi.video_id
|
||||||
|
left join system_user u on u.id = vi.create_by_id
|
||||||
|
left join system_organization o on o.id = u.dept_id
|
||||||
|
left join vod_view2 v2 on v2.create_by_id = vi.create_by_id and v2.video_id = vi.video_id
|
||||||
|
where vi.create_time >= %s and vi.create_time <= %s
|
||||||
|
group by o.id
|
||||||
|
order by "观看完成视频总数" desc, o.sort
|
||||||
|
limit %s
|
||||||
|
"""
|
||||||
|
return Response(query_all_dict(sql_str, [vdata['start_time'], vdata['end_time'], vdata['limit']]))
|
|
@ -33,14 +33,15 @@ class SoftDeletableManagerMixin(object):
|
||||||
'''
|
'''
|
||||||
_queryset_class = SoftDeletableQuerySet
|
_queryset_class = SoftDeletableQuerySet
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self, all=False):
|
||||||
'''
|
'''
|
||||||
Return queryset limited to not deleted entries.
|
Return queryset limited to not deleted entries.
|
||||||
'''
|
'''
|
||||||
kwargs = {'model': self.model, 'using': self._db}
|
kwargs = {'model': self.model, 'using': self._db}
|
||||||
if hasattr(self, '_hints'):
|
if hasattr(self, '_hints'):
|
||||||
kwargs['hints'] = self._hints
|
kwargs['hints'] = self._hints
|
||||||
|
if all:
|
||||||
|
return self._queryset_class(**kwargs)
|
||||||
return self._queryset_class(**kwargs).filter(is_deleted=False)
|
return self._queryset_class(**kwargs).filter(is_deleted=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
from django.db import connection
|
||||||
|
|
||||||
|
def query_all_dict(sql, params=None):
|
||||||
|
'''
|
||||||
|
查询所有结果返回字典类型数据
|
||||||
|
:param sql:
|
||||||
|
:param params:
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
if params:
|
||||||
|
cursor.execute(sql, params=params)
|
||||||
|
else:
|
||||||
|
cursor.execute(sql)
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
return [dict(zip(columns, row)) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
def query_one_dict(sql, params=None):
|
||||||
|
"""
|
||||||
|
查询一个结果返回字典类型数据
|
||||||
|
:param sql:
|
||||||
|
:param params:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
if params:
|
||||||
|
cursor.execute(sql, params=params)
|
||||||
|
else:
|
||||||
|
cursor.execute(sql)
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return dict(zip(columns, row))
|
Loading…
Reference in New Issue