質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.05%

python + openpyxl 値を検索し、セルが空白の時に値を入れる

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,011

suger365

score 7

初心者ですので不足なコード、記載ミスなどありましたらお教えください。
コードがきれいとはいいがたいのですがどこが悪さしているかわからないのですべて公開します。
コードは長いですが本題は退出記録を記載したいだけなのでそれ以外は飛ばしていただいて構いません。

https://teratail.com/questions/227911
が必要そうな内容を切り抜いて質問させていただいていると思うのですが 不足していそうなので別で質問させていただきます。
不足していませんでした。むしろ単純ミスで、これだけ長いコードを公開したせいで分かりにくくなっていました。
解決した内容を記入しておきましたので許して…

前提・実現したいこと

・セルの値が空白かつIDが一致したときに値をセルに入れたい

・入退出管理システムを作っています
・入室のみのログ、退出のみのログはとれるようになりました
・このままだと二つのログを合わせて計算しないといけません

・システム自体はこのようになっています
ウィンドウに出勤ボタンと退勤ボタンを設定
ボタンが押されたときに4桁の数字かチェックし、OKならSub_Excelを呼び出し
Sub_Excelでは入室記録と退出記録、入退出記録を別で管理。
(入室記録:In_LogLIst.xlsx)
(退出記録:Out_LogList.xlsx)
(入退出記録:Main_LogList.xlsx)←これがうまくいっていない

なのでpython+openpyxlで次のようなことを行いたいです
・入室ログと退出ログを合わせたものを作りたいです。具体的には以下の通り
*入室時に入室時間と社員番号を入れる
*退出時に社員番号が一致し、(かつセルが空白であるとき(この部分は一致したときに入らないので未実装です))退出時間と社員番号を入れる(セルが空白でない=ほかの人が入れているため)
(一致しない場合やほかの人が入れてしまっていてかぶっている場合はエラーリストに入れておく(未実装))

発生している問題・エラーメッセージ

・一致していない扱いなのか退出記録が記録されません。
if ID == EmpNum でID(Excelに記載の社員番号)とEmpNum(GUI側からとってきた社員番号)
が一致した場合はExcelの入室記録のわきに退出記録を入れたいです。

import openpyxl
from datetime import datetime 

def In_Excelwrite(Status,EmpNum):
    #Excelファイルを開く
    Inwb = openpyxl.load_workbook(r"C:\python\tkinter\In_LogLIst.xlsx")
    Inws = Inwb["IN_LIST"]

    #最終行取得など
    MaxLow = Inwb["IN_LIST"].max_row

    #各種データ
    d_now = str(format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
    print(EmpNum,d_now,Status)
    #出勤にデータを入れる
    Inws.cell(row = MaxLow + 1 , column = 1 ,value = EmpNum)
    Inws.cell(row = MaxLow + 1 , column = 2 ,value = d_now)
    Inws.cell(row = MaxLow + 1 , column = 3 ,value = Status)

    #保存する
    Inwb.save(r"C:\python\tkinter\In_LogLIst.xlsx")

    ##ここからメインファイル用
    #メインのExcelファイルを開く
    Mainwb = openpyxl.load_workbook(r"C:\python\tkinter\Main_LogList.xlsx")
    Mainws = Mainwb["MAIN_LIST"]

    MainMaxLow = Mainwb["MAIN_LIST"].max_row
    #メインにデータを入れる**(データが入りません)**
    Mainws.cell(row = MainMaxLow + 1 , column = 1 ,value = EmpNum)
    Mainws.cell(row = MainMaxLow + 1 , column = 2 ,value = d_now)
    Mainws.cell(row = MainMaxLow + 1 , column = 3 ,value = Status)
    #保存する
    Mainwb.save(r"C:\python\tkinter\Main_LogList.xlsx")

    #処理完了
    print("処理が完了しました")

def Out_Excelwrite(Status,EmpNum):
    #Excelファイルを開く
    Outwb = openpyxl.load_workbook(r"C:\python\tkinter\Out_LogList.xlsx")
    Outws = Outwb["OUT_LIST"]

    #最終行取得など
    MaxLow = Outwb["OUT_LIST"].max_row
    #各種データ
    d_now = str(format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
    print(EmpNum,d_now,Status)
    #退勤にデータを入れる
    Outws.cell(row = MaxLow + 1 , column = 1 ,value = EmpNum)
    Outws.cell(row = MaxLow + 1 , column = 2 ,value = d_now)
    Outws.cell(row = MaxLow + 1 , column = 3 ,value = Status)

    #保存する
    Outwb.save(r"C:\python\tkinter\Out_LogList.xlsx")

    ##ここからメインファイル用
    #メインのExcelファイルを開く
    Mainwb = openpyxl.load_workbook(r"C:\python\tkinter\Main_LogList.xlsx")
    Mainws = Mainwb["MAIN_LIST"]

    MainMaxLow = Mainwb["MAIN_LIST"].max_row
    #メインにデータを入れる ここ違う
    for row_num in range(1,Mainws.max_row):
        ID = Mainws.cell(row = row_num , column = 1).value
        if str(ID) in EmpNum:
            Mainws.cell(row = row_num , column = 4 ,value = EmpNum)#ここでvalueを入れている位置が違ったようです
            Mainws.cell(row = row_num , column = 5 ,value = d_now)
            Mainws.cell(row = row_num , column = 6 ,value = Status)
            print("ALLRIGHT")
        else:
            print("Error")

    #保存する
    Mainwb.save(r"C:\python\tkinter\Main_LogList.xlsx")

    #処理完了
    print("処理が完了しました")

試したこと

エラーが出ていないので調べようにも調べられずやれたことは少ないですが…
・IDがStr型なのでEmpNumの型を確認
str型でした。
・実行した結果を確認
入退出記録に入室時のデータは入っているのに退出時のみ記録できません。
・セルが空白であるとき~の条件付けを削除
問題を切り分けるために削除しましたが駄目でした。

補足情報(FW/ツールのバージョンなど)

python 3.8.0

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

今回の問題に関係ないかもですが。。。
for row_num in range(1,Mainws.max_row):だと最後の行が参照されなくないですか?

あと、この方法だと同じEmpNumの人が2回入退室した時に2回目以降の退室が記録できないと思いますが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/08 13:04

    TaroToyotomi 様
    ・for row_num は最終行参照になりませんか?
    range(1,Mainws.max_row)でセル1行目から最終行まで検索させているつもりなのですが…
    ・同じEmpNumの人が二回入退出~
    In_LogListやOut_LogList.xlsxでは入室退出がかぶっても記録は可能なようです。
    今回問題となっているMainListでは
    入室→退出→入室→退出 の正常系なら動くと思うのですが…
    入室→(違う人が間違って)退出→(本人が)退出ではおっしゃる通りうまくいかないかと。

    キャンセル

  • 2019/12/08 14:38

    Mainws.max_rowがセルに入っている最後の行番号を示しているならNGです。
    試しにpythonのシェルから range(1,10) と入力してみればわかると思います。
    max_rowが10だと仮定したら期待したリストにならないですよね?

    入室(1)→退出(2)→入室(3)→退出(4)で考えます。
    入室(3)は、
    Mainws.cell(row = MainMaxLow + 1 , column = 1 ,value = EmpNum)
    とあるので最終行の次の行に追加されます。
    退室(4)は、Mainwsの先頭の行からIDを比較しているため、入室(1)の行を一致したと判断しませんか?

    キャンセル

  • 2019/12/08 21:12

    TaroToyotomi 様
    ご指摘ありがとうございます。

    ・Mainws_max_rowが最後の行番号を示しているならNG
    まさに最後の行番号を表していました…
    range(1,10)をシェルで入力した場合"range(1, 10)"で帰ってきました。
    少し調べてlist(range(1,10))としたところ
    [1, 2, 3, 4, 5, 6, 7, 8, 9]が出力で、おっしゃる通り10行まで取りたいのに取れていないです!
    1プラスして
    for row_num in range(1,Mainws.max_row+1)とすればいけるかな?

    ・Mainwsの先頭の行から~
    こちらは空白セルでない場合は記入させないように後程実装する予定でした。
    問題切り分けのためにいったん保留していた部分です。
    「退出時に社員番号が一致し、(かつセルが空白であるとき」と書いてお伝えしたつもりでしたが質問時の書き方が悪かったようで申し訳ないです。

    キャンセル

0

最初に

今回の問題点は主に二点でした。
1.値が一致したときに入るはずの値が入らない
2.値が一致したときという条件だけではすでに値が入っているセルに入ってしまう
→セルが空白であるときのみ値を入れたい
つまり条件としては「セルが空白であり、かつ値が一致している」
という条件になれば値が一致した、値がまだ入っていないところに値を入れてくれるはずです。

1の解決法

結論から言えば値を入れるための文法が違いました。

カッコ内で"value=xxx"とする場合はほかのところでxxx=yyyとしておかなければならないようです。
今回は行っていなかったのでカッコの外で行わなければならなったようです。

さらに、値が一致するものを見つけた後もfor文が回っているせいで一致する限り何回も値が入ってしまいました。
なので一致した場合はbreakでfor文を抜けさせるようにしました。

また、TaroToyotomi様がおっしゃる通り、pythonのシェルから range(1,10) と入力してみると、10行目まで探してほしいのに9行目までしか探してくれていませんでした。
なので+1しておくことで解決させました(よい解決方法なのかはわかりません)

これらを踏まえた修正後のコードがこちらです

    for row_num in range(1,Mainws.max_row + 1):
        ID = Mainws.cell(row = row_num , column = 1).value
        if str(ID) in EmpNum:
            Mainws.cell(row = row_num , column = 4 ).value = EmpNum
            Mainws.cell(row = row_num , column = 5 ).value = d_now
            Mainws.cell(row = row_num , column = 6 ).value = Status
            print("ALLRIGHT")
            break
        else:
            print("Error")

元のコードがこちらです。

#メインにデータを入れる
    for row_num in range(1,Mainws.max_row):
        ID = Mainws.cell(row = row_num , column = 1).value
        if str(ID) in EmpNum:
            Mainws.cell(row = row_num , column = 4 ,value = EmpNum)
            Mainws.cell(row = row_num , column = 5 ,value = d_now)
            Mainws.cell(row = row_num , column = 6 ,value = Status)
            print("ALLRIGHT")
        else:
            print("Error")

2の解決法

「セルが空白である」ということを考えたときに真っ先にcell.value==""が浮かびました。
が、どうもうまくいかない…

https://stackoverrun.com/ja/q/2033147 では
if sheet.cell(row = 1, column=7).value == None: 
print("Blank") 

となっているに、cell.value=="" ではなく、cell.value==None
とすれば空白セルを認識してくれる?ようです。

結論

結果、このようなコードになりました

for row_num in range(1,Mainws.max_row + 1):
        ID = Mainws.cell(row = row_num , column = 1).value
        if Mainws.cell(row = row_num , column = 4).value == None and str(ID) in EmpNum:
            Mainws.cell(row = row_num , column = 4 ).value = EmpNum
            Mainws.cell(row = row_num , column = 5 ).value = d_now
            Mainws.cell(row = row_num , column = 6 ).value = Status
            print("ALLRIGHT")
            break
        else:
            print("Error")

これでやりたかったことをやってくれるようになった…と思います。

最後に

最後になりますがTaroToyotomi様、meg_様に感謝申し上げます。ありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る