class ConnectionFactory(object): def __init__(self): self._configs = Inject('app configuration') self._logger = Inject('logger') @staticmethod def get_external_resources(): return [{ 'name': 'database connection', 'creator': ConnectionFactory.create_connection }] def create_connection(self): # TODO Make this more generic, should not use fdc keys here! self._logger.debug('Connecting to database at {}...', self._configs['fdc.db_full_path']) connection = sqlite3.connect(self._configs['fdc.db_full_path']) sqlite3.register_adapter(decimal.Decimal, lambda d: str(d)) sqlite3.register_converter('decimal', lambda s: decimal.Decimal(s)) return connection
class FDCInitCommand(object): def __init__(self): self._configs = Inject('app configuration') self._git = Inject('fdc git wrapper') self._logger = Inject('logger') def database_parser_created_handler(self, db_parser): db_parser.add_parser( "init", help= "Inicializa a pasta do FDC e o banco de dados com uma estrutura vazia. Se houver um banco de dados já " "existente o mesmo será excluído e reiniciado.").set_defaults( event='database_init_command') def database_init_command_handler(self, args): # TODO Verificar se o arquivo de banco de dados é um SQLITE válido! self._ensure_fdc_folder_exists() self._ensure_fdc_folder_is_git_repository() self._ensure_database_structure_with_no_data() return 'ok' def _ensure_fdc_folder_exists(self): if not os.path.isdir(self._configs['fdc.folder']): self._logger.info('Creating fdc folder at "{}"', self._configs['fdc.folder']) os.makedirs(self._configs['fdc.folder'], exist_ok=True) else: self._logger.info('fdc folder found at "{}"', self._configs['fdc.folder']) def _ensure_fdc_folder_is_git_repository(self): if not self._git.is_repository(): self._logger.info( 'Git repository not found at fdc folder, creating it...') if not self._git.init(): raise FDCInitializationException( 'Error initializing git repository at "{}"', self._git.repository_folder) def _ensure_database_structure_with_no_data(self): if os.path.isfile(self._configs['fdc.db_full_path']): self._logger.warn('Database file "{}" found, removing it...', self._configs['fdc.db_full_path']) os.remove(self._configs['fdc.db_full_path']) connection = di_container.get_resource('database connection') # TODO Create these tables via controller events to centralize table handling in specialized classes self._logger.debug('Creating table "Conta"...') connection.executescript('create table Conta (' ' nome text not null,' ' descricao text,' ' data_aquisicao date,' ' propriedades text not null,' ' observacao text);') self._logger.debug('Creating table "Cotacao"...') connection.executescript('create table Cotacao (' ' data date not null,' ' moeda text not null,' ' valor integer not null);') self._logger.debug('Creating table "Orcamento"...') connection.executescript('create table Orcamento (' ' nome text not null,' ' descricao text not null);') self._logger.debug('Creating table "OrcamentoLancamento"...') connection.executescript('create table OrcamentoLancamento (' ' orcamento not null references Orcamento,' ' regra_data_vencimento text not null,' ' origem_padrao not null references Conta,' ' destino_padrao not null references Conta,' ' valor_padrao integer not null,' ' cotacao_moeda text,' ' regra_cotacao_data text,' ' regra_periodo_referencia text not null,' ' produto references Produto,' ' quantidade integer,' ' observacao text);') self._logger.debug('Creating table "Lancamento"...') connection.executescript('create table Lancamento (' ' data date not null,' ' origem references Conta not null,' ' destino references Conta not null,' ' valor integer not null,' ' cotacao references Cotacao,' ' referencia_inicio text,' ' referencia_fim text,' ' realizado boolean not null default false,' ' produto references Produto,' ' fornecedor references Fornecedor,' ' quantidade integer,' ' observacao text);') self._logger.debug('Creating table "Produto"...') connection.executescript('create table Produto (' ' nome text not null,' ' medida text,' ' unidade text);') self._logger.debug('Creating table "Fornecedor"...') connection.executescript('create table Fornecedor (' ' nome text not null);') # TODO Handle errors def database_init_command_ok_handler(self): print('Structure created successfully')
class Main(object): def __init__(self): self._logger = Inject('logger') self._configs = Inject('app configuration') def main(self): packages = [ 'commons', 'di_container', __package__ + '.commons', __package__ + '.conta', __package__ + '.database', __package__ + '.import', 'fdc.lancamento', 'fdc.produto', 'fdc.fornecedor', ] di_container.load_resources(packages) controller.load_listeners(packages) di_container.inject_resources(self) self._logger.info('Loaded resources and listeners from packages: {}', packages) self._logger.debug('Using configurations: {}', self._configs.dump()) args = self.parse_command_line() self._logger.info('Command line parsed: {}', args) event_results = controller.event(args.event, inject_dependencies=True, args=args) for result in event_results: self._logger.info( 'Calling front end for "{}" with data "{}, {}" due to status "{}"', args.event, result.kwdata, result.data, result.status) # inject_dependencies parameter put first event_args = True, *result.data controller.event(args.event + '_' + result.status, *event_args, **result.kwdata) def parse_command_line(self): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() controller.event('root_parser_created', root_parser=subparsers) args = parser.parse_args() if not hasattr(args, "event"): parser.print_help() parser.exit(0, "No arguments supplied\n") else: return args
class AbstractDao(object): def __init__(self, entity_class, metadata): self._connection = Inject('database connection') self._logger = Inject('logger') self._entity_class = entity_class self._metadata = metadata def new_builder(self, kind): if kind == 'SELECT': return SelectBuilder(self._metadata) else: return None def get_single(self, where=None, *values): list = self.list(where, *values) return list[0] if len(list) > 0 else None def list(self, where=None, *values): cursor = self._create_select_cursor((), where, *values) entity_list = self._load_cursor(cursor) cursor.close() return entity_list def exists(self, where, *values): cursor = self._create_select_cursor(('count(*)', ), where, *values) count = cursor.fetchone()[0] return count > 0 def _create_select_cursor(self, fields=(), where=None, *values): builder = self.new_builder('SELECT') builder.where = where builder.fields(*fields) if len(fields) > 0 else None query = builder.build() return self._connection.execute(query, values) def _load_cursor(self, cursor): entity_list = [] for row in cursor: entity = self._load_row(row) entity_list.append(entity) return entity_list def _load_row(self, row): entity_values = {} for i, field_name in enumerate(self._metadata.fields): value = row[i] entity_values[field_name] = value return SimpleNamespace(**entity_values) def insert(self, entity): builder = InsertBuilder(self._metadata) sql = builder.build() # TODO Essa recuperação de valores é responsabilidade do InsertBuilder values = self._get_field_values(entity) self._logger.debug('Running "{}" with values "{}"', sql, values) self._connection.execute(sql, values) self._connection.commit() def _get_field_values(self, entity): t = () for field in self._metadata.fields: if field == 'rowid': continue value = getattr(entity, field, None) if hasattr(value, 'rowid'): t += value.rowid, else: t += value, return t