匯東網


py 作業登記器(C 程序設計)

[編輯] [转简体]
|
作者:huidong | 分類:【編程】python
[ 16 瀏覽 0 評論 5 贊 5 踩 ]

概要
用於登記作業提交情況

正文

######################################
#
#   C 程序設計 作業登記器
#   
#   by huidong <mailhuid@163.com>
#   2024.10.12  Python 3.9(64-bit)
#

import csv
from gettext import find
from msvcrt import getch
import os  
import tkinter as tk  
from tkinter import filedialog  

# 學生信息記錄文件
student_data_path = "students_data.csv"

############## 文件操作函數 ##############

# 讓用戶選擇文件夾(未選擇返回 False)
def select_folder():  
    # 创建一个隐藏的主窗口  
    root = tk.Tk() 
    root.withdraw()     # 隐藏主窗口  
    
    # 打开文件夹选择对话框  
    folder_path = filedialog.askdirectory()  
    
    root.destroy()      # 銷燬窗口 
  
    # 检查用户是否选择了文件夹  
    if folder_path:  
        return folder_path
    else:  
        print("未選擇文件夾")  
        return False

# 查詢文件夾中的所有文件(成功則返回文件名列表,失敗返回 False)
def get_filenames_in_folder(folder_path : str):  
    try:  
        # 获取文件夹中的所有文件和子文件夹  
        files_and_dirs = os.listdir(folder_path)  
  
        # 过滤出文件  
        files = [f for f in files_and_dirs if os.path.isfile(os.path.join(folder_path, f))]  

        return files
    except Exception as e:  
        print(f"獲取文件夾內容時出錯: {e}")  
        return False

# # 從記錄的數據中讀取學生信息列表
# # 包含以下信息:
# # npuid             學生學號
# # name              學生姓名    
# # submit            交作業次數    
# def read_students_info():
#     students = []  
#     with open(student_data_path, mode='r', encoding='utf-8') as file:  
#         for line in file:  
#             parts = line.strip().split(',')  
#             students.append({  
#                 'npuid' : parts[0],         # 學號
#                 'name' : parts[1],          # 姓名
#                 'submit' : int(parts[2])    # 交作業次數
#             })  
#     return students

# 從記錄的數據中讀取學生信息列表
# 包含以下信息:
# npuid             學生學號
# name              學生姓名    
# submit            交作業次數    
def read_students_info():  
    students = []  
    with open(student_data_path, mode='r', encoding='utf-8', newline='') as file:  
        reader = csv.DictReader(file, fieldnames=['npuid', 'name', 'submit'])  
        
        is_first_row = True     # 跳過第一行的表頭
        for row in reader:
            if is_first_row: is_first_row = False; continue
            row['submit'] = int(row['submit'])  # 提交次數字段轉整數
            students.append(row)
    return students

# # 將學生信息列表寫入數據記錄中
# def write_students_info(students : list):
#     with open(student_data_path, mode='w', encoding='utf-8') as file:  
#         for student in students:  
#             file.write(f"{student['npuid']},{student['name']},{student['submit']}\n")

# 將學生信息列表寫入數據記錄中
def write_students_info(students: list):  
    with open(student_data_path, mode='w', encoding='utf-8', newline='') as file:  
        fieldnames = ['npuid', 'name', 'submit']  
        writer = csv.DictWriter(file, fieldnames=fieldnames)  
        
        # 寫入表頭
        writer.writeheader()  
          
        # 寫入數據行  
        for student in students:  
            writer.writerow(student)

############## 算法相關函數 ##############

# 從文件名(不包括 .*)中解碼學生信息(返回 (學號, 姓名) 的元組,若學號不存在則元組字符串爲空)
def decode_filename(filename: str):
    
    # 第一步,通過學號 2024 開頭,直接讀 10 位數字,學號讀出來一般都不會有問題
    # 要是連學號都沒有,那就不讀了
    student_id = ""
    begin_student_id = filename.find("2024")
    if begin_student_id != -1:
        end_student_id = begin_student_id + 10
        student_id = filename[begin_student_id : end_student_id]
    else:
        return "", ""

    # 第二步,收集姓名,對文件名的要求是:
    # 作業名稱可以沒有,但是有的話一定要放在學號和名字之後
    # 分隔符可以是 ' ', '_', '-' 或者直接依靠學號的數字進行分隔
    student_name = ""
    separator = {' ', '_', '-'}
    if begin_student_id == 0:   # 如果一開始就是學號,那麼認爲下一段中文是姓名
        for ch in filename[10:]:
            if ch in separator:
                if student_name.__len__(): break
                else: continue
            student_name += ch
    else:                       # 否則一開始就是姓名
        for ch in filename[:begin_student_id]:
            if ch in separator:
                if student_name.__len__(): break
                else: continue
            student_name += ch
            
    return student_id, student_name    

# 根據文件全名獲取作業信息
# 返回作業信息,包含以下內容:
# filename          作業文件名稱
# file_extension    作業文件擴展名
# npuid             學生學號
# name              學生姓名
def get_homework_info(file_fullname : str):
    file_name, file_extension = os.path.splitext(file_fullname)      # 分離文件名和後綴
    student_id, student_name = decode_filename(file_name)   # 從文件名解碼學生學號和姓名
    homework_info = {       # 記錄這一份作業的信息
        'filename': file_name, 
        'file_extension' : file_extension, 
        'npuid': student_id, 
        'name' : student_name
    }
    return homework_info

############## 主要模塊函數 ##############

# 審覈新的學生(作業)信息,返回審覈結果(bool)
def review_student_info(homework_list : list):
    print("")
    print("============ 學生(作業)信息 ============")
    for homework in homework_list:
        print(
            homework['npuid'],
            homework['name'] + '\t\t' + '(' + homework['filename'] + homework['file_extension'] + ')')
    print(f"總計:{len(homework_list)} 條信息")
    print("==========================================")
    print("審覈通過?(y / n)")
    print("")
    while True:
        ch = getch().decode()
        if ch == 'n':
            return False
        elif ch == 'y':
            return True

# 登記作業(完成此次登記返回 True,放棄此次登記返回 False)
def register():
    
    folder = select_folder()                        # 選擇作業文件夾
    if folder == False:
        return False
    
    filenames = get_filenames_in_folder(folder)     # 讀取文件夾下的所有文件

    # 讀入已有學生數據
    students_info = read_students_info()

    # 作業分揀箱
    list_noID = []          # 沒有寫學號的作業
    list_new = []           # 屬於新同學的作業
    list_old = []           # 屬於之前有完整記錄(學號和姓名)的學生的作業
    list_noname_old = []    # 屬於之前有學號但沒登記姓名的學生的作業

    # 分揀作業
    for filename in filenames:  
        homework_info = get_homework_info(filename)
        if homework_info['npuid'] == '':        # 沒寫學號的作業不收
            list_noID.append(homework_info)
            
        # 寫了學號的作業要進行進一步分揀
        else:                                   
            isNew = True            # 是否爲新學生
            isNoName = False        #(對於老生)是否登記有他的名字
            
            for student in students_info:       # 查詢登記在冊的學生信息表
                if student['npuid'] == homework_info['npuid']:
                    isNew = False   # 查到此人,則非新學生
                    isNoName = student['name'] == ''
            
            # 是新生
            if isNew:       list_new.append(homework_info)
            # 是之前沒寫過名字的老生
            elif isNoName:  list_noname_old.append(homework_info)
            # 是信息齊全的老生
            else:           list_old.append(homework_info)
    
    ####### 作業分揀完成,以下進行學生信息的新增和補充 #######

    # 登記新同學信息
    if list_new.__len__():
        print("【審覈】有新同學信息需要審覈:")
        print("【備註】以下是所有新同學提交的作業,可能包含同一人的重複提交,實際寫入學生數據時會去重。")
        if not review_student_info(list_new): return False
            
        # 登記這些同學的信息
        unique_npuid_record = []    # 防止重複添加的學號唯一記錄列表
        for homework in list_new:
            
            # 防止重複添加
            if homework['npuid'] in unique_npuid_record: continue
            unique_npuid_record.append(homework['npuid'])
            
            student = {
                'npuid' :  homework['npuid'],
                'name' :  homework['name'],
                'submit' :  0
            }
            students_info.append(student) 
                    
    # 若存在曾經沒寫名字的老生交作業,則對這部分老生重新登記名字
    if list_noname_old.__len__():
        list_have_name = []     # 從裏面找出現在寫了名字的
        for homework in list_noname_old:
            if homework['name'] != '':
                list_have_name.append(homework)
        if list_have_name.__len__():
            print("【審覈】有曾經沒寫過名字的老生補充了他們的名字,需要審覈:")
            if not review_student_info(list_have_name): return False
            
            # 更新這些同學的信息
            for homework in list_have_name:
                for student in students_info:
                    if student['npuid'] == homework['npuid']:
                        student['name'] = homework['name']

    ####### 學生信息處理完成,以下進行作業登記 #######

    homework_all = list_new + list_old + list_noname_old
    npuid_submit = []       # 記錄提交過有效作業的學生學號
    valid_homework = []     # 提交有效的作業
    invalid_homework = []   # 提交無效的作業
    
    # 合法後綴名
    valid_ext = ('.c', '.cpp', '.exe')
    
    # 登記作業
    for homework in homework_all:
        
        # 後綴名合法才可提交
        ext = homework['file_extension']
        if ext not in valid_ext:
            invalid_homework.append(homework)
            continue
        
        # 在同一次登記中,一個人提交多次作業,只登記第一次
        if homework['npuid'] not in npuid_submit:
            npuid_submit.append(homework['npuid'])
            valid_homework.append(homework)
    
    # 存在無學號作業
    if list_noID.__len__():
        print("【審覈】存在無學號的作業,需要審覈:")
        if not review_student_info(list_noID): return False

    # 存在無效作業
    if invalid_homework.__len__():
        print("【審覈】存在文件後綴名錯誤的作業,需要審覈:")
        if not review_student_info(invalid_homework): return False
        
    # 存在有效作業
    if valid_homework.__len__():
        print("【審覈】以下是提交有效的作業,需要審覈:")
        print("【備註】提交有效,即去重了無學號、後綴名錯誤、重複提交的作業。")
        if not review_student_info(valid_homework): return False
    else:
        print("【警告】沒有有效提交的作業")

    # 寫入學生作業登記表
    for student in students_info:
        if student['npuid'] in npuid_submit:
            student['submit'] += 1
    
    write_students_info(students_info)
    print(f"【完成】有效提交作業人次 {valid_homework.__len__()}")

    return True

# 主菜單
def menu():
    print("")
    print("==============================")
    print("+   作業登記器 - by huidong")
    print("============================== ")
    print("按任意鍵開始登記作業\n")

    getch()
    if register():
        print("【恭喜】完成本次作業登記")
    else:
        print("【中止】放棄本次作業登記")

############## 主函數 ##############
        
if __name__ == "__main__":  
    while True:
        menu()


[ 5] [ 5]


 評論區  0 條評論

+ 添加評論