import xml.etree.ElementTree as ET
import pandas as pd
xml_file = "exchanger.xml"
output_file = "all_devices_changes.xlsx"
try:
tree = ET.parse(xml_file)
root = tree.getroot()
print("Парсинг прошел успешно")
except Exception as e:
print(f"Ошибка: {e}")
exit()
all_devices = root.findall('.//Device')
data_rows = []
all_conflicts = []
for device in all_devices:
device_name = device.get("Name", "Unknown")
all_states = device.findall('.//State')
for state in all_states:
cur_date = state.get("Key", "No_Date")
seen_in_state = {}
for prop in state.findall('.//Property'):
key = prop.get("Key")
val = str(prop.get("Value", ""))
# Проверка на дубликаты внутри одного стейта
if key in seen_in_state:
all_conflicts.append({
"Device": device_name,
"Date": cur_date,
"Property": key,
"Values": f"{seen_in_state[key]} -> {val}"
})
seen_in_state[key] = val
# Собираем общую базу данных
data_rows.append({
"Device": device_name,
"Property": key,
"Date": cur_date,
"Value": val
})
if not data_rows:
print("Данные не найдены.")
exit()
# Создаем один общий DataFrame
df = pd.DataFrame(data_rows)
# Формируем таблицу: строки сгруппированы по Девайсу и Параметру
pivot_df = df.pivot_table(
index=['Device', 'Property'],
columns='Date',
values='Value',
aggfunc=lambda x: " | ".join(x)
)
# Сортируем даты (столбцы)
pivot_df = pivot_df.reindex(columns=sorted(pivot_df.columns))
# Фильтруем: оставляем только те строки, где значения менялись
# nunique(axis=1) считает уникальные значения в строке (горизонтально)
changes_only = pivot_df[pivot_df.nunique(axis=1, dropna=True) > 1]
# Сохраняем в один лист
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
changes_only.to_excel(writer, sheet_name="Changes_Report")
if all_conflicts:
pd.DataFrame(all_conflicts).to_excel(writer, sheet_name="Conflicts_Log", index=False)
print(f"Внимание: обнаружено дублей: {len(all_conflicts)}. См. лист Conflicts_Log")
print(f"Файл сохранен: {output_file}")
aW1wb3J0IHhtbC5ldHJlZS5FbGVtZW50VHJlZSBhcyBFVAppbXBvcnQgcGFuZGFzIGFzIHBkCgp4bWxfZmlsZSA9ICJleGNoYW5nZXIueG1sIgpvdXRwdXRfZmlsZSA9ICJhbGxfZGV2aWNlc19jaGFuZ2VzLnhsc3giCgp0cnk6CiAgICB0cmVlID0gRVQucGFyc2UoeG1sX2ZpbGUpCiAgICByb290ID0gdHJlZS5nZXRyb290KCkKICAgIHByaW50KCLQn9Cw0YDRgdC40L3QsyDQv9GA0L7RiNC10Lsg0YPRgdC/0LXRiNC90L4iKQpleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICBwcmludChmItCe0YjQuNCx0LrQsDoge2V9IikKICAgIGV4aXQoKQoKYWxsX2RldmljZXMgPSByb290LmZpbmRhbGwoJy4vL0RldmljZScpCmRhdGFfcm93cyA9IFtdCmFsbF9jb25mbGljdHMgPSBbXQoKZm9yIGRldmljZSBpbiBhbGxfZGV2aWNlczoKICAgIGRldmljZV9uYW1lID0gZGV2aWNlLmdldCgiTmFtZSIsICJVbmtub3duIikKICAgIGFsbF9zdGF0ZXMgPSBkZXZpY2UuZmluZGFsbCgnLi8vU3RhdGUnKQogICAgCiAgICBmb3Igc3RhdGUgaW4gYWxsX3N0YXRlczoKICAgICAgICBjdXJfZGF0ZSA9IHN0YXRlLmdldCgiS2V5IiwgIk5vX0RhdGUiKQogICAgICAgIHNlZW5faW5fc3RhdGUgPSB7fQogICAgICAgIAogICAgICAgIGZvciBwcm9wIGluIHN0YXRlLmZpbmRhbGwoJy4vL1Byb3BlcnR5Jyk6CiAgICAgICAgICAgIGtleSA9IHByb3AuZ2V0KCJLZXkiKQogICAgICAgICAgICB2YWwgPSBzdHIocHJvcC5nZXQoIlZhbHVlIiwgIiIpKQogICAgICAgICAgICAKICAgICAgICAgICAgIyDQn9GA0L7QstC10YDQutCwINC90LAg0LTRg9Cx0LvQuNC60LDRgtGLINCy0L3Rg9GC0YDQuCDQvtC00L3QvtCz0L4g0YHRgtC10LnRgtCwCiAgICAgICAgICAgIGlmIGtleSBpbiBzZWVuX2luX3N0YXRlOgogICAgICAgICAgICAgICAgYWxsX2NvbmZsaWN0cy5hcHBlbmQoewogICAgICAgICAgICAgICAgICAgICJEZXZpY2UiOiBkZXZpY2VfbmFtZSwKICAgICAgICAgICAgICAgICAgICAiRGF0ZSI6IGN1cl9kYXRlLAogICAgICAgICAgICAgICAgICAgICJQcm9wZXJ0eSI6IGtleSwKICAgICAgICAgICAgICAgICAgICAiVmFsdWVzIjogZiJ7c2Vlbl9pbl9zdGF0ZVtrZXldfSAtPiB7dmFsfSIKICAgICAgICAgICAgICAgIH0pCiAgICAgICAgICAgIHNlZW5faW5fc3RhdGVba2V5XSA9IHZhbAogICAgICAgICAgICAKICAgICAgICAgICAgIyDQodC+0LHQuNGA0LDQtdC8INC+0LHRidGD0Y4g0LHQsNC30YMg0LTQsNC90L3Ri9GFCiAgICAgICAgICAgIGRhdGFfcm93cy5hcHBlbmQoewogICAgICAgICAgICAgICAgIkRldmljZSI6IGRldmljZV9uYW1lLAogICAgICAgICAgICAgICAgIlByb3BlcnR5Ijoga2V5LAogICAgICAgICAgICAgICAgIkRhdGUiOiBjdXJfZGF0ZSwKICAgICAgICAgICAgICAgICJWYWx1ZSI6IHZhbAogICAgICAgICAgICB9KQoKaWYgbm90IGRhdGFfcm93czoKICAgIHByaW50KCLQlNCw0L3QvdGL0LUg0L3QtSDQvdCw0LnQtNC10L3Riy4iKQogICAgZXhpdCgpCgojINCh0L7Qt9C00LDQtdC8INC+0LTQuNC9INC+0LHRidC40LkgRGF0YUZyYW1lCmRmID0gcGQuRGF0YUZyYW1lKGRhdGFfcm93cykKCiMg0KTQvtGA0LzQuNGA0YPQtdC8INGC0LDQsdC70LjRhtGDOiDRgdGC0YDQvtC60Lgg0YHQs9GA0YPQv9C/0LjRgNC+0LLQsNC90Ysg0L/QviDQlNC10LLQsNC50YHRgyDQuCDQn9Cw0YDQsNC80LXRgtGA0YMKcGl2b3RfZGYgPSBkZi5waXZvdF90YWJsZSgKICAgIGluZGV4PVsnRGV2aWNlJywgJ1Byb3BlcnR5J10sIAogICAgY29sdW1ucz0nRGF0ZScsIAogICAgdmFsdWVzPSdWYWx1ZScsIAogICAgYWdnZnVuYz1sYW1iZGEgeDogIiB8ICIuam9pbih4KQopCgojINCh0L7RgNGC0LjRgNGD0LXQvCDQtNCw0YLRiyAo0YHRgtC+0LvQsdGG0YspCnBpdm90X2RmID0gcGl2b3RfZGYucmVpbmRleChjb2x1bW5zPXNvcnRlZChwaXZvdF9kZi5jb2x1bW5zKSkKCiMg0KTQuNC70YzRgtGA0YPQtdC8OiDQvtGB0YLQsNCy0LvRj9C10Lwg0YLQvtC70YzQutC+INGC0LUg0YHRgtGA0L7QutC4LCDQs9C00LUg0LfQvdCw0YfQtdC90LjRjyDQvNC10L3Rj9C70LjRgdGMCiMgbnVuaXF1ZShheGlzPTEpINGB0YfQuNGC0LDQtdGCINGD0L3QuNC60LDQu9GM0L3Ri9C1INC30L3QsNGH0LXQvdC40Y8g0LIg0YHRgtGA0L7QutC1ICjQs9C+0YDQuNC30L7QvdGC0LDQu9GM0L3QvikKY2hhbmdlc19vbmx5ID0gcGl2b3RfZGZbcGl2b3RfZGYubnVuaXF1ZShheGlzPTEsIGRyb3BuYT1UcnVlKSA+IDFdCgojINCh0L7RhdGA0LDQvdGP0LXQvCDQsiDQvtC00LjQvSDQu9C40YHRggp3aXRoIHBkLkV4Y2VsV3JpdGVyKG91dHB1dF9maWxlLCBlbmdpbmU9J29wZW5weXhsJykgYXMgd3JpdGVyOgogICAgY2hhbmdlc19vbmx5LnRvX2V4Y2VsKHdyaXRlciwgc2hlZXRfbmFtZT0iQ2hhbmdlc19SZXBvcnQiKQogICAgCiAgICBpZiBhbGxfY29uZmxpY3RzOgogICAgICAgIHBkLkRhdGFGcmFtZShhbGxfY29uZmxpY3RzKS50b19leGNlbCh3cml0ZXIsIHNoZWV0X25hbWU9IkNvbmZsaWN0c19Mb2ciLCBpbmRleD1GYWxzZSkKICAgICAgICBwcmludChmItCS0L3QuNC80LDQvdC40LU6INC+0LHQvdCw0YDRg9C20LXQvdC+INC00YPQsdC70LXQuToge2xlbihhbGxfY29uZmxpY3RzKX0uINCh0LwuINC70LjRgdGCIENvbmZsaWN0c19Mb2ciKQoKcHJpbnQoZiLQpNCw0LnQuyDRgdC+0YXRgNCw0L3QtdC9OiB7b3V0cHV0X2ZpbGV9IikK