diff --git a/point_system/formats/__init__.py b/point_system/formats/__init__.py new file mode 100644 index 0000000..25f9d8a --- /dev/null +++ b/point_system/formats/__init__.py @@ -0,0 +1 @@ +# Package initialization for formats diff --git a/point_system/formats/en/__init__.py b/point_system/formats/en/__init__.py new file mode 100644 index 0000000..65eb672 --- /dev/null +++ b/point_system/formats/en/__init__.py @@ -0,0 +1 @@ +# Package initialization for formats.en diff --git a/point_system/formats/en/formats.py b/point_system/formats/en/formats.py new file mode 100644 index 0000000..ac59595 --- /dev/null +++ b/point_system/formats/en/formats.py @@ -0,0 +1,19 @@ +from django.conf.locale.en.formats import * + +DATE_INPUT_FORMATS = [ + '%Y-%m-%d', + '%Y/%m/%d', + '%Y.%m.%d', + '%Y年%m月%d日', + '%Y年%n月%j日', + '%m/%d/%Y', + '%m/%d/%y', + '%b %d %Y', + '%b %d, %Y', + '%d %b %Y', + '%d %b, %Y', + '%B %d %Y', + '%B %d, %Y', + '%d %B %Y', + '%d %B, %Y', +] diff --git a/point_system/formats/zh_Hant/__init__.py b/point_system/formats/zh_Hant/__init__.py new file mode 100644 index 0000000..cc72320 --- /dev/null +++ b/point_system/formats/zh_Hant/__init__.py @@ -0,0 +1 @@ +# Package initialization for formats.zh_hant diff --git a/point_system/formats/zh_Hant/formats.py b/point_system/formats/zh_Hant/formats.py new file mode 100644 index 0000000..d374059 --- /dev/null +++ b/point_system/formats/zh_Hant/formats.py @@ -0,0 +1 @@ +from formats.zh_Hant.formats import * diff --git a/point_system/locale/en/LC_MESSAGES/django.po b/point_system/locale/en/LC_MESSAGES/django.po index d354d5c..09cfa0b 100644 --- a/point_system/locale/en/LC_MESSAGES/django.po +++ b/point_system/locale/en/LC_MESSAGES/django.po @@ -225,5 +225,80 @@ msgstr "%(count)s time" msgid "無在學學生資料" msgstr "No active student data" +msgid "在學學生: %(count)s 人" +msgstr "Active Students: %(count)s" + +msgid "考勤紀錄提示:" +msgstr "Attendance Note: " + +msgid "請勾選要紀錄考勤簽到的學生,點擊「確認考勤簽到」按鈕即可批次寫入點名紀錄。" +msgstr "Please select the students to record attendance for and click \"Submit Attendance\" to save." + +msgid "學生考勤點名單" +msgstr "Student Attendance Checklist" + +msgid "全選" +msgstr "Select All" + +msgid "取消全選" +msgstr "Deselect All" + +msgid "搜尋學生姓名..." +msgstr "Search student name..." + +msgid "已勾選:" +msgstr "Selected: " + +msgid "確認考勤簽到" +msgstr "Submit Attendance" + +msgid "本次已成功紀錄考勤" +msgstr "Attendance Recorded Successfully" + +msgid "共 %(count)s 筆紀錄" +msgstr "%(count)s Record(s)" + +msgid "身分 / 學位" +msgstr "Category / Degree" + +msgid "考勤簽到時間" +msgstr "Attendance Timestamp" + +msgid "已簽到" +msgstr "Recorded" + +msgid "最新紀錄: %(count)s 筆" +msgstr "Latest Records: %(count)s" + +msgid "系統狀態: 即時記錄" +msgstr "Status: Real-time" + +msgid "顯示系統中最新輸入之 20 筆學生考勤簽到時間明細,點擊學生姓名或查看詳情可瀏覽個人完整簽到與報告紀錄。" +msgstr "Displays the 20 most recent student attendance records. Click student name or view details to see full history." + +msgid "最近 20 筆考勤簽到紀錄" +msgstr "Recent 20 Attendance Records" + +msgid "目前沒有考勤紀錄。" +msgstr "No attendance records found." + +msgid "統計學生數: %(count)s 人" +msgstr "Students Count: %(count)s" + +msgid "報表統計提示:" +msgstr "Report Summary: " + +msgid "統計各學生於去年度 (%(ly)s) 與本年度 (%(cy)s) 之累積考勤簽到次數總和。" +msgstr "Annual cumulative attendance counts for last year (%(ly)s) and current year (%(cy)s)." + +msgid "年度出席統計報表 (%(ly)s vs %(cy)s)" +msgstr "Annual Attendance Report (%(ly)s vs %(cy)s)" + +msgid "出席次數" +msgstr "Attendance Count" + +msgid "尚無符合條件之考勤統計資料" +msgstr "No attendance statistics available." + diff --git a/point_system/point_system/settings.py b/point_system/point_system/settings.py index c205e55..a738fea 100644 --- a/point_system/point_system/settings.py +++ b/point_system/point_system/settings.py @@ -147,6 +147,21 @@ LOCALE_PATHS = [ BASE_DIR / 'locale', ] +FORMAT_MODULE_PATH = [ + 'formats', +] + +DATE_INPUT_FORMATS = [ + '%Y-%m-%d', + '%Y/%m/%d', + '%Y.%m.%d', + '%Y年%m月%d日', + '%Y年%n月%j日', + '%m/%d/%Y', + '%m/%d/%y', +] + + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.2/howto/static-files/ diff --git a/point_system/static/weekly_schedule_en.html b/point_system/static/weekly_schedule_en.html index 3a61d0d..704cd07 100644 --- a/point_system/static/weekly_schedule_en.html +++ b/point_system/static/weekly_schedule_en.html @@ -77,7 +77,7 @@
2026-08-10 (Monday)
- 6 presenters + 4 presenters
@@ -92,22 +92,6 @@ - - BUSHRA UROOJ - - - Ph.D. Student - - - - - Regular - - - - 林 樸 @@ -125,7 +109,7 @@ - 李亮慶 + Frank - Emran Khan - - - Master's Student - - - - - Regular - - - - - - 林宇晨 + Eric 2026-08-17 (Monday) - 6 presenters + 4 presenters
@@ -220,7 +188,7 @@ - 林孟忻 + Gloria - 劉冠岑 - - - Master's Student - - - - - Regular - - - - - - 江霈 + Sandy - - 孫艾琳 - - - Master's Student - - - - - In-Service - - - -
@@ -378,7 +314,7 @@ - 劉睿恩 + Roy - 林靖哲 + Jimmy - 林綺音 + Yin - 林孟忻 + Gloria - 劉冠岑 + Sandy - 林宇晨 + Eric - 陳其蒙 + Kimo 2026-09-14 (Monday) - 6 presenters + 5 presenters
@@ -706,23 +642,7 @@ - BUSHRA UROOJ - - - Ph.D. Student - - - - - Regular - - - - - - 劉睿恩 + Roy - 林綺音 + Yin - 林靖哲 + Jimmy - 江霈 + Pei - 李亮慶 + Frank - 黃子芬 + Melissa 2026-08-10 (Monday) - 6 presenters + 4 presenters
@@ -92,22 +92,6 @@ - - BUSHRA UROOJ - - - Ph.D. Student - - - - - Regular - - - - 林 樸 @@ -125,7 +109,7 @@ - 李亮慶 + Frank - Emran Khan - - - Master's Student - - - - - Regular - - - - - - 林宇晨 + Eric 2026-08-17 (Monday) - 6 presenters + 4 presenters
@@ -220,7 +188,7 @@ - 林孟忻 + Gloria - 劉冠岑 - - - Master's Student - - - - - Regular - - - - - - 江霈 + Sandy - - 孫艾琳 - - - Master's Student - - - - - In-Service - - - -
@@ -378,7 +314,7 @@ - 劉睿恩 + Roy - 林靖哲 + Jimmy - 林綺音 + Yin - 林孟忻 + Gloria - 劉冠岑 + Sandy - 林宇晨 + Eric - 陳其蒙 + Kimo 2026-09-14 (Monday) - 6 presenters + 5 presenters
@@ -706,23 +642,7 @@ - BUSHRA UROOJ - - - Ph.D. Student - - - - - Regular - - - - - - 劉睿恩 + Roy - 林綺音 + Yin - 林靖哲 + Jimmy - 江霈 + Pei - 李亮慶 + Frank - 黃子芬 + Melissa

© 2023-2026 1218 Lab. All rights reserved.

-

Active Directory DC: dc1.samdom.mdi.bar (Domain: samdom.mdi.bar) -

+

Active Directory Single Sign-On

diff --git a/point_system/students/templates/students/attendance_report.html b/point_system/students/templates/students/attendance_report.html index 871e5a6..3e6fbc7 100644 --- a/point_system/students/templates/students/attendance_report.html +++ b/point_system/students/templates/students/attendance_report.html @@ -1,23 +1,93 @@ {% extends 'base.html' %} +{% load i18n %} + +{% block title %}{% trans "考勤報告" %} - 1218 Lab{% endblock %} + {% block content %} -

年度出席統計報表

+
+

+ {% trans "考勤報告" %} +

+
+ + {{ last_year }} - {{ current_year }} + + + {% blocktrans with count=attendance_data|length %}統計學生數: {{ count }} 人{% endblocktrans %} + +
+
- - - - - - - - {% for entry in attendance_data %} - - - - - - - {% endfor %} -
學號姓名{{ last_year }} 出席次數{{ current_year }} 出席次數
{{ entry.student.student_id }}{{ entry.student.name }}{{ entry.last_year_count }}{{ entry.current_year_count }}
+
+ + {% trans "報表統計提示:" %}{% blocktrans with ly=last_year cy=current_year %}統計各學生於去年度 ({{ ly }}) 與本年度 ({{ cy }}) 之累積考勤簽到次數總和。{% endblocktrans %} +
-返回首頁 +
+
+
+ {% blocktrans with ly=last_year cy=current_year %}年度出席統計報表 ({{ ly }} vs {{ cy }}){% endblocktrans %} +
+
+
+ {% if attendance_data %} +
+ + + + + + + + + + + + {% for entry in attendance_data %} + + + + + + + + {% endfor %} + +
#{% trans "姓名" %}{% trans "身分 / 學位" %}{{ last_year }} {% trans "出席次數" %}{{ current_year }} {% trans "出席次數" %}
{{ forloop.counter }} + {% get_current_language as CURRENT_LANG %} + {% if CURRENT_LANG == 'en' and entry.student.foreign_alias %} + {{ entry.student.foreign_alias }} + {% else %} + {{ entry.student.name }} + {% endif %} + + {% if entry.student.degree_type == '博士生' %} + {% trans "博士生" %} + {% elif entry.student.degree_type == '學士生' %} + {% trans "學士生" %} + {% else %} + {% trans "碩士生" %} + {% endif %} + + {% if entry.student.student_category == '在職生' %} + {% trans "在職生" %} + {% elif entry.student.student_category == '交換中' %} + {% trans "交換中" %} + {% else %} + {% trans "一般生" %} + {% endif %} + + {{ entry.last_year_count }} + + {{ entry.current_year_count }} +
+
+ {% else %} +
+ + {% trans "尚無符合條件之考勤統計資料" %} +
+ {% endif %} +
+
{% endblock %} \ No newline at end of file diff --git a/point_system/students/templates/students/login.html b/point_system/students/templates/students/login.html index 95355a7..b8c4a0f 100644 --- a/point_system/students/templates/students/login.html +++ b/point_system/students/templates/students/login.html @@ -8,7 +8,6 @@

Active Directory 登入

- DC: dc1.samdom.mdi.bar
{% if messages %} @@ -27,7 +26,7 @@ AD 帳號 {{ form.username }} -
可以使用短帳號 (例如: username) 或完整 UPN (例如: username@samdom.mdi.bar)。
+
可以使用短帳號 (例如: username) 或完整 UPN (例如: username@domain)。
diff --git a/point_system/students/templates/students/recent_attendance.html b/point_system/students/templates/students/recent_attendance.html index 02f8f17..0fdeff3 100644 --- a/point_system/students/templates/students/recent_attendance.html +++ b/point_system/students/templates/students/recent_attendance.html @@ -1,34 +1,89 @@ {% extends 'base.html' %} +{% load i18n tz %} -{% block title %}最近考勤紀錄{% endblock %} +{% block title %}{% trans "最近考勤" %} - 1218 Lab{% endblock %} {% block content %} -
-

最近20筆考勤紀錄

- - {% if recent_records %} +
+

+ {% trans "最近考勤" %} +

+
+ + {% blocktrans with count=recent_records|length %}最新紀錄: {{ count }} 筆{% endblocktrans %} + + + {% trans "系統狀態: 即時記錄" %} + +
+
+ +
+ + {% trans "考勤紀錄提示:" %}{% trans "顯示系統中最新輸入之 20 筆學生考勤簽到時間明細,點擊學生姓名或查看詳情可瀏覽個人完整簽到與報告紀錄。" %} +
+ +
+
+
+ {% trans "最近 20 筆考勤簽到紀錄" %} +
+
+
+ {% if recent_records %}
- - +
+ - - - + + + + {% for record in recent_records %} - - - + + + + {% endfor %}
學生姓名學號考勤時間#{% trans "姓名" %}{% trans "身分 / 學位" %}{% trans "考勤簽到時間" %}
{{ record.student.name }}{{ record.student.student_id }}{{ record.datetime|date:"Y-m-d H:i:s" }}{{ forloop.counter }} + {% get_current_language as CURRENT_LANG %} + {% if CURRENT_LANG == 'en' and record.student.foreign_alias %} + {{ record.student.foreign_alias }} + {% else %} + {{ record.student.name }} + {% endif %} + + {% if record.student.degree_type == '博士生' %} + {% trans "博士生" %} + {% elif record.student.degree_type == '學士生' %} + {% trans "學士生" %} + {% else %} + {% trans "碩士生" %} + {% endif %} + + {% if record.student.student_category == '在職生' %} + {% trans "在職生" %} + {% elif record.student.student_category == '交換中' %} + {% trans "交換中" %} + {% else %} + {% trans "一般生" %} + {% endif %} + + {{ record.datetime|timezone:"Asia/Taipei"|date:"Y-m-d H:i:s" }} +
- {% else %} -

目前沒有考勤紀錄。

- {% endif %} + {% else %} +
+ + {% trans "目前沒有考勤紀錄。" %} +
+ {% endif %} +
{% endblock %} \ No newline at end of file diff --git a/point_system/students/templates/students/record_attendance.html b/point_system/students/templates/students/record_attendance.html index 2513679..2f8e6cb 100644 --- a/point_system/students/templates/students/record_attendance.html +++ b/point_system/students/templates/students/record_attendance.html @@ -1,30 +1,203 @@ {% extends 'base.html' %} -{% load tz %} +{% load i18n tz %} + +{% block title %}{% trans "記錄考勤" %} - 1218 Lab{% endblock %} {% block content %} -

紀錄點名

- -
- {% csrf_token %} - -
- {% for student in students %} -
- {% endfor %} +
+

+ {% trans "記錄考勤" %} +

+
+ + + + {% blocktrans with count=students|length %}在學學生: {{ count }} 人{% endblocktrans %} + + + {% now "Y-m-d H:i" %} + +
+
+ + + {% csrf_token %} +
+
+
+ + + {% for student in students %} + + + + + + {% empty %} + + + + {% endfor %} + +
+ + + + + {% if student.degree_type == '博士生' %} + {% trans "博士生" %} + {% elif student.degree_type == '學士生' %} + {% trans "學士生" %} + {% else %} + {% trans "碩士生" %} + {% endif %} + + {% if student.student_category == '在職生' %} + {% trans "在職生" %} + {% elif student.student_category == '交換中' %} + {% trans "交換中" %} + {% else %} + {% trans "一般生" %} + {% endif %} +
+ {% trans "無在學學生資料" %} +
+
+
+ +
- {% if point_records %} -

Recent Attendance Records

-
    - {% for record in point_records %} -
  • {{ record.datetime|timezone:"Asia/Taipei"|date:"Y-m-d H:i:s" }} - {{ record.student.name }}
  • - {% endfor %} -
+
+
+
+ {% trans "本次已成功紀錄考勤" %} +
+ {% blocktrans with count=point_records|length %}共 {{ count }} 筆紀錄{% endblocktrans %} +
+
+
+ + + + + + + + + + + {% for record in point_records %} + + + + + + + {% endfor %} + +
#{% trans "姓名" %}{% trans "身分 / 學位" %}{% trans "考勤簽到時間" %}
{{ forloop.counter }} + {% get_current_language as CURRENT_LANG %} + {% if CURRENT_LANG == 'en' and record.student.foreign_alias %} + {{ record.student.foreign_alias }} + {% else %} + {{ record.student.name }} + {% endif %} + + {% if record.student.degree_type == '博士生' %} + {% trans "博士生" %} + {% elif record.student.degree_type == '學士生' %} + {% trans "學士生" %} + {% else %} + {% trans "碩士生" %} + {% endif %} + + {% if record.student.student_category == '在職生' %} + {% trans "在職生" %} + {% elif record.student.student_category == '交換中' %} + {% trans "交換中" %} + {% else %} + {% trans "一般生" %} + {% endif %} + + {{ record.datetime|timezone:"Asia/Taipei"|date:"Y-m-d H:i:s" }} +
+
+
+
{% endif %} + {% endblock %} \ No newline at end of file diff --git a/point_system/students/templates/students/show_attendance.html b/point_system/students/templates/students/show_attendance.html index 7e2a9ce..75e12d4 100644 --- a/point_system/students/templates/students/show_attendance.html +++ b/point_system/students/templates/students/show_attendance.html @@ -18,20 +18,21 @@
-
-
+
+
學號 {{ student.student_id }}
-
+
姓名 {{ student.name }} + {% if student.foreign_alias %}({{ student.foreign_alias }}){% endif %}
-
+
學位 / 身分
{% if student.degree_type == '博士生' %} @@ -52,8 +53,14 @@
-
-
+
+
+ 入學日期 + {{ student.admission_date|date:"Y-m-d"|default:"--" }} +
+
+
+
總簽到次數 {% if can_view_records %}{{ point_records|length }} 次{% else %}受限{% endif %}
diff --git a/point_system/students/templates/students/student_list.html b/point_system/students/templates/students/student_list.html index e491a77..9b5cb70 100644 --- a/point_system/students/templates/students/student_list.html +++ b/point_system/students/templates/students/student_list.html @@ -42,17 +42,20 @@ + + {% if user.is_authenticated %}{% endif %} {% for item in student_data %} + + {% if user.is_authenticated %}{% endif %} {% empty %} - + {% endfor %} diff --git a/point_system/students/templates/students/weekly_schedule.html b/point_system/students/templates/students/weekly_schedule.html index fd79f00..ac0f55b 100644 --- a/point_system/students/templates/students/weekly_schedule.html +++ b/point_system/students/templates/students/weekly_schedule.html @@ -127,7 +127,7 @@ {% endif %} {% for row in summary_data %} - + {% for student in item.students %} - + - +
學號 姓名 在學狀態 學位類別 身分類別 班級/年級入學日期詳細資料
{{ item.student.student_id }} {{ item.student.name }} {% if item.student.is_active %} @@ -80,11 +83,12 @@ {% endif %} {{ item.student.grade|default:"未填" }}{{ item.student.admission_date|date:"Y-m-d"|default:"--" }}查看詳情
沒有符合條件的學生資料沒有符合條件的學生資料
{% if user.is_authenticated %}{% endif %}{{ student.name }} + class="fas fa-grip-vertical text-muted me-2">{% endif %}{% get_current_language as CURRENT_LANG %}{% if CURRENT_LANG == 'en' and student.foreign_alias %}{{ student.foreign_alias }}{% else %}{{ student.name }}{% endif %} {% if student.degree_type == '博士生' %} @@ -216,7 +216,7 @@ {% endif %} {% if user.is_authenticated %}{% endif %}{{ student.name }} + class="fas fa-grip-vertical text-muted me-2">{% endif %}{% get_current_language as CURRENT_LANG %}{% if CURRENT_LANG == 'en' and student.foreign_alias %}{{ student.foreign_alias }}{% else %}{{ student.name }}{% endif %} {% if student.degree_type == '博士生' %} @@ -280,7 +280,7 @@
{{ row.student.name }}{% get_current_language as CURRENT_LANG %}{% if CURRENT_LANG == 'en' and row.student.foreign_alias %}{{ row.student.foreign_alias }}{% else %}{{ row.student.name }}{% endif %} {{ row.rule_desc }} {% if row.assigned_dates %} diff --git a/point_system/students/templates/students/weekly_schedule_en_iframe.html b/point_system/students/templates/students/weekly_schedule_en_iframe.html index 5aeee27..2e87374 100644 --- a/point_system/students/templates/students/weekly_schedule_en_iframe.html +++ b/point_system/students/templates/students/weekly_schedule_en_iframe.html @@ -72,7 +72,7 @@
{{ student.name }}{% if student.foreign_alias %}{{ student.foreign_alias }}{% else %}{{ student.name }}{% endif %} {% if student.degree_type == '博士生' %} {% for student in item.students %}
{{ student.name }}{% if student.foreign_alias %}{{ student.foreign_alias }}{% else %}{{ student.name }}{% endif %} {% if student.degree_type == '博士生' %} = today] or m1_mondays valid_m2 = [d for d in m2_mondays if d >= today] or m2_mondays + # Remove any existing records that are prior to student's admission date + WeeklySchedule.objects.filter( + year__in=[m1_year, m2_year], + month__in=[m1_month, m2_month], + student__admission_date__isnull=False, + date__lt=F('student__admission_date') + ).delete() + if not m1_exists or force: for idx, student in enumerate(m1_pool): - monday_date = valid_m1[idx % len(valid_m1)] + valid_mondays_for_student = [ + d for d in valid_m1 + if not student.admission_date or d >= student.admission_date + ] + if not valid_mondays_for_student: + continue + monday_date = valid_mondays_for_student[idx % len(valid_mondays_for_student)] WeeklySchedule.objects.get_or_create(student=student, date=monday_date) if not m2_exists or force: for idx, student in enumerate(m2_pool): - monday_date = valid_m2[idx % len(valid_m2)] + valid_mondays_for_student = [ + d for d in valid_m2 + if not student.admission_date or d >= student.admission_date + ] + if not valid_mondays_for_student: + continue + monday_date = valid_mondays_for_student[idx % len(valid_mondays_for_student)] WeeklySchedule.objects.get_or_create(student=student, date=monday_date) +@login_required(login_url='login') def index(request): """Home page view showing the Weekly Seminar Schedule.""" return weekly_schedule(request) @@ -360,6 +410,7 @@ def ad_login(request): form = ADLoginForm() return render(request, 'students/login.html', {'form': form}) +@login_required(login_url='login') def ad_logout(request): """Active Directory Logout View""" logout(request) @@ -462,6 +513,7 @@ def recent_attendance(request): recent_records = PointRecord.objects.select_related('student').order_by('-datetime')[:20] return render(request, 'students/recent_attendance.html', {'recent_records': recent_records}) +@login_required(login_url='login') def student_list(request): active_only_param = request.GET.get('active_only') if active_only_param is None: @@ -492,6 +544,7 @@ def student_list(request): 'is_admin_user': is_admin_user, }) +@login_required(login_url='login') def weekly_schedule(request): """ Weekly seminar schedule view listing Mondays of current and next months. @@ -632,6 +685,12 @@ def move_schedule_item(request): if student.student_category == '交換中': return JsonResponse({'success': False, 'error': '交換生無需報告,無法排程'}, status=400) + if student.admission_date and to_date < student.admission_date: + return JsonResponse({ + 'success': False, + 'error': f'排程調整限制:該學生入學日期為 {student.admission_date.strftime("%Y-%m-%d")},不可將報告日期移轉至入學日期前 (無法移至 {to_date})。' + }, status=400) + is_monthly = (student.degree_type in ('碩士生', '學士生')) or (student.degree_type == '博士生' and student.student_category == '一般生') if is_monthly: @@ -655,7 +714,6 @@ def move_schedule_item(request): log_msg = f"AUDIT LOG: User '{request.user.username}' (authenticated) moved weekly schedule for student '{student.student_id}' ({student.name}) from {from_date} to {to_date}." logger.info(log_msg) schedule_logger.info(log_msg) - print(log_msg) return JsonResponse({ 'success': True, @@ -671,7 +729,7 @@ def move_schedule_item(request): def verify_schedule_rules(request): """ Verify and clean up WeeklySchedule records based on frequency rules: - - Rule 1: Delete all schedule cards for Exchange students (交換中) or inactive students. + - Rule 1: Delete all schedule cards for Exchange students (交換中), inactive students, or dates before admission_date. - Rule 2: 碩士生 & 博士一般生 must have exactly 1 report per month. Delete excess cards, create missing cards. - Rule 3: 博士在職生 must have exactly 1 report per 2 months. Delete excess cards, create missing cards. Logs user action and results. @@ -690,13 +748,14 @@ def verify_schedule_rules(request): deleted_count = 0 created_count = 0 - # 1. Delete all cards for Exchange students (交換中) or inactive students - from django.db.models import Q + # 1. Delete cards for Exchange students (交換中), inactive students, or dates before admission_date invalid_records = WeeklySchedule.objects.filter( year__in=[m1_year, m2_year], month__in=[m1_month, m2_month] ).filter( - Q(student__student_category='交換中') | Q(student__is_active=False) + Q(student__student_category='交換中') | + Q(student__is_active=False) | + Q(student__admission_date__isnull=False, date__lt=F('student__admission_date')) ) deleted_count += invalid_records.count() invalid_records.delete() @@ -710,6 +769,10 @@ def verify_schedule_rules(request): for s in active_students: is_monthly = (s.degree_type in ('碩士生', '學士生')) or (s.degree_type == '博士生' and s.student_category == '一般生') + valid_m1_for_s = [d for d in valid_m1_mondays if not s.admission_date or d >= s.admission_date] + valid_m2_for_s = [d for d in valid_m2_mondays if not s.admission_date or d >= s.admission_date] + valid_all_for_s = [d for d in valid_all_mondays if not s.admission_date or d >= s.admission_date] + if is_monthly: # Check Month 1 m1_records = list(WeeklySchedule.objects.filter(student=s, year=m1_year, month=m1_month).order_by('date')) @@ -718,8 +781,8 @@ def verify_schedule_rules(request): r.delete() deleted_count += 1 elif len(m1_records) == 0: - if valid_m1_mondays: - monday = min(valid_m1_mondays, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) + if valid_m1_for_s: + monday = min(valid_m1_for_s, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) WeeklySchedule.objects.create(student=s, date=monday) created_count += 1 @@ -730,8 +793,8 @@ def verify_schedule_rules(request): r.delete() deleted_count += 1 elif len(m2_records) == 0: - if valid_m2_mondays: - monday = min(valid_m2_mondays, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) + if valid_m2_for_s: + monday = min(valid_m2_for_s, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) WeeklySchedule.objects.create(student=s, date=monday) created_count += 1 else: @@ -742,15 +805,14 @@ def verify_schedule_rules(request): r.delete() deleted_count += 1 elif len(bimonthly_records) == 0: - if valid_all_mondays: - monday = min(valid_all_mondays, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) + if valid_all_for_s: + monday = min(valid_all_for_s, key=lambda d: WeeklySchedule.objects.filter(date=d).count()) WeeklySchedule.objects.create(student=s, date=monday) created_count += 1 log_msg = f"AUDIT LOG: User '{request.user.username}' executed Verify & Clean Schedule Rules. Deleted {deleted_count} non-compliant/exchange cards, added {created_count} missing cards." logger.info(log_msg) schedule_logger.info(log_msg) - print(log_msg) # Update static English HTML for iframe embed generate_english_schedule_static_html()