2021年10月31日日曜日

看護実習科目の年間スケジュール表の作成-GUI編

 前回、シフト連 成人1>成人2 を全スタッフについて制約するコアアルゴリズムをPythonで記述しました。今回は、任意のシフトの連順をGUIで操作できるように、GUIとPythonの記述追加を行います。

 下図は、シフト連の設定画面例です。スタッフ名1Gでは、成人1>成人2>老年>母性>地域>精神>小児>統合 の順でシフト連順を設定しています。その他のスタッフでは、成人1>成人2のみに設定する画面となっています。 

これにより、任意のスタッフに任意のシフト連順を設定することが出来ます。さて、スタッフプロパティでこのように設定するためには、スタッフプロパティ属性を記述しています。


つまり、

シフト連名=シフト名+'_'+連順(1,2,3,4,5,6,7,8)

としています。ルールです。このようにするのは、スタッフプロパティアイテム同士で、名前が衝突しないようにするためです。注意する点は、以下の2点です。

■シフト名と一致しないこと

■連順同士で区別できること

Python上で同じ名前になってしまうと意図しない動作となってしまい為です。(エラーは出ません。)

設定が多く面倒そうですが、テキストエディタで1行を記述しておいて、コピペ、置換、コピペ..で行うと、それほど面倒でもありません。



RUNした後、ソース全体を見ると、下のように成人1と、成人2には、全スタッフ[0,1,...]が設定されていることが分かります。また、老年は、スタッフ1Gである[0]だけが設定されていることが分かります。


よって、これらをPythonで拾ってくれば、GUIの設定を拾うことが出来ます。

<メンテナンス性を上げる>

Pythonソースを見ると特定のシフト名に対する記述がありません。今後のシフト名の変更、追加では、Pythonソースをいじらなくて良いようにしています。(プログラムは、書いたときは覚えていますが、1年後は、初見と同じです。)シフト名の変更追加があったときは、スタッフプロパティのみを変更すればOKです。覚えておくべきルールは、ルールだけです。

以下がPythonで書いた全ソースになります。



import sc3
import re #正規表現

def get_sequence_shift(person,seq):
    for s in globals():#全ての変数名をスキャンする
        rs=re.sub('_\d+', '', s)#正規表現 sから'_数字'を削除する ルール参照
        if s !=rs and rs in shiftdef:#削除した名前がシフト名と一致するものがあるなら
            ss=rs+'_'+str(seq)    
            if ss==s:#要求連順Noと等しいなら
                if person in eval(s):#文字列を評価して要求personと一致しているならば
                    #sc3.print('person='+str(person)+' '+rs+'\n')
                    return rs #シフト名を返す
    return ''#要求に一致するものはないことを報告する


def 順序制約(person,shift1,shift2):#person シフト連名 シフト連名+1の制約を行う。どちらも空白ではない

    first_day=今月[0]
    last_day=今月[len(今月)-1]

    st=staffdef[person]+' 順序制約 '+shift1+' > '+shift2 
    sc3.print(st+'  を制約します。\n')
    for day in 今月:
        vlist2=[]
        vlist1=[]
        for d in range(first_day,day+1):
            v2=sc3.GetShiftVar(person,d,shift2)
            vlist2.append(v2)
        for d in range(day+1,last_day+1):
            v1=sc3.GetShiftVar(person,d,shift1)
            vlist1.append(v1)
        V2=sc3.Or(vlist2)
        V1=sc3.Or(vlist1)
        st_day=st+' '+ str(day)
        sc3.AddHard(~(V2&V1),st_day) 
 
def 順序():
    n=len(shiftdef)
    for person in 全グループ:#全スタッフについて
        for seq in range(1,n+1):#全シフトについて
            shift1=get_sequence_shift(person,seq)# 連1
            shift2=get_sequence_shift(person,seq+1)#連2
            if len(shift1)>=1 and len(shift2)>=1:#どちらも空白でないときだけ
                順序制約(person,shift1,shift2)#制約する
 
順序()

上記ソースで、get_seq_shiftが、ルールに基づいたシフト名を返しているルーチンになります。ここが、一手にGUIとのインタフェースを引き受けています。正規表現とかglobals()とか、私もいちいち関数名を覚えているわけではなく、ググリながらコピペすることが多いです。

制約部については、前のコアアルゴリズムを改造しています。

以上です。

<短く書ける理由>

意外に短く書けているのは、Python自体のライブラリが充実していることと、既にGUIで記述した中身がPythonで記述されているからです。(ソース全体を見ると、結構長いソースになっています。)

<GUIは、まとめ制約にすぎない>

普段、なにげなく記述しているGUIも、裏では、今回記述したみたいに、展開すると沢山のprimitiveなAddHardや、AddSoftが動いていることが想像できると思います。Python上の制約も、Engineの中では、区別がありません。仮にC++で組み込んだとして比較しても性能上の優劣はありません。

その気になれば、殆どGUIを使わずにPython主体に組むことも可能ですが、GUIで設定仕切れない部分だけをPythonで記述することをお勧めします。


0 件のコメント:

コメントを投稿