2022年6月12日日曜日

Pywin32によるExcel整形出力

 ソルバのPythonソース出力追加を行いました。デバッグ用としてお医者さまプロジェクトを使用しました。このプロジェクトは、シフト解とタスク解、両方とも有意な値を持ち、なおかつ、シフト・タスク予定が詰まっていて検証用として最適です。(Covid-19支援プロジェクト)

最初は、従来のCSV出力例です。シフト・タスク出力を唯一つのファイルに出力したものですが、日付等もないので、もう一段整形が必要であること分かります。

次は、今回の出力です。
予定ラベルを追加した他、出力をラベル出力として、色を定義のものに合わせています。なおかつ、見易くするために夜勤に纏わる部分だけの出力に整形しています。
そして、お客さまフォーマットに合わせて、解を出力しています。例えば、スタッフの順序を入れ替れたり、空白行があっても、問題なく出力されます。つまりそのまま最終出力としても良い形に(ご自分で)整形が可能になった、ということです。

以下は、今回のpythonソースです。予定シフト、予定タスク、解シフト、解タスクをラベルで出力しています。

<Scan Objects>
面倒なのは、ラベル色の処理で、該当するラベルは、以下を検索して一致するものを出力する必要があります。

予定タスク
    task→task_aggregate

予定シフト

    shift→shift_aggregate→phase_variables→phase_variable_aggregate

をScanする必要があります。

<Alias Labelの処理>

また、予定シフトがAliasで記述されていた場合、例えば、今回”業務”は、”業務AM”、"業務PM”をAliasとして持ちます。予定がそういう風に記述されていたら、解ラベルもそれに応じて、同じラベルで出力するのが親切でしょう。

def post_main():
    sc3.hook_stdout()#printをリダイレクトする
    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 = True
    os.chdir(project_file_path)
    file=os.path.join(project_file_path,"excel_post_export.xlsx")
    file.replace("/","\\") #なぜか逆スラッシュでないと動かない
    wb = xl.Workbooks.Open(file)
    # Excelシートオブジェクト
    ws = wb.Worksheets(1)

    # 指定したシートを選択
    # Select()の使用前にシートのActivate()が必要
    ws.Activate()
    #print(task_label_color_map['日直'][0])
    #return
    for person in 全スタッフ:
        staff_name=staffdef[person]
        c=ws.Range("B1:B51").Find(staff_name)
        #print(c.Row,c.Column)
        for day in 今月:
            col=day-制約開始日
            #print(day)
            tph=col*2
            shift_label=shift_schedules[person][day][2]
            ws.Cells(c.Row,c.Column+3+col*2).Value=shift_label#シフト予定
            shift=shift_schedules[person][day][0]
            shift_color=get_shift_label_color(shift_label)    
            ws.Cells(c.Row,c.Column+3+col*2).Interior.Color=shift_color#シフト予定色


            t02=task_schedules[person][day*2][2]#label
            t0_color=get_task_label_color(t02)
            t00=task_schedules[person][day*2][0]
            
            t12=task_schedules[person][day*2+1][2]#label
            t1_color=get_task_label_color(t12)
            t10=task_schedules[person][day*2+1][0]
            
            ws.Cells(c.Row+1,c.Column+3+tph).Value=t02#タスク予定
            ws.Cells(c.Row+1,c.Column+3+tph).Interior.Color=t0_color
            ws.Cells(c.Row+1,c.COlumn+3+tph+1).Value=t12#タスク予定
            ws.Cells(c.Row+1,c.Column+3+tph+1).Interior.Color=t1_color

            shift=shift_solution[person][day]
            if shift=='業務なし日勤':
                ws.Cells(c.Row+2,c.Column+3+col*2).Value='' #empty
                ws.Cells(c.Row+2,c.Column+3+col*2).Interior.Color=0xffffff
            else:
                label_color=get_shift_label(shift,shift_label)#return (label,color_str)
                ws.Cells(c.Row+2,c.Column+3+col*2).Value=label_color[0]
                ws.Cells(c.Row+2,c.Column+3+col*2).Interior.Color=rgb_str_to_hex(label_color[1])

            task0=task_solution[person][day*2]
            task1=task_solution[person][day*2+1]
            t0_color=get_task_color(task0)
            t1_color=get_task_color(task1)
            ws.Cells(c.Row+3,c.Column+3+tph).Interior.Color=t0_color
            ws.Cells(c.Row+3,c.Column+3+tph+1).Interior.Color=t1_color

            if task0=='' or task0=='DummyDay':
                ws.Cells(c.Row+3,c.Column+3+tph).Value=''
            else:
                task_label=get_task_label(task0)
                ws.Cells(c.Row+3,c.Column+3+tph).Value=task_label
            if task1=='' or task1=='Dummy':
                ws.Cells(c.Row+3,c.Column+3+tph+1).Value=''
            else:
                task_label=get_task_label(task1)
                ws.Cells(c.Row+3,c.Column+3+tph+1).Value=task_label


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

def get_shift_label(shift,scheduled_shift_label):
    for key in shift_label_color_map.keys():
        if key ==shift:
            item_array=shift_label_color_map[key]
            i=0
            for item in item_array:
                if i==0:
                    solution_shift_label=item[0]
                    solution_label_color=item[1]
                else:
                    if len(scheduled_shift_label)>=1 and item[0]==scheduled_shift_label:
                        return item[0],item[1]
                i+=1
            return solution_shift_label,solution_label_color
    print("Fatal Error",shift,scheduled_shift_label)
    exit()

def get_task_label(task):
    
    if task in  task_label_color_map:
        return task_label_color_map[task][0][0]
    else:
        print("Invalid Task", task)
        exit()
def rgb_to_hex(rgb):
    '''
    ws.Cells(1, i).Interior.color uses bgr in hex

    '''
    bgr = (int(rgb[2]),int(rgb[1]),int(rgb[0]))
    strValue = '%02x%02x%02x' % bgr
    # print(strValue)
    iValue = int(strValue, 16)
    return iValue

def get_task_label_color(task_label):
    if len(task_label)==0 or task_label=='.':
        return 0xffffff #空白,Dummy DummyDay
    for item_array in task_label_color_map.values():#task集合から探して
        for item in item_array:#taskは複数のラベルを持つ
            task_label_candidate=item[0]
            if task_label==task_label_candidate:#ラベル名が一致したなら
                color_str=item[1]
                rgb = re.findall(r"\d+",color_str)
                if len(rgb)==3:
                    return rgb_to_hex(rgb)
                else:
                    return 0xffffff #rgb形式でなければWhiteを返す
    for item in task_collections_def.values():#無かったらtask aggregatesから探す
        task_label_candidate=item[0]
        #if len(task_label)>=1:
        #    print("list",task_label,task_label_candidate)
        if task_label==task_label_candidate:
            #print("matched",task_label_candidate)
            color_str=item[1]
            rgb = re.findall(r"\d+",color_str)
            if len(rgb)==3:
                return rgb_to_hex(rgb)
            else:
                return 0xffffff #rgb形式でなければWhiteを返す
    #print("unmatched",task_label)
    return 0xffffff #なければWhiteを返す

def get_shift_label_color(shift_label):
    if len(shift_label)==0 or shift_label=='・':
        return 0xffffff #空白ならWhiteを返す
    for item_array in shift_label_color_map.values():#task集合から探して
        for item in item_array:#taskは複数のラベルを持つ
            shift_label_candidate=item[0]
            #print(shift_label,shift_label_candidate)
            if shift_label==shift_label_candidate:#ラベル名が一致したなら
                color_str=item[1]
                rgb = re.findall(r"\d+",color_str)
                if len(rgb)==3:
                    return rgb_to_hex(rgb)
                else:
                    return 0xffffff #rgb形式でなければWhiteを返す
    for item in shift_collections_def.values():#無かったらtask aggregatesから探す
        shift_label_candidate=item[0]
        #if len(task_label)>=1:
        #    print("list",task_label,task_label_candidate)
        if shift_label==shift_label_candidate:
            #print("matched",task_label_candidate)
            color_str=item[1]
            rgb = re.findall(r"\d+",color_str)
            if len(rgb)==3:
                return rgb_to_hex(rgb)
            else:
                return 0xffffff #rgb形式でなければWhiteを返す
    for item in phase_objects_def.values():
        shift_label_candidate=item[0]
        if shift_label==shift_label_candidate:
            color_str=item[1]
            rgb = re.findall(r"\d+",color_str)
            if len(rgb)==3:
                return rgb_to_hex(rgb)
            else:
                return 0xffffff #rgb形式でなければWhiteを返す
    for item in phase_aggregate_object_def.values():
        shift_label_candidate=item[0]
        if shift_label==shift_label_candidate:
            color_str=item[1]
            rgb = re.findall(r"\d+",color_str)
            if len(rgb)==3:
                return rgb_to_hex(rgb)
            else:
                return 0xffffff 
    #print("unmatched",task_label)
    return 0xffffff #なければWhiteを返す  
def get_task_color(task):
    if len(task)==0 or task=='Dummy' or task=='DummyDay':
        return 0xffffff
    if task in  task_label_color_map:
        color_str=task_label_color_map[task][0][1]
        rgb = re.findall(r"\d+",color_str)
        if len(rgb)==3:
            return rgb_to_hex(rgb)
        else:
            return 255 + 255*256 + 255*256*256
    else:
        return 255 + 255*256 + 255*256*256


def rgb_str_to_hex(color_str):
    rgb = re.findall(r"\d+",color_str)
    if len(rgb)==3:
        return rgb_to_hex(rgb)
    else:
        return 0xffffff

0 件のコメント:

コメントを投稿