feat: add path tracking to items and improve checkout/checkin logic with discard support
This commit is contained in:
@@ -1751,7 +1751,8 @@ class SharePointApp(wx.Frame):
|
|||||||
self.list_ctrl.SetItem(i, 3, "") # Sidst ændret
|
self.list_ctrl.SetItem(i, 3, "") # Sidst ændret
|
||||||
self.current_items.append({
|
self.current_items.append({
|
||||||
"type": "SITE", "id": site['id'], "name": name,
|
"type": "SITE", "id": site['id'], "name": name,
|
||||||
"size": None, "modified": "", "web_url": site.get('webUrl')
|
"size": None, "modified": "", "web_url": site.get('webUrl'),
|
||||||
|
"path": ["SharePoint", name]
|
||||||
})
|
})
|
||||||
|
|
||||||
def on_tree_expanding(self, event):
|
def on_tree_expanding(self, event):
|
||||||
@@ -1873,10 +1874,12 @@ class SharePointApp(wx.Frame):
|
|||||||
drives = res.json().get('value', [])
|
drives = res.json().get('value', [])
|
||||||
drives.sort(key=lambda x: x.get('name', '').lower())
|
drives.sort(key=lambda x: x.get('name', '').lower())
|
||||||
for drive in drives:
|
for drive in drives:
|
||||||
|
name = drive.get('name', '')
|
||||||
items_data.append({
|
items_data.append({
|
||||||
"type": "DRIVE", "id": drive['id'], "name": drive.get('name', ''),
|
"type": "DRIVE", "id": drive['id'], "name": name,
|
||||||
"drive_id": drive['id'], "modified": "", "size": None,
|
"drive_id": drive['id'], "modified": "", "size": None,
|
||||||
"web_url": drive.get('webUrl')
|
"web_url": drive.get('webUrl'),
|
||||||
|
"path": data['path'] + [name]
|
||||||
})
|
})
|
||||||
elif data['type'] in ["DRIVE", "FOLDER"]:
|
elif data['type'] in ["DRIVE", "FOLDER"]:
|
||||||
drive_id = data['drive_id']
|
drive_id = data['drive_id']
|
||||||
@@ -1898,7 +1901,8 @@ class SharePointApp(wx.Frame):
|
|||||||
"drive_id": drive_id, "modified": modified,
|
"drive_id": drive_id, "modified": modified,
|
||||||
"size": item.get('size') if not is_folder else None,
|
"size": item.get('size') if not is_folder else None,
|
||||||
"web_url": item.get('webUrl'),
|
"web_url": item.get('webUrl'),
|
||||||
"hash": item.get('file', {}).get('hashes', {}).get('quickXorHash') if not is_folder else None
|
"hash": item.get('file', {}).get('hashes', {}).get('quickXorHash') if not is_folder else None,
|
||||||
|
"path": data['path'] + [item['name']]
|
||||||
})
|
})
|
||||||
|
|
||||||
wx.CallAfter(self._populate_list_ctrl, items_data, data)
|
wx.CallAfter(self._populate_list_ctrl, items_data, data)
|
||||||
@@ -2192,14 +2196,20 @@ class SharePointApp(wx.Frame):
|
|||||||
if remote_hash and ENABLE_HASH_VALIDATION:
|
if remote_hash and ENABLE_HASH_VALIDATION:
|
||||||
file_size = os.path.getsize(get_long_path(local_path))
|
file_size = os.path.getsize(get_long_path(local_path))
|
||||||
if file_size <= (HASH_THRESHOLD_MB * 1024 * 1024):
|
if file_size <= (HASH_THRESHOLD_MB * 1024 * 1024):
|
||||||
original_hash = quickxorhash(local_path)
|
# Vi bruger fjern-hash direkte som vores 'original', hvis den er tilgængelig.
|
||||||
if original_hash != remote_hash:
|
# Vi tjekker dog lige at downloaden rent faktisk matchede.
|
||||||
logger.warning(f"Hash mismatch for {file_name}: local={original_hash}, remote={remote_hash}")
|
local_check = quickxorhash(local_path)
|
||||||
|
if local_check == remote_hash:
|
||||||
|
original_hash = remote_hash
|
||||||
|
logger.info(f"Download ok for {file_name}. Bruger XOR hash til ændrings-detektering.")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Hash mismatch efter download af {file_name}!")
|
||||||
self.show_info(f"Advarsel: Filens integritet kunne ikke bekræftes (XorHash mismatch)", wx.ICON_WARNING)
|
self.show_info(f"Advarsel: Filens integritet kunne ikke bekræftes (XorHash mismatch)", wx.ICON_WARNING)
|
||||||
|
original_hash = local_check
|
||||||
|
|
||||||
# Hvis vi ikke beregnede hash pga. størrelse eller manglende remote_hash, gør det nu for lokal detektering
|
# Hvis vi ikke beregnede hash pga. størrelse eller manglende remote_hash, gør det nu for lokal detektering
|
||||||
if original_hash is None:
|
if original_hash is None:
|
||||||
# Her bruger vi SHA256 af hastighedsårsager hvis XOR ikke er absolut nødvendig til lokal sammenligning
|
# Her bruger vi SHA256 af hastighedsårsager til lokal sammenligning (før/efter)
|
||||||
sha256 = hashlib.sha256()
|
sha256 = hashlib.sha256()
|
||||||
with open(get_long_path(local_path), 'rb') as f:
|
with open(get_long_path(local_path), 'rb') as f:
|
||||||
while True:
|
while True:
|
||||||
@@ -2207,9 +2217,16 @@ class SharePointApp(wx.Frame):
|
|||||||
if not chunk: break
|
if not chunk: break
|
||||||
sha256.update(chunk)
|
sha256.update(chunk)
|
||||||
original_hash = "SHA256:" + sha256.hexdigest()
|
original_hash = "SHA256:" + sha256.hexdigest()
|
||||||
|
logger.info(f"Bruger lokal SHA256 til ændrings-detektering for {file_name}")
|
||||||
|
|
||||||
# Checkout
|
# Checkout
|
||||||
requests.post(f"{base_url}/checkout", headers=self.headers)
|
is_checked_out = False
|
||||||
|
checkout_res = requests.post(f"{base_url}/checkout", headers=self.headers)
|
||||||
|
if checkout_res.status_code in [200, 201, 204]:
|
||||||
|
is_checked_out = True
|
||||||
|
logger.info(f"Fil {file_name} udtjekket succesfuldt.")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Kunne ikke udtjekke {file_name} (Status: {checkout_res.status_code}). Fortsætter dog...")
|
||||||
|
|
||||||
# 3. Åbn & Overvåg
|
# 3. Åbn & Overvåg
|
||||||
self.set_status(self.get_txt("msg_opening_file", name=file_name))
|
self.set_status(self.get_txt("msg_opening_file", name=file_name))
|
||||||
@@ -2259,18 +2276,29 @@ class SharePointApp(wx.Frame):
|
|||||||
new_hash = quickxorhash(local_path)
|
new_hash = quickxorhash(local_path)
|
||||||
|
|
||||||
if original_hash == new_hash:
|
if original_hash == new_hash:
|
||||||
|
logger.info(f"Ingen ændringer fundet i {file_name}. (Hash: {new_hash[:16]}...) Springer upload over.")
|
||||||
self.set_status(self.get_txt("msg_file_unchanged"))
|
self.set_status(self.get_txt("msg_file_unchanged"))
|
||||||
|
|
||||||
|
if is_checked_out:
|
||||||
|
logger.info(f"Annullerer udtjekning (discardCheckout) for {file_name}...")
|
||||||
|
res = requests.post(f"{base_url}/discardCheckout", headers=self.headers)
|
||||||
|
if res.status_code in [200, 204]:
|
||||||
|
is_checked_out = False
|
||||||
else:
|
else:
|
||||||
# 5. Upload (kun hvis ændret)
|
# 5. Upload (kun hvis ændret)
|
||||||
|
logger.info(f"Ændring fundet! Uploader {file_name}...")
|
||||||
self.set_status(self.get_txt("msg_updating_changes"))
|
self.set_status(self.get_txt("msg_updating_changes"))
|
||||||
with open(local_path, 'rb') as f:
|
with open(local_path, 'rb') as f:
|
||||||
upload_res = requests.put(f"{base_url}/content", headers=self.headers, data=f)
|
upload_res = requests.put(f"{base_url}/content", headers=self.headers, data=f)
|
||||||
if upload_res.status_code not in [200, 201]:
|
if upload_res.status_code not in [200, 201]:
|
||||||
raise Exception(f"{self.get_txt('msg_update_failed_code', code=upload_res.status_code)}")
|
raise Exception(f"{self.get_txt('msg_update_failed_code', code=upload_res.status_code)}")
|
||||||
|
|
||||||
# 6. Checkin (Uanset om ændret eller ej, for at frigive lås)
|
# 6. Checkin (Kun hvis vi faktisk uploadede noget)
|
||||||
|
if is_checked_out:
|
||||||
self.set_status(self.get_txt("msg_checking_in", name=file_name))
|
self.set_status(self.get_txt("msg_checking_in", name=file_name))
|
||||||
requests.post(f"{base_url}/checkin", headers=self.headers, json={"comment": "SP Explorer Edit"})
|
res = requests.post(f"{base_url}/checkin", headers=self.headers, json={"comment": "SP Explorer Edit"})
|
||||||
|
if res.status_code in [200, 201, 204]:
|
||||||
|
is_checked_out = False
|
||||||
|
|
||||||
# Oprydning: Slet fil og derefter mappe
|
# Oprydning: Slet fil og derefter mappe
|
||||||
try:
|
try:
|
||||||
@@ -2287,6 +2315,11 @@ class SharePointApp(wx.Frame):
|
|||||||
self.set_status(f"{self.get_txt('msg_error')}: {str(e)}")
|
self.set_status(f"{self.get_txt('msg_error')}: {str(e)}")
|
||||||
self.show_info(f"{self.get_txt('msg_error')}: {e}", wx.ICON_ERROR)
|
self.show_info(f"{self.get_txt('msg_error')}: {e}", wx.ICON_ERROR)
|
||||||
finally:
|
finally:
|
||||||
|
if is_checked_out:
|
||||||
|
# Emergency cleanup hvis vi stadig har fat i filen (f.eks. ved crash eller afbrydelse)
|
||||||
|
logger.info(f"Rydder op: Kalder discardCheckout for {file_name}...")
|
||||||
|
requests.post(f"{base_url}/discardCheckout", headers=self.headers)
|
||||||
|
|
||||||
if item_id in self.active_edits:
|
if item_id in self.active_edits:
|
||||||
del self.active_edits[item_id]
|
del self.active_edits[item_id]
|
||||||
self.update_edit_ui()
|
self.update_edit_ui()
|
||||||
|
|||||||
Reference in New Issue
Block a user