This commit is contained in:
Eden Kirin
2025-10-31 13:34:56 +01:00
commit da97fada0e
12 changed files with 427 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/output

52
CLAUDE.md Normal file
View File

@ -0,0 +1,52 @@
# Entity maker
## Description
Application connects to postgres database, inspects targeted table and creates:
- sqlalchemy table
- model
- filter
- load options
- repository
- manager
- factory
Generated source files are targeted for Python 3.13 using SQLAlchemy.
## Input parameters
Application have following command line parameters:
- optional db host - default `localhost`
- optional db port - default `5432`
- optional db name
- optional db schema - default `public`
- optional db user - default `postgres`
- optional db password - default `postgres`
- optional db table
- optional output directory
- optional entity name
When application starts, it will offer users to enter missing parameters from command line, using defaults as listed above. Save entered values in `~/.config/entity-maker` and use it for the next time.
Generated files are placed in specificied output directory, in subdirectory named after table name, but in sigular. Check naming section for details.
## Examples
Example sql table structure is located in `./example/cashbag_conforms.sql`.
Example output is located in `./example/cashbag_conform`.
## Naming example
Table name: `cashbag_conforms`.
Output subdirectory: `cashbag_conform`
Model name: `CashbagConform`
Filter name `CashbagConformFilter`
Load options name `CashbagConformLoadOptions`
Repository name `CashbagConformRepository`
Manager name `CashbagConformManager`
Factory name `CashbagConformFactory`
## Technologies used
This is command line application. Application is written in golang. Application needs to be run on Linux only, no need to support other operating systems. Make application output modern and colorful.

View File

@ -0,0 +1,100 @@
CREATE TABLE cashbag_conforms
(
id integer DEFAULT NEXTVAL('cashbag_conforms_id_seq'::regclass) NOT NULL
PRIMARY KEY,
alive boolean NOT NULL,
count_coins numeric(12, 4) NOT NULL,
count_bills numeric(12, 4) NOT NULL,
tokens_number numeric(12, 4) NOT NULL,
tokens_total numeric(12, 4) NOT NULL,
count_timestamp timestamp with time zone,
count_receive_timestamp timestamp with time zone,
collect numeric(12, 4) NOT NULL,
collect_timestamp timestamp with time zone,
diff numeric(12, 4) NOT NULL,
author_info_id integer NOT NULL
UNIQUE
CONSTRAINT cashbag_conf_author_info_id_1663387ed38e04b8_fk_author_infos_id
REFERENCES author_infos
DEFERRABLE INITIALLY DEFERRED,
cashbag_id integer
CONSTRAINT cashbag_conforms_cashbag_id_83139826_fk_cashbags_id
REFERENCES cashbags
DEFERRABLE INITIALLY DEFERRED,
cashflow_collection_id integer
CONSTRAINT "D4f92ee700d19ebb5990b785681a01fe"
REFERENCES cashflow_collections
DEFERRABLE INITIALLY DEFERRED,
collect_user_id integer
CONSTRAINT ca_collect_user_id_7d633e574f6c25a9_fk_custom_users_user_ptr_id
REFERENCES custom_users
DEFERRABLE INITIALLY DEFERRED,
count_user_id integer
CONSTRAINT cash_count_user_id_2601834f194be798_fk_custom_users_user_ptr_id
REFERENCES custom_users
DEFERRABLE INITIALLY DEFERRED,
machine_id integer
CONSTRAINT cashbag_conforms_machine_id_246129c5a92a0a91_fk_machines_id
REFERENCES machines
DEFERRABLE INITIALLY DEFERRED,
route_id integer
CONSTRAINT cashbag_conforms_route_id_18cfcd0d417870ef_fk_route_route_id
REFERENCES route_route
DEFERRABLE INITIALLY DEFERRED,
external_route_id bigint,
external_route_name varchar(255),
no_cashbag_reason integer,
_ver bigint,
description text,
status cashbag_conform_status_enum
);
ALTER TABLE cashbag_conforms
OWNER TO svc_cloud;
CREATE INDEX cashbag_conforms_6788849c
ON cashbag_conforms (cashbag_id);
CREATE INDEX cashbag_conforms_6fce81cc
ON cashbag_conforms (cashflow_collection_id);
CREATE INDEX cashbag_conforms_7016d9d6
ON cashbag_conforms (collect_user_id);
CREATE INDEX cashbag_conforms_b4347999
ON cashbag_conforms (route_id);
CREATE INDEX cashbag_conforms_d8f07203
ON cashbag_conforms (count_user_id);
CREATE INDEX cashbag_conforms_external_route_id_12045111
ON cashbag_conforms (external_route_id);
CREATE INDEX idx_cashbag_conforms_ver
ON cashbag_conforms (COALESCE(_ver, 0::bigint));
CREATE INDEX idx_cashbag_conforms_collect_timestamp
ON cashbag_conforms (collect_timestamp);
CREATE INDEX idx_cashbag_conforms_count_timestamp
ON cashbag_conforms (count_timestamp);
CREATE INDEX idx_cashbag_conforms_machine_collect_timestamp
ON cashbag_conforms (machine_id, collect_timestamp);
CREATE TRIGGER tr_ver_i_cashbag_conforms
BEFORE INSERT
ON cashbag_conforms
FOR EACH ROW
EXECUTE PROCEDURE tr_ver_cashbag_conforms();
CREATE TRIGGER tr_ver_u_cashbag_conforms
BEFORE UPDATE
ON cashbag_conforms
FOR EACH ROW
WHEN (old.* IS DISTINCT FROM new.*)
EXECUTE PROCEDURE tr_ver_cashbag_conforms();
GRANT INSERT, UPDATE ON cashbag_conforms TO cloud_write;
GRANT SELECT ON cashbag_conforms TO cloud_read;

View File

View File

@ -0,0 +1,9 @@
from enum import StrEnum
from televend_core.databases.enum import EnumMixin
class CashBagConformStatusEnum(EnumMixin, StrEnum):
OPEN = "OPEN"
IN_PROGRESS = "IN_PROGRESS"
DONE = "DONE"

View File

@ -0,0 +1,84 @@
from __future__ import annotations
from typing import Type
import factory
from televend_core.databases.televend_repositories.author_info.factory import (
AuthorInfoFactory,
)
from televend_core.databases.televend_repositories.cashbag.factory import (
CashBagFactory,
)
from televend_core.databases.televend_repositories.cashbag_conform.model import (
CashBagConform,
)
from televend_core.databases.televend_repositories.cashflow_collection.factory import (
CashFlowCollectionFactory,
)
from televend_core.databases.televend_repositories.custom_user.factory import (
CustomUserFactory,
)
from televend_core.databases.televend_repositories.machine.factory import MachineFactory
from televend_core.test_extras.factory_boy_utils import (
CustomSelfAttribute,
TelevendBaseFactory,
)
class CashBagConformFactory(TelevendBaseFactory):
alive = True
id = None
count_coins = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
count_bills = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
tokens_number = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
tokens_total = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
count_timestamp = factory.Faker("date_time")
count_receive_timestamp = factory.Faker("date_time")
collect = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
collect_timestamp = factory.Faker("date_time")
diff = factory.Faker("pydecimal", left_digits=7, right_digits=4, positive=True)
author_info = CustomSelfAttribute("..author_info", AuthorInfoFactory)
author_info_id = factory.LazyAttribute(lambda a: a.author_info.id if a.author_info else None)
cashbag = CustomSelfAttribute("..cashbag", CashBagFactory)
cashbag_id = factory.LazyAttribute(lambda a: a.cashbag.id if a.cashbag else None)
cashflow_collection = CustomSelfAttribute("..cashflow_collection", CashFlowCollectionFactory)
cashflow_collection_id = factory.LazyAttribute(
lambda a: a.cashflow_collection.id if a.cashflow_collection else None
)
collect_user = CustomSelfAttribute("..collect_user", CustomUserFactory)
collect_user_id = factory.LazyAttribute(
lambda a: a.collect_user.user_ptr_id if a.collect_user else None
)
count_user = CustomSelfAttribute("..count_user", CustomUserFactory)
count_user_id = factory.LazyAttribute(
lambda a: a.count_user.user_ptr_id if a.count_user else None
)
machine = CustomSelfAttribute("..machine", MachineFactory)
machine_id = factory.LazyAttribute(lambda a: a.machine.id if a.machine else None)
external_route_id = factory.Faker("pyint")
external_route_name = factory.Faker("pystr", max_chars=255)
no_cashbag_reason = factory.Faker("pyint")
class Meta:
model = CashBagConform
@classmethod
def create_minimal(cls: Type[CashBagConformFactory], **kwargs) -> CashBagConform:
minimal_params = {
"author_info": kwargs.pop("author_info", None) or AuthorInfoFactory.create_minimal(),
"cashbag": None,
"cashflow_collection": None,
"collect_user": None,
"count_user": None,
"machine": None,
}
minimal_params.update(kwargs)
return cls.create(**minimal_params)

View File

@ -0,0 +1,14 @@
from televend_core.databases.base_filter import BaseFilter
from televend_core.databases.common.filters.filters import EQ, IN, filterfield
from televend_core.databases.televend_repositories.cashbag_conform.model import CashBagConform
class CashBagConformFilter(BaseFilter):
model_cls = CashBagConform
alive: bool | None = filterfield(operator=EQ, default=True)
ids: list[int] | None = filterfield(field="id", operator=IN)
machine_ids: list[int] | None = filterfield(field="machine_id", operator=IN)
cashflow_collections_ids: list[int] | None = filterfield(
field="cashflow_collections_id", operator=IN
)

View File

@ -0,0 +1,12 @@
from televend_core.databases.base_load_options import LoadOptions
from televend_core.databases.common.load_options import joinload
from televend_core.databases.televend_repositories.cashbag_conform.model import CashBagConform
class CashBagConformLoadOptions(LoadOptions):
model_cls = CashBagConform
load_cashflow_collection: bool = joinload(relations=["cashflow_collection"])
load_machine: bool = joinload(relations=["machine"])
load_cashbag: bool = joinload(relations=["cashbag"])
load_denominations: bool = joinload(relations=["denominations"])

View File

@ -0,0 +1,14 @@
from televend_core.databases.base_manager import CRUDManager
from televend_core.databases.televend_repositories.cashbag_conform.filter import (
CashBagConformFilter,
)
from televend_core.databases.televend_repositories.cashbag_conform.model import CashBagConform
from televend_core.databases.televend_repositories.cashbag_conform.repository import (
CashBagConformRepository,
)
class CashBagConformManager(
CRUDManager[CashBagConform, CashBagConformFilter, CashBagConformRepository]
):
repository_cls = CashBagConformRepository

View File

@ -0,0 +1,54 @@
from dataclasses import dataclass, field
from datetime import datetime
from decimal import Decimal
from televend_core.databases.base_model import Base
from televend_core.databases.televend_repositories.author_info.model import AuthorInfo
from televend_core.databases.televend_repositories.cashbag.model import CashBag
from televend_core.databases.televend_repositories.cashbag_conform.enum import (
CashBagConformStatusEnum,
)
from televend_core.databases.televend_repositories.cashbag_conform_denominations.model import (
CashBagConformDenomination,
)
from televend_core.databases.televend_repositories.cashflow_collection.model import (
CashFlowCollection,
)
from televend_core.databases.televend_repositories.custom_user.model import CustomUser
from televend_core.databases.televend_repositories.machine.model import Machine
@dataclass
class CashBagConform(Base):
count_coins: Decimal
count_bills: Decimal
tokens_number: Decimal
tokens_total: Decimal
collect: Decimal
diff: Decimal
author_info_id: int
author_info: AuthorInfo
count_timestamp: datetime | None = None
count_receive_timestamp: datetime | None = None
collect_timestamp: datetime | None = None
cashbag_id: int | None = None
cashbag: CashBag | None = None
cashflow_collection_id: int | None = None
cashflow_collection: CashFlowCollection | None = None
collect_user_id: int | None = None
collect_user: CustomUser | None = None
count_user_id: int | None = None
count_user: CustomUser | None = None
machine_id: int | None = None
machine: Machine | None = None
external_route_id: int | None = None
external_route_name: str | None = None
no_cashbag_reason: int | None = None
description: str | None = None
status: CashBagConformStatusEnum | None = None
denominations: list[CashBagConformDenomination] | None = field(default_factory=list)
alive: bool = True
id: int | None = None

View File

@ -0,0 +1,9 @@
from televend_core.databases.base_repository import CRUDRepository
from televend_core.databases.televend_repositories.cashbag_conform.filter import (
CashBagConformFilter,
)
from televend_core.databases.televend_repositories.cashbag_conform.model import CashBagConform
class CashBagConformRepository(CRUDRepository[CashBagConform, CashBagConformFilter]):
model_cls = CashBagConform

View File

@ -0,0 +1,78 @@
from sqlalchemy import (
BigInteger,
Boolean,
Column,
DateTime,
Enum,
ForeignKey,
Integer,
Numeric,
String,
Table,
Text,
)
from televend_core.databases.televend_repositories.cashbag_conform.enum import (
CashBagConformStatusEnum,
)
from televend_core.databases.televend_repositories.table_meta import metadata_obj
CASHBAG_CONFORM_TABLE = Table(
"cashbag_conforms",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("alive", Boolean, nullable=False),
Column("count_coins", Numeric(12, 4), nullable=False),
Column("count_bills", Numeric(12, 4), nullable=False),
Column("tokens_number", Numeric(12, 4), nullable=False),
Column("tokens_total", Numeric(12, 4), nullable=False),
Column("count_timestamp", DateTime(timezone=True)),
Column("count_receive_timestamp", DateTime(timezone=True)),
Column("collect", Numeric(12, 4), nullable=False),
Column("collect_timestamp", DateTime(timezone=True)),
Column("diff", Numeric(12, 4), nullable=False),
Column(
"author_info_id",
Integer,
ForeignKey("author_infos.id", deferrable=True, initially="DEFERRED"),
nullable=False,
unique=True,
),
Column(
"cashbag_id",
Integer,
ForeignKey("cashbags.id", deferrable=True, initially="DEFERRED"),
),
Column(
"cashflow_collection_id",
Integer,
ForeignKey("cashflow_collections.id", deferrable=True, initially="DEFERRED"),
),
Column(
"collect_user_id",
Integer,
ForeignKey("custom_users.user_ptr_id", deferrable=True, initially="DEFERRED"),
),
Column(
"count_user_id",
Integer,
ForeignKey("custom_users.user_ptr_id", deferrable=True, initially="DEFERRED"),
),
Column(
"machine_id",
Integer,
ForeignKey("machines.id", deferrable=True, initially="DEFERRED"),
),
Column("external_route_id", BigInteger),
Column("external_route_name", String(255)),
Column("no_cashbag_reason", Integer),
Column("description", Text, nullable=True),
Column(
"status",
Enum(
*CashBagConformStatusEnum.to_value_list(),
name="cashbag_conform_status_enum",
),
nullable=True,
),
)