Initial commit

This commit is contained in:
“SamoilenkoVadym” 2025-02-14 11:57:46 +00:00
parent c79c6f4e6b
commit 173dedbf41
11 changed files with 885 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
Book1 copy.xlsx Normal file

Binary file not shown.

BIN
Book1 copy_updated.xlsx Normal file

Binary file not shown.

BIN
Book1.xlsx Normal file

Binary file not shown.

BIN
Book1_updated.xlsx Normal file

Binary file not shown.

View file

@ -0,0 +1,7 @@
{
"folders": [
{
"path": "."
}
]
}

1
config.json Normal file
View file

@ -0,0 +1 @@
{"last_dir": "/Users/vadymsamoilenko/Documents/GIT/Script"}

297
cookies.json Normal file
View file

@ -0,0 +1,297 @@
[
{
"domain": ".adobe.com",
"expirationDate": 1773416846.396066,
"hostOnly": false,
"httpOnly": false,
"name": "AMCV_9E1005A551ED61CA0A490D45%40AdobeOrg",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "1176715910%7CMCMID%7C17349451639425099143064126811155089188%7CMCAAMLH-1739461646%7C6%7CMCAAMB-1739461646%7CRKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y%7CMCOPTOUT-1738864046s%7CNONE%7CMCAID%7CNONE%7CvVersion%7C5.4.0",
"id": 1
},
{
"domain": ".adobe.com",
"hostOnly": false,
"httpOnly": false,
"name": "AMCVS_9E1005A551ED61CA0A490D45%40AdobeOrg",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": true,
"storeId": "0",
"value": "1",
"id": 2
},
{
"domain": ".adobe.com",
"expirationDate": 1770323817.206102,
"hostOnly": false,
"httpOnly": true,
"name": "aux_sid",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "0",
"value": "ARveyFS2d5-rYByIEKNVQNpPLmxTkVev8z6gJDDB0ZK3yAi7FR2pZ99q-CmiaFoeNAv305oPWfwjN1EM7ZknomKJpsNtCzz_5gS6ynKMEFQIThpefdkgPIIi2ID43fyqB_hp7llLxEKha_MiNJAf7xQI9jC30p-tLpN4nG3Xyw3R_BuZMphR333c6cgH39UeUcmioemcKJjqBVKahqun1U5IUHzlhnwqwu5vJ04x7FF7Q-qW5py22Yv9vC5QZplCPcjrFQgMlRUzUuA2sJ1tmA0j0aAD9bigzuMf32MYE8bra1H07PxPUMcV5LsKhe8cWP_0WzhD05p2dVAQI5Pp3O1E8J-i6RCf3uX_ehbqCpDPaJMzj_F4av6yFchvCjgsPOFhakXWxKnvuJWd_TeOJ0kgF5K2tMGEUKL6V4m7wCriM3zXHfG83cZMATeCcLoQtB8BvcP-dMOJvRKvVha3bN0oM7cauMSBiA5OQdm2rprMqJxX6VrGlh7t1Ozr7IvszMt91SO8EH8U3jCzkYL32ttvAkHGDjHfz-PZNWMMVkAGpgJO4JYPZphtZJllB54NH_1WO8iX5vAJ2T-qOpWQwmbLYqKQbcRt6Sb7AxvpEuRuFURHSxRfZQrGOKUIkzstiAI-LHOZOAL7Fhoa-NB9r0lqWY9cVas2kGjqUvEoIAXFJaLW1pGrXu3XpiPZcFWYTNyg1MvL4nkFm4i4VyLlQcFJnXosGSU3JmTch4o71cm4QwHvCEuzMF1mGkIr3ruhZkrXUrXZVhtbJu6XjNcebNUxCF69ecy-lUbVZOw7WOcwBY4",
"id": 3
},
{
"domain": ".adobe.com",
"expirationDate": 1738858648,
"hostOnly": false,
"httpOnly": false,
"name": "gpv",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "Account:IMS:Process:Complete",
"id": 4
},
{
"domain": ".adobe.com",
"expirationDate": 1770392846,
"hostOnly": false,
"httpOnly": false,
"name": "OptanonConsent",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "0",
"value": "isGpcEnabled=0&datestamp=Thu+Feb+06+2025+15%3A47%3A26+GMT%2B0000+(Greenwich+Mean+Time)&version=202311.1.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=e40ce7eb-a167-4f1e-a646-2783e554a9c1&interactionCount=1&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A0%2CC0003%3A0%2CC0004%3A0&AwaitingReconsent=false",
"id": 5
},
{
"domain": ".adobe.com",
"hostOnly": false,
"httpOnly": false,
"name": "s_cc",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": true,
"storeId": "0",
"value": "true",
"id": 6
},
{
"domain": ".adobe.com",
"expirationDate": 1773416848.218817,
"hostOnly": false,
"httpOnly": false,
"name": "s_ecid",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "MCMID%7C17349451639425099143064126811155089188",
"id": 7
},
{
"domain": ".adobe.com",
"expirationDate": 1773416845.222818,
"hostOnly": false,
"httpOnly": false,
"name": "s_fid",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "2959E3DDB076BFB0-013B63E267B41648",
"id": 8
},
{
"domain": ".adobe.com",
"hostOnly": false,
"httpOnly": true,
"name": "s_ims",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": true,
"storeId": "0",
"value": "https://ims-na1.adobelogin.com/ims/session/v1/ZTQxMzZlZWQtZmIyNC00YjVhLTk0YjItZDZjZmNlZWJlZWJmLS02RDk2MUU5MTY3NjVENzREMEE0OTVFRTNAZWZhNTNkZmY2NDQ5MzZiZTQ5NWZiMi5l",
"id": 9
},
{
"domain": ".adobe.com",
"hostOnly": false,
"httpOnly": false,
"name": "s_ppv",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": true,
"storeId": "0",
"value": "[%22auth.services.adobe.com/en_US/deeplink.html%22%2C100%2C0%2C1199%2C2544%2C1199%2C2560%2C1440%2C2%2C%22P%22]",
"id": 10
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": false,
"name": "ApplicationGatewayAffinity",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": true,
"storeId": "0",
"value": "773965041c0c59ebea497d34b646d89f",
"id": 11
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": false,
"name": "ApplicationGatewayAffinityCORS",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": true,
"storeId": "0",
"value": "773965041c0c59ebea497d34b646d89f",
"id": 12
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"expirationDate": 1773326870.932259,
"hostOnly": true,
"httpOnly": false,
"name": "bp-system-notifications",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "uid-e4ea986f4acffba136080287c5412be6e039ae8d",
"id": 13
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"expirationDate": 1739461692,
"hostOnly": true,
"httpOnly": false,
"name": "cq-assets-files",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "card",
"id": 14
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"expirationDate": 1739371666.692624,
"hostOnly": true,
"httpOnly": false,
"name": "cq-authoring-mode",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "0",
"value": "TOUCH",
"id": 15
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": true,
"name": "JSESSIONID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "gzkyrt1sverd1v2izhudp0zqc",
"id": 16
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": true,
"name": "login-token",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "b373b976-515c-44af-a48f-f88f93e9f8c7%3ad07ac9e0-74e4-4a85-a003-b9a3398c999a_18d4b4f70b20d06d108d03bbdf0c7333%3acrx.default",
"id": 17
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": true,
"name": "MC_AoD2",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "\"{9e5029df170e469714ac05a728d40da7fbb935fa88f0b39f13b7357f913b50db5fac90a34db3ca79403bca01d8ba5c820537af63204ce1f5ed5109354e312786f6e435a5746586c5d2f98dfdbb5938a0226ba3bd1d0e281fb6a774040ff4b8a72934968c0f699390f02dad020914edb4921b5e80653550aec6366fe401e47c06}\"",
"id": 18
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": true,
"name": "oauth-authid",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "ims",
"id": 19
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": true,
"name": "oauth-configid",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "ims",
"id": 20
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": false,
"name": "renderid",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": true,
"storeId": "0",
"value": "rend0",
"id": 21
},
{
"domain": "mmmspinco.brand-portal.adobe.com",
"hostOnly": true,
"httpOnly": false,
"name": "shell.omnisearch.results.layoutId",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": true,
"storeId": "0",
"value": "card",
"id": 22
}
]

376
main.py Normal file
View file

@ -0,0 +1,376 @@
import json
import os
import re
import time
import logging
from datetime import datetime
import openpyxl
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.progressbar import ProgressBar
from kivy.uix.filechooser import FileChooserListView
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException, StaleElementReferenceException
from colorama import Fore, Style, init
init(autoreset=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename=f'app_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
)
CONFIG_FILE = 'config.json'
CHROME_PROFILE_PATH = "/Users/vadymsamoilenko/Library/Application Support/Google/Chrome/Profile 1"
PORTAL_URL = "https://mmmspinco.brand-portal.adobe.com"
WAIT_TIMEOUT = 30
LONG_TIMEOUT = 60
MAX_RETRIES = 3
SEARCH_DELAY = 3
def get_last_used_directory():
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as file:
config = json.load(file)
return config.get('last_dir', '')
return ''
def save_last_used_directory(directory):
with open(CONFIG_FILE, 'w') as file:
json.dump({'last_dir': directory}, file)
def normalize_string(s):
return re.sub(r'[-_\s]+', '_', s.lower().strip())
def get_search_key(filename):
if not filename:
return ""
base = os.path.splitext(filename)[0]
return base.replace("-", "_")
class FileSearchApp(App):
file_path = ObjectProperty(None)
def __init__(self):
super().__init__()
self.setup_logging()
self.driver: webdriver.Chrome | None = None
self.continue_button = None
self.progress = None
self.status_label = None
self.label = None
def setup_logging(self):
self.logger = logging.getLogger(__name__)
def build(self):
self.file_path = ""
self.extensions = [".psd", ".indd", ".tif", ".tiff"]
layout = BoxLayout(orientation='vertical')
self.label = Label(text="Select an Excel file with Asset IDs")
layout.add_widget(self.label)
# Создаем кнопки с использованием on_press вместо bind
choose_button = Button(text="Choose File", on_press=self.select_file)
layout.add_widget(choose_button)
start_button = Button(text="Open Login Page", on_press=self.open_login_page)
layout.add_widget(start_button)
self.continue_button = Button(
text="Continue After Login",
disabled=True,
on_press=self.start_search
)
layout.add_widget(self.continue_button)
self.progress = ProgressBar(max=100)
layout.add_widget(self.progress)
self.status_label = Label(text="Ready")
layout.add_widget(self.status_label)
return layout
def select_file(self, instance):
last_dir = get_last_used_directory() or '.'
content = BoxLayout(orientation='vertical')
filechooser = FileChooserListView(path=last_dir, filters=['*.xlsx'])
def on_selection(instance, selection, touch=None):
if selection:
self.on_file_select(selection, popup)
filechooser.bind(selection=on_selection)
content.add_widget(filechooser)
popup = Popup(
title="Choose Excel File",
content=content,
size_hint=(0.9, 0.9)
)
popup.open()
def on_file_select(self, selection, popup):
if selection:
self.file_path = selection[0]
self.label.text = f"Selected File: {self.file_path}"
self.logger.info(f"Selected file: {self.file_path}")
save_last_used_directory(os.path.dirname(self.file_path))
popup.dismiss()
else:
self.logger.warning("No file selected.")
def wait_for_element(self, by, value, timeout=WAIT_TIMEOUT, retries=3):
if self.driver is None:
raise ValueError("WebDriver is not initialized")
for attempt in range(retries):
try:
element = WebDriverWait(self.driver, timeout).until(
EC.presence_of_element_located((by, value))
)
return element
except TimeoutException:
if attempt == retries - 1:
raise
time.sleep(2)
return None
def wait_for_search_results(self, timeout=LONG_TIMEOUT):
if self.driver is None:
return False
try:
WebDriverWait(self.driver, timeout).until(
EC.presence_of_element_located((By.ID, "granite-omnisearch-result"))
)
time.sleep(SEARCH_DELAY)
return True
except TimeoutException:
return False
def open_login_page(self, instance):
if not self.file_path:
popup = Popup(
title='Warning',
content=Label(text='Select a file before starting!'),
size_hint=(0.8, 0.3)
)
popup.open()
return
try:
options = Options()
options.add_argument(f"user-data-dir={CHROME_PROFILE_PATH}")
self.driver = webdriver.Chrome(options=options)
if self.driver is not None:
self.driver.get(f"{PORTAL_URL}/mediaportal.html/content/dam/mac/mmmspinco")
time.sleep(5)
if self.wait_for_element(By.XPATH, "/html/body/coral-shell/coral-shell-content/div[1]/a/img"):
self.continue_button.disabled = False
self.logger.info("Login successful, ready to continue.")
login_popup = Popup(
title='Login',
content=Label(text='Please log in and complete 2FA.\nClick "Continue After Login" once logged in.'),
size_hint=(0.8, 0.3)
)
login_popup.open()
except Exception as e:
self.logger.error(f"Error opening login page: {e}")
if self.driver:
self.driver.quit()
def find_matching_link(self, file_name, psd_links):
try:
normalized_search_key = normalize_string(file_name)
self.logger.info(f"Looking for links matching: {normalized_search_key}")
best_match = None
best_ratio = 0
for link in psd_links if isinstance(psd_links, list) else [psd_links]:
try:
href = link.get_attribute("href")
if href:
link_filename = os.path.splitext(href.split('/')[-1])[0]
normalized_link = normalize_string(link_filename)
if normalized_search_key in normalized_link or normalized_link in normalized_search_key:
ratio = len(set(normalized_search_key.split('_')) & set(normalized_link.split('_'))) / \
len(set(normalized_search_key.split('_')) | set(normalized_link.split('_')))
if ratio > best_ratio:
best_ratio = ratio
best_match = href
self.logger.info(f"Found better match: {href} (ratio: {ratio})")
except Exception as e:
self.logger.error(f"Error processing link: {e}")
continue
return best_match if best_match and best_ratio >= 0.5 else None
except Exception as e:
self.logger.error(f"Error finding PSD links: {e}")
return None
def process_row(self, row, total_rows):
if self.driver is None:
raise ValueError("WebDriver is not initialized")
if not row or not hasattr(row[0], 'value'):
return None, None
asset_id = row[0].value
file_name = row[2].value if len(row) >= 3 else None
found_link = None
if not asset_id or not file_name:
return row[0].row if hasattr(row[0], 'row') else None, None
self.status_label.text = f"Processing {asset_id}"
self.logger.info(f"Processing Asset ID: {asset_id} with filename: {file_name}")
for attempt in range(MAX_RETRIES):
try:
self.driver.get(f"{PORTAL_URL}/aem/search.html")
time.sleep(SEARCH_DELAY)
search_box = WebDriverWait(self.driver, WAIT_TIMEOUT).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='fulltext']"))
)
search_box.clear()
search_box.send_keys(asset_id)
search_box.send_keys(Keys.RETURN)
WebDriverWait(self.driver, LONG_TIMEOUT).until(
EC.presence_of_element_located((By.ID, "granite-omnisearch-result"))
)
time.sleep(SEARCH_DELAY)
asset_element = WebDriverWait(self.driver, WAIT_TIMEOUT).until(
EC.presence_of_element_located((
By.XPATH,
f"//coral-card-subtitle[contains(@class, 'foundation-collection-item-subtitle') and text()='{file_name}']"
))
)
if asset_element:
asset_card = asset_element.find_element(By.XPATH, "./ancestor::coral-card")
self.driver.execute_script("arguments[0].scrollIntoView(true);", asset_card)
time.sleep(1)
self.driver.execute_script("""
var element = arguments[0];
var rect = element.getBoundingClientRect();
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true,
'clientX': rect.left + rect.width/2,
'clientY': rect.top + rect.height/2
});
element.dispatchEvent(event);
""", asset_card)
time.sleep(1)
properties_button = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button[title='Properties']"))
)
self.driver.execute_script("arguments[0].click();", properties_button)
time.sleep(2)
psd_links = WebDriverWait(self.driver, LONG_TIMEOUT).until(
EC.presence_of_all_elements_located((
By.XPATH,
"//div[contains(@class, 'references-referencing')]//a[contains(@data-asset-path, '.psd')]"
))
)
if psd_links:
found_link = self.find_matching_link(file_name, psd_links)
if found_link:
break
else:
self.logger.warning(f"Asset card not found for {asset_id}")
except Exception as e:
self.logger.error(f"Error processing {asset_id} (attempt {attempt + 1}): {e}")
if attempt == MAX_RETRIES - 1:
break
time.sleep(2)
if hasattr(row[0], 'row'):
progress = (row[0].row / total_rows) * 100 if total_rows > 0 else 0
self.progress.value = progress
return row[0].row if hasattr(row[0], 'row') else None, found_link
def start_search(self, instance):
if not self.file_path or self.continue_button.disabled:
popup = Popup(
title='Warning',
content=Label(text='Ensure you are logged in before starting!'),
size_hint=(0.8, 0.3)
)
popup.open()
return
try:
wb = openpyxl.load_workbook(self.file_path)
if wb is None:
raise ValueError("Failed to load workbook")
sheet = wb.active
if sheet is None:
raise ValueError("Failed to get active sheet")
total_rows = sheet.max_row - 1 if sheet.max_row > 1 else 1
for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row):
try:
row_num, found_link = self.process_row(row, total_rows)
if row_num is not None:
sheet.cell(row=row_num, column=2, value=found_link if found_link else "Links not found")
save_path = self.file_path.replace(".xlsx", "_updated.xlsx")
wb.save(save_path)
except Exception as e:
self.logger.error(f"Error processing row: {e}")
continue
self.logger.info("Processing completed")
success_popup = Popup(
title='Success',
content=Label(text=f"Processing completed. File saved."),
size_hint=(0.8, 0.3)
)
success_popup.open()
except Exception as e:
self.logger.error(f"Error during search: {e}")
error_popup = Popup(
title='Error',
content=Label(text=f"An error occurred: {str(e)}"),
size_hint=(0.8, 0.3)
)
error_popup.open()
finally:
if self.driver:
self.driver.quit()
if __name__ == '__main__':
FileSearchApp().run()

201
main_backup.py Normal file
View file

@ -0,0 +1,201 @@
import json
import os
import time
import openpyxl
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.filechooser import FileChooserListView
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException
# Файл конфигурации для сохранения последнего пути
CONFIG_FILE = 'config.json'
def get_last_used_directory():
"""Возвращает последний использованный каталог из файла конфигурации."""
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as file:
config = json.load(file)
return config.get('last_dir', '')
return ''
def save_last_used_directory(directory):
"""Сохраняет последний использованный каталог в файл конфигурации."""
with open(CONFIG_FILE, 'w') as file:
json.dump({'last_dir': directory}, file)
def get_search_key(filename):
"""
Преобразует имя файла: удаляет расширение и заменяет дефисы на подчёркивания.
Например, "3M-Protemp4-Banner-900x450-BG.jpg" превращается в "3M_Protemp4_Banner_900x450_BG".
"""
if not filename:
return ""
base = os.path.splitext(filename)[0]
return base.replace("-", "_")
class FileSearchApp(App):
def build(self):
self.file_path = ""
self.extensions = [".psd", ".indd", ".tif", ".tiff"]
layout = BoxLayout(orientation='vertical')
self.label = Label(text="Select an Excel file with Asset IDs")
layout.add_widget(self.label)
choose_button = Button(text="Choose File")
choose_button.bind(on_press=self.select_file)
layout.add_widget(choose_button)
start_button = Button(text="Open Login Page")
start_button.bind(on_press=self.open_login_page)
layout.add_widget(start_button)
self.continue_button = Button(text="Continue After Login", disabled=True)
self.continue_button.bind(on_press=self.start_search)
layout.add_widget(self.continue_button)
return layout
def select_file(self, instance):
last_dir = get_last_used_directory() or '.'
filechooser = FileChooserListView(path=last_dir, filters=['*.xlsx'])
popup = Popup(title="Choose Excel File", content=filechooser, size_hint=(0.9, 0.9))
filechooser.bind(on_submit=lambda chooser, selection, touch: self.on_file_select(selection, popup))
popup.open()
def on_file_select(self, selection, popup):
if selection:
self.file_path = selection[0]
self.label.text = f"Selected File: {self.file_path}"
print(f"Selected file: {self.file_path}")
save_last_used_directory(os.path.dirname(self.file_path))
popup.dismiss()
else:
print("No file selected.")
def open_login_page(self, instance):
if not self.file_path:
popup = Popup(title='Warning',
content=Label(text='Select a file before starting!'),
size_hint=(0.8, 0.3))
popup.open()
return
options = Options()
options.add_argument("user-data-dir=/Users/vadymsamoilenko/Library/Application Support/Google/Chrome/Profile 1")
self.driver = webdriver.Chrome(options=options)
try:
self.driver.get("https://mmmspinco.brand-portal.adobe.com/mediaportal.html/content/dam/mac/mmmspinco")
time.sleep(5)
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.XPATH, "/html/body/coral-shell/coral-shell-content/div[1]/a/img"))
)
self.continue_button.disabled = False
print("Login successful, ready to continue.")
login_popup = Popup(title='Login',
content=Label(text='Please log in and complete 2FA.\nClick "Continue After Login" once logged in.'),
size_hint=(0.8, 0.3))
login_popup.open()
except Exception as e:
print("Error opening login page:", e)
self.driver.quit()
def start_search(self, instance):
if not self.file_path or self.continue_button.disabled:
popup = Popup(title='Warning',
content=Label(text='Ensure you are logged in before starting!'),
size_hint=(0.8, 0.3))
popup.open()
return
try:
wb = openpyxl.load_workbook(self.file_path)
sheet = wb.active
# Предполагаем, что:
# - Столбец 1: Asset ID
# - Столбец 3: Имя файла (с расширением)
for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row):
asset_id = row[0].value # Asset ID
file_name = row[2].value if len(row) >= 3 else None # Имя файла
found_link = None
if asset_id and file_name:
search_key = get_search_key(file_name)
print(f"Processing Asset ID: {asset_id} with search key: {search_key}")
self.driver.get("https://mmmspinco.brand-portal.adobe.com/aem/search.html")
search_box = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.NAME, "fulltext"))
)
# Очистка поля ввода через clear() и JavaScript
time.sleep(3)
search_box.clear()
self.driver.execute_script("arguments[0].value = '';", search_box)
# Поиск по asset_id для первоначального отбора
search_box.send_keys(asset_id)
search_box.send_keys(Keys.RETURN)
time.sleep(3)
try:
properties_button = WebDriverWait(self.driver, 30).until(
EC.element_to_be_clickable((By.XPATH, "//button/coral-icon[@icon='infoCircle']"))
)
properties_button.click()
time.sleep(3)
# Получаем все PSD-ссылки из секции "Others"
psd_link_elements = WebDriverWait(self.driver, 30).until(
EC.presence_of_all_elements_located(
(By.XPATH, "//div[contains(@class, 'references-referencing')]//a[@data-relation='others' and contains(@data-asset-path, '.psd')]")
)
)
time.sleep(3)
# Выводим отладочную информацию для каждого кандидата
for elem in psd_link_elements:
title_attr = elem.get_attribute("title")
href = elem.get_attribute("href")
print(f"Candidate element -> title: {title_attr}, href: {href}")
# Выбираем элемент, где search_key встречается в title или href (без учета расширения)
for elem in psd_link_elements:
title_attr = elem.get_attribute("title")
href = elem.get_attribute("href")
search_key_lower = search_key.lower()
if (search_key_lower in title_attr.lower() if title_attr else False) or \
(search_key_lower in href.lower() if href else False):
found_link = href
print(f"Selected link matching search key: {found_link}")
break
except TimeoutException:
print("Timeout while waiting for the PSD link element.")
found_link = None
except NoSuchElementException:
print("PSD link element not found.")
found_link = None
except Exception as e:
print(f"Unexpected error occurred: {e}")
found_link = None
if not found_link:
print("No suitable link found for asset:", asset_id)
sheet.cell(row=row[0].row, column=2, value=found_link if found_link else "Links not found")
print(f"Processed asset {asset_id}, link: {found_link}")
save_path = self.file_path.replace(".xlsx", "_updated.xlsx")
wb.save(save_path)
success_popup = Popup(title='Success',
content=Label(text=f"File saved: {save_path}"),
size_hint=(0.8, 0.3))
success_popup.open()
finally:
self.driver.quit()
if __name__ == '__main__':
FileSearchApp().run()

3
zshrc Normal file
View file

@ -0,0 +1,3 @@
export PATH="/opt/homebrew/opt/tcl-tk/bin:$PATH"
export LDFLAGS="-L/opt/homebrew/opt/tcl-tk/lib"
export CPPFLAGS="-I/opt/homebrew/opt/tcl-tk/include"