Этика тарификации за работу, сделанную на платформе, Вы только что начали учиться [закрытый]

Прежде всего у вас есть следующие плохие практики программирования:

  • Вы путаете бизнес-логику с GUI.
  • Имена переменных не описывают использование переменных.
  • Много кода в файле.

С другой стороны, при переходе на Qt у вас есть следующие ошибки:

  • Вы не должны изменять GUI из другого потока, так как GUI не является безопасным для протектора, вы должны использовать сигналы, события и т. д.
  • Вы должны использовать макеты для графического интерфейса, чтобы адаптироваться при изменении размера.

С другой стороны, я не проверял ваш код, но я вижу, что одна из ваших ошибок -

t = threading.Thread(target=self.generate())

, так как вы вызываете функцию вместо передачи ее в функцию, она должна be

t = threading.Thread(target=self.generate)

Учитывая вышеизложенное, я реализовал следующее:

fuse_ui.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'fuse.ui'
#
# Created by: PyQt5 UI code generator 5.12.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Fuse(object):
    def setupUi(self, Fuse):
        Fuse.setObjectName("Fuse")
        Fuse.resize(556, 513)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Fuse.sizePolicy().hasHeightForWidth())
        Fuse.setSizePolicy(sizePolicy)
        self.verticalLayout = QtWidgets.QVBoxLayout(Fuse)
        self.verticalLayout.setObjectName("verticalLayout")
        spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.verticalLayout.addItem(spacerItem)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        spacerItem1 = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem1)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.userNameLabel = QtWidgets.QLabel(Fuse)
        self.userNameLabel.setObjectName("userNameLabel")
        self.gridLayout.addWidget(self.userNameLabel, 0, 0, 1, 1)
        self.userNameLineEdit = QtWidgets.QLineEdit(Fuse)
        self.userNameLineEdit.setObjectName("userNameLineEdit")
        self.gridLayout.addWidget(self.userNameLineEdit, 0, 1, 1, 1)
        self.passwordLabel = QtWidgets.QLabel(Fuse)
        self.passwordLabel.setObjectName("passwordLabel")
        self.gridLayout.addWidget(self.passwordLabel, 1, 0, 1, 1)
        self.passwordLineEdit = QtWidgets.QLineEdit(Fuse)
        self.passwordLineEdit.setObjectName("passwordLineEdit")
        self.gridLayout.addWidget(self.passwordLineEdit, 1, 1, 1, 1)
        self.invalidLabel = QtWidgets.QLabel(Fuse)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.invalidLabel.sizePolicy().hasHeightForWidth())
        self.invalidLabel.setSizePolicy(sizePolicy)
        self.invalidLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.invalidLabel.setObjectName("invalidLabel")
        self.gridLayout.addWidget(self.invalidLabel, 2, 1, 1, 1)
        self.widget = QtWidgets.QWidget(Fuse)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setObjectName("widget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.submitButton = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.submitButton.sizePolicy().hasHeightForWidth())
        self.submitButton.setSizePolicy(sizePolicy)
        self.submitButton.setObjectName("submitButton")
        self.horizontalLayout.addWidget(self.submitButton)
        self.resetButton = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.resetButton.sizePolicy().hasHeightForWidth())
        self.resetButton.setSizePolicy(sizePolicy)
        self.resetButton.setObjectName("resetButton")
        self.horizontalLayout.addWidget(self.resetButton)
        self.gridLayout.addWidget(self.widget, 3, 1, 1, 1)
        self.horizontalLayout_2.addLayout(self.gridLayout)
        spacerItem2 = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        spacerItem3 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.verticalLayout.addItem(spacerItem3)
        self.logTextEdit = QtWidgets.QTextEdit(Fuse)
        self.logTextEdit.setObjectName("logTextEdit")
        self.verticalLayout.addWidget(self.logTextEdit)
        self.progressBar = QtWidgets.QProgressBar(Fuse)
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.verticalLayout.addWidget(self.progressBar)

        self.retranslateUi(Fuse)
        QtCore.QMetaObject.connectSlotsByName(Fuse)

    def retranslateUi(self, Fuse):
        _translate = QtCore.QCoreApplication.translate
        Fuse.setWindowTitle(_translate("Fuse", "Dialog"))
        self.userNameLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-weight:600;\">User Name</span></p></body></html>"))
        self.passwordLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-weight:600;\">Password</span></p></body></html>"))
        self.invalidLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-size:7pt; color:red; font-weight:600;\">Invalid User Name or Password</span></p></body></html>"))
        self.submitButton.setText(_translate("Fuse", "Submit"))
        self.resetButton.setText(_translate("Fuse", "Reset"))




if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Fuse = QtWidgets.QDialog()
    ui = Ui_Fuse()
    ui.setupUi(Fuse)
    Fuse.show()
    sys.exit(app.exec_())

fusi_worker.py

import os
import time

from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException,ElementClickInterceptedException
from selenium.webdriver.support.ui import WebDriverWait,Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from selenium.webdriver.common.action_chains import ActionChains

from PyQt5 import QtCore

import pandas as pd

class FusiWorker(QtCore.QObject):
    progressChanged = QtCore.pyqtSignal(int)
    logSignal = QtCore.pyqtSignal(str)
    invalidSignal = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()

    @QtCore.pyqtSlot(str, str)
    def start(self, username, password):

        options = Options()
        options.add_argument("--headless")
        profile = FirefoxProfile()
        driver_path = os.path.expandvars('%userprofile%\\Desktop\\RPA\\Tools\\geckodriver.exe')
        driver = webdriver.Firefox(firefox_profile=profile, options=options, executable_path=driver_path)
        driver.get("https://example.com/Login.aspx")
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "loginBtnn")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')

        search_field = driver.find_element_by_id("txtUserName")
        search_field.clear()
        search_field.send_keys(password)
        time.sleep(5)
        driver.find_element_by_id("BtnLogin").click()
        self.logSignal.emit('Clicked on Login')
        time.sleep(5)
        try:
            element = driver.find_element_by_xpath("//span[contains(@id,'lblFailure')]")
            if element.text == "Invalid User Name or Password":
                self.logSignal.emit("Invalid User Name or Password")
                self.invalidSignal.emit()
                driver.quit()
                driver.close()
        except NoSuchElementException:
            self.logSignal.emit("Correct User Name or Password")
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "/html/body/form/div[4]/a/img")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')    
        driver.find_element_by_xpath("/html/body/form/div[4]/a/img").click()
        self.logSignal('Clicked Product Links')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "lnkFuse")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_id("lnkFuse").click()
        self.logSignal.emit('Clicked on Fuse Link')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "//span[@class='Invoice Processing'][contains(.,'Invoice Processing')]")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        element_to_hover_over = driver.find_element_by_xpath("//span[@class='Invoice Processing'][contains(.,'Invoice Processing')]")
        hover = ActionChains(driver).move_to_element(element_to_hover_over)
        hover.perform()
        self.logSignal.emit('Clicked on Invoice Processing')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='../../RS/Batch/AuditInvoice.aspx']")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_xpath("//a[@href='../../RS/Batch/AuditInvoice.aspx']").click()
        self.logSignal.emit('Clicked on Audit Invoices')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_ddlSearchInvoiceStatus")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        invoice_audit = 'Audited' 
        InvoiceStatus = Select(driver.find_element_by_id("MainContent_ddlSearchInvoiceStatus"))
        for option in InvoiceStatus.options:
            option_text = option.text
            if invoice_audit in option_text:
                option.click()
                break
        self.logSignal.emit('Selected Audited from Dropdown')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_btnSearch")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_id('MainContent_btnSearch').click() 
        self.logSignal.emit('Clicked on Search Button')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_ddlItemsPerPage")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        element = driver.find_element_by_id('MainContent_ddlItemsPerPage')
        element.location_once_scrolled_into_view
        driver.find_element_by_id('MainContent_ddlItemsPerPage').click() 
        items_perpage = '500' 
        records_list = Select(driver.find_element_by_id("MainContent_ddlItemsPerPage"))
        for option in records_list.options:
            option_text = option.text
            if items_perpage in option_text:
                option.click()
                break
        time.sleep(5)
        self.logSignal.emit('Selected Max items per page')
        list_links = driver.find_elements_by_partial_link_text('Generate')
        time.sleep(5)
        id_list = []
        for i in list_links:
            data = i.get_attribute('id')
            self.logSignal.emit(data)
            id_list.append(data)
            # TODO
            id_df = pd.DataFrame(id_list)
            self.logSignal.emit(id_df.to_string())
        for row in id_df.values:
            row_val = str(row)[2:-2]
            try:
                WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_gvAuditInvoice")))
                self.logSignal.emit('Element Found')
                element = driver.find_element_by_id(row_val)
                element.location_once_scrolled_into_view
                WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, row_val)))
                driver.find_element_by_id(row_val).click()
                localtime = time.strftime("%m-%d-%Y  %I:%M:%S")
                self.logSignal.emit(row_val + "|" + 'Clicked' + '|' + localtime)
                time.sleep(5)
            except ElementClickInterceptedException:
                continue
                self.logSignal.emit('Timed out waiting for page to load')
            time.sleep(5)
        self.logSignal.emit('Completed')
        self.finished.emit()

    @QtCore.pyqtSlot()
    def load_data(self):
        completed = 0
        while completed < 100:
            completed += 0.0001
            self.progressChanged.emit(completed)
            time.sleep(0.01)

main.py

import threading
from PyQt5 import QtCore, QtGui, QtWidgets

from fuse_worker import FusiWorker
from fuse_ui import Ui_Fuse

class Fuse(QtWidgets.QDialog, Ui_Fuse):
    def __init__(self, parent=None):
        super(Fuse, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | 
                             QtCore.Qt.MSWindowsFixedSizeDialogHint |
                             QtCore.Qt.WindowCloseButtonHint | 
                             QtCore.Qt.WindowMinimizeButtonHint)
        self.init()
        self.connections()

    def init(self):
        self._worker = FusiWorker()
        self.reset()
        thread = QtCore.QThread(self)
        thread.start()
        self._worker.moveToThread(thread)
        self.progressBar.setValue(0)

    def connections(self):
        self.submitButton.clicked.connect(self.submit)
        self.resetButton.clicked.connect(self.reset)
        self._worker.logSignal.connect(self.logTextEdit.insertPlainText)
        self._worker.invalidSignal.connect(self.invalidate)
        self._worker.finished.connect(self.reset)
        self._worker.progressChanged.connect(self.progressBar.setValue)
        self.progressBar.setValue(0)

    @QtCore.pyqtSlot()
    def submit(self):
        username = self.userNameLineEdit.text()
        password = self.passwordLineEdit.text()
        self.logTextEdit.clear()
        self.userNameLineEdit.setEnabled(False)
        self.passwordLineEdit.setEnabled(False)
        threading.Thread(target=self._worker.start, args=(username, password,), daemon=True).start()
        threading.Thread(target=self._worker.load_data, daemon=True).start()

    @QtCore.pyqtSlot()
    def reset(self):
         self.userNameLineEdit.clear()
         self.passwordLineEdit.clear()
         self.invalidLabel.hide()
         self.userNameLineEdit.setEnabled(True)
         self.passwordLineEdit.setEnabled(True)
         self.submitButton.setEnabled(True)

    @QtCore.pyqtSlot()
    def invalidate(self):
        self.invalidLabel.show()
        self.reset()

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Fuse()
    w.show()
    sys.exit(app.exec_())

Эти файлы должны находиться в одной папке:

├── fuse_ui.py
├── fuse_worker.py
└── main.py
9
задан Kevin Brown 1 March 2015 в 22:18
поделиться

6 ответов

Я не вижу этической проблемы здесь, если Вы раскрываете, что Ваши основные экспертные знания находятся на платформах кроме той, на которой они нанимают Вас для разработки.

14
ответ дан 4 December 2019 в 07:15
поделиться

Принятие Вы тарифицируете каждый час:

Если Вы - опытный разработчик затем, необходимо смочь сказать то, что стоит Вам времени из-за изучения новой платформы по сравнению с решением проблемы под рукой.

Отслеживайте то, что Вы делаете (использование приложения снимка экрана могло помочь здесь), и если это - чистое исследование (читающий статьи, ища документацию) затем не тарифицируют его. Кроме того, если Вы исправляете ошибку, которая оказывается ошибкой новичка (такой как недоразумение некоторой информации), затем не тарифицируйте это. Остальная часть Вашего времени будет потрачена в продуктивной работе для клиента, и это должно быть тем, что тарифицировано.

5
ответ дан 4 December 2019 в 07:15
поделиться

Нет - его весьма этическое. Наша профессия требует, чтобы мы изучили что-то новое на постоянной основе. Поэтому мы, как могут ожидать, будем заряжаться / заплачены то, что мы делаем. Работодатели часто желают к не, только покупают книги для нас, но также и плату за сертификации, семинары, и любое время, которое мы проводим при упомянутом расширенном изучении. Рациональность позади него - то, что, если мы изучаем что-то новое, существует неизбежно выплата для них (быть - оно эффективность, производительность, и т.д.).

Я подозреваю, что Вы будете утверждать, что его различное, потому что Вы - фриланс, таким образом, я поставлю вопрос - почему он несколько отличается? Ваш работодатель является Вашим клиентом - если Вы чувствуете, что они получат выгоду с упомянутой платформы по другому затем, Вы делаете их сервис и должны быть компенсированы как таковые.

4
ответ дан 4 December 2019 в 07:15
поделиться

Это сортирует, само собой разумеется, но Ваш клиент был бы тем, чтобы определить, хотят ли они даже пойти с Вами как консультант, если бы Вы не знаете язык/платформу, они хотят, чтобы Вы продолжили работать, и они также были бы теми, чтобы сказать Вам, заплатят ли они за Вас для "изучения, поскольку Вы кодируете". Просто необходимо быть искренны и честны со всем от запуска. Не действуйте как Вы, знают платформу/язык, если Вы не делаете.

4
ответ дан 4 December 2019 в 07:15
поделиться

Это кажется, что Вы все говорите "О времени и Материалах" проекты, где клиент платит Вам однако много времени, Вы берете для завершения задания. На проекте как этот я вижу, как это играет роль, и я рекомендовал бы быть честным.

Большинством проектов я продолжаю работать, является "Фиксированная цена". Клиент дает нам общее представление о том, что они хотят. Мы затем обрабатываем кавычку "Фиксированной цены". Если клиенту не нравятся числа, они идут в другое место, если они соглашаются с числами затем, они соглашаются на цену. Должны ли мы изучить что-то, чтобы сделать задание или нет, цена клиента остается тем же. В этой ситуации это не имеет значения. Клиент или соглашается на цену или не, и не имеет значения, сколько времени требуетесь Вы.

3
ответ дан 4 December 2019 в 07:15
поделиться

Я был в такой ситуации с воздухом Adobe. Да это точно не похоже на спрыгивание с Java к.NET или от PHP до Python, так как я уже знал JavaScript. На самом деле я боялся, что оторвался как слишком дорогой и дал ниже граничной цены за проект. Но не сожалел о нем, потому что клиент был очень удовлетворен и возвращен еще с 2 проектами позже.

Если цена, которую Вы предлагаете, приемлема для Вашего клиента, и Вы абсолютно уверены, что можно поставить высококачественный продукт нет ничего неэтичного с нею.

1
ответ дан 4 December 2019 в 07:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: