def resolver_string(self): if not self.is_alias_key: return None if self.is_range: # 子テーブル且つレンジキーの場合はエイリアスキーとなる l = len(self.parent.tag_name) + len(TAG_SEPARATOR) parent = self.parent.parent_table parent_rk_name = parent.range_key.name body = IndentString() # getter body.add('@property') body.indent( f'def {self.name}(self) -> def_enum.{self.target_enum_name}:') body.add( f'return self.Meta.key_alias_type.to(int(self.{parent_rk_name}[{l}:]))' ) body.outdent() body.blank_line() # setter body.add(f'@{self.name}.setter') body.indent(f'def {self.name}(self, val):') body.indent('if val is None:') body.add('return') body.outdent() body.add( f'self.{parent_rk_name} = f"{self.parent.tag_name}{TAG_SEPARATOR}{{val.value}}"' ) body.outdent() body.outdent() return body return None
def resolver_string(self): """ リゾルバ :return: IndexString """ if not self.is_alias_key: # エイリアスキーでなければ不要 return None # エイリアスキーはリゾルバが必要 l = len(self.parent.tag_name) + len(TAG_SEPARATOR) parent = self.parent.parent_table body = IndentString() # getter body.add('@property') body.indent(f'def {self.name}(self) -> {self.PythonValueTypeStr}:') body.add(f'return {self.PythonValueTypeStr}(self.{parent.range_key.name}[{l}:])') body.outdent() body.blank_line() # setter body.add(f'@{self.name}.setter') body.indent(f'def {self.name}(self, val: {self.PythonValueTypeStr}):') body.indent('if val is None:') body.add('return') body.outdent() body.add( f'self.{self.parent.parent_table.range_key.name} = f"{self.parent.tag_name}{TAG_SEPARATOR}{self.resolver_setter_name}"') body.outdent() return body
def render_header(self): u = IndentString() u.add('import enum') u.add( 'from hatsudenki.packages.master.model import EnumResolver, MasterResolver' ) u.blank_line(2) return u
def serial_resolver_default(): i = IndentString() i.add('@property') i.indent('def log_id(self):') i.add('return self.one_cursor') return i
def render_units(self): ToolOutput.anchor('Wiki出力を開始') u = IndentString() for k, table in self.master_loader.iter(): un = MasterDataWikiRenderUnit(table).render() u.add(un) ToolOutput.pop('OK') return u
def cs_access_name(self): u = IndentString() t = self.get_target_table() u.add(f'public {t.class_name} {self.cs_name}Table()') u.indent('{') u.add(f'return {t.class_name}.Instance;') u.outdent('}') return u
def _get_list(self): hk = self.data.hash_key rk = self.data.range_key if rk is None: return None u = IndentString() n = f'{hk.cs_type_name} {hk.column_name}' u.add(f'public List<{self.record_type}> GetList({n})') u.indent('{') u.add(f'return _records[{hk.column_name}];') u.outdent('}') return u
def serial_resolver_name(self): if not self.is_serial_key: return None serial = f"'{self.table_name}_'" + f' + str(self.{self.column_name}).zfill(10)' i = IndentString() i.add('@property') i.indent('def log_id(self):') i.add(f'return {serial}') return i
def render_init(self): """ イニシャライザの出力 :return: IndexString """ table = self.data # キー情報の取得 hk = table.hash_key rk = table.range_key keys = [k.name for k in [hk, rk] if k is not None] # hashキーとrangeキーだけ明示的にパラメータとして定義する o = ', '.join( [f'{k}: {table.attributes[k].python_init_type_str} = None' for k in keys]) if len(o) > 0: o = f'{o}, ' body = IndentString(f'def __init__(self, {o}**kwargs):') body.add('super().__init__(**kwargs)') # 記述を短くするためにFieldクラスの参照を保持する body.add('ft = self.__class__.Field') is_alone = table.is_alone if not is_alone and rk is None: # 子テーブル且つレンジキーがない場合は単一子テーブルとなる parent = table.parent_table # 単一子テーブルのrangeキーの値は常にtagで固定 body.add(f'self.{parent.range_key.name} = "{table.tag_name}"') # 各フィールドの初期化処理 for key, attr in table.attributes.items(): body.add(attr.gen_init_str()) return body
def render_resolver(self): """ リゾルバの出力 :return: IndexString """ # 今の所aliasキーのリゾルバしか無い body = IndentString() for k, attr in self.data.attributes.items(): b = attr.resolver_string if b: body.add(b) return body
async def render_to_wiki(self, base_url, token_str): ToolOutput.anchor('Wiki出力を開始') wiki = GitWikiUploader(base_url, token_str) page_list = await wiki.get_page_list() page_slug_list = [v['slug'] for v in page_list] d: DefaultDict[str, List[MasterDataWikiRenderUnit]] = defaultdict(list) ToolOutput.anchor('YAML読み込み') for k, table in self.master_loader.iter(): ru = MasterDataWikiRenderUnit(table) ToolOutput.out(f'{ru.page_name} # {ru.data.excel_sheet_name}') d[ru.page_name].append(ru) ToolOutput.pop('OK') ToolOutput.anchor('書き出し') num = 1 index = MarkdownTableRenderer( ['ID', 'Book', 'Client', 'Server', 'Planner', 'Table']) for p, rus in d.items(): ToolOutput.anchor(p) u = IndentString() for r in rus: u.add(r.render()) idx = r.render_index() idx['ID'] = str(num) num += 1 index.append(idx) if p in page_slug_list: ToolOutput.print(f'{p}更新します') await wiki.update_page(content=u.render(), page_name=p) else: ToolOutput.print(f'{p}作成します') await wiki.create_page(content=u.render(), page_name=p) ToolOutput.pop('OK') ix = self.render_index(index) index_title = 'MasterData/☆INDEX☆' if index_title in page_slug_list: ToolOutput.print(f'{index_title}更新します') await wiki.update_page(content=ix.render(), page_name=index_title) else: ToolOutput.print(f'{index_title}作成します') await wiki.create_page(content=ix.render(), page_name=index_title) ToolOutput.pop('OK')
def render_units(self): u = IndentString() [ u.add(MasterDataStoreRenderUnit(table).render()) for k, table in self.master_loader.iter() ] return u
def _store(self): u = IndentString() u.add('[MessagePackObject]') u.add(f'public class {self.record_type}s') u.indent('{') u.add('[Key(0)]') u.add(f'public {self.record_type}[] Array;') u.outdent('}') return u
def render(self): u = IndentString() if self.data.is_out_desc: u.add(f'using System.ComponentModel;') u.blank_line() u.add(f'public enum Enum{self.data.full_classname}') u.indent('{') for v in self.data.values: if self.data.is_out_desc: u.add(f'{v.cs_desc}') u.add(f'{v.cs_str}') u.add(f'MAX = {len(self.data.values)},') u.outdent('}') return u
def _get(self): u = IndentString() hk = self.data.hash_key rk = self.data.range_key if rk: n = f'{hk.cs_type_name} {hk.column_name}, {rk.cs_type_name} {rk.column_name}' c = f'return _records[{hk.column_name}].Find(x => x.GetRangeKey() == {rk.column_name});' else: n = f'{hk.cs_type_name} {hk.column_name}' c = f'return _records[{hk.column_name}];' u.add(f'public {self.record_type} Get({n})') u.indent('{') u.add(f'{c}') u.outdent('}') return u
def resolver_name(self, selector: ColumnSelect): u = IndentString() te = selector.resolve_relation() u.add('@property') u.indent(f'def resolved_{self.python_name}(self):') u.add('from common.enums import def_enum') u.add(f'tbl = def_enum.Enum{te.full_classname}.resolve(self.{selector.python_name})') u.add(f'return tbl.get(self.{self.python_name})') return u
def render(self): """ 出力 :return: IndentString """ table = self.data # 継承元の決定 if table.is_alone: # solo or multi if table.range_key is not None: parent_cls_name = 'MultiHatsudenkiTable' else: parent_cls_name = 'SoloHatsudenkiTable' else: # solo or multi if table.range_key: parent_cls_name = f'{table.parent_table.class_name}, ChildMultiHatsudenkiTable' else: parent_cls_name = f'{table.parent_table.class_name}, ChildSoloHatsudenkiTable' # クラス定義 cls = IndentString(f'class {table.class_name}({parent_cls_name}):') # Metaクラス cls.add(self.render_meta()) cls.blank_line() cls.add(self.render_fields()) cls.blank_line() # Index cls.add(self.render_index()) cls.blank_line() # init cls.add(self.render_init()) cls.blank_line() # resolver cls.add(self.render_resolver()) cls.blank_line() return cls
def _find(self, is_all: bool): if self.data.range_key is None: return None u = IndentString() hk = self.data.hash_key if is_all: t = f'List<{self.record_type}>' n = 'FindAll' else: t = self.record_type n = 'Find' u.add( f'public {t} {n}({hk.cs_type_name} {hk.column_name}, Predicate<{self.record_type}> func)' ) u.indent('{') u.add(f'return _records[{hk.column_name}].{n}(func);') u.outdent('}') return u
def render_fields(self): """ Fieldクラスの出力 :return: IndexString """ table = self.data # 継承元の決定 is_alone = table.is_alone if table.is_alone: body = IndentString('class Field(SoloHatsudenkiTable.Field):') else: body = IndentString(f'class Field({table.parent_table.class_name}.Field):') attr_num = 0 for key, attr in table.attributes.items(): if attr.is_alias_key: # aliasキーなので実際のフィールドとしては存在しない continue body.add(attr.class_string) body.add(attr.gen_def_str()) attr_num += 1 # 属性が一つもない場合はpass if attr_num is 0: body.add('pass') return body
def render_header(self) -> IndentString: """ ヘッダ出力 :return: IndentString """ # インポートするモジュール群 d = IndentString() l = [ 'from __future__ import annotations', 'import uuid', f'from {self.out_module_name} import masters', 'from datetime import datetime', f'from {self.out_module_name} import def_enum', 'from hatsudenki.packages import field', 'from hatsudenki.packages.marked import MarkedObject, Markable, MarkedObjectWithIndex', 'from hatsudenki.packages.table.index import PrimaryIndex, LSI, GSI', 'from hatsudenki.packages.table.multi import MultiHatsudenkiTable', 'from hatsudenki.packages.table.solo import SoloHatsudenkiTable', 'from hatsudenki.packages.table.child import ChildMultiHatsudenkiTable', 'from hatsudenki.packages.table.child_solo import ChildSoloHatsudenkiTable', ] d.add(*l) d.blank_line(2) d.indent('def table_setup():') d.add('from hatsudenki.packages.manager.table import TableManager') d.add( 'print(f"load all tables OK. collections={TableManager.get_collection_num()} tables={TableManager.get_table_num()}")') d.blank_line(2) return d
def render_header(self) -> IndentString: u = IndentString() u.add('from hatsudenki.packages.cache.base import column') u.add('from hatsudenki.packages.cache.base.index import PrimaryIndex') u.add( 'from hatsudenki.packages.master.model import MasterModelSolo, MasterModelMulti' ) u.blank_line(2) return u
def render(self): u = IndentString() u.add('|' + '|'.join(self.header) + '|') u.add('|' + '|'.join(['---------'] * len(self.header)) + '|') for row in self.rows: s = [] for h in self.header: s.append(self._get(row.get(h, '----'))) u.add('|' + '|'.join(s) + '|') return u
def _records(self): u = IndentString() hk = self.data.hash_key rk = self.data.range_key # レコード本体 if rk: t = f'Dictionary<{hk.cs_type_name}, List<{self.record_type}>>' else: t = f'Dictionary<{hk.cs_type_name}, {self.record_type}>' u.add(f'private {t} _records = new {t}();') u.add(f'public {t} Records') u.indent('{') u.add('get') u.indent('{') u.add('return _records;') u.outdent('}') u.outdent('}') return u
def render_index(self, index: MarkdownTableRenderer): ToolOutput.anchor('目次') u = IndentString() u.add('# MasterData目次') u.add(index.render()) return u
def render(self, name): hash_key = next((v for k, v in self.attributes.items() if v.is_hash)) u = IndentString() u.indent(f'class {name}(MarkedObjectWithIndex):') u.indent('class Meta:') u.add(f"hash_key = '{hash_key.name}'") u.outdent() u.blank_line() u.indent('class Field:') for k, v in self.attributes.items(): u.add(v.class_string) u.add(v.gen_def_str()) u.outdent() u.blank_line() u.indent('def __init__(self, name, parent: Markable, **kwargs):') u.add('super().__init__(name, parent)') u.add('ft = self.__class__.Field') for k, v in self.attributes.items(): u.add(v.gen_init_str(direct_assign=False)) u.add('') return u
def render(self, name): """ クラス出力 :param name: フィールド名 :return: IndentString """ u = IndentString() u.indent(f'class {name}(MarkedObject):') u.indent('class Field:') for k, v in self.attributes.items(): u.add(v.class_string) u.add(v.gen_def_str()) u.outdent() u.blank_line() u.indent('def __init__(self, name, parent: Markable, **kwargs):') u.add('super().__init__(name, parent)') u.add('ft = self.__class__.Field') for k, v in self.attributes.items(): u.add(v.gen_init_str(direct_assign=False)) u.add('') return u
def class_string(self): u = IndentString( f'class {snake_to_camel(self.name)}Field(field.MasterKeysField):') u.indent('class Value(field.MasterKeysField.Value):') acce = IndentString() excep = IndentString() opt_strings = [] con_body = IndentString() for idx, t in enumerate(self.targets): acce.add('@property') acce.indent(f'def t{idx}(self):') c = self.get_target_table_class_name(idx) if self.is_enum(t): acce.add( f'return {c}(int(self.label_list[{idx}])) if self.label_list[{idx}] else None' ) else: acce.add(f'return self.label_list[{idx}]') acce.outdent('') if self.is_master(t): acce.indent(f'def resolved_t{idx}(self):') acce.add(f'return {c}.get_by_cursor(self.t{idx})') acce.outdent('') elif self.is_enum(t): # enumにリゾルバは存在しない pass else: acce.indent(f'async def resolved_t{idx}(self):') acce.add(f'return await {c}.query_by_cursor(self.t{idx})') acce.outdent('') excep.indent(f'if not isinstance(t{idx}, {c}):') excep.add( f'raise Exception(f"not [{c}] given {{type(t{idx}).__name__}}")' ) excep.outdent() opt_strings.append(f't{idx}: {c}') if self.is_enum(t): con_body.add(f'self.label_list[{idx}] = str(t{idx}.value)') else: con_body.add(f'self.label_list[{idx}] = t{idx}.one_cursor') if not self.parent.is_alone: con_body.add('self.update_key()') con_head = IndentString( f'def connect(self, {", ".join(opt_strings)}):') con_head.add(excep) con_head.add(con_body) u.add(acce, con_head) return u
def resolver_string(self): if not self.is_alias_key: return None if self.is_range: # 子テーブル且つレンジキーの場合はエイリアスキーとなる l = len(self.parent.tag_name) + len(TAG_SEPARATOR) parent = self.parent.parent_table parent_rk_name = parent.range_key.name body = IndentString() # getter body.add('@property') body.indent( f'def {self.name}(self) -> {self.python_value_type_str}:') body.indent(f'if self.{parent_rk_name}:') body.add( f'd = self.Meta.key_alias_type.get_data(self.{parent_rk_name}[{l}:])' ) body.outdent() body.indent('else:') body.add('d = self.Meta.key_alias_type.get_data(None)') body.outdent() body.add('d.parent = self') body.add('return d') body.outdent() body.blank_line() # setter body.add(f'@{self.name}.setter') body.indent(f'def {self.name}(self, val):') body.indent('if val is None:') body.add('return') body.outdent() body.indent('if val.is_empty():') body.add('return') body.outdent() body.add( f'self.{parent_rk_name} = f"{self.parent.tag_name}{TAG_SEPARATOR}{{val}}"' ) body.outdent() return body return None
def render(self): ToolOutput.anchor(f'{self.data.label} をMarkdownに変換') u = IndentString() u.add(f'# {self.data.excel_sheet_name}') u.add(f'## 情報') u.add(self.data.description if self.data. description else '*!! descriptionが設定されていません !!*') u.blank_line() u.add(self._gen_info().render()) u.add(f'## 担当者') u.add(self._gen_assigner().render()) u.add(f'## スキーマ情報') u.add(self._column().render()) ToolOutput.pop('OK') return u
def render_index(self): """ インデックス情報の出力 :return: IndexString """ table = self.data # 単一か子かによって継承元が変わる is_alone = table.is_alone if is_alone: # 個別 lsi_class_str = IndentString('class LSIndex:') gsi_class_str = IndentString('class GSIndex:') else: # 子 pn = table.parent_table.class_name lsi_class_str = IndentString(f'class LSIndex({pn}.LSIndex):') gsi_class_str = IndentString(f'class GSIndex({pn}.LSIndex):') # インデックスは複数件あるのでループ for idx in table.indexes: opt = {} opt['projection_keys'] = idx.get('projection', []) # LSI or GSI if idx['type'] == 'global': # GSIはhashキーとrangeキーとキャパシティユニット opt['hash_key'] = idx['hash'] # インデックス名はgsi__{hash}__{range} opt['name'] = f'gsi_{idx["hash"]}' if 'range' in idx: opt['range_key'] = idx['range'] opt['name'] += '__' + idx['range'] # キャパシティユニット caps = idx.get('capacity_units', {}) if 'capacity_units' in caps: c = caps['capacity_units'] opt['read_cap'] = c['read'] opt['write_cap'] = c['write'] else: opt['read_cap'] = 1 opt['write_cap'] = 1 s = ', '.join((f'{k}={json.dumps(v)}' for k, v in opt.items())) gsi_class_str.add(f'{opt["name"]} = GSI({s})') else: # LSIはrangeキーのみ opt['name'] = f'lsi_{idx["range"]}' opt['range_key'] = idx['range'] # 処理の都合上PrimaryのHashキーを便宜上のhashキーとして登録する if is_alone: opt['hash_key'] = table.hash_key.name else: # 子テーブルの場合はPrimaryキーを親テーブルから引く opt['hash_key'] = table.parent_table.hash_key.name s = ', '.join((f'{k}={json.dumps(v)}' for k, v in opt.items())) lsi_class_str.add(f'{opt["name"]} = LSI({s})') # LSIが一つもない場合はpass if lsi_class_str.line_num is 1: lsi_class_str.add('pass') # GSIが一つもない場合はpass if gsi_class_str.line_num is 1: gsi_class_str.add('pass') body = IndentString() body.add(lsi_class_str, gsi_class_str) return body