2024年5月31日金曜日

当直拘束表Excel出力

 以下のように出力しました。


各日、次のような出力です。

(短時間宿直)

当直

内科拘束

整形拘束


短時間宿直シフトは、暦日通りに出力しています。スケジュールナースのシフトから補正しています。

プロジェクトの全Pythonソースです。mainのPython部(制約)が最初、post部のPythonのmain def post_main() が最後に配置することが注意点です。制約のpythonとpostのpythonは、別プロセスであることにも注意してください。通信はできません。

post処理では、shift_solutionがシフト解配列として利用可能ですので、ひたすら解を読んで上記フォーマットとなるように整形しています。タスク解(診療科)も利用可能ですが、上記フォーマット出力するには、シフト解のみで十分です。

本プロジェクトは、大変に複雑な仕様です。本コードは、そのための開発デバッグ検証用も兼ねており、開発当初から、改変を積み重ねてきました。冗長なコードになっているのはそのためです。

ユーザには、スケジュール解ではなく、開発当初より、本Excel解を提出してデバッグにご協力いただきました。元々の顧客フォーマットが上のようなものなので、違和感なく検証いただけたと思います。Excel解の提出のフェーズが完了しました。以降、スケジュールナースプロジェクトを使用した、デバッグフェーズに移行します。


import sc3
import sys
import os
import csv
import re
import win32gui, win32con
import pywin.dialogs.list
import calendar
from collections import namedtuple

for person in 非常勤:
    for day in 今月:
            if shift_schedules[person][day][0]=='':
                v=sc3.GetShiftVar(person,day,'その他')
                s='非常勤は予定入力がないときはその他 '+staffdef[person]+' '+daydef[day]
                sc3.AddHard(v,s)

def get_open_file_name(title):
    filter='xlsx\0*.xlsx\0'
    customfilter='Other file types\0*.*\0'
    fname, customfilter, flags=win32gui.GetOpenFileNameW(
    InitialDir=project_file_path, 
    Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
    File='', DefExt='csv',
    Title=title,#
    Filter=filter,
    CustomFilter=customfilter,
    FilterIndex=0)
    return str(fname)


def draw拘束(ws,c,person,str):   
        if person in 内科:
            ws.Cells(c.Row+3,c.Column).Value=str
        elif person in 整形外科:
            ws.Cells(c.Row+4,c.Column).Value=str

def day_solution_analysis(ws,c,n,day):

    #clear 
   
    
    宿日直person=-1
    日直person=-1
    宿直person=-1
    
    for person in 非常勤:
        if shift_solution[person][day]=='その他':
            continue
        
        if '宿日直' in shift_solution[person][day]:
            if 宿日直person!=-1:
                print("fatal error 宿日直は、 most one work per a day")
                exit()
            宿日直person=person
            continue

        if '日直(日中のみ)' == shift_solution[person][day]:
            if 日直person!=-1:

                print("fatal error 日直は、 most one work per a day")
                exit()
            日直person=person
            continue

        if '宿直'in shift_solution[person][day]:
            if 宿直person!=-1:
                print("fatal error 宿直は、 at most one work per a day")
                exit()
            宿直person=person
            continue

        print("fatal error ")
        exit()
        
    list=[]
    
    短宿person=-1
    拘束宿list=[]
    拘束日直list=[]
    日直拘束宿日直person=-1
    宿直拘束宿日直person=-1

    for person in 常勤:
        if shift_solution[person][day]=='その他':
             continue
        if shift_solution[person][day]=='宿日直':
            if 宿日直person!=-1:
                print("fatal error 宿日直は、 most one work per a day")
                exit()
            宿日直person=person
            continue

        if '30分'in shift_solution[person][day] or '60分' in shift_solution[person][day] or '90分' in shift_solution[person][day]:
            
            if 短宿person!=-1:
                print(staffdef[person],daydef[day])
                print("fatal error 短宿は、 at most one work per a day")
                exit()
            短宿person=person
            print(staffdef[短宿person],daydef[day],shift_solution[短宿person][day])
            continue

        if '日直(日中のみ)' in shift_solution[person][day] :
            if 日直person!=-1:
                print(staffdef[person],daydef[day])
                print("fatal error 日直は、 at most one work per a day")
                exit()
            日直person=person
            continue

        if '宿直'==shift_solution[person][day] :
            if 宿直person!=-1:
                print("fatal error 宿直は、 at most one work per a day")
                exit()
            宿直person=person
            continue

        if '日直拘束宿日直'==shift_solution[person][day]:
            if 日直拘束宿日直person!=-1:
                print("fatal error 日直拘束宿日直は、 at most one work per a day")
                exit()
            日直拘束宿日直person=person
            continue

        if '宿直拘束宿日直'==shift_solution[person][day]:
            if 宿直拘束宿日直person!=-1:
                print("fatal error 宿直拘束宿日直は、 at most one work per a day")
                exit()
            宿直拘束宿日直person=person
            continue

        if '拘束宿'==shift_solution[person][day]:
            拘束宿list.append(person)
            continue

        if '拘束日直'==shift_solution[person][day]:
            拘束日直list.append(person)
            continue


        #print("list add",staffdef[person])
        list.append((person,shift_solution[person][day]))

    if 短宿person!=-1:#Draw 短縮
        print(短宿person,staffdef[短宿person],daydef[day],n,shift_solution[短宿person][day])
        
        str=staffdef[短宿person]+' '
        if '拘束' in shift_solution[短宿person][day]:
            draw拘束(ws,c,短宿person,staffdef[短宿person])

        if shift_solution[短宿person][day]=='夜30分宿直' or shift_solution[短宿person][day]=='夜30分宿直拘束付':
            str +='17:00~17:30'
            ws.Cells(c.Row+1,c.Column+1).Value=str
            
        elif shift_solution[短宿person][day]=='夜60分宿直' or shift_solution[短宿person][day]=='夜60分宿直拘束付':
            str +='17:00~18:00'
            ws.Cells(c.Row+1,c.Column+1).Value=str
        elif shift_solution[短宿person][day]=='朝30分宿直' or shift_solution[短宿person][day]=='朝30分宿日直拘束付':
            str +='8:00~8:30'
            print("!!朝30分",staffdef[person],daydef[day],n.Row,n.Column)
            if n!=None:
                print(staffdef[短宿person],daydef[day])
                ws.Cells(n.Row+1,n.Column+1).Value=str
        elif shift_solution[短宿person][day]=='朝60分宿直' or shift_solution[短宿person][day]=='朝60分宿日直拘束付':
            str +='7:30~8:30'
            if n!=None:
                ws.Cells(n.Row+1,n.Column+1).Value=str
                print(staffdef[短宿person],daydef[day],n.Row+1,n.Column,ws.Cells(n.Row+1,n.Column).Value)
        elif shift_solution[短宿person][day]=='朝90分宿直' or shift_solution[短宿person][day]=='朝90分宿日直拘束付':
            str +='7:00~8:30'
            if n!=None:
                ws.Cells(n.Row+1,n.Column+1).Value=str

    if 日直person!=-1 or 日直拘束宿日直person!=-1:
        if 宿直person==-1 and  宿直拘束宿日直person==-1:
            print("fatal error 日直があるなら宿直もないとだめ")
            exit()
        if 宿直person!=-1:
            strlast=staffdef[宿直person]
        else:
            strlast=staffdef[宿直拘束宿日直person]
        if 日直person!=-1:
            str=staffdef[日直person]+'/'+strlast
            ws.Cells(c.Row+2,c.Column).Value=str
            if 宿直拘束宿日直person!=-1:
                draw拘束(ws,c,宿直拘束宿日直person,strlast)
            else:
                draw拘束(ws,c,宿直person,strlast)
        elif 日直拘束宿日直person!=-1:
            str=staffdef[日直拘束宿日直person]+'/'+strlast
            ws.Cells(c.Row+2,c.Column).Value=str
            draw拘束(ws,c,日直拘束宿日直person,str)

    elif 宿日直person!=-1:
        str=staffdef[宿日直person]
        ws.Cells(c.Row+2,c.Column).Value=str
        draw拘束(ws,c,宿日直person,str)

    elif 宿直person!=-1:
        str=staffdef[宿直person]
        ws.Cells(c.Row+2,c.Column).Value=str
        draw拘束(ws,c,宿直person,str)
        
    else:
        print("fatal error ")
        exit()

    
    if len(拘束日直list)==1:
        str=staffdef[拘束日直list[0]]
        if 宿直person==-1 and len(拘束宿list)!=1:

            print("fatal error 拘束日直があるなら、拘束宿日直もしくは宿直もないとだめ")
            exit()
        if 拘束日直list[0] in 内科:
            if 宿直person not in 内科 and 拘束宿list[0] not in 内科:
                print("fatal error")
                exit()
        elif 拘束日直list[0] in 整形外科:
            if 宿直person not in 整形外科 and 拘束宿list[0] not in 整形外科:
                print("fatal error")
                exit()
        if 宿直person !=-1:
            str=staffdef[拘束日直list[0]]+'/'+staffdef[宿直person]
        elif len(拘束宿list)==1:
            str=staffdef[拘束日直list[0]]+'/'+staffdef[拘束宿list[0]]
        print("拘束日直list[0]",str)
        draw拘束(ws,c,拘束日直list[0],str)
    elif len(拘束日直list)==2:
        if 宿直person==-1 or len(拘束宿list)!=1:
            print("fatal error 拘束日直があるなら、拘束宿直および宿直どちらもないとだめ")
            exit()
        if 拘束日直list[0] in 内科:
            if 宿直person in 内科:
                str=staffdef[拘束日直list[0]]+'/'+staffdef[宿直person]
                draw拘束(ws,c,拘束日直list[0],str)
                str=staffdef[拘束日直list[1]]+'/'+staffdef[拘束宿list[0]]
                draw拘束(ws,c,拘束日直list[1],str)

            elif 拘束宿list[0] in 内科:
                str=staffdef[拘束日直list[0]]+'/'+staffdef[拘束宿list[0]]
                draw拘束(ws,c,拘束日直list[0],str)
                str=staffdef[拘束日直list[1]]+'/'+staffdef[宿直person]
                draw拘束(ws,c,拘束日直list[1],str)
            else:
                print("fatal error 拘束日直が内科なら、拘束宿直および宿直どちらは内科")
                exit()
        elif 拘束日直list[0] in 整形外科:
            if 宿直person in 整形外科:
                str=staffdef[拘束日直list[0]]+'/'+staffdef[宿直person]
                draw拘束(ws,c,拘束日直list[0],str)
                str=staffdef[拘束日直list[1]]+'/'+staffdef[拘束宿list[0]]
                draw拘束(ws,c,拘束日直list[1],str)

            elif 拘束宿list[0] in 整形外科:
                str=staffdef[拘束日直list[0]]+'/'+staffdef[拘束宿list[0]]
                draw拘束(ws,c,拘束日直list[0],str)
                str=staffdef[拘束日直list[1]]+'/'+staffdef[宿直person]
                draw拘束(ws,c,拘束日直list[1],str)
            else:
                print("fatal error 拘束日直が整形外科なら、拘束宿直および宿直どちらは整形外科")
                exit()
 
    elif len(拘束宿list) >=1:
        for person in 拘束宿list:
            str=staffdef[person]
            draw拘束(ws,c,person,str)

        #draw拘束(ws,c,拘束宿person,str)
        
    if len(list) <=2:
        for tuple in list:
            if '拘束宿日直' not in tuple[1]:
                print(staffdef[tuple[0]],tuple[1])
                print("fatal error ")
                exit()
            str=staffdef[tuple[0]]
            print('拘束宿日直',str)
            draw拘束(ws,c,tuple[0],str)
        
    else:
        print("len(list)",len(list),len(拘束宿list),len(拘束日直list))
        print("fatal error programming error")
        exit()


def get_sheet_name():
    s=daydef[制約開始日]

    year_str=s[0:4]
    month=int(s[5:7])
    sheet_name=year_str+'年'+str(month)+'月'
    return sheet_name

def get_year():
    s=daydef[制約開始日]
    year_str=s[0:4]
    return int(year_str)

def get_month():
    s=daydef[制約開始日]
    month=int(s[5:7])
    return month

def get_day():
    s=daydef[制約開始日]
    day=int(s[8:10])
    return day


def find_address(moncal,D):
    row=0
    for r in moncal:
        if D not in r:
            row+=1
            continue
        return (row,r.index(D))
    #次月最初の日:最終行の最終列+1を出す
    row=len(moncal)-1
    col=len(moncal[row])
    return (row,col)
    raise IndexError

def init_row_columns():
    global start_row
    global start_column
    global row_interval
    global column_interval

    start_row=6
    start_column=2
    row_interval=6
    column_interval=2

def findcell(ws,moncal,D):
    tuple=find_address(moncal,D)
    init_row_columns()
    row=start_row+row_interval*tuple[0]
    column=start_column+column_interval*tuple[1]
    c=namedtuple("Row","Column")
    c.Row=row
    c.Column=column
    return c

def clear_cells(ws):
    init_row_columns()
    for r in range(6):
        for d in range(7):
            row=start_row+r*row_interval
            column=start_column+d*column_interval
            print(row,column)
            ws.Cells(row  ,column+1).Value=""
            ws.Cells(row  ,column).Value=""
            ws.Cells(row+1,column+1).Value=""
            ws.Cells(row+1,column).Value=""
            ws.Cells(row+2,column+1).Value=""
            ws.Cells(row+2,column).Value=""
            ws.Cells(row+3,column).Value=""
            ws.Cells(row+4,column).Value=""





def post_main():
    
    print('\n\n*********ポスト処理を実行中です。*************\n')
    import win32com.client#pywin32をインポート

#すでにExcelが起動されている場合はそのタスクが使われる
#エラー終了するとタスクは残ります
    try :
        xl = win32com.client.Dispatch("Excel.Application")
    except:
        print("can not invoke excel")
        exit()
    #動いている様子を見てみる
    xl.Visible = False
    #file=get_open_file_name("Open Excel File")
    #print(file)
    os.chdir(project_file_path)
    filename="2024当直拘束表.xlsx"
   
    file=os.path.join(project_file_path,filename)
    file.replace("/","\\") #なぜか逆スラッシュでないと動かない
    wb = xl.Workbooks.Open(file)
    # Excelシートオブジェクト
    ws = wb.Worksheets(get_sheet_name())

    # 指定したシートを選択
    # Select()の使用前にシートのActivate()が必要
    ws.Activate()

    calendar.setfirstweekday(6)#日曜日を最初に
    mon_cal=calendar.monthcalendar(get_year(),get_month())#行列でリスト

    print(findcell(ws,mon_cal,1))

    clear_cells(ws)


    for day in 今月区間:
        D=day-制約開始日+1
        print(D)
        c=findcell(ws,mon_cal,D)
        n=findcell(ws,mon_cal,D+1)
        #print(c.Row,c.Column)
        day_solution_analysis(ws,c,n,day)

    wb.Close(True)# Trueで保存。False=Defaultでブックを保存せずにクローズ
    # Excel終了
    xl.Quit()
    print('\n\n*********ポスト処理を実行終了しました。*************\n')

0 件のコメント:

コメントを投稿