2020年6月29日月曜日

Python Post処理

解生成後に、解を整形したり、解を統計解析したい場合があります。そんな用途に、ポスト処理用Pythonを用意しました。

例えば、フェーズオブジェクトを用いた制約系では、次のような解出力になります。3フェーズ/Day出力となります。


しかし、実際にユーザが管理しているのは、次のようなExcelフォーマットです。このフォーマットはユーザ毎に違います。SC3のExcel出力オプションでなんとかなるレベルではありません。


このGAPを埋めるのに、Excelマクロを使ったり、Openpyxl等を使った処理が必要になります。残念ながら、Openpyxlは、巨大すぎて実装できていないので、次善の策として、Pythonで整形したCSVファイルを出力することにしました。

Pythonソースは、以下です。
#post operation
def get_day_index(day):
    return day-今月[0]+1 #名前項目分
    
def draw_row(person,day,row0,row1):
    phases=3
    t0=task_solution[person][day*phases+0]
    t1=task_solution[person][day*phases+1]
    t2=task_solution[person][day*phases+2]
    d=get_day_index(day)

    #debug sc3.print(str(d)+' '+str(len(row0))+'\n')

#勤務処理
    if t0=='日T' and t1=='日T':
        if day in 休勤日今月:
            row0[d]='休勤'
        else :
            row0[d]='日勤'
    elif t0=='日T' and t1=='有給':
        row0[d]='後有'
    elif t1=='日T' and t0=='有給':
        row0[d]='前有'
    elif t0=='有給' and t1=='有給':
        row0[d]='有休'
    elif t0=='公休' and t1=='公休':
        row0[d]='休み'
    elif t0=='日T' and t1=='公休':
        row0[d]='午前'
    elif t1=='日T' and t0=='公休':
        row0[d]='午後'
    elif t0=='希望休み' and t1=='希望休み':
        row0[d]='希休'
    else:
        row0[d]='M休'
    

#属性処理
    if t2 !='公休':
        if day in 土:
            row1[d]='土拘'
        else :
            row1[d]='拘束'
        if shift_schedules[person][day]=='希望休み扱い':
            if row0[d] !='希休':
                row1[d]='希休'



def post_main():
    sc3.print('\n\n*********ポスト処理を実行中です。*************\n')
    sol_list=[]
    for person in 全スタッフ:
        row0=[]
        row1=[]
        row0.append(staffdef[person])
        row1.append('')
        for day in 今月:
            row0.append('') #配列確保
            row1.append('') #配列確保
            draw_row(person,day,row0,row1)
        sol_list.append(row0)
        sol_list.append(row1)
#Write csv file
    sc3.print('CSVファイルを生成中です。\n')
    os.chdir(project_file_path)
    file_path=project_file_path+'/this_month_solution.csv'
    with open(file_path, 'w', newline='') as file_path:
        writer = csv.writer(file_path)
        writer.writerows(sol_list)
    sc3.print('CSVファイルを生成しました。\n')
    sc3.print('********ポスト処理を終了します。*******************\n')

#Main rountine

勤務日数設定OneShift()
有給日数設定OneShift()
週あたりの勤務日数設定OneShift()



このPythonソース中、def post_main(): がポスト処理で使用されるルーチンです。構文チェックは、
制約実行時に行われるので、構文エラーは、制約実行段階でチェックされます。制約実行時、解は未だありませんが、インタプリタの利点で、実行までその存在チェックはされないので、なくても問題ありません。

ポスト処理時は、制約実行のmain routineは、カットされます。このために、次のような記述構造としてください。


解は、shift_solution/task_solutionとして、2次元リストで得られます。これを解析してCSV出力しています。


処理内容としては、作業フォルダをプロジェクトファイルにした後、解をユーザフォーマットに変換して、CSV処理するだけです。ユーザは出力されたCSVをExcelで読み、そのシートを貼り付けるだけで済みます。
ポスト処理時のソースは、以下のソース全体(ポスト)で見ることが出来ます。(ReadOnlyです)




#staffdef
staffdef=['スタッフ1','スタッフ2','スタッフ3','スタッフ4']
#daydef
制約開始日=6
制約終了日=35
表示開始日=0
daydef=['2020-05-26','2020-05-27','2020-05-28','2020-05-29','2020-05-30','2020-05-31','2020-06-01','2020-06-02','2020-06-03','2020-06-04','2020-06-05','2020-06-06','2020-06-07','2020-06-08','2020-06-09','2020-06-10','2020-06-11','2020-06-12','2020-06-13','2020-06-14','2020-06-15','2020-06-16','2020-06-17','2020-06-18','2020-06-19','2020-06-20','2020-06-21','2020-06-22','2020-06-23','2020-06-24','2020-06-25','2020-06-26','2020-06-27','2020-06-28','2020-06-29','2020-06-30']
#staffcollection
全スタッフ=[0,1,2,3]
正社員=[0,1]
パート=[2,3]
Work=[0,1,2,3]
日T=[0,1,2,3]
西拘束=[0,1,2,3]
拘束=[0,1,2,3]
有給=[0,1,2,3]
公休=[0,1,2,3]
希望休み=[0,1,2,3]
NoTaskVar=[0,1,2,3]

#daycollection
今月=[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
日=[5,12,19,26,33]
月=[6,13,20,27,34]
火=[0,7,14,21,28,35]
水=[1,8,15,22,29]
木=[2,9,16,23,30]
金=[3,10,17,24,31]
土=[4,11,18,25,32]
全日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
祝=[]
振=[]
Dr1人木金=[9,16,24,31]
特別休業=[]
パート給与計算1週目=[5,6,7,8,9,10,11]
パート給与計算2週目=[12,13,14,15,16,17,18]
パート給与計算3週目=[19,20,21,22,23,24,25]
パート給与計算4週目=[26,27,28,29,30,31,32]
パート給与計算5週目=[33,34,35]
増員火曜日=[7,35]
特別全員出勤日=[]
稼働日=[0,1,2,3,4,6,7,8,9,10,11,13,14,15,16,17,18,20,21,22,23,24,25,27,28,29,30,31,32,34,35]
制約開始日一日前=[5]
制約開始日二日前=[4]
制約開始日三日前=[3]
制約開始日四日前=[2]
制約開始日五日前=[1]
制約開始日六日前=[0]
制約開始日七日前=[]
制約開始日P1=[7]
制約開始日P2=[8]
制約開始日P3=[9]
制約開始日P4=[10]
制約開始日P5=[11]
制約開始日P6=[12]
第一週=[6,7,8,9,10,11,12]
第二週=[13,14,15,16,17,18,19]
第三週=[20,21,22,23,24,25,26]
第四週=[27,28,29,30,31,32,33]
第五週=[34,35]
第六週=[]
四週間=[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33]
制約開始日1日前から=[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日2日前から=[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日3日前から=[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日4日前から=[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日5日前から=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日6日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日7日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約終了日六日前=[29]
制約終了日五日前=[30]
制約終了日四日前=[31]
制約終了日三日前=[32]
制約終了日二日前=[33]
制約終了日一日前=[34]
金土日=[3,4,5,10,11,12,17,18,19,24,25,26,31,32,33]
金土日月=[3,4,5,6,10,11,12,13,17,18,19,20,24,25,26,27,31,32,33,34]
休日でない月=[6,13,20,27,34]
休日でない火=[0,14,21,28]
休日でない水=[1,8,15,22,29]
休日でない木=[2,9,16,23,30]
休日でない金=[3,10,17,24,31]
休日でない土=[4,11,25]
実月水=[1,6,8,13,15,20,22,27,29,34]
実月火水=[0,1,6,7,8,13,14,15,20,21,22,27,28,29,34,35]
実火土=[0,4,11,14,21,25,28]
実土ではない=[0,1,2,3,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35]
実木金=[2,3,9,10,16,17,23,24,30,31]
Dr2人の木=[2,23,30]
Dr2人の金=[3,10,17]
今月月=[6,13,20,27,34]
今月火=[7,14,21,28,35]
今月水=[8,15,22,29]
今月木=[9,16,23,30]
今月金=[10,17,24,31]
今月土=[11,18,25,32]
実土=[4,11,18,25,32]
今月実土=[11,18,25,32]
実土でない=[0,1,2,3,5,6,7,8,9,10,12,13,14,15,16,17,19,20,21,22,23,24,26,27,28,29,30,31,33,34,35]
実土でない今月=[6,7,8,9,10,12,13,14,15,16,17,19,20,21,22,23,24,26,27,28,29,30,31,33,34,35]
土日=[4,11,18,19,25,32,33]
土日今月=[11,18,19,25,32,33]
休勤日=[5,12,19,26,33]
休勤日今月=[12,19,26,33]
#shiftcollection

#classcollection
全スタッフ属性=[全スタッフ]
雇用形態=[正社員,パート]

#shiftdef
shiftdef={'Work':Work}
#taskdef
taskdef={'日T':日T,'西拘束':西拘束,'拘束':拘束,'有給':有給,'公休':公休,'希望休み':希望休み,'NoTaskVar':NoTaskVar}
#non_auto_tasks
non_auto_tasks=['希望休み']
#phase_list
午前=0
午後=1
拘束=2

#task collections
休日集合={'有給':有給,'公休':公休,'希望休み':希望休み}
拘束集合={'拘束':拘束,'西拘束':西拘束}
働きカウント={'有給':有給,'日T':日T}
働きカウントしない={'公休':公休,'希望休み':希望休み}
#phase_objects def
phase_objects_def={'日勤','午前','午後','日勤拘束','日勤PV','公休PV','有休PV','希望PV','午前拘束PV','午前拘束西PV','午後拘束PV','午後拘束西PV','日勤拘束西PV','拘束ダメ','午前カウント','午後カウント','西の','拘束カウント','午前有休PV','午後有休PV','午前働','午後働','前後働'}
#phase_object_aggregates
phase_aggregate_object_def={'お休み','午前希望','午後希望','希望休み扱い','Any勤務','日勤扱い','午後扱い','有休集合','働集合'}
#digited group
勤務日数最大={0:22,1:22,2:14.5,3:14.5}
勤務日数最小={0:22,1:22,2:13,3:13.5}
有給日数最大={0:2,1:2,2:1,3:1}
有給日数最小={0:0,1:2,2:0,3:0}
週あたりの勤務日数最大={2:4,3:4}
週あたりの勤務日数最小={2:3,3:3}
補助制約={'拘束タスクは、午前なし':B_拘束タスクは午前なし,'拘束タスクは、午後なし':C_拘束タスクは午後なし,'日タスクは、拘束フェーズなし':D_日タスクは拘束フェーズなし,'西野先生ではない休日':西野先生ではない休日,'西野先生休日':西野先生休日,'西野先生平日拘束':西野先生平日拘束,'西野先生ではない日(ひ)':E_西野先生ではない日ひ,'西野先生土曜日':西野先生土曜日}
column_constraints={'列制約グループ1':列制約グループ1,'補助制約':補助制約}
#shift schedules
shift_schedules=[[('',0),('日勤',0),('日勤拘束',0),('日勤',0),('お休み',0),('お休み',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('希望休み扱い',0),('',0),('',0),('',0),('',0),('',0)]
,[('',0),('日勤拘束',0),('日勤',0),('お休み',0),('午前',0),('日勤拘束',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('拘束ダメ',0),('希望休み扱い',0),('希望休み扱い',0),('',0),('',0)]
,[('',0),('お休み',0),('有休PV',0),('午前',0),('午前拘束PV',0),('お休み',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('拘束ダメ',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0)]
,[('',0),('日勤',0),('午前',0),('日勤拘束',0),('お休み',0),('お休み',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('午前希望',0),('',0),('希望休み扱い',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('希望休み扱い',0),('希望休み扱い',0),('',0),('',0)]
]
task_schedules=[[('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0)]
,[('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0)]
,[('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0)]
,[('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0),('',0)]
]
project_file_path='C:/Users/sugaw/Documents/FA/sc3'
shift_solution=[['Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work']
,['Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work']
,['Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work']
,['Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work','Work']
]
task_solution=[['公休','日T','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','公休','公休','公休','公休','公休','公休','日T','日T','拘束','日T','日T','公休','日T','日T','公休','日T','日T','拘束','公休','公休','公休','日T','公休','公休','日T','日T','拘束','公休','公休','公休','日T','日T','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','公休','公休','公休','公休','公休','公休','日T','日T','拘束','日T','日T','公休','日T','日T','公休','有給','有給','公休','日T','日T','拘束','日T','公休','公休','公休','公休','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','有給','有給','公休','日T','公休','公休','日T','公休','西拘束','公休','公休','公休','日T','日T','公休','日T','日T','公休']
,['公休','日T','公休','日T','日T','拘束','日T','日T','公休','公休','公休','公休','日T','公休','公休','日T','日T','拘束','公休','公休','公休','有給','有給','公休','日T','日T','公休','日T','日T','公休','日T','日T','公休','日T','公休','拘束','公休','公休','公休','日T','日T','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','有給','有給','公休','日T','公休','公休','日T','日T','西拘束','公休','公休','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','日T','日T','公休','公休','公休','公休','日T','日T','拘束','公休','公休','公休','日T','日T','公休','日T','日T','公休','日T','日T','拘束','日T','日T','公休','公休','公休','公休','公休','公休','公休','日T','日T','公休','日T','日T','拘束']
,['日T','公休','公休','公休','公休','公休','有給','有給','公休','日T','公休','公休','日T','公休','拘束','公休','公休','公休','日T','日T','公休','日T','日T','公休','日T','日T','拘束','公休','公休','公休','日T','公休','公休','公休','公休','公休','公休','公休','公休','日T','日T','拘束','公休','公休','公休','日T','日T','公休','公休','公休','公休','日T','日T','拘束','公休','公休','公休','公休','公休','公休','日T','日T','公休','公休','日T','拘束','有給','日T','公休','日T','公休','公休','公休','公休','公休','日T','公休','拘束','公休','公休','公休','日T','日T','公休','公休','公休','公休','日T','日T','拘束','日T','公休','公休','公休','公休','公休','日T','公休','公休','日T','日T','西拘束','公休','公休','公休','公休','日T','公休']
,['日T','日T','公休','日T','日T','公休','日T','公休','公休','日T','日T','拘束','公休','公休','公休','公休','公休','公休','日T','日T','公休','日T','日T','拘束','公休','公休','公休','公休','公休','公休','日T','日T','拘束','公休','公休','公休','公休','公休','公休','日T','日T','公休','有給','日T','拘束','公休','公休','公休','公休','公休','公休','日T','公休','公休','日T','公休','西拘束','公休','公休','公休','日T','日T','公休','有給','公休','公休','日T','公休','公休','日T','日T','拘束','公休','公休','公休','公休','公休','公休','公休','公休','公休','日T','日T','拘束','公休','日T','公休','公休','公休','公休','日T','日T','公休','公休','日T','拘束','公休','公休','公休','公休','公休','公休','日T','日T','拘束','日T','公休','公休']
]
import sc3
import sys
import os
import csv

def 勤務カウントOneShift(person,day,vlist):
            v=sc3.GetTaskVar(person,day,0,'日T');#AM
            vlist.append(v)
            v=sc3.GetTaskVar(person,day,1,'日T');#PM
            vlist.append(v)
            v=sc3.GetTaskVar(person,day,0,'有給');#AM
            vlist.append(v)
            v=sc3.GetTaskVar(person,day,1,'有給');#PM
            vlist.append(v)



def 勤務日数設定OneShift():
    for person in 全スタッフ:
        max=-1
        min=0

        st=staffdef[person]+' 勤務日数を制約'
        sc3.print(st+'します。\n')
        if person in 勤務日数最大:
                max=勤務日数最大[person]#float
        if person in 勤務日数最小:
                min=勤務日数最小[person]#float
        
                    

        max *=2
        min *=2
        if isinstance(max, float) and not max.is_integer():
            sc3.print('勤務日数設定でmaxが整数型ではありません。')
            continue
        if isinstance(min, float) and not min.is_integer():
            sc3.print('勤務日数設定でminが整数型ではありません。')
            continue

        max =int(max) #float to int
        min =int(min) #float to int
        sc3.print('最大='+str(max)+' 最小='+str(min)+'\n')
        vlist=[]
        for day in 今月:#午前・午後の日勤と有給taskと有給シフトをカウントする 有給シフトは、2回分
            勤務カウントOneShift(person,day,vlist)
        #sc3.AddSoft(sc3.SeqError(min,max,15,vlist),st,7)#許容2 ->8hours
        sc3.AddHard(sc3.SeqLE(min,max,vlist),st)#hard constraint
def 有給日数設定OneShift():
    min_sum=0;
    vlist_sum=[];
    for person in 全スタッフ:
        max=-1
        min=0

        st=staffdef[person]+' 有給日数を制約'
        sc3.print(st+'します。\n')
        if person in 有給日数最大:
                max=有給日数最大[person]#float
        if person in 有給日数最小:
                min=有給日数最小[person]#float
        
                    

        max *=2
        min *=2
        min_sum+=min;
        if isinstance(max, float) and not max.is_integer():
            sc3.print('勤務日数設定でmaxが整数型ではありません。')
            continue
        if isinstance(min, float) and not min.is_integer():
            sc3.print('勤務日数設定でminが整数型ではありません。')
            continue

        max =int(max) #float to int
        min =int(min) #float to int
        sc3.print('最大='+str(max)+' 最小='+str(min)+'\n')
        vlist=[]
        for day in 今月:#午前・午後の日勤と有給taskと有給シフトをカウントする 有給シフトは、2回分
            v=sc3.GetTaskVar(person,day,0,'有給');#AM
            vlist.append(v)
            v=sc3.GetTaskVar(person,day,1,'有給');#PM
            vlist.append(v)

        sc3.AddSoft(sc3.SeqError(min,max,1,vlist),st,7)
        vlist_sum+=vlist;
    sc3.AddSoft(sc3.SeqError(min_sum,min_sum,10,vlist_sum),'Total有給の極小化',5)#重みは調整ください

def 週あたりの勤務日数設定OneShift():
    for person in 全スタッフ:
        max=14
        min=0

        st=staffdef[person]+' 週あたりの勤務日数を制約'
        sc3.print(st+'します。\n')
        if person in 週あたりの勤務日数最大:
                max=週あたりの勤務日数最大[person]#float
        if person in 週あたりの勤務日数最小:
                min=週あたりの勤務日数最小[person]#float
        if max==14 and min==0:
            continue

                    

        max *=2
        min *=2
        if isinstance(max, float) and not max.is_integer():
            sc3.print('週あたりの勤務日数設定でmaxが整数型ではありません。')
            continue
        if isinstance(min, float) and not min.is_integer():
            sc3.print('週あたりの勤務日数設定でminが整数型ではありません。')
            continue

        max =int(max) #float to int
        min =int(min) #float to int
        sc3.print('最大='+str(max)+' 最小='+str(min)+'\n')

        week=0        
        for sun in 日:#日曜日を起点に1週間見る
            sc3.print('日曜日='+str(sun)+'\n')
            VList=[]
            days=0
            for d in range(7):
                day=sun+d
                
                if day >制約終了日:
                    break
                else:
                    勤務カウントOneShift(person,day,VList)
                days+=1

            s="週あたりの勤務回数設定"+str(week)+' '+"person"+str(person) #Keyword+ space +variable word
            week+=1
            if days==7:
                sc3.AddSoft(sc3.SeqError(min,max,2,VList),s,7) #level 3 許容エラー4
            else:
                nmax=int( days*max/7);#7日に満たない場合は、リニア目標値設定 端数切捨て
                nmin=int( days*min/7);#7日に満たない場合は、リニア目標値設定 端数切捨て
                sc3.print(str(nmax)+"に目標値最大を設定しました。\n")
                sc3.print(str(nmin)+"に目標値最最小を設定しました。\n")
                sc3.AddSoft(sc3.SeqError(nmin,nmax,2,VList),s,7)#level 3 許容エラー4 

#post operation
def get_day_index(day):
    return day-今月[0]+1 #名前項目分
    
def draw_row(person,day,row0,row1):
    phases=3
    t0=task_solution[person][day*phases+0]
    t1=task_solution[person][day*phases+1]
    t2=task_solution[person][day*phases+2]
    d=get_day_index(day)

    #debug sc3.print(str(d)+' '+str(len(row0))+'\n')

#勤務処理
    if t0=='日T' and t1=='日T':
        if day in 休勤日今月:
            row0[d]='休勤'
        else :
            row0[d]='日勤'
    elif t0=='日T' and t1=='有給':
        row0[d]='後有'
    elif t1=='日T' and t0=='有給':
        row0[d]='前有'
    elif t0=='有給' and t1=='有給':
        row0[d]='有休'
    elif t0=='公休' and t1=='公休':
        row0[d]='休み'
    elif t0=='日T' and t1=='公休':
        row0[d]='午前'
    elif t1=='日T' and t0=='公休':
        row0[d]='午後'
    elif t0=='希望休み' and t1=='希望休み':
        row0[d]='希休'
    else:
        row0[d]='M休'
    

#属性処理
    if t2 !='公休':
        if day in 土:
            row1[d]='土拘'
        else :
            row1[d]='拘束'
        if shift_schedules[person][day]=='希望休み扱い':
            if row0[d] !='希休':
                row1[d]='希休'



def post_main():
    sc3.print('\n\n*********ポスト処理を実行中です。*************\n')
    sol_list=[]
    for person in 全スタッフ:
        row0=[]
        row1=[]
        row0.append(staffdef[person])
        row1.append('')
        for day in 今月:
            row0.append('') #配列確保
            row1.append('') #配列確保
            draw_row(person,day,row0,row1)
        sol_list.append(row0)
        sol_list.append(row1)
#Write csv file
    sc3.print('CSVファイルを生成中です。\n')
    os.chdir(project_file_path)
    file_path=project_file_path+'/this_month_solution.csv'
    with open(file_path, 'w', newline='') as file_path:
        writer = csv.writer(file_path)
        writer.writerows(sol_list)
    sc3.print('CSVファイルを生成しました。\n')
    sc3.print('********ポスト処理を終了します。*******************\n')

#Main rountine

post_main()

プリ・ポスト共GUI集合情報を元に記述できるので便利です。大半の情報は、既にGUIで記述済みですので、記述量が少なくて済みます。

実装では、pythonのソース解析が必要かなと一瞬思いましたが、Pythonの単純な文法構造のおかげで、Bisonを用いての真面目な処理は、行っていません。

以上の実装後の、ログは次のようになりました。
コンパイルの準備中ソルバを呼び出し中です。
 Python ファイルを生成中です。
 Python プロパティファイルの生成が終わりました。
 制約をコンパイル中です。
スタッフ1 勤務日数を制約します。
最大=44 最小=44
スタッフ2 勤務日数を制約します。
最大=44 最小=44
スタッフ3 勤務日数を制約します。
最大=29 最小=26
スタッフ4 勤務日数を制約します。
最大=29 最小=27
スタッフ1 有給日数を制約します。
最大=4 最小=0
スタッフ2 有給日数を制約します。
最大=4 最小=4
スタッフ3 有給日数を制約します。
最大=2 最小=0
スタッフ4 有給日数を制約します。
最大=2 最小=0
スタッフ1 週あたりの勤務日数を制約します。
スタッフ2 週あたりの勤務日数を制約します。
スタッフ3 週あたりの勤務日数を制約します。
最大=8 最小=6
日曜日=5
日曜日=12
日曜日=19
日曜日=26
日曜日=33
3に目標値最大を設定しました。
2に目標値最最小を設定しました。
スタッフ4 週あたりの勤務日数を制約します。
最大=8 最小=6
日曜日=5
日曜日=12
日曜日=19
日曜日=26
日曜日=33
3に目標値最大を設定しました。
2に目標値最最小を設定しました。
 Algorithm 1 Solving Process Started..
   o 9920   0.964000(sec) 
   o 8930   0.970000(sec) 
   o 6780   0.987000(sec) 
   o 5770   0.999000(sec) 
   o 2780   1.010000(sec) 
   o 1780   1.018000(sec) 
   o 630   1.085000(sec) 
   o 580   3.105000(sec) 
   o 530   3.157000(sec) 
   o 520   4.958000(sec) 
 充足解を書き込みました。
 139612[KB] used.
 5.729000(sec)
 Python ファイルを生成中です。
 Python プロパティファイルの生成が終わりました。
 _____________________________________
|           |           |             |
|   Weight  |   Errors  |    Cost     |
|___________|___________|_____________|
|           |           |             |
|      1000 |         0 |           0 |
|       100 |         0 |           0 |
|        50 |         9 |         450 |
|        10 |         7 |          70 |
|___________|___________|_____________|
|                       |             |
|         Total         |         520 |
|_______________________|_____________|
o 520(0)


*********ポスト処理を実行中です。*************
CSVファイルを生成中です。
CSVファイルを生成しました。
********ポスト処理を終了します。*******************
解探索が終了しました。 7 (秒)
解が得られました。



良くあるエラーは、次です、<string>(434)ダブルクリックして次の表示となります。アクセス拒否されたので、Excelを終了させて再試行してください。


0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。