示例#1
0
    def __init__(self, data_hub_entry: DataHubEntry,
                 update_table: UpdateTableEx):
        super(DataUpdateUi, self).__init__()

        # Access entry
        self.__data_hub = data_hub_entry
        self.__data_center = self.__data_hub.get_data_center()
        self.__update_table = update_table

        # Table content
        self.__display_uri = []
        self.__display_identities = None
        self.__display_table_lines = []

        # Page related
        self.__page = 0
        self.__item_per_page = 20

        # For processing updating
        self.__processing_update_tasks = []
        # Fot task counting
        self.__processing_update_tasks_count = []

        self.task_finish_signal.connect(self.__on_task_done)
        self.refresh_finish_signal.connect(self.update_table_display)

        # Timer for update status
        self.__timer = QTimer()
        self.__timer.setInterval(1000)
        self.__timer.timeout.connect(self.on_timer)
        self.__timer.start()

        # UI related
        self.__info_panel = QLabel(DEFAULT_INFO)

        self.__table_main = TableViewEx()
        self.__button_head_page = QPushButton('<<')
        self.__button_prev_page = QPushButton('<')
        self.__button_next_page = QPushButton('>')
        self.__button_tail_page = QPushButton('>>')
        self.__button_upper_level = QPushButton('↑')

        self.__button_refresh = QPushButton('Refresh')
        self.__button_batch_auto_update = QPushButton('Auto Update Select')
        self.__button_batch_force_update = QPushButton('Force Update Select')

        self.init_ui()

        # Post update and cache stock list after posting RefreshTask
        data_utility = self.__data_hub.get_data_utility()
        StockAnalysisSystem().get_task_queue().add_observer(self)
示例#2
0
class DataUpdateUi(QWidget, TaskQueue.Observer):
    task_finish_signal = pyqtSignal([UpdateTask])
    refresh_finish_signal = pyqtSignal()

    INDEX_CHECK = 0
    INDEX_ITEM = 1
    INDEX_STATUS = 8
    TABLE_HEADER = [
        '', 'Item', 'Local Data Since', 'Local Data Until', 'Latest Update',
        'Update Estimation', 'Sub Update', 'Update', 'Status'
    ]

    NO_SUB_UPDATE_URI = [
        'Market.SecuritiesInfo',
        'Market.IndexInfo',
    ]
    SUB_UPDATE_STOCK_URI = [
        'Finance.Audit', 'Finance.BalanceSheet', 'Finance.IncomeStatement',
        'Finance.CashFlowStatement', 'Stockholder.PledgeStatus',
        'Stockholder.PledgeHistory', 'Stockholder.Statistics',
        'TradeData.Stock.Daily', 'Market.NamingHistory'
    ]
    SUB_UPDATE_INDEX_URI = ['TradeData.Index.Daily']
    SUB_UPDATE_STOCK_EXCHANGE_URI = ['Market.TradeCalender']

    def get_uri_sub_update(self, uri: str) -> list or None:
        if uri in DataUpdateUi.SUB_UPDATE_STOCK_URI:
            data_utility = self.__data_hub.get_data_utility()
            return data_utility.get_stock_identities()
        elif uri in DataUpdateUi.SUB_UPDATE_INDEX_URI:
            return list(DEPENDS_INDEX.keys())
        elif uri in DataUpdateUi.SUB_UPDATE_STOCK_EXCHANGE_URI:
            return A_SHARE_MARKET
        elif uri in DataUpdateUi.NO_SUB_UPDATE_URI:
            return None
        else:
            print('Sub update declare missing.')
            assert False

    def __init__(self, data_hub_entry: DataHubEntry,
                 update_table: UpdateTableEx):
        super(DataUpdateUi, self).__init__()

        # Access entry
        self.__data_hub = data_hub_entry
        self.__data_center = self.__data_hub.get_data_center()
        self.__update_table = update_table

        # Table content
        self.__display_uri = []
        self.__display_identities = None
        self.__display_table_lines = []

        # Page related
        self.__page = 0
        self.__item_per_page = 20

        # For processing updating
        self.__processing_update_tasks = []
        # Fot task counting
        self.__processing_update_tasks_count = []

        self.task_finish_signal.connect(self.__on_task_done)
        self.refresh_finish_signal.connect(self.update_table_display)

        # Timer for update status
        self.__timer = QTimer()
        self.__timer.setInterval(1000)
        self.__timer.timeout.connect(self.on_timer)
        self.__timer.start()

        # UI related
        self.__info_panel = QLabel(DEFAULT_INFO)

        self.__table_main = TableViewEx()
        self.__button_head_page = QPushButton('<<')
        self.__button_prev_page = QPushButton('<')
        self.__button_next_page = QPushButton('>')
        self.__button_tail_page = QPushButton('>>')
        self.__button_upper_level = QPushButton('↑')

        self.__button_refresh = QPushButton('Refresh')
        self.__button_batch_auto_update = QPushButton('Auto Update Select')
        self.__button_batch_force_update = QPushButton('Force Update Select')

        self.init_ui()

        # Post update and cache stock list after posting RefreshTask
        data_utility = self.__data_hub.get_data_utility()
        StockAnalysisSystem().get_task_queue().add_observer(self)
        # StockAnalysisSystem().get_task_queue().append_task(UpdateStockListTask(data_utility))

    # ---------------------------------------------------- UI Init -----------------------------------------------------

    def init_ui(self):
        self.__layout_control()
        self.__config_control()
        self.__to_top_level()

    def __layout_control(self):
        main_layout = QVBoxLayout()
        self.setLayout(main_layout)
        self.setMinimumSize(600, 400)
        main_layout.addWidget(self.__table_main)

        bottom_control_area = QHBoxLayout()
        main_layout.addLayout(bottom_control_area)

        bottom_right_area = QVBoxLayout()
        bottom_control_area.addWidget(self.__info_panel, 99)
        bottom_control_area.addLayout(bottom_right_area, 0)

        line = horizon_layout([
            self.__button_head_page, self.__button_prev_page,
            self.__button_next_page, self.__button_tail_page,
            self.__button_upper_level, self.__button_refresh
        ])
        bottom_right_area.addLayout(line)

        line = horizon_layout([
            self.__button_batch_auto_update, self.__button_batch_force_update
        ])
        bottom_right_area.addLayout(line)

    def __config_control(self):
        self.__table_main.SetColumn(DataUpdateUi.TABLE_HEADER)
        self.__table_main.SetCheckableColumn(DataUpdateUi.INDEX_CHECK)
        self.__table_main.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)

        self.__button_head_page.clicked.connect(
            partial(self.on_page_control, '<<'))
        self.__button_prev_page.clicked.connect(
            partial(self.on_page_control, '<'))
        self.__button_next_page.clicked.connect(
            partial(self.on_page_control, '>'))
        self.__button_tail_page.clicked.connect(
            partial(self.on_page_control, '>>'))
        self.__button_upper_level.clicked.connect(
            partial(self.on_page_control, '^'))
        self.__button_refresh.clicked.connect(
            partial(self.on_page_control, 'r'))

        self.__button_batch_auto_update.clicked.connect(
            partial(self.on_batch_update, False))
        self.__button_batch_force_update.clicked.connect(
            partial(self.on_batch_update, True))

    def on_detail_button(self, uri: str):
        print('Detail of ' + uri)
        self.__page = 0
        self.__to_detail_level(uri)

    def on_auto_update_button(self, uri: str, identity: str):
        print('Auto update ' + uri + ':' + str(identity))
        self.__build_post_update_task(uri, None, False)

    def on_force_update_button(self, uri: str, identity: str):
        print('Force update ' + uri + ':' + str(identity))
        self.__build_post_update_task(uri, None, True)

    def on_batch_update(self, force: bool):
        for i in range(self.__table_main.RowCount()):
            if self.__table_main.GetItemCheckState(
                    i, DataUpdateUi.INDEX_CHECK) == Qt.Checked:
                item_id = self.__table_main.GetItemText(
                    i, DataUpdateUi.INDEX_ITEM)
                # A little ugly...To distinguish it's uri or securities ideneity
                if self.__display_identities is None:
                    self.__build_post_update_task(item_id, None, force)
                else:
                    self.__build_post_update_task(self.__display_uri[0],
                                                  item_id, force)

    def on_page_control(self, control: str):
        # data_utility = self.__data_hub.get_data_utility()
        # stock_list = data_utility.get_stock_list()
        # max_page = len(stock_list) // self.__item_per_page

        if self.__display_identities is None:
            max_item_count = len(self.__display_uri)
        else:
            max_item_count = len(self.__display_identities)
        max_page = max_item_count // self.__item_per_page

        if control == '<<':
            self.__page = 0
        elif control == '<':
            self.__page = max(self.__page - 1, 0)
        elif control == '>':
            self.__page = min(self.__page + 1, max_page)
        elif control == '>>':
            self.__page = max_page
        elif control == '^':
            self.__to_top_level()

        if control in ['<<', '<', '>', '>>', '^', 'r']:
            self.update_table()

    def on_timer(self):
        return
        for i in range(self.__table_main.RowCount()):
            item_id = self.__table_main.GetItemText(i, DataUpdateUi.INDEX_ITEM)
            # A little ugly...To distinguish it's uri or securities identity
            if self.__display_identities is None:
                uri = item_id
                prog_id = uri
            else:
                uri = self.__display_uri[0]
                prog_id = [uri, item_id]
            for task in self.__processing_update_tasks:
                if not task.in_work_package(uri):
                    continue
                text = []
                if task.status() in [
                        TaskQueue.Task.STATUS_IDLE,
                        TaskQueue.Task.STATUS_PENDING
                ]:
                    text.append('等待中...')
                else:
                    if task.progress.has_progress(prog_id):
                        rate = task.progress.get_progress_rate(prog_id)
                        text.append('%ss' % task.clock.elapsed_s())
                        text.append('%.2f%%' % (rate * 100))
                    if task.status() == TaskQueue.Task.STATUS_CANCELED:
                        text.append('[Canceled]')
                    elif task.status() == TaskQueue.Task.STATUS_FINISHED:
                        text.append('[Finished]')
                    elif task.status() == TaskQueue.Task.STATUS_EXCEPTION:
                        text.append('[Error]')
                self.__table_main.SetItemText(i, DataUpdateUi.INDEX_STATUS,
                                              ' | '.join(text))
                break

    # def closeEvent(self, event):
    #     if self.__task_thread is not None:
    #         QMessageBox.information(self,
    #                                 QtCore.QCoreApplication.translate('', '无法关闭窗口'),
    #                                 QtCore.QCoreApplication.translate('', '更新过程中无法关闭此窗口'),
    #                                 QMessageBox.Close, QMessageBox.Close)
    #         event.ignore()
    #     else:
    #         event.accept()

    # ---------------------------------------- Table Update ----------------------------------------

    def update_table(self):
        self.__table_main.Clear()
        self.__table_main.SetRowCount(0)
        self.__table_main.SetColumn(DataUpdateUi.TABLE_HEADER)
        self.__table_main.AppendRow(['', '刷新中...', '', '', '', '', '', '', ''])
        task = RefreshTask(self)
        # StockAnalysisSystem().get_task_queue().append_task(task)

    def update_table_display(self):
        return
        self.__table_main.Clear()
        self.__table_main.SetRowCount(0)
        self.__table_main.SetColumn(DataUpdateUi.TABLE_HEADER)

        for line in self.__display_table_lines:
            self.__table_main.AppendRow(line)
            index = self.__table_main.RowCount() - 1

            # Add check box
            # check_item = QTableWidgetItem()
            # check_item.setCheckState(QtCore.Qt.Unchecked)
            # self.__table_main.setItem(index, 0, check_item)

            # Add detail button
            # Only if currently in top level
            if self.__display_identities is None or len(
                    self.__display_identities) == 0:
                if line[1] not in DataUpdateUi.NO_SUB_UPDATE_URI:
                    button = QPushButton('Enter')
                    button.clicked.connect(
                        partial(self.on_detail_button, line[1]))
                    self.__table_main.SetCellWidget(index, 6, button)

            # Add update button
            button_auto = QPushButton('Auto')
            button_force = QPushButton('Force')
            button_auto.clicked.connect(
                partial(self.on_auto_update_button, line[1], None))
            button_force.clicked.connect(
                partial(self.on_force_update_button, line[1], None))
            self.__table_main.SetCellWidget(index, 7,
                                            [button_auto, button_force])

    def update_table_content(self):
        contents = []
        count = self.__item_per_page
        offset = self.__page * self.__item_per_page

        for uri in self.__display_uri:
            update_details = self.__display_identities if \
                self.__display_identities is not None else [None]
            for index in range(offset, offset + count):
                if index >= len(update_details):
                    break
                line = self.generate_line_content(uri, update_details[index])
                if line is not None:
                    contents.append(line)
        self.__display_table_lines = contents

    def generate_line_content(self, uri: str, identity: str
                              or None) -> [list] or None:
        line = []

        data_table, _ = self.__data_center.get_data_table(uri)
        update_table = self.__update_table

        if data_table is None:
            return None

        since, until = update_table.get_since_until(uri.split('.'))
        if since is None or until is None:
            # TODO: Workaround - because each stock storage in each table.
            # So we cannot fetch its time range with this method.
            since, until = data_table.range(uri, identity)
        if until is not None:
            update_since = min(tomorrow_of(until), now())
            update_until = now()
        else:
            update_since, update_until = self.__data_center.calc_update_range(
                uri, identity)

        update_tags = uri.split('.')
        latest_update = self.__update_table.get_last_update_time(update_tags)

        line.append('')  # Place holder for check box
        line.append(identity if str_available(identity) else uri)
        line.append(date2text(since) if since is not None else ' - ')
        line.append(date2text(until) if until is not None else ' - ')
        line.append(
            date2text(latest_update) if latest_update is not None else ' - ')

        if update_since is not None and update_until is not None:
            line.append(
                date2text(update_since) + ' - ' + date2text(update_until))
        else:
            line.append(' - ')
        line.append('-')  # Place holder for detail button
        line.append('')  # Place holder for update button
        line.append('')  # Place holder for status

        return line

    # --------------------------------------------------------------------------

    def __to_top_level(self):
        self.__display_uri = [declare[0] for declare in DATA_FORMAT_DECLARE]
        self.__display_identities = None
        self.__page = 0
        self.update_table()

    def __to_detail_level(self, uri: str):
        self.__display_uri = [uri]
        self.__display_identities = self.get_uri_sub_update(uri)
        # if uri in ['Market.TradeCalender']:
        #     self.__display_identities = ['SSE']
        # elif uri in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
        #     data_utility = self.__data_hub.get_data_utility()
        #     self.__display_identities = data_utility.get_stock_identities()
        self.__page = 0
        self.update_table()

    def __build_post_update_task(self, uri: str, identities: list or None,
                                 force: bool) -> bool:
        task = UpdateTask(self, self.__data_hub, self.__data_center, force)
        if identities is None:
            identities = self.get_uri_sub_update(uri)
            # if uri == 'Market.TradeCalender':
            #     identities = 'SSE'
            # elif uri in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
            #     data_utility = self.__data_hub.get_data_utility()
            #     identities = data_utility.get_stock_identities()
        task.set_work_package(uri, identities)
        self.__processing_update_tasks.append(task)
        self.__processing_update_tasks_count.append(task)
        ret = StockAnalysisSystem().get_task_queue().append_task(task)
        # After updating market info, also update stock list cache
        if ret and uri == 'Market.SecuritiesInfo':
            data_utility = self.__data_hub.get_data_utility()
            StockAnalysisSystem().get_task_queue().append_task(
                UpdateStockListTask(data_utility))
        return ret

    # ---------------------------------------------------------------------------------

    def on_task_updated(self, task, change: str):
        if change in ['canceled', 'finished']:
            if task in self.__processing_update_tasks_count:
                self.task_finish_signal[UpdateTask].emit(task)

    def __on_task_done(self, task: UpdateTask):
        if task in self.__processing_update_tasks_count:
            self.__processing_update_tasks_count.remove(task)
            print('Finish task: %s, remaining count: %s' %
                  (task.name(), len(self.__processing_update_tasks_count)))
            if len(self.__processing_update_tasks_count) == 0:
                QMessageBox.information(
                    self, QtCore.QCoreApplication.translate('main', '更新完成'),
                    QtCore.QCoreApplication.translate('main', '数据更新完成'),
                    QMessageBox.Ok, QMessageBox.Ok)
                self.__processing_update_tasks.clear()
                self.update_table()
        else:
            print('Impossible: Cannot find finished task in task list.')