diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 245a313..0000000 --- a/.gitignore +++ /dev/null @@ -1,177 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# PyPI configuration file -.pypirc -*.xlsx -config.json -coockie.json -*.code-workspace -zshrc - diff --git a/Book1 copy.xlsx b/Book1 copy.xlsx deleted file mode 100644 index 6ac1568..0000000 Binary files a/Book1 copy.xlsx and /dev/null differ diff --git a/Book1 copy_updated.xlsx b/Book1 copy_updated.xlsx deleted file mode 100644 index d605350..0000000 Binary files a/Book1 copy_updated.xlsx and /dev/null differ diff --git a/Book1.xlsx b/Book1.xlsx deleted file mode 100644 index 5f70b28..0000000 Binary files a/Book1.xlsx and /dev/null differ diff --git a/Book1_updated.xlsx b/Book1_updated.xlsx deleted file mode 100644 index 649d474..0000000 Binary files a/Book1_updated.xlsx and /dev/null differ diff --git a/Solventum_2.code-workspace b/Solventum_2.code-workspace deleted file mode 100644 index 362d7c2..0000000 --- a/Solventum_2.code-workspace +++ /dev/null @@ -1,7 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ] -} \ No newline at end of file diff --git a/config.json b/config.json deleted file mode 100644 index c5d3242..0000000 --- a/config.json +++ /dev/null @@ -1 +0,0 @@ -{"last_dir": "/Users/vadymsamoilenko/Documents/GIT/Script"} \ No newline at end of file diff --git a/cookies.json b/cookies.json deleted file mode 100644 index 2c426d8..0000000 --- a/cookies.json +++ /dev/null @@ -1,297 +0,0 @@ -[ - { - "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 - } - ] \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index a5323d6..0000000 --- a/main.py +++ /dev/null @@ -1,376 +0,0 @@ -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() \ No newline at end of file diff --git a/main_backup.py b/main_backup.py deleted file mode 100644 index 8914dd7..0000000 --- a/main_backup.py +++ /dev/null @@ -1,201 +0,0 @@ -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() \ No newline at end of file diff --git a/zshrc b/zshrc deleted file mode 100644 index 6765014..0000000 --- a/zshrc +++ /dev/null @@ -1,3 +0,0 @@ -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"