def devolver_reservas(app, reservas): actions = 0 app.linhas.selection_set(*[]) for _, reserva in reservas: estado.reservas.discard(reserva) app.reservas.delete(reserva) onibus = reserva[:-6] linha = onibus[:-7] if onibus in estado.linhas_entradas[linha].onibus: del estado.linhas_entradas[linha].onibus[onibus][ decodificar_id_reserva(reserva).assento] prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) + 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) actions += 1 app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) return actions
def rereservar_reservas(app, reservas): actions = 0 app.reservas.selection_set(*[]) app.linhas.selection_set(*[]) for indice, reserva in reservas: estado.reservas.add(reserva) app.reservas.insert(parent='', index=indice, values=campos_reserva_formatados(reserva), iid=reserva) app.reservas.selection_add(reserva) onibus = reserva[:-6] linha = onibus[:-7] if onibus in estado.linhas_entradas[linha].onibus: info_reserva = decodificar_id_reserva(reserva) assento, passagem = info_reserva.assento, info_reserva.passagem.tipo estado.linhas_entradas[linha].onibus[onibus][assento] = passagem prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) - 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) actions += 1 app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) return actions
def undo_reserva(event): historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo try: evento = chave, *dados = historico_undo.pop(-1) historico_redo.append(evento) actions = hst.desfazer[chave](app, *dados) try: close = dados[0][:-6] != onibus except (IndexError, TypeError): close = True if not close: info_reserva = decodificar_id_reserva(dados[0]) assento = info_reserva.assento nonlocal selected if selected['text'] != 'None': selected['style'] = 'available.TLabel' labels[assento]['style'] = 'selected.TLabel' labels[assento]['cursor'] = 'hand2' labels[assento].bind('<Button-1>', selecionar) selected = labels[assento] button_state() if len(estado.linhas_entradas[linha].onibus[onibus]) == 0: shift = (sum(coordenadas_assento(assento)) + 1) % 2 assentos_invisiveis = { assento_coordenadas(i, 2 * j + (i + shift) % 2) for j in range(2) for i in range(num_fileiras) } for assento_indisponivel in assentos_invisiveis: labels[assento_indisponivel] = labels.get( assento_indisponivel, ttk.Label(painel_assentos, text=assento_indisponivel, anchor='center', takefocus=True, width=4, style='available.TLabel', cursor='hand2')) i, j = coordenadas_assento(assento_indisponivel) labels[assento_indisponivel].grid( row=i, column=j, padx=(0, 10) if j == 1 else (10, 0) if j == 2 else 0) painel_assentos.rowconfigure(i, weight=1) if actions == 0: hst.undo(app) else: ctrl.update_action(app, cte.ACOES_COMPLEMENTARES.get(chave, chave)) except IndexError: close = False if close: monitoradas.janela_reservas.destroy()
def reserva_to_csv(reserva): ''' Retorna uma string com os dados da reserva separados por vírgula. O argumento é o id da reserva. ''' reserva = decodificar_id_reserva(reserva) campus = reserva.campus.title() partida = reserva.partida.strftime('%d/%m/%y %H:%M') assento = str(reserva.assento) passagem = cte.INDICE_PASSAGEM[reserva.passagem.tipo] linha = ', '.join([campus, partida, assento, passagem])+'\n' return linha
def devolver(app): ''' Remove as reservas selecionadas, devolvendo as vagas. ''' selecao = app.reservas.selection() if len(selecao) == 0: texto = 'Selecione reservas cujos ônibus ainda não partiram.' messagebox.showwarning(title='Não Há O Que Devolver', message=texto) return devolviveis = [ reserva for reserva in selecao if app.linhas.exists(reserva[:-6]) ] if len(devolviveis) < len(selecao): if len(devolviveis) == 0: texto = 'Ônibus já partiram. Impossível devolver.' messagebox.showwarning(title='Impossível Devolver', message=texto) return else: texto = 'Os ônibus de algumas reservas já partiram, deseja devolver as demais?' resposta = messagebox.askyesno(title='ônibus Partiram', message=texto, default='yes') if not resposta: return reservas = [] app.linhas.selection_set(*[]) for reserva in devolviveis: reservas.insert(0, [app.reservas.index(reserva), reserva]) estado.reservas.discard(reserva) app.reservas.delete(reserva) onibus = reserva[:-6] linha = onibus[:-7] del estado.linhas_entradas[linha].onibus[onibus][ decodificar_id_reserva(reserva).assento] prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) + 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) monitoradas.historico_redo.clear() monitoradas.historico_undo.append(['refund', reservas])
def redo_reserva(event): historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo try: evento = chave, *dados = historico_redo.pop(-1) historico_undo.append(evento) actions = hst.refazer[chave](app, *dados) try: close = dados[0][:-6] != onibus except (IndexError, TypeError): close = True if not close: info_reserva = decodificar_id_reserva(dados[0]) assento, tipo = info_reserva.assento, info_reserva.passagem.tipo labels[assento]['cursor'] = 'X_cursor' labels[assento]['style'] = estilo[tipo] labels[assento].unbind('<Button-1>') nonlocal selected selected = ttk.Label(painel_assentos, text='None') button_state() if len(estado.linhas_entradas[linha].onibus[onibus]) == 1: shift = (sum(coordenadas_assento(assento)) + 1) % 2 assentos_invisiveis = { assento_coordenadas(i, 2 * j + (i + shift) % 2) for j in range(2) for i in range(num_fileiras) } for assento_indisponivel in assentos_invisiveis: labels[assento_indisponivel].grid_remove() if actions == 0: hst.redo(app) else: ctrl.update_action(app, chave) except IndexError: close = False if close: monitoradas.janela_reservas.destroy()
def exibir_relatorio(app): ''' Constrói e exibe a janela do relatório. ''' data_atual = monitoradas.data_atual janela = tk.Toplevel() janela.grab_set() janela.title('Relatório') janela.resizable(True, True) janela.minsize(700, 700) janela.transient(app.root) frame = ttk.Frame(janela, padding=10) frame.pack(fill='both', expand=True) def toggle_states(master, children): master_value = master.get() for state in children: state.set(master_value) if master_value: janela.minsize(700, 700) botao_exportar.state(['!disabled']) painel_arrecadado.grid() painel_passagens.grid() painel_ocupacao.grid() else: janela.minsize(700, 250) botao_exportar.state(['disabled']) painel_arrecadado.grid_remove() painel_passagens.grid_remove() painel_ocupacao.grid_remove() master_state = tk.IntVar(value=1) checkbox_master = ttk.Checkbutton(frame, variable=master_state) descriptive_label = ttk.Label(frame, text='Informações', anchor='center') distribuicao_passageiros = [0, 0, 0] # [gratuita, meia, inteira] linha_dia_reservas = dict() monitoradas.arrecadado = dict() monitoradas.ocupacao_media_semanal = dict() total_passageiros = len(estado.reservas) def hide_widget(widget, show=False): x, y = janela.minsize() if not show: janela.minsize(x, y - 150) widget.grid_remove() if not any(x.get() for x in [arrecadado_state, passagens_state, ocupacao_state]): botao_exportar.state(['disabled']) return botao_exportar.state(['!disabled']) janela.minsize(x, y + 150) widget.grid() def form_arrecadado(n): return ' Total Arrecadado: R$ %s ' % fm.separar_milhares_decimal(n) painel_arrecadado = ttk.LabelFrame(frame, text='Total Arrecadado', padding=10) arrecadado_state = tk.IntVar(value=1) checkbox_arrecadado = ttk.Checkbutton( frame, variable=arrecadado_state, command=lambda: hide_widget(painel_arrecadado, arrecadado_state.get())) treeview_arrecadado = ttk.Treeview(painel_arrecadado, columns=('Linha', 'Arrecadado'), show='headings', selectmode='none', name='arrecadado') for i, coluna in enumerate(('Linha', 'Arrecadado (R$)')): treeview_arrecadado.heading( i, text=coluna.title(), command=lambda col=i: util.treeview_sort_column( app, treeview_arrecadado, col, False)) treeview_arrecadado.column(i, width=0, minwidth=len(coluna) * 15, anchor='center') treeview_arrecadado.bind( '<Motion>', lambda ev: util.last_separator(ev, treeview_arrecadado)) treeview_arrecadado.bind( '<1>', lambda ev: util.last_separator(ev, treeview_arrecadado)) treeview_arrecadado_scroller_v = ttk.Scrollbar( painel_arrecadado, orient='vertical', command=treeview_arrecadado.yview) treeview_arrecadado_scroller_h = ttk.Scrollbar( painel_arrecadado, orient='horizontal', command=treeview_arrecadado.xview) treeview_arrecadado['yscrollcommand'] = treeview_arrecadado_scroller_v.set treeview_arrecadado['xscrollcommand'] = treeview_arrecadado_scroller_h.set passagens_state = tk.IntVar(value=1) painel_passagens = ttk.LabelFrame(frame, text=' Distribuição de Passagens ', padding=10) checkbox_passagens = ttk.Checkbutton( frame, variable=passagens_state, command=lambda: hide_widget(painel_passagens, passagens_state.get())) label_passageiros = ttk.Label(painel_passagens, text='Total de Passageiros: %s' % fm.separar_milhares(total_passageiros), anchor='center') label_inteira = ttk.Label(painel_passagens, anchor='center') label_meia = ttk.Label(painel_passagens, anchor='center') label_gratuita = ttk.Label(painel_passagens, anchor='center') def form_ocupacao(n): return ' Ocupação Média: %s%% ' % fm.separar_milhares_decimal(n) painel_ocupacao = ttk.LabelFrame(frame, text=form_ocupacao(0), padding=10) ocupacao_state = tk.IntVar(value=1) checkbox_ocupacao = ttk.Checkbutton( frame, variable=ocupacao_state, command=lambda: hide_widget(painel_ocupacao, ocupacao_state.get())) treeview_ocupacao = ttk.Treeview(painel_ocupacao, columns=('Linha', 'Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'), show='headings', selectmode='none', name='ocupacao') for i, coluna in enumerate( ('Linha', 'Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab')): treeview_ocupacao.heading( i, text=coluna.title(), command=lambda col=i: util.treeview_sort_column( app, treeview_ocupacao, col, False)) treeview_ocupacao.column(i, width=0, minwidth=len(coluna) * 15, anchor='center') treeview_ocupacao.bind( '<Motion>', lambda ev: util.last_separator(ev, treeview_ocupacao)) treeview_ocupacao.bind( '<1>', lambda ev: util.last_separator(ev, treeview_ocupacao)) treeview_ocupacao_scroller_v = ttk.Scrollbar( painel_ocupacao, orient='vertical', command=treeview_ocupacao.yview) treeview_ocupacao_scroller_h = ttk.Scrollbar( painel_ocupacao, orient='horizontal', command=treeview_ocupacao.xview) treeview_ocupacao['yscrollcommand'] = treeview_ocupacao_scroller_v.set treeview_ocupacao['xscrollcommand'] = treeview_ocupacao_scroller_h.set checkbox_master['command'] = lambda: toggle_states( master_state, [arrecadado_state, passagens_state, ocupacao_state]) def exportar_relatorio(): ''' Exporta o relatório como um arquivo .txt ''' # pede um caminho ao usuário endereco = filedialog.asksaveasfilename(title='Exportar Relatório', defaultextension='.txt', filetypes=[('txt', '*.txt'), ('csv', '*.csv'), ('todos', '*')], parent=janela) if endereco == '': return # cria o arquivo no endereço especificado pelo usuário with open(endereco, 'w') as arquivo_relatorio: # as variáveis *_state são IntVar, que possuem valor 1 se o check está selecionado, e 0 caso contrário # vamos escrever no arquivo .txt apenas as seções em que os checks estão marcados if arrecadado_state.get(): arquivo_relatorio.write('{:*^101}\n'.format(' ARRECADAÇÃO ')) arquivo_relatorio.write('%s\n' % painel_arrecadado['text']) arquivo_relatorio.write('{:*^49} | {:*^49}\n'.format( ' LINHA ', ' ARRECADADO (R$) ')) for linha in treeview_arrecadado.get_children(): arquivo_relatorio.write( '{Linha:^50}|{Arrecadado:^50}\n'.format_map( treeview_arrecadado.set(linha))) if passagens_state.get(): if arrecadado_state.get(): arquivo_relatorio.write('\n\n') arquivo_relatorio.write( '{:*^101}\n'.format(' DISTRIBUIÇÃO DE PASSAGENS ')) arquivo_relatorio.write('{:^49} : {:^49}\n'.format( 'TOTAL', fm.separar_milhares(total_passageiros))) arquivo_relatorio.write('{:^49} : {:^49}\n'.format( 'INTEIRA', fm.separar_milhares_precisao(percentual_inteira) + ' %')) arquivo_relatorio.write('{:^49} : {:^49}\n'.format( 'MEIA', fm.separar_milhares_precisao(percentual_meia) + ' %')) arquivo_relatorio.write('{:^49} : {:^49}\n'.format( 'GRATUITA', fm.separar_milhares_precisao(percentual_gratuita) + ' %')) if ocupacao_state.get(): if passagens_state.get(): arquivo_relatorio.write('\n\n') arquivo_relatorio.write( '{:*^167}\n'.format(' OCUPAÇÃO MÉDIA SEMANAL (%) ')) arquivo_relatorio.write( ('|'.join(['{:*^20}'] * 8) + '\n').format( ' LINHA ', ' DOM ', ' SEG ', ' TER ', ' QUAR ', ' QUI ', ' SEX ', ' SAB ')) for linha in treeview_ocupacao.get_children(): nome_linha = treeview_ocupacao.set(linha, 'Linha') arquivo_relatorio.write( ('|'.join(['{:*^20}'] * 8) + '\n').format( f' {nome_linha} ', *map( lambda x: ' ' + fm.separar_milhares_decimal( 100 * x) + ' ', monitoradas.ocupacao_media_semanal[linha]))) botao_exportar = ttk.Button(frame, text='Exportar', command=exportar_relatorio) checkbox_master.grid(row=0, column=0) descriptive_label.grid(row=0, column=1) checkbox_arrecadado.grid(row=1, column=0) painel_arrecadado.grid(row=1, column=1, sticky='news') treeview_arrecadado.grid(row=0, column=0, sticky='news') treeview_arrecadado_scroller_v.grid(row=0, column=1, sticky='ns') treeview_arrecadado_scroller_h.grid(row=1, column=0, sticky='we') painel_arrecadado.rowconfigure(0, weight=1) painel_arrecadado.columnconfigure(0, weight=1) checkbox_passagens.grid(row=2, column=0) painel_passagens.grid(row=2, column=1, sticky='ns') label_passageiros.grid(pady=(0, 10)) label_inteira.grid() label_meia.grid() label_gratuita.grid() for i in range(4): painel_passagens.rowconfigure(i, weight=1) painel_passagens.columnconfigure(0, weight=1) checkbox_ocupacao.grid(row=3, column=0) painel_ocupacao.grid(row=3, column=1, sticky='news') treeview_ocupacao.grid(row=0, column=0, sticky='news') treeview_ocupacao_scroller_v.grid(row=0, column=1, sticky='ns') treeview_ocupacao_scroller_h.grid(row=1, column=0, sticky='we') painel_ocupacao.rowconfigure(0, weight=1) painel_ocupacao.columnconfigure(0, weight=1) botao_exportar.grid(row=4, column=0, columnspan=2) for i in range(3): frame.rowconfigure(i + 1, weight=1) frame.columnconfigure(1, weight=1) for reserva in estado.reservas: onibus = reserva[:-6] linha = onibus[:-7] dados_reserva = decodificar_id_reserva(reserva) passagem, inteira = dados_reserva[-1] monitoradas.arrecadado[linha] = monitoradas.arrecadado.get( linha, 0) + it.centavos(inteira, passagem) distribuicao_passageiros[passagem] += 1 linha_dia_reservas[linha] = linha_dia_reservas.get(linha, [0] * 7) linha_dia_reservas[linha][decodificar_id_onibus(onibus).isoweekday() % 7] += 1 if total_passageiros == 0: percentual_inteira = percentual_meia = percentual_gratuita = 0 else: (percentual_gratuita, percentual_meia, percentual_inteira) = [ 100 * distribuicao_passageiros[i] / total_passageiros for i in range(3) ] label_inteira['text'] = 'Inteira: %s%%' % fm.separar_milhares_decimal( percentual_inteira) label_meia['text'] = 'Meia: %s%%' % fm.separar_milhares_decimal( percentual_meia) label_gratuita['text'] = 'Gratuita: %s%%' % fm.separar_milhares_decimal( percentual_gratuita) total_arrecadado = 0 def onibus_por_dia(linha): data_cadastro = estado.linhas_entradas[linha].horario dia_da_semana = data_cadastro.isoweekday() intervalo = (data_atual + dt.timedelta(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA) - data_cadastro).days onibus_diarios = [0] * 7 for i in range(7): if i in range((intervalo + 1) % 7): num_onibus = (intervalo + 1) // 7 + 1 else: num_onibus = (intervalo + 1) // 7 onibus_diarios[(dia_da_semana + i) % 7] = num_onibus return onibus_diarios.copy() vagas_ofertadas_totais = 0 for linha in estado.linhas_entradas: destino, horario = estado.linhas_entradas[linha][:2] nome_linha = '%s-%s' % (destino.upper(), fm.form_tempo(horario)) monitoradas.arrecadado[linha] = monitoradas.arrecadado.get(linha, 0) quantia = monitoradas.arrecadado[linha] / 100 total_arrecadado += quantia treeview_arrecadado.insert( '', 0, linha, values=(nome_linha, fm.separar_milhares_decimal(quantia))) onibus_diarios = onibus_por_dia(linha) capacidade_linha = estado.linhas_entradas[linha].fileiras * 2 vagas_ofertadas_totais += sum(onibus_diarios) * capacidade_linha # print(f'Ônibus diários: {onibus_diarios}\nCapacidade linha: {capacidade_linha}') monitoradas.ocupacao_media_semanal[linha] = [ linha_dia_reservas.get(linha, {d: 0})[d] / (onibus_diarios[d] * capacidade_linha) if onibus_diarios[d] != 0 else 0 for d in range(7) ] treeview_ocupacao.insert( '', 0, linha, values=(nome_linha, *map( lambda x: '%s%%' % fm.separar_milhares_decimal(100 * x), monitoradas.ocupacao_media_semanal[linha]))) painel_ocupacao['text'] = form_ocupacao( 100 * len(estado.reservas) / vagas_ofertadas_totais if vagas_ofertadas_totais != 0 else 0) painel_arrecadado['text'] = form_arrecadado(total_arrecadado)