FileMakerで工事契約情報を管理する(5)

#目的
ファイルメーカーのファイルをxmlで出力できることを最近知ったので、保守がしやすいようにPythonを使ってTSVファイルにしようと思います。


#環境
windows10(windows7からのアップグレード)
FileMaker 18 Advance(体験版)


#0.前提条件
xmlファイルには多くの情報がありますので、今回はフィールドに関するところのみに焦点をあてて作成しています。

また、ソートの条件に使用しているテーブルオカレンスは「テーブルが見つかりません」とxmlには記載されていますが、ファイルメーカーのファイルを確認すると問題はないようなので仕様のようです。

＊自分の環境でしか試していないので、下記のコード内で考慮されていないタグがあるかと思いますのでご自身の環境に合わせて編集してもらえればと思います。

##1.コード
###1-1.コード全文
```python:fm_xml_parse.py
import xml.etree.ElementTree as ET
import re
import os, tkinter, tkinter.filedialog, tkinter.messagebox
import sys


# ファイル選択ダイアログの表示
tkroot = tkinter.Tk()
tkroot.withdraw()
fTyp = [("xml","xml")]
iDir = os.path.abspath(os.path.dirname(__file__))
file = tkinter.filedialog.askopenfilenames(filetypes = fTyp,initialdir = iDir)

if(file == ""):
 tkinter.messagebox.showinfo('info','ファイルが選択されていません終了します。')
 sys.exit()

# 選択ファイルリスト作成
list = list(file)
listParseFileName= []
for a in file:
  listParseFileName.append(os.path.basename(a))

#TSVファイルに書き込む情報を配列に入れる。
array = []

line = '項目1' + '\t' + '項目2' + '\t' + '項目3' + '\t' '項目4' + '\t' '項目5'
array.append(line)
###ここからの処理をforで回す。
for a in listParseFileName:
  listScriptTag = []
  listScriptName = []

  listLayoutTag = []
  listLayoutName = []

  listValueTag = []
  listValueListName = []
  
  listRelationshipTag = []
  listLeftRelationshipName = [] 

  listFieldAddActionTableOccurrenceName = []
  listFieldAddActionCatalogTag = []

  listFieldModifyActionTableOccurrenceName = []
  listFieldModifyActionCatalogTag = []
 
  listTableOccurrenceNotesTag = []


  listTag = []
  tree = ET.parse(a)
  root = tree.getroot()
  
  
  for FMDynamicTemplate in root.iter('FMDynamicTemplate'):
    FileName = FMDynamicTemplate.get('File')


  ###1.下準備
  ##1-1.Script####################################################################
  #<StepsForScripts>配下の<Script>タグを取得
  for e in root.iter(tag='StepsForScripts'):
      for f in e.iter(tag='Script'):
        listScriptTag.append(f)


  #<Script>配下の<ScriptReference>のname属性（スクリプト名）を取得
  for e in listScriptTag:
    for ScriptReference in e.findall('ScriptReference'):
      listScriptName.append(ScriptReference.get('name'))


  #<Script>配下のタグ全てにStepsForScriptsとスクリプト名を属性として追加
  i = 0
  for e in listScriptTag:
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'StepsForScripts') 
      g.set('mygroup_2', listScriptName[i]) 
    i = i + 1
            

  ##1-2.Layout####################################################################  
  #<LayoutCatalog>配下のLayoutタグを取得
  #レイアウトタグのisFolder属性がなければレイアウト名を取得
  for e in root.iter(tag='LayoutCatalog'):
      for f in e.iter(tag='Layout'):
        if(not f.get('isFolder')):
          listLayoutTag.append(f)
          listLayoutName.append(f.get('name'))      


  #<LayoutCatalog>配下の<Layout>タグと配下のタグ全てにLayoutCatalogとレイアウト名を属性として追加
  i = 0
  for e in listLayoutTag:
    e.set('mygroup_1', 'LayoutCatalog') 
    e.set('mygroup_2', listLayoutName[i]) 
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'LayoutCatalog') 
      g.set('mygroup_2', listLayoutName[i]) 
    i = i + 1


  ##1-3.ValueList####################################################################  
  #<ValueListCatalog>配下のValueListタグを取得
  for e in root.iter(tag='ValueListCatalog'):
      for f in e.iter(tag='ValueList'):
        listValueTag.append(f)


  #ValueListタグのname属性（値一覧名）を取得
  for e in listValueTag:  
    listValueListName.append(e.get('name'))


  #ValueList配下のタグ全てにValueListCatalogと値一覧名を属性として追加
  i = 0
  for e in listValueTag:
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'ValueListCatalog') 
      g.set('mygroup_2', listValueListName[i]) 
    i = i + 1    


  ##1-4.Relationship####################################################################  
  #<RelationshipCatalog>配下の<Relationship>配下のLeftTableタグを取得
  for e in root.iter(tag='RelationshipCatalog'):
    for f in e.iter(tag='Relationship'):
      for g in f.iter(tag='LeftTable'):      
        listLeftRelationshipName.append(g.get('name'))       


  #<RelationshipCatalog>配下のRelationshipタグを取得  
  for e in root.iter(tag='RelationshipCatalog'):
    for f in e.iter(tag='Relationship'):
      listRelationshipTag.append(f)


  #RelationshipタグとRelationship配下のタグ全てにRelationshipCatalogとレアウト名を属性として追加
  i = 0
  for e in listRelationshipTag:
    e.set('mygroup_1', 'RelationshipCatalog') 
    e.set('mygroup_2', listLeftRelationshipName[i]) 
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'RelationshipCatalog') 
      g.set('mygroup_2', listLeftRelationshipName[i]) 
    i = i + 1


  ##1-5.Field(AddAction)####################################################################   
  #<Structure>配下の<AddAction>配下の<FieldCatalog>直下のタグのテーブルオカレンス名取得  
  for e in root.iter(tag='Structure'):
    for f in e.iter(tag='AddAction'):
      for g in f.iter(tag='FieldsForTables'):
        for h in g.iter(tag='FieldCatalog'):
          for i in h.findall('TableOccurrenceReference'):
            listFieldAddActionTableOccurrenceName.append(i.get('name'))                  
  
  
  for e in root.iter(tag='Structure'):
    for f in e.iter(tag='AddAction'):
      for g in f.iter(tag='FieldsForTables'):
        for h in g.iter(tag='FieldCatalog'):
          listFieldAddActionCatalogTag.append(h)
         
    
  i = 0
  for e in listFieldAddActionCatalogTag:
    e.set('mygroup_1', 'AddAction > FieldCatalog') 
    e.set('mygroup_2', listFieldAddActionTableOccurrenceName[i]) 
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'AddAction > FieldCatalog') 
      g.set('mygroup_2', listFieldAddActionTableOccurrenceName[i]) 
    i = i + 1

  
  ##1-6.Field(ModifyAction)####################################################################   
  #<Structure>配下の<AddAction>配下の<FieldCatalog>直下のタグのテーブルオカレンス名取得  
  for e in root.iter(tag='Structure'):
    for f in e.iter(tag='ModifyAction'):
      for g in f.iter(tag='FieldsForTables'):
        for h in g.iter(tag='FieldCatalog'):
          for i in h.findall('TableOccurrenceReference'):
            listFieldModifyActionTableOccurrenceName.append(i.get('name'))                  
  
  
  for e in root.iter(tag='Structure'):
    for f in e.iter(tag='ModifyAction'):
      for g in f.iter(tag='FieldsForTables'):
        for h in g.iter(tag='FieldCatalog'):
          listFieldModifyActionCatalogTag.append(h)
         
    
  i = 0
  for e in listFieldModifyActionCatalogTag:
    e.set('mygroup_1', 'ModifyAction > FieldCatalog') 
    e.set('mygroup_2', listFieldModifyActionTableOccurrenceName[i]) 
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'ModifyAction > FieldCatalog') 
      g.set('mygroup_2', listFieldModifyActionTableOccurrenceName[i]) 
    i = i + 1


  ##1-7.TableOccurrenceNotes####################################################################   
  #<TableOccurrenceCatalog>配下のTableOccurrenceNotes
  for e in root.iter(tag='TableOccurrenceCatalog'):
    for f in e.iter(tag='TableOccurrenceNotes'):
      listTableOccurrenceNotesTag.append(f)  

  i = 0
  for e in listTableOccurrenceNotesTag:
    for g in e.findall('.//*'):
      g.set('mygroup_1', 'TableOccurrenceNotes') 
      g.set('mygroup_2', 'None') 
    i = i + 1 


  ##1-.8ファイル上書き
  tree.write(a, encoding='UTF-8')


  ###2.tsvfile作成
  ##2-1.フィールド名が影響するタグは以下の4種類のためrootからタグを取得して配列に入れる。
  for e in root.findall('.//FieldReference'):
      listTag.append(e)  
  for e in root.findall('.//Data'):
      listTag.append(e)  
  for e in root.findall('.//Text'):
      listTag.append(e)  
  for e in root.findall('.//Name'):
      listTag.append(e)  
  for e in root.findall('.//Field'):
      listTag.append(e)  


  ##2-2.配列に挿入していく。    
  for element in listTag:    
    if(element.iter("Text")):    
      element2 = str(element.text).replace("\t","")
      element3 = element2.replace('\r','')
      element4 = element3.replace('\n','')
      
      if(element4 == ""):
        element4 = "なし"
      else:
        element4
  
    line = FileName + '\t' + str(element.get('mygroup_1')) + '\t' + str(element.get('mygroup_2')) + '\t' + str(element.tag) + '\t' + str(element.attrib) + element4
    array.append(line)  
  

with open("fm_xml.tsv", 'wt') as f:
  for ele in array:
    f.write(ele+'\n')
```

###1-2.コードの概略
流れとしては下記のとおりです。

- フィールドが関係するタグは以下の5つなのでそのタグ情報を取り出す。
   - FieldReference
   - Data
   - Text
   - Name
   - Field

- 上記のタグ情報を取り出しやすいように下準備をする。
例えばScriptReferenceタグのname属性にはスクリプト名があります。そのスクリプト名をScriptタグ配下のタグ全てにmygroup_2という属性を与えます。またmygroup_1という属性に値StepsForScriptsを与えます。レイアウトなども同じように処理します。詳細はコードを参照ください。

- タグを配列にいれてファイルに書き込み
＊タグに挟まれたテキストは改行コード、タブコードを削除

###1-2.実行結果
下記の画像のようになるのでxmlを直接みるよりは見やすいのでしょうか。
![コメント 2020-05-05 205609.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213168/9695f1cd-a561-e5ed-efca-3c56d8c2d9c0.png)

##2.改善点
ファイルメーカーではjavascriptが使えるので、ファイルメーカー内でできればもっと使いやすくできると思う。