"""Generating summary data
TODO: Remove type errors
"""
from typing import Dict, Any
from datetime import *
from collections import defaultdict, Counter
from django.db.models import *
from django.db.models.functions import TruncDay
from utils.models.query import *
from app.config import *
from app.models import *
from app.YQPoint_utils import get_income_expenditure
from generic.models import User, YQPointRecord
from Appointment.models import Appoint, CardCheckInfo, Room
from Appointment.utils.identity import get_participant
# from Appointment.config import CONFIG
SUMMARY_YEAR1 = 2023
SUMMARY_SEMSTER1 = '秋'
SUMMARY_YEAR2 = 2022
SUMMARY_SEMSTER2 = '春'
SUMMARY_SEM_START = datetime(2023, 2, 1)
SUMMARY_SEM_END: datetime = datetime(2024, 1, 15)
[文档]
def remove_local_var(d: Dict[str, Any]):
keys = list(d.keys())
for k in keys:
if k.startswith('_'):
d.pop(k)
if k == 'np':
d.pop(k)
if k == 'jieba':
d.pop(k)
return d
[文档]
def generic_info():
generic_info = {}
generic_info.update(cal_all_underground())
generic_info.update(cal_all_org())
generic_info.update(cal_all_course())
return generic_info
[文档]
def person_info(np: 'NaturalPerson|User'):
if isinstance(np, User):
np = NaturalPerson.objects.get_by_user(np)
person_info = dict(Sname=np.name)
# 个人的书院部分信息统计
person_info.update(cal_login_num(np))
person_info.update(cal_act(np))
person_info.update(cal_course(np))
person_info.update(cal_anual_yqpoint(np))
person_info.update(cal_anual_academic(np))
# 个人的地下室部分信息统计
person_info.update(cal_study_room(np))
person_info.update(cal_appoint(np))
person_info.update(cal_sharp_appoint(np))
person_info.update(cal_co_appoint(np))
# person_info.update(cal_early_room(np))
# person_info.update(cal_late_room(np))
# person_info.update(cal_appoint_kw(np))
# person_info.update(cal_appoint_sum(np))
return person_info
[文档]
def person_infos(min=0, max=10000, count=10000):
npd = {}
num = 0
for np in NaturalPerson.objects.filter(
mq(NaturalPerson.person_id, User.id, gte=min, lte=max)
).select_related(f(NaturalPerson.person_id)):
npd[np.person_id.username] = person_info(np)
count -= 1
if count <= 0:
break
num += 1
if num % 100 == 0:
print(num)
return npd
# 通用统计部分从此开始
__generics = None
[文档]
def cal_all_underground():
"""
- 地下室年度使用情况总览
(1) 本年度地下室总刷卡记录
(2) 本年度总研讨室预约次数、时长
(3) 本年度总功能室预约次数、时长
(4) 最受欢迎的研讨室和预约次数
(5) 最受欢迎的功能房和预约次数
"""
_room_reords = CardCheckInfo.objects.filter(
Q(Cardtime__gt=SUMMARY_SEM_START, Cardtime__lt=SUMMARY_SEM_END,))
_func_rooms = Room.objects.function_rooms().values_list('Rid')
_talk_rooms = Room.objects.talk_rooms().values_list('Rid')
# 总刷卡次数
total_swipe_num = _room_reords.aggregate(cnt=Count('*')).get('cnt', 0)
# 研讨室刷卡总次数
total_talk_room_num = _room_reords.filter(
Q(Cardroom__in=_talk_rooms)).aggregate(cnt=Count('*')).get('cnt', 0)
# 研讨室预约总时长
_act_appoint = Appoint.objects.not_canceled().filter(
Astart__gt=SUMMARY_SEM_START, Astart__lt=SUMMARY_SEM_END)
_act_talk_appoint = _act_appoint.filter(Room__in=_talk_rooms)
total_discuss_appoint_hour = sum([(finish - start).seconds for start,
finish in _act_talk_appoint.values_list('Astart', 'Afinish')])//3600
# 最受欢迎的研讨室和预约次数
total_talk_appoint_most_name, total_talk_appoint_most_num = Counter(
_act_talk_appoint.values_list('Room__Rid')).most_common(1)[0]
total_talk_appoint_most_name = total_talk_appoint_most_name[0]
# 功能房刷卡总次数
total_func_room_num = _room_reords.filter(
Q(Cardroom__in=_func_rooms)).aggregate(cnt=Count('*')).get('cnt', 0)
# 功能房预约总时长
_act_func_appoint = _act_appoint.filter(Room__in=_func_rooms)
total_func_appoint_hour = sum([(finish - start).seconds for start,
finish in _act_func_appoint.values_list('Astart', 'Afinish')])//3600
# 最受欢迎的功能房和预约次数
total_func_appoint_most_name, total_func_appoint_most_num = Counter(
_act_func_appoint.values_list('Room__Rid')).most_common(1)[0]
total_func_appoint_most_name = total_func_appoint_most_name[0]
return remove_local_var(locals())
[文档]
def get_hottest_courses(year, semester):
"""
根据年份和学期获取最热门的前三门课程
"""
courses = Course.objects.filter(
year=year, semester=semester).exclude(status=Course.Status.ABORT)
# # 计算每门课程的预选人数
course_with_preselect_count = courses.annotate(
preselect_count=Count(
'participant_set',
filter=Q(participant_set__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS,
CourseParticipant.Status.FAILED,
])
)
)
# 计算预选人数与选课名额之比,使用ExpressionWrapper来确保结果为浮点数
hottest_courses = course_with_preselect_count.annotate(
hotness=ExpressionWrapper(
F('preselect_count') / F('capacity'),
output_field=FloatField()
)
).order_by('-hotness')[:3] # 按照hotness降序排列,并取前三门课程
hottest_courses_list = []
for course in hottest_courses:
ele = {}
ele[course.name] = course.hotness
hottest_courses_list.append(ele)
return hottest_courses_list
[文档]
def cal_all_org():
"""
- YPPF年度使用情况总览
(1) 本年度所有小组、学生小组的总数
(2) 本年度小组发起活动的总数量、总时长
"""
# 所有小组的总数
total_org_num = Organization.objects.activated().count()
# 学生小组总数量
total_club_num = ModifyOrganization.objects.filter(
otype__otype_name='学生小组', status=ModifyOrganization.Status.CONFIRMED).count()
# 活动总数量,注意这里是学年制
total_act = Activity.objects.exclude(status__in=[
Activity.Status.REVIEWING,
Activity.Status.CANCELED,
Activity.Status.ABORT,
Activity.Status.REJECT
]).filter(Q(year=SUMMARY_YEAR1, semester=Semester.FALL) | Q(year=SUMMARY_YEAR2, semester=Semester.SPRING))
total_act_num = total_act.count()
# 总活动时长
total_act_hour: timedelta = sum(
[(a.end-a.start) for a in total_act], timedelta(0))
total_act_hour = round(total_act_hour.total_seconds() / 3600, 2)
return dict(total_org_num=total_org_num, total_club_num=total_club_num,
total_act_num=total_act_num, total_act_hour=total_act_hour,
)
[文档]
def cal_all_course():
"""
- YPPF年度使用情况总览
(3) 书院本年度开课课程总数, 总课程活动数量
(4) 本年度课程活动时长
(5) 本年度参与一门课程的人数、参与三门课程的人数
(6) 23年春季、秋季学期,最热门的三门书院课程(以预选人数和选课名额之比计算)
"""
# 书院本年度开课总数
total_course_num = Course.objects.exclude(status=Course.Status.ABORT).filter(
Q(year=SUMMARY_YEAR1, semester=Semester.FALL) | Q(year=SUMMARY_YEAR2, semester=Semester.SPRING)).count()
# 本年度课程活动
course_act = Activity.objects.exclude(status__in=[
Activity.Status.REVIEWING,
Activity.Status.CANCELED,
Activity.Status.ABORT,
Activity.Status.REJECT
]).filter(
Q(year=SUMMARY_YEAR1, semester=Semester.FALL) | Q(year=SUMMARY_YEAR2, semester=Semester.SPRING), category=Activity.ActivityCategory.COURSE)
total_course_act_num = len(course_act)
# 本年度课程活动时长
total_course_act_hour: timedelta = sum(
[(a.end-a.start) for a in course_act], timedelta(0))
total_course_act_hour = round(
total_course_act_hour.total_seconds() / 3600, 2)
# 本年度参与一门课程的人数、参与三门课程的人数
persons = NaturalPerson.objects.annotate(cc=Count(
'courseparticipant', filter=Q(
courseparticipant__course__year=SUMMARY_YEAR1,
courseparticipant__course__semester=Semester.FALL,
courseparticipant__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS],
) | Q(
courseparticipant__course__year=SUMMARY_YEAR2,
courseparticipant__course__semester=Semester.SPRING,
courseparticipant__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS],
)
)
)
have_course_num = persons.filter(cc__gte=1).count()
have_three_course_num = persons.filter(cc__gte=3).count()
# 23年春季、秋季学期,最热门的三门书院课程(以预选人数和选课名额之比计算)
hottest_courses_23_Fall = get_hottest_courses(
year=SUMMARY_YEAR1, semester=Semester.FALL)
hottest_courses_23_Spring = get_hottest_courses(
year=SUMMARY_YEAR2, semester=Semester.SPRING)
return dict(total_course_num=total_course_num,
total_course_act_num=total_course_act_num, total_course_act_hour=total_course_act_hour,
have_course_num=have_course_num, have_three_course_num=have_three_course_num,
hottest_courses_23_Fall=hottest_courses_23_Fall,
hottest_courses_23_Spring=hottest_courses_23_Spring
)
# 个人统计部分从此开始
__persons = None
[文档]
def cal_login_num(np: NaturalPerson):
"""
- 系统登录介绍
(0) 该用户的注册日期
(1) 该用户本年度登陆系统次数
(2) 该用户本年度最长连续登录系统天数
"""
# 注册日期
date_joined = np.get_user().date_joined
# 该用户本年度登陆系统次数
_user = np.get_user()
_day_check_kws = {}
_day_check_kws.update(time__date__gt=SUMMARY_SEM_START,
time__date__lt=SUMMARY_SEM_END)
_signin_days = set(YQPointRecord.objects.filter(
user=_user,
source_type=YQPointRecord.SourceType.CHECK_IN,
**_day_check_kws,
).order_by('time').values_list('time__date', flat=True).distinct())
checkin_num = len(_signin_days)
# 计算该用户最长连续登录系统天数
_signin_days = sorted(_signin_days)
max_consecutive_days = 0
_current_streak = 0
_previous_date = None
for _current_date in _signin_days:
if _previous_date is not None and _current_date == _previous_date + timedelta(days=1):
# 如果当前日期和前一天相差一天,则增加连续天数
_current_streak += 1
else:
# 否则,重置连续天数
_current_streak = 1
# 更新最大连续天数
max_consecutive_days = max(max_consecutive_days, _current_streak)
_previous_date = _current_date
return {'date_joined': date_joined, 'checkin_num': checkin_num, 'max_consecutive_days': max_consecutive_days}
[文档]
def cal_act(np: NaturalPerson):
"""
- 小组板块
(1) 该用户参与的学生小组与书院课程小组数量
(2) 该用户创建或担任职务的小组名称、担任职务
(3) 该用户参与的小组活动数
(4) 该用户参与活动的出现频率最高的三个活动关键词、活动频率最高的时间段
"""
import jieba.analyse
from collections import defaultdict
pos = Position.objects.activated(noncurrent=None).filter(
Q(person=np, year=SUMMARY_YEAR1, semester__in=[Semester.FALL, Semester.ANNUAL]) | Q(
person=np, year=SUMMARY_YEAR2, semester__in=[Semester.SPRING, Semester.ANNUAL]),
)
# 参与的学生小组的数量
club_num = pos.filter(org__otype__otype_name='学生小组').count()
# 参与的书院课程的数量
course_org_num = pos.filter(org__otype__otype_name='书院课程').count()
#
participated_acts = Participation.objects.activated().filter(
sq(Participation.person, np),
(
Q(activity__year=SUMMARY_YEAR1, activity__semester=Semester.FALL) |
Q(activity__year=SUMMARY_YEAR2, activity__semester=Semester.SPRING)
))
# 参与的活动的数量
act_num = participated_acts.count()
# 参与活动的关键词
keyword_freq = defaultdict(int)
activity_titles = participated_acts.values_list(
'activity__title', flat=True)
# 该用户参与活动的出现频率最高的三个活动关键词
for text in activity_titles:
for keyword in jieba.analyse.extract_tags(text):
keyword_freq[keyword] += 1
act_top_three_keywords = sorted(
keyword_freq, key=keyword_freq.get, reverse=True)[:3]
# 活动次数最高的时间段,按照每小时作为时间段。
time_periods = Activity.objects.filter(id__in=participated_acts.values_list(
'activity_id', flat=True)).values_list('start', 'end')
if time_periods.exists():
hour_frequencies = Counter()
for start, end in time_periods:
duration = int((end - start).total_seconds() / 3600)
for hour in range(start.hour, start.hour + duration):
hour_frequencies[hour % 24] += 1
most_act_common_hour = hour_frequencies.most_common(1)[0][0]
else:
most_act_common_hour = None
# 担任的职务的数量
position_num = pos.count()
# 担任负责人的小组名称
admin_pos = pos.filter(is_admin=True)
admin_org_names = [position.org.oname for position in admin_pos]
# 该用户是否创建小组、创建的小组名称
orgs = ModifyOrganization.objects.filter(
pos=np.person_id, status=ModifyOrganization.Status.CONFIRMED)
IScreate = bool(orgs)
myclub_name = ''
if IScreate:
myclub_name = ','.join(orgs.values_list('oname', flat=True))
return dict(
IScreate=IScreate, myclub_name=myclub_name,
club_num=club_num, course_org_num=course_org_num, act_num=act_num,
position_num=position_num, admin_org_names=admin_org_names,
act_top_three_keywords=act_top_three_keywords, most_act_common_hour=most_act_common_hour
)
[文档]
def cal_course(np: NaturalPerson):
"""
- 书院课程板块
(1) 该用户选修的课程总数、总学时
(2) 该用户选修的课程在五类课程中的哪几类
(3) 该用户投入学时最长的课程及学时时长
(4) 用户春季学期、秋季学期选课数量
(5) 用户春季学期、秋季学期选中书院课数量
"""
# 这里计算的实际参与的课程活动总数,即便学时可能无效,但是只要有学时,就算
course_me_past = CourseRecord.objects.filter(person=np, total_hours__gt=0)
course_num = course_me_past.count()
# 计算每个类别学时的时候,只考虑有效学时
course_me_past = course_me_past.filter(invalid=False)
pro = []
# 计算每个类别的学时
for _course_type in list(Course.CourseType): # CourseType.values亦可
t = course_me_past.filter(course__type=_course_type)
if not t:
continue
t = t.aggregate(Sum('total_hours'), Sum(
'attend_times'), count=Count('*'))
pro.append([_course_type.label, t['total_hours__sum']
or 0, t['attend_times__sum'] or 0, t['count'] or 0])
unclassified_hour = course_me_past.filter(course__isnull=True).aggregate(
Sum('total_hours'))['total_hours__sum'] or 0
# 个人选修课程的总学时,选修课程类别学时最多的是哪一类、多少学时
course_hour = 0
types = []
max_type_info = '无', 0
for label, hour, _, count in pro:
if count > max_type_info[1]:
max_type_info = label, count
types.append(label)
course_hour += hour
if unclassified_hour:
types.append('其它')
course_hour += unclassified_hour
# 该用户选修的课程在五类课程中的哪几类
course_type = '/'.join(types) + f' {len(types)}'
type_count = len(types)
# 该用户投入学时最长的课程、学时时长、参与次数
if course_me_past:
most_time: CourseRecord = max(
course_me_past, key=lambda x: x.total_hours)
most_num: CourseRecord = max(
course_me_past, key=lambda x: x.attend_times)
course_most_time_name, course_most_hour = most_time.get_course_name(), most_time.total_hours
course_most_num_name, course_most_num = most_num.get_course_name(), most_num.attend_times
else:
course_most_time_name, course_most_hour = "无", 0
course_most_num_name, course_most_num = "无", 0
elect_course = Course.objects.exclude(
status=Course.Status.ABORT).filter(
participant_set__person=np,
participant_set__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS,
CourseParticipant.Status.FAILED,
])
# 该用户23秋、23春课程选课数量(包含失败的情况)
preelect_course_23fall = elect_course.filter(
year=SUMMARY_YEAR1, semester=Semester.FALL,)
preelect_course_23spring = elect_course.filter(
year=SUMMARY_YEAR2, semester=Semester.SPRING,)
preelect_course_23fall_num = preelect_course_23fall.count()
preelect_course_23spring_num = preelect_course_23spring.count()
# 该用户23秋、23春课程选上课数量
elected_course_23fall_num = preelect_course_23fall.filter(
participant_set__person=np,
participant_set__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS,
]).count()
elected_course_23spring_num = preelect_course_23spring.filter(
participant_set__person=np,
participant_set__status__in=[
CourseParticipant.Status.SELECT,
CourseParticipant.Status.SUCCESS,
]).count()
return dict(course_num=course_num,
course_hour=course_hour, course_type=course_type,
course_most_time_name=course_most_time_name, course_most_hour=course_most_hour,
course_most_num_name=course_most_num_name, course_most_num=course_most_num,
max_type_info=max_type_info, type_count=type_count,
preelect_course_23fall_num=preelect_course_23fall_num,
preelect_course_23spring_num=preelect_course_23spring_num,
elected_course_23fall_num=elected_course_23fall_num,
elected_course_23spring_num=elected_course_23spring_num,
)
[文档]
def cal_anual_yqpoint(np: NaturalPerson):
"""
- 元气值板块
(1) 获取元气值总值
(2) 消耗元气值总值
(3) 兑换奖品种类数量
(4) 盲盒兑换次数、抽中次数
"""
_user = np.get_user()
# 获取元气值总值, 消耗元气值总值
income, expenditure = get_income_expenditure(
_user, SUMMARY_SEM_START, SUMMARY_SEM_END)
_pool_records = PoolRecord.objects.filter(
user=_user, time__gte=SUMMARY_SEM_START, time__lt=SUMMARY_SEM_END)
_lucky_pool_records = _pool_records.filter(status__in=[
PoolRecord.Status.UN_REDEEM,
PoolRecord.Status.REDEEMED,
]).exclude(prize__name__contains='空盒')
_unique_prizes = _lucky_pool_records.values_list(
'prize', flat=True).distinct()
# 兑换奖品种类
number_of_unique_prizes = len(_unique_prizes)
# 盲盒兑换次数
_mystery_boxes = _pool_records.filter(pool__type=Pool.Type.RANDOM)
mystery_boxes_num = _mystery_boxes.count()
# 盲盒抽中次数
lucky_mystery_boxes_num = _lucky_pool_records.filter(
pool__type=Pool.Type.RANDOM).count()
return remove_local_var(locals())
[文档]
def cal_anual_academic(np: NaturalPerson):
"""
- 学术地图板块
(1) 学术地图标签关键词数量
(2) 学术地图提问次数
"""
_academic_tags = AcademicTagEntry.objects.activated().filter(person=np)
academic_tags_num = _academic_tags.count()
# # 获取并统计每种类型的标签数量
# _tag_counts = _academic_tags.values('tag__atype').annotate(count=Count('tag')).order_by('tag__atype')
# for _tag_count in _tag_counts:
# _atype = AcademicTag.Type(_tag_count['tag__atype']).label
# _count = _tag_count['count']
# 学术地图提问次数
academic_QA_num = AcademicQA.objects.filter(
chat__questioner=np.get_user()).count()
return remove_local_var(locals())
[文档]
def cal_study_room(np: NaturalPerson):
"""
- 自习室
(1) 用户本年度自习室刷卡次数、天数、超越“%”的同学
(2) 用户本年度最常去的自习室、次数
"""
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
if _par is None:
return {}
_study_room_record_filter = Q(Cardroom__Rtitle__contains='自习',
Cardtime__gt=_start_time,
Cardtime__lt=_end_time,
Cardstudent=_par)
_study_room_reords = CardCheckInfo.objects.filter(
_study_room_record_filter)
if not _study_room_reords.exists():
return dict(study_room_num=0)
# 用户本年度自习室刷卡次数
study_room_num = _study_room_reords.aggregate(cnt=Count('*')).get('cnt', 0)
# 用户本年度自习室刷卡天数
study_room_day = _study_room_reords.values_list('Cardtime__date').annotate(
cnt=Count('*')).aggregate(cnt=Count('*')).get('cnt', 0)
# 个人最常去的自习室,和对应的天数
_cnt_dict = defaultdict(int)
for r, _, _ in _study_room_reords.values_list('Cardroom__Rid', 'Cardtime__date').annotate(cnt=Count('*')):
_cnt_dict[r] += 1
study_room_top, study_room_top_day = max(
[(r, cnt) for r, cnt in _cnt_dict.items()], key=lambda x: x[1])
return dict(study_room_num=study_room_num,
study_room_day=study_room_day,
study_room_top=study_room_top,
study_room_top_day=study_room_top_day,
)
[文档]
def cal_anual_appoint(_me_appoint: QuerySet[Appoint], _room_type: str = None):
""""
根据不同的房间类型,获取以下内容:
(1) 用户本年度{_room_type}总预约次数、总时长
(2) 用户本年度最多使用的{_room_type}预约理由关键词
(3) 用户本年度最多预约的{_room_type}、次数
(5) {_room_type}预约时长最多的日期,当日预约时长,当天的预约关键词
"""
import jieba.analyse
if _room_type is None:
# 所有类型房间
_room_list = Room.objects.permitted().values_list('Rid')
elif _room_type == 'Discuss':
# 研讨室
_room_list = Room.objects.talk_rooms().values_list('Rid')
elif _room_type == 'Function':
# 功能房
_room_list = Room.objects.function_rooms().values_list('Rid')
_me_act_appoints = _me_appoint.filter(Room__in=_room_list)
if not _me_act_appoints.exists():
appoint_num = appoint_hour = 0
else:
# 用户本年度{room_type}预约次数
appoint_num = _me_act_appoints.aggregate(cnt=Count('*'))['cnt']
# 用户本年度{room_type}预约时长
appoint_hour = sum([(finish - start).seconds for start,
finish in _me_act_appoints.values_list('Astart', 'Afinish')])//3600
# 用户本年度最长预约的{room_type}、时长
# _my_rooms = set(_me_act_appoints.values_list('Room', flat=True))
# appoint_long_room, appoint_long_hour = max([(r, _me_act_appoint.filter(Room=r).aggregate(
# tol=Sum(F('Afinish') - F('Astart')))['tol'].total_seconds()//3600) for r in _my_rooms], key=lambda x: x[1])
# 用户本年度最多预约的{room_type}、次数
appoint_most_room, appoint_most_room_num = Counter(
_me_act_appoints.values_list('Room__Rtitle', flat=True)).most_common(1)[0]
# 预约时长最多的日期,当日预约时长,当天的预约关键词
# 计算每个预约的时长
_discuss_duration_appointments = _me_act_appoints.annotate(
duration=ExpressionWrapper(
F('Afinish') - F('Astart'),
output_field=DurationField()
)
)
# 按天分组并计算总时长
_daily_duration = _discuss_duration_appointments.annotate(
day=TruncDay('Astart')
).values('day').annotate(
total_duration=Sum('duration')
).order_by('-total_duration')
# 获取时长最长的那一天
_longest_day = _daily_duration.first() if _daily_duration else None
if _longest_day:
_hours, _remainder = divmod(
_longest_day['total_duration'].seconds, 3600)
_minutes = _remainder // 60
if _minutes == 0:
appoint_longest_duration = f"{_hours}小时"
else:
appoint_longest_duration = f"{_hours}小时{_minutes}分钟"
appoint_longest_day = _longest_day['day']
_purposes = _me_act_appoints.filter(
Astart__date=appoint_longest_day).values_list('Ausage', flat=True)
if len(_purposes) == 1:
_keywords = jieba.analyse.extract_tags(_purposes[0], topK=1)
appoint_longest_day_keyword = _keywords[0] if _keywords else None
else:
# 多个文本:提取频率最高的关键词
_all_words = []
for _text in _purposes:
_words = jieba.cut(_text)
_all_words.extend(_words)
_most_common_words = Counter(_all_words).most_common(1)
appoint_longest_day_keyword = _most_common_words[0][0] if _most_common_words else None
else:
appoint_longest_duration = None
appoint_longest_day = None
appoint_longest_day_keyword = None
# 用户本年度最常使用的关键词
_year_purposes = _me_act_appoints.values_list('Ausage', flat=True)
_year_all_words = []
for _text in _year_purposes:
_year_all_words.extend(jieba.cut(_text))
_year_most_words = Counter(_year_all_words).most_common(1)
appoint_year_keyword = _year_most_words[0][0] if _year_most_words else None
_room_dict = remove_local_var(locals())
_prefix_room_dict = {f"{_room_type}_" +
_key: _value for _key, _value in _room_dict.items()}
return _prefix_room_dict
[文档]
def cal_appoint(np: NaturalPerson):
"""
-研讨室
(1) 用户本年度研讨室总预约次数、总时长
(2) 用户本年度最多使用的研讨室预约理由关键词
(3) 用户本年度最多预约的研讨室、次数
(5) 研讨室预约时长最多的日期,当日预约时长,当天的预约关键词
-功能房
内容同研讨室
"""
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
room_dict = {}
if _par is None:
return room_dict
_me_act_appoint = Appoint.objects.not_canceled().filter(
students=_par, Astart__gt=_start_time, Astart__lt=_end_time)
if not _me_act_appoint.exists():
return room_dict
func_room_dict = cal_anual_appoint(_me_act_appoint, _room_type='Function')
talk_room_dict = cal_anual_appoint(_me_act_appoint, _room_type='Discuss')
room_dict.update(func_room_dict)
room_dict.update(talk_room_dict)
return room_dict
[文档]
def cal_sharp_appoint(np: NaturalPerson):
"""
- 极限预约 & 违约次数
(1) 在预约时间前30分钟内预约次数
(2) 预约最紧的一次的时长、日期、理由、房间号
(3) 违约次数,总计扣分
"""
appoints = Appoint.objects.filter(
Astart__gte=SUMMARY_SEM_START,
Astart__lt=SUMMARY_SEM_END,
major_student__Sid=np.person_id)
sharp_appoints = appoints.exclude(Atype=Appoint.Type.TEMPORARY).filter(
Astart__lt=F('Atime') + timedelta(minutes=30))
# 在预约时间前30分钟内预约次数
sharp_appoint_num = sharp_appoints.count()
if not sharp_appoint_num:
return dict(sharp_appoint_num=sharp_appoint_num)
# 预约最紧的一次的日期
sharp_appoint: Appoint = min(
sharp_appoints, key=lambda x: x.Astart-x.Atime)
sharp_appoint_day = sharp_appoint.Astart.strftime('%Y年%m月%d日')
sharp_appoint_reason = sharp_appoint.Ausage
sharp_appoint_min = (sharp_appoint.Astart -
sharp_appoint.Atime).total_seconds()
if sharp_appoint_min < 60:
sharp_appoint_min = f'{round(sharp_appoint_min)}秒'
else:
sharp_appoint_min = f'{round((sharp_appoint_min / 60) % 60)}分钟'
sharp_appoint_room = str(sharp_appoint.Room)
# 违约次数
disobey_num = appoints.filter(Astatus=Appoint.Status.VIOLATED).count()
return dict(
sharp_appoint_num=sharp_appoint_num,
sharp_appoint_day=sharp_appoint_day,
sharp_appoint_reason=sharp_appoint_reason,
sharp_appoint_min=sharp_appoint_min,
sharp_appoint_room=sharp_appoint_room,
disobey_num=disobey_num
)
[文档]
def cal_co_appoint(np: NaturalPerson):
"""
- 海内存知己,天涯若比邻
(1) 该用户本年度一起预约最多的同学
(2) 一起预约的次数、时长
(3) 一起预约最多的理由
(4) 获得称号
"""
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
if _par is None:
return {}
_me_act_appoint = Appoint.objects.not_canceled().filter(
students=_par, Astart__gt=_start_time, Astart__lt=_end_time)
_co_np_list = []
for _appoint in _me_act_appoint:
for _co_np in _appoint.students.all():
if _co_np != _par:
_co_np_list.append(_co_np)
if not _co_np_list:
return {}
# 该用户本年度一起预约最多的同学、次数
co_mate, co_appoint_num = Counter(_co_np_list).most_common(1)[0]
_co_act_appoint = _me_act_appoint.filter(students=co_mate)
# 一起预约的时长
co_appoint_hour = _co_act_appoint.aggregate(
tol=Sum(F('Afinish') - F('Astart')))['tol'].total_seconds()//3600
co_mate = co_mate.name
# 获得称号
co_title = ''
if co_appoint_hour > 30:
co_title = '莫逆之交'
elif co_appoint_hour > 15:
co_title = '形影不离'
elif co_appoint_hour > 8:
co_title = '结伴同行'
elif co_appoint_hour > 3:
co_title = '一拍即合'
# 一起预约最多的理由
import jieba
_key_words = []
for _usage in _co_act_appoint.values_list('Ausage'):
if _usage[0] in ['临时预约', '[MASK]']:
continue
_key_words.extend(jieba.cut(_usage[0]))
co_keyword = Counter(_key_words).most_common(1)[0][0]
return remove_local_var(locals())
# 本年度未用到以下部分
__useless = None
[文档]
def cal_appoint_kw(np: NaturalPerson):
"""
计算个人,年度预约关键词(最常出现的前三名)
"""
import jieba
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
if _par is None:
return {}
_talk_rooms = Room.objects.talk_rooms().values_list('Rid')
_func_rooms = Room.objects.function_rooms().values_list('Rid')
_me_act_appoint = Appoint.objects.not_canceled().filter(
students=_par, Astart__gt=_start_time, Astart__lt=_end_time).exclude(Atype=Appoint.Type.TEMPORARY)
_key_words = []
for _usage in _me_act_appoint.values_list('Ausage'):
_key_words.extend(jieba.cut(_usage[0]))
Skeywords = Counter(_key_words).most_common(3)
return remove_local_var(locals())
[文档]
def cal_appoint_sum(np: NaturalPerson):
"""
个人总预约时长、次数
"""
appoints = Appoint.objects.not_canceled().filter(
Astart__gte=SUMMARY_SEM_START,
Astart__lt=SUMMARY_SEM_END,
major_student__Sid=np.person_id
)
total_time = appoints.aggregate(
time=Sum(F('Afinish')-F('Astart')))['time'] or timedelta()
appoint_hour = round(total_time.total_seconds() / 3600, 1)
appoint_num = appoints.count()
return dict(
appoint_hour=appoint_hour, appoint_num=appoint_num,
# poem_word=??
)
[文档]
def cal_early_room(np: NaturalPerson):
"""
计算个人 上午6-8点到达地下室的次数,每年度最早到地下室的时间
"""
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
if _par is None:
return {}
_record_filter = Q(Cardtime__gt=_start_time,
Cardtime__lt=_end_time,
Cardstudent=_par,
Cardtime__hour__lt=8,
Cardtime__hour__gte=6)
_room_reords = CardCheckInfo.objects.filter(_record_filter)
if not _room_reords.exists():
return dict(early_day_num=0)
early_day_num = _room_reords.values_list('Cardtime__date').annotate(
cnt=Count('*')).aggregate(cnt=Count('*')).get('cnt', 0)
if early_day_num:
early_room, early_room_day, early_room_time = min(list(_room_reords.values_list(
'Cardroom__Rid', 'Cardtime__date').annotate(time=Min('Cardtime__time'))), key=lambda x: x[2])
return remove_local_var(locals())
[文档]
def cal_late_room(np: NaturalPerson):
"""
计算个人 凌晨23-5点在地下室的次数,有多少人在同一时期陪同,哪间自习室陪你到最晚
"""
_start_time = SUMMARY_SEM_START
_end_time = SUMMARY_SEM_END
_user = np.get_user()
_par = get_participant(_user)
if _par is None:
return {}
_record_filter = Q(Cardtime__gt=_start_time,
Cardtime__lt=_end_time,
Cardstudent=_par)
_late_filter_night = Q(Cardtime__hour__gte=23)
_late_filter_dawn = Q(Cardtime__hour__lt=5)
_room_reords = CardCheckInfo.objects.filter(_record_filter)
late_room_num = len(list(set(_room_reords.filter(
_late_filter_night).values_list('Cardtime__date'))))
if not late_room_num:
return dict(late_room_num=0)
_dawn_records = list(_room_reords.filter(_late_filter_dawn).values_list(
'Cardroom', 'Cardtime__date', 'Cardtime__time'))
if _dawn_records:
_latest_record = max(_dawn_records, key=lambda x: x[2])
else:
_latest_record = max(_room_reords.filter(_late_filter_night).values_list(
'Cardroom', 'Cardtime__date', 'Cardtime__time'), key=lambda x: x[2])
late_room, late_room_date, late_room_time = _latest_record
_late_room_ref_date = late_room_date
if late_room_time.hour < 23:
_late_room_ref_date = late_room_date - timedelta(days=1)
late_room_people = len(list(set(CardCheckInfo.objects.filter(Cardtime__gt=_start_time,
Cardtime__lt=_end_time,
Cardtime__date=_late_room_ref_date,
Cardtime__hour__gte=22
).values_list('Cardstudent'))))
return remove_local_var(locals())