Skip to content

py-yprinciple-gen API Documentation

genapi

Created on 2023-01-30

@author: wf

GeneratorAPI

generator API e.g. to be used as a

command line generator

Source code in yprinciple/genapi.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
class GeneratorAPI:
    """

    generator API e.g. to be used as a

    command line generator
    """

    def __init__(self, verbose: bool = True, debug: bool = False):
        """
        constructor

        Args:
            verbose(bool): if True show verbose messages
            debug(bool): if True switch debugging on
        """
        self.verbose = verbose
        self.debug = debug
        self.args = None

    @classmethod
    def fromArgs(cls, args) -> "GeneratorAPI":
        """
        create a GeneratorAPI for the given command line arguments

        Args:
            args: command line arguments

        Returns:
            GeneratorAPI:
        """
        gen = GeneratorAPI(verbose=not args.quiet, debug=args.debug)
        gen.setWikiAndGetContexts(args)
        if args.sidif:
            gen.context, gen.error, gen.errMsg = Context.fromSiDIF_input(
                args.sidif, debug=args.debug
            )
        else:
            wikiId = args.source if args.push else args.wikiId
            gen.readContext(wikiId, args.context)
        # remember complete arguments (e.g. for push)
        gen.args = args
        return gen

    def setWikiAndGetContexts(self, args):
        """
        set my wiki and get Contexts for the given args

        Args:
            args: command line arguments
        """
        self.wikiId = args.wikiId
        self.smwAccess = SMWAccess(args.wikiId)
        self.smwSourceAccess = SMWAccess(args.source) if args.push else None
        self.smwContextAccess = self.smwSourceAccess if args.push else self.smwAccess
        self.mw_contexts = self.smwContextAccess.getMwContexts()

    def readContext(self, wikiId: str, context_name: str):
        """
        Args:
            wikiId(str): the wikiId of the wiki to read the context from
            context_name: the name of the context to read
        """
        self.mw_context = self.mw_contexts.get(context_name, None)
        if not self.mw_context:
            self.context = None
            self.errmsg = f"Could not read context {context_name} from {wikiId}"
            self.error = Exception(self.errmsg)
        else:
            self.context, self.error, self.errMsg = Context.fromWikiContext(
                self.mw_context, debug=self.debug
            )

    def filterTargets(self, target_names: list = None) -> dict:
        """
        filter targets by a list of target_names

        Args:
            target_names(list): an optional list of target names

        Returns:
            dict: mapping from target names to targets
        """
        allTargets = SMWTarget.getSMWTargets()
        if target_names is None:
            targets = allTargets
        else:
            targets = {}
            for target_name in target_names:
                if target_name in allTargets:
                    targets[target_name] = allTargets[target_name]
        return targets

    def yieldYpCells(
        self,
        hint: str,
        target_names: list = None,
        topic_names: list = None,
        with_subcells: bool = True,
    ):
        """
        generate/yield topics and targets via nested loop

        Args:
            hint(str): hint message to show how the yield is used
            with_subcells: if True yield sub cells (one level)
            target_name(list): if set filter targets by name
            topic_names(list): if set filter topics by name
        Returns:
            generator(YpCell)
        """

        def showMsg(topic_name: str, ypCell: YpCell):
            """
            show a message for the given topic_name

            Args:
                topic_name(str): topic
            """
            if self.verbose:
                target_name = ypCell.target.name
                print(f"generating {target_name} for {topic_name} {hint}...")
            pass

        targets = self.filterTargets(target_names)
        for topic_name, topic in self.context.topics.items():
            # filter topic names
            if topic_names is not None and not topic_name in topic_names:
                continue
            for _target_name, target in targets.items():
                if target.showInGrid:
                    ypCell = YpCell.createYpCell(target=target, topic=topic)
                    if ypCell.target.is_multi:
                        if with_subcells:
                            for subCell in ypCell.subCells.values():
                                showMsg(topic_name, subCell)
                                yield subCell
                    else:
                        showMsg(topic_name, ypCell)
                        yield ypCell

    def handleFailure(self, ypCell, ex):
        """
        handle the given failure
        """
        print(
            f"Warning ⚠️: generating {ypCell.target} for {ypCell.modelElement} failed with {str(ex)}",
            file=sys.stderr,
            flush=True,
        )
        if self.debug:
            print(traceback.format_exc())

    def generateViaMwApi(
        self,
        target_names: list = None,
        topic_names: list = None,
        dryRun: bool = True,
        withEditor: bool = False,
    ):
        """
        start the generation via MediaWiki API

        Args:
            target_names(list): an optional list of target names
            topic_name(list): an optional list of topic names
            dryRun(bool): if True do not transfer results
            withEditor(bool): if True - start editor

        Return:
            list(MwGenResult): a list of Mediawiki Generator Results
        """
        self.smwAccess.wikiClient.login()
        genResults = []
        for ypCell in self.yieldYpCells("via Mediawiki Api", target_names, topic_names):
            try:
                genResult = ypCell.generateViaMwApi(
                    smwAccess=self.smwAccess, dryRun=dryRun, withEditor=withEditor
                )
                if self.debug or self.verbose:
                    diff_url = genResult.getDiffUrl()
                    diff_info = "" if diff_url is None else diff_url
                    diff_info += f"({len(genResult.markup_diff)})"
                    print(f"diff: {diff_info}")
                genResults.append(genResult)
            except Exception as ex:
                self.handleFailure(ypCell, ex)
        return genResults

    def generateToFile(
        self,
        target_dir=None,
        target_names: list = None,
        topic_names: list = None,
        dryRun: bool = True,
        withEditor: bool = False,
    ):
        """
        start the generation via MediaWiki Backup Directory

        Args:
            target_dir(str): the path to the target directory
            target_names(list): an optional list of target names
            topic_name(list): an optional list of topic names

            dryRun(bool): if True do not transfer results
            withEditor(bool): if True - start editor

        Return:
            list(FileGenResult): a list of File Generator Results
        """
        genResults = []
        if target_dir is None:
            home = Path.home()
            target_dir = f"{home}/wikibackup/{self.wikiId}"
        for ypCell in self.yieldYpCells(
            f" to file in {target_dir}", target_names, topic_names
        ):
            try:
                genResult = ypCell.generateToFile(
                    target_dir=target_dir, dryRun=dryRun, withEditor=withEditor
                )
                genResults.append(genResult)
            except Exception as ex:
                self.handleFailure(ypCell, ex)
        return genResults

    def push(self):
        """
        push according to my command line args
        """
        if not self.args.source:
            raise "missing source wiki"
        if self.args.topics:
            topic_names = self.args.topics
        else:
            topic_names = self.context.topics.keys()
        login = self.args.login
        force = self.args.force
        ignore = True
        fromWikiId = self.args.source
        wikiPush = WikiPush(
            fromWikiId=fromWikiId,
            toWikiId=self.smwAccess.wikiId,
            login=login,
            verbose=not self.args.quiet,
            debug=self.args.debug,
        )
        if not self.args.quiet:
            print(
                f"pushing concept {self.args.context} from {self.args.source} to {self.wikiId} ..."
            )
        all_page_titles = []
        for topic_name in topic_names:
            topic = self.context.topics[topic_name]
            for page_titles, page_query, query_field in [
                ([f"Concept:{topic_name}"], None, None),
                ([f"Category:{topic_name}"], None, None),
                ([f"Template:{topic_name}"], None, None),
                ([f"Form:{topic_name}"], None, None),
                ([f"Help:{topic_name}"], None, None),
                ([f"List of {topic.pluralName}"], None, None),
                (
                    None,
                    f"{{{{#ask: [[Property topic::Concept:{topic_name}]]|?#=page}}}}",
                    "page",
                ),
                (
                    None,
                    f"{{{{#ask: [[Topic name::{topic_name}]]|?Topic context=context}}}}",
                    "context",
                ),
            ]:
                if not page_titles:
                    page_titles = wikiPush.query(
                        page_query,
                        wiki=self.smwSourceAccess.wikiClient,
                        queryField=query_field,
                    )
                all_page_titles.extend(
                    page_title
                    for page_title in page_titles
                    if page_title not in all_page_titles
                )
        failed = wikiPush.push(
            pageTitles=all_page_titles, force=force, ignore=ignore, withImages=True
        )
        if len(failed) > 0:
            print(f"️Error {len(failed)} push attempts failed ❌️")
            for i, fail_name in enumerate(failed[:20]):
                print(f"    {i+1:2}: {fail_name} ❌")

__init__(verbose=True, debug=False)

constructor

Parameters:

Name Type Description Default
verbose(bool)

if True show verbose messages

required
debug(bool)

if True switch debugging on

required
Source code in yprinciple/genapi.py
27
28
29
30
31
32
33
34
35
36
37
def __init__(self, verbose: bool = True, debug: bool = False):
    """
    constructor

    Args:
        verbose(bool): if True show verbose messages
        debug(bool): if True switch debugging on
    """
    self.verbose = verbose
    self.debug = debug
    self.args = None

filterTargets(target_names=None)

filter targets by a list of target_names

Parameters:

Name Type Description Default
target_names(list)

an optional list of target names

required

Returns:

Name Type Description
dict dict

mapping from target names to targets

Source code in yprinciple/genapi.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def filterTargets(self, target_names: list = None) -> dict:
    """
    filter targets by a list of target_names

    Args:
        target_names(list): an optional list of target names

    Returns:
        dict: mapping from target names to targets
    """
    allTargets = SMWTarget.getSMWTargets()
    if target_names is None:
        targets = allTargets
    else:
        targets = {}
        for target_name in target_names:
            if target_name in allTargets:
                targets[target_name] = allTargets[target_name]
    return targets

fromArgs(args) classmethod

create a GeneratorAPI for the given command line arguments

Parameters:

Name Type Description Default
args

command line arguments

required

Returns:

Name Type Description
GeneratorAPI GeneratorAPI
Source code in yprinciple/genapi.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@classmethod
def fromArgs(cls, args) -> "GeneratorAPI":
    """
    create a GeneratorAPI for the given command line arguments

    Args:
        args: command line arguments

    Returns:
        GeneratorAPI:
    """
    gen = GeneratorAPI(verbose=not args.quiet, debug=args.debug)
    gen.setWikiAndGetContexts(args)
    if args.sidif:
        gen.context, gen.error, gen.errMsg = Context.fromSiDIF_input(
            args.sidif, debug=args.debug
        )
    else:
        wikiId = args.source if args.push else args.wikiId
        gen.readContext(wikiId, args.context)
    # remember complete arguments (e.g. for push)
    gen.args = args
    return gen

generateToFile(target_dir=None, target_names=None, topic_names=None, dryRun=True, withEditor=False)

start the generation via MediaWiki Backup Directory

Parameters:

Name Type Description Default
target_dir(str)

the path to the target directory

required
target_names(list)

an optional list of target names

required
topic_name(list)

an optional list of topic names

required
dryRun(bool)

if True do not transfer results

required
withEditor(bool)

if True - start editor

required
Return

list(FileGenResult): a list of File Generator Results

Source code in yprinciple/genapi.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
def generateToFile(
    self,
    target_dir=None,
    target_names: list = None,
    topic_names: list = None,
    dryRun: bool = True,
    withEditor: bool = False,
):
    """
    start the generation via MediaWiki Backup Directory

    Args:
        target_dir(str): the path to the target directory
        target_names(list): an optional list of target names
        topic_name(list): an optional list of topic names

        dryRun(bool): if True do not transfer results
        withEditor(bool): if True - start editor

    Return:
        list(FileGenResult): a list of File Generator Results
    """
    genResults = []
    if target_dir is None:
        home = Path.home()
        target_dir = f"{home}/wikibackup/{self.wikiId}"
    for ypCell in self.yieldYpCells(
        f" to file in {target_dir}", target_names, topic_names
    ):
        try:
            genResult = ypCell.generateToFile(
                target_dir=target_dir, dryRun=dryRun, withEditor=withEditor
            )
            genResults.append(genResult)
        except Exception as ex:
            self.handleFailure(ypCell, ex)
    return genResults

generateViaMwApi(target_names=None, topic_names=None, dryRun=True, withEditor=False)

start the generation via MediaWiki API

Parameters:

Name Type Description Default
target_names(list)

an optional list of target names

required
topic_name(list)

an optional list of topic names

required
dryRun(bool)

if True do not transfer results

required
withEditor(bool)

if True - start editor

required
Return

list(MwGenResult): a list of Mediawiki Generator Results

Source code in yprinciple/genapi.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def generateViaMwApi(
    self,
    target_names: list = None,
    topic_names: list = None,
    dryRun: bool = True,
    withEditor: bool = False,
):
    """
    start the generation via MediaWiki API

    Args:
        target_names(list): an optional list of target names
        topic_name(list): an optional list of topic names
        dryRun(bool): if True do not transfer results
        withEditor(bool): if True - start editor

    Return:
        list(MwGenResult): a list of Mediawiki Generator Results
    """
    self.smwAccess.wikiClient.login()
    genResults = []
    for ypCell in self.yieldYpCells("via Mediawiki Api", target_names, topic_names):
        try:
            genResult = ypCell.generateViaMwApi(
                smwAccess=self.smwAccess, dryRun=dryRun, withEditor=withEditor
            )
            if self.debug or self.verbose:
                diff_url = genResult.getDiffUrl()
                diff_info = "" if diff_url is None else diff_url
                diff_info += f"({len(genResult.markup_diff)})"
                print(f"diff: {diff_info}")
            genResults.append(genResult)
        except Exception as ex:
            self.handleFailure(ypCell, ex)
    return genResults

handleFailure(ypCell, ex)

handle the given failure

Source code in yprinciple/genapi.py
160
161
162
163
164
165
166
167
168
169
170
def handleFailure(self, ypCell, ex):
    """
    handle the given failure
    """
    print(
        f"Warning ⚠️: generating {ypCell.target} for {ypCell.modelElement} failed with {str(ex)}",
        file=sys.stderr,
        flush=True,
    )
    if self.debug:
        print(traceback.format_exc())

push()

push according to my command line args

Source code in yprinciple/genapi.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
def push(self):
    """
    push according to my command line args
    """
    if not self.args.source:
        raise "missing source wiki"
    if self.args.topics:
        topic_names = self.args.topics
    else:
        topic_names = self.context.topics.keys()
    login = self.args.login
    force = self.args.force
    ignore = True
    fromWikiId = self.args.source
    wikiPush = WikiPush(
        fromWikiId=fromWikiId,
        toWikiId=self.smwAccess.wikiId,
        login=login,
        verbose=not self.args.quiet,
        debug=self.args.debug,
    )
    if not self.args.quiet:
        print(
            f"pushing concept {self.args.context} from {self.args.source} to {self.wikiId} ..."
        )
    all_page_titles = []
    for topic_name in topic_names:
        topic = self.context.topics[topic_name]
        for page_titles, page_query, query_field in [
            ([f"Concept:{topic_name}"], None, None),
            ([f"Category:{topic_name}"], None, None),
            ([f"Template:{topic_name}"], None, None),
            ([f"Form:{topic_name}"], None, None),
            ([f"Help:{topic_name}"], None, None),
            ([f"List of {topic.pluralName}"], None, None),
            (
                None,
                f"{{{{#ask: [[Property topic::Concept:{topic_name}]]|?#=page}}}}",
                "page",
            ),
            (
                None,
                f"{{{{#ask: [[Topic name::{topic_name}]]|?Topic context=context}}}}",
                "context",
            ),
        ]:
            if not page_titles:
                page_titles = wikiPush.query(
                    page_query,
                    wiki=self.smwSourceAccess.wikiClient,
                    queryField=query_field,
                )
            all_page_titles.extend(
                page_title
                for page_title in page_titles
                if page_title not in all_page_titles
            )
    failed = wikiPush.push(
        pageTitles=all_page_titles, force=force, ignore=ignore, withImages=True
    )
    if len(failed) > 0:
        print(f"️Error {len(failed)} push attempts failed ❌️")
        for i, fail_name in enumerate(failed[:20]):
            print(f"    {i+1:2}: {fail_name} ❌")

readContext(wikiId, context_name)

Parameters:

Name Type Description Default
wikiId(str)

the wikiId of the wiki to read the context from

required
context_name str

the name of the context to read

required
Source code in yprinciple/genapi.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def readContext(self, wikiId: str, context_name: str):
    """
    Args:
        wikiId(str): the wikiId of the wiki to read the context from
        context_name: the name of the context to read
    """
    self.mw_context = self.mw_contexts.get(context_name, None)
    if not self.mw_context:
        self.context = None
        self.errmsg = f"Could not read context {context_name} from {wikiId}"
        self.error = Exception(self.errmsg)
    else:
        self.context, self.error, self.errMsg = Context.fromWikiContext(
            self.mw_context, debug=self.debug
        )

setWikiAndGetContexts(args)

set my wiki and get Contexts for the given args

Parameters:

Name Type Description Default
args

command line arguments

required
Source code in yprinciple/genapi.py
63
64
65
66
67
68
69
70
71
72
73
74
def setWikiAndGetContexts(self, args):
    """
    set my wiki and get Contexts for the given args

    Args:
        args: command line arguments
    """
    self.wikiId = args.wikiId
    self.smwAccess = SMWAccess(args.wikiId)
    self.smwSourceAccess = SMWAccess(args.source) if args.push else None
    self.smwContextAccess = self.smwSourceAccess if args.push else self.smwAccess
    self.mw_contexts = self.smwContextAccess.getMwContexts()

yieldYpCells(hint, target_names=None, topic_names=None, with_subcells=True)

generate/yield topics and targets via nested loop

Parameters:

Name Type Description Default
hint(str)

hint message to show how the yield is used

required
with_subcells bool

if True yield sub cells (one level)

True
target_name(list)

if set filter targets by name

required
topic_names(list)

if set filter topics by name

required

Returns: generator(YpCell)

Source code in yprinciple/genapi.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def yieldYpCells(
    self,
    hint: str,
    target_names: list = None,
    topic_names: list = None,
    with_subcells: bool = True,
):
    """
    generate/yield topics and targets via nested loop

    Args:
        hint(str): hint message to show how the yield is used
        with_subcells: if True yield sub cells (one level)
        target_name(list): if set filter targets by name
        topic_names(list): if set filter topics by name
    Returns:
        generator(YpCell)
    """

    def showMsg(topic_name: str, ypCell: YpCell):
        """
        show a message for the given topic_name

        Args:
            topic_name(str): topic
        """
        if self.verbose:
            target_name = ypCell.target.name
            print(f"generating {target_name} for {topic_name} {hint}...")
        pass

    targets = self.filterTargets(target_names)
    for topic_name, topic in self.context.topics.items():
        # filter topic names
        if topic_names is not None and not topic_name in topic_names:
            continue
        for _target_name, target in targets.items():
            if target.showInGrid:
                ypCell = YpCell.createYpCell(target=target, topic=topic)
                if ypCell.target.is_multi:
                    if with_subcells:
                        for subCell in ypCell.subCells.values():
                            showMsg(topic_name, subCell)
                            yield subCell
                else:
                    showMsg(topic_name, ypCell)
                    yield ypCell

gengrid

Created on 25.11.2022

@author: wf

GeneratorGrid

generator and selection grid

see https://wiki.bitplan.com/index.php/Y-Prinzip#Example

Source code in yprinciple/gengrid.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
class GeneratorGrid:
    """
    generator and selection grid

    see https://wiki.bitplan.com/index.php/Y-Prinzip#Example
    """

    def __init__(
        self, targets: dict, parent, solution: WebSolution, iconSize: str = "32px"
    ):
        """
        constructor

        Args:
            targets(dict): a list of targets
            parent: the parent element
            solution(WebSolution): the solution

        """
        self.parent = parent
        self.solution = solution
        self.color_schema = solution.config.color_schema
        self.iconSize = iconSize
        self.cell_hide_size_info = True
        self.checkboxes = {}
        self.ypcell_by_id = {}
        self.checkbox_by_id = {}
        self.header_checkbox_by_id = {}
        self.cell_debug_msg_divs = []
        self.targets = targets
        self.setup_styles()
        self.setup_ui()

    def setup_ui(self):
        """
        setup the user interface
        """
        with self.parent:
            target_columns = len(self.displayTargets())
            # two more for the Topics and check box columns
            target_columns += 2
            self.grid = ui.grid(columns=target_columns).classes("w-full gap-0")
            self.setup_target_header_row()
            self.setup_topic_header_row()

    def setup_styles(self):
        """
        setup the styles for the ui
        """
        self.header_classes = "text-center"
        # centering
        self.center_classes = "flex flex-col items-center justify-center"
        # see https://www.materialpalette.com/indigo/indigo
        # and https://github.com/WolfgangFahl/nicegui_widgets/blob/main/ngwidgets/color_schema.py
        # light primary color
        self.header_background = "#c5cae9"
        #
        self.light_header_background = "#f5f5f5"
        self.bs_secondary = "#6c757d"
        self.header_style = (
            f"font-size: 1.0rem;background-color: {self.header_background}"
        )
        self.light_header_style = f"background-color: {self.light_header_background}"

    def add_header_cell(self, title: str):
        """
        add a header cell with the given title

        Args:
            title(str): the title of the cell
        """
        with self.grid:
            classes = self.header_classes + self.center_classes
            header_cell = ui.row().classes(classes).style(self.header_style)
            with header_cell:
                header_div = (
                    ui.html().classes(self.header_classes).style(self.header_style)
                )
                header_div.content = f"<strong>{title}</strong>"
        return header_cell

    def setup_target_header_row(self):
        """
        setup the header row
        """
        with self.grid:
            self.generateButton = ui.button(
                icon="play_circle", on_click=self.onGenerateButtonClick
            )
        self.targets_column_header = self.add_header_cell("Target")
        for target in self.displayTargets():
            target_header_cell = self.add_header_cell(target.name)
            with target_header_cell:
                ui.icon(target.icon_name, size=self.iconSize, color=self.bs_secondary)
                # <i class="mdi mdi-archive" style="color: rgb(108, 117, 125); font-size: 32px;"></i>
                # markup+=f"""<i class="mdi mdi-{target.icon_name}" style="color: {self.bs_secondary};font-size:{self.iconSize};"></i>"""
                # <i class="q-icon notranslate material-icons" aria-hidden="true" role="presentation" id="c48">archive</i>
                # markup+=f"""<i class="q-icon notranslate material-icons" aria-hidden="true" role="presentation">{target.icon_name}</i>"""
                pass

    def setup_topic_header_row(self):
        """
        setup the second header row
        """
        with self.grid:
            self.topics_column_header = (
                ui.html().classes(self.header_classes).style(self.header_style)
            )
            self.topics_column_header.content = "<strong>Topics</strong>"
        self.header_checkboxes = {}
        self.header_checkboxes["all"] = self.create_simple_checkbox(
            parent=self.grid,
            label_text="↘",
            title="select all",
            classes=self.center_classes,
            on_change=self.on_select_all,
        )
        for target in self.displayTargets():
            self.header_checkboxes[target.name] = self.create_simple_checkbox(
                parent=self.grid,
                label_text="↓",
                title=f"select all {target.name}",
                classes=self.center_classes,
                on_change=self.on_select_column,
            )

    def getCheckedYpCells(self) -> List[YpCell]:
        """
        get all checked YpCells
        """
        checkedYpCells = []
        # generate in order of rows
        for checkbox_row in self.checkboxes.values():
            for checkbox, ypCell in checkbox_row.values():
                if checkbox.value and ypCell.ui_ready:
                    checkedYpCells.append(ypCell)
                for subCell in ypCell.subCells.values():
                    if subCell.ui_ready:
                        checkbox = self.checkbox_by_id[subCell.checkbox_id]
                        if checkbox.value:
                            checkedYpCells.append(subCell)
        return checkedYpCells

    def generateCheckedCells(self, cellsToGen: List[YpCell]):
        try:
            # force login
            if not self.solution.smwAccess.wikiClient._is_logged_in:
                ex=self.solution.smwAccess.wikiClient.try_login()
                if ex:
                    self.solution.handle_exception(ex)
                    return
            for ypCell in cellsToGen:
                cell_checkbox = self.checkbox_by_id.get(ypCell.checkbox_id, None)
                status_div = cell_checkbox.status_div
                with status_div:
                    status_div.clear()
                    status_div.content = ""
                try:
                    genResult = ypCell.generateViaMwApi(
                        smwAccess=self.solution.smwAccess,
                        dryRun=self.solution.dryRun,
                        withEditor=self.solution.openEditor,
                    )
                    if genResult is not None and cell_checkbox is not None:
                        delta_color = ""
                        diff_url = genResult.getDiffUrl()
                        if diff_url is not None:
                            if genResult.page_changed():
                                delta_color = "text-red-500"
                            else:
                                delta_color = "text-green-500"
                        else:
                            delta_color = "text-gray-500"
                        with status_div:
                            link = Link.create(url=diff_url, text="Δ")
                            _link_html = ui.html(link).classes(
                                "text-xl font-bold " + delta_color,
                            )
                except BaseException as ex:
                    with status_div:
                        status_div.content = f"❗ error:{str(ex)}"
                    self.solution.handle_exception(ex)
                self.updateProgress()
        except Exception as outer_ex:
            self.solution.handle_exception(outer_ex)

    async def onGenerateButtonClick(self, _msg):
        """
        react on the generate button having been clicked
        """
        cellsToGen = self.getCheckedYpCells()
        total = len(cellsToGen)
        ui.notify(f"running {total} generator tasks")
        self.resetProgress("generating", total)
        await run.io_bound(self.generateCheckedCells, cellsToGen)

    def check_ypcell_box(self, checkbox, ypCell, checked: bool):
        """
        check the given checkbox and the ypCell belonging to it
        """
        checkbox.value = checked
        self.checkSubCells(ypCell, checked)

    def checkSubCells(self, ypCell, checked):
        # loop over all subcells
        for subcell in ypCell.subCells.values():
            # and set the checkbox value accordingly
            checkbox = self.checkbox_by_id[subcell.checkbox_id]
            checkbox.value = checked

    def check_row(self, checkbox_row, checked: bool):
        for checkbox, ypCell in checkbox_row.values():
            self.check_ypcell_box(checkbox, ypCell, checked)

    async def on_select_all(self, args):
        """
        react on "select all" being clicked
        """
        try:
            checked = args.value
            for checkbox_row in self.checkboxes.values():
                self.check_row(checkbox_row, checked)
        except BaseException as ex:
            self.solution.handle_exception(ex)
        pass

    def get_select(self, args) -> str:
        """
        get the select from the sender's tooltip
        """
        select = None
        slots = args.sender.slots.get("default")
        if slots:
            children = slots.children
            if len(children) > 0:
                tooltip = children[0]
                if isinstance(tooltip, Tooltip):
                    title = tooltip.text
                    select = title.replace("select all", "").strip()
        return select

    async def on_select_column(self, args):
        """
        react on "select all " for a column being clicked
        """
        try:
            checked = args.value
            target_name = self.get_select(args)
            if target_name:
                for checkboxes in self.checkboxes.values():
                    checkbox, ypCell = checkboxes[target_name]
                    self.check_ypcell_box(checkbox, ypCell, checked)
        except BaseException as ex:
            self.solution.handle_exception(ex)

    async def on_select_row(self, args):
        """
        react on "select all " for a row being clicked
        """
        try:
            checked = args.value
            topic_name = self.get_select(args)
            if topic_name:
                checkbox_row = self.checkboxes[topic_name]
                self.check_row(checkbox_row, checked)
        except BaseException as ex:
            self.solution.handle_exception(ex)

    async def onParentCheckboxClick(self, args):
        """
        a ypCell checkbox has been clicked for a ypCell that has subCells
        """
        # get the parent checkbox
        checkbox = args.sender
        checked = args.value
        # lookup the ypCell
        ypCell = self.ypcell_by_id[checkbox.id]
        self.checkSubCells(ypCell, checked)

    def displayTargets(self):
        # return self.targets.values()
        dt = []
        for target in self.targets.values():
            if target.showInGrid:
                dt.append(target)
        return dt

    def get_colums(self, target: Target) -> int:
        """
        get the number of columns for the given target

        Args:
            target(Target): the target

        Returns:
            int: the number of columns to be displayed
        """
        cols = 2 if target.is_multi else 1
        return cols

    def create_simple_checkbox(
        self,
        parent,
        label_text: str,
        title: str,
        classes: str = None,
        on_change: Callable = None,
        **kwargs,
    ):
        """
        Create a NiceGUI checkbox with a label and optional tooltip, adding it to the specified parent container.

        Args:
            parent: The parent UI element to attach the checkbox to. Must be a NiceGUI container.
            label_text (str): The text label to display next to the checkbox.
            title (str): The tooltip text to display when hovering over the checkbox.
            classes (str, optional): CSS classes for additional styling. If None, uses a default.
            **kwargs: Additional keyword arguments to pass to the checkbox.

        Returns:
            ui.checkbox: The created checkbox instance.
        """
        if classes is None:
            classes = self.header_classes
        with parent:
            checkbox = ui.checkbox(
                text=label_text,
                on_change=on_change,
                **kwargs,
            )
            checkbox.classes(classes)
            checkbox.style(self.light_header_style)
            checkbox.tooltip(title)
        return checkbox

    def create_check_box_for_cell(
        self, yp_cell: YpCell, parent, columns: int = 1
    ) -> ui.checkbox:
        """
        create a nicegui CheckBox for the given YpCell

        Args:
            yp_cell: YpCell - the YpCell to create a checkbox for
            parent: the nicegui parent element
            columns(int) the number of columns

        Returns:
            ui.checkbox: The created NiceGUI checkbox element.
        """
        with parent:
            yp_cell_card = ui.card()
        label_text = yp_cell.getLabelText()
        checkbox = self.create_simple_checkbox(
            parent=yp_cell_card, label_text=label_text, title=label_text
        )
        yp_cell.getPage(self.solution.smwAccess)
        color = "blue" if yp_cell.status == "✅" else "red"
        link = f"<a href='{yp_cell.pageUrl}' style='color:{color}'>{label_text}<a>"
        if yp_cell.status == "ⓘ":
            link = f"{label_text}"
        # in a one column setting we need to break link and status message
        if columns == 1:
            label_text = label_text.replace(":", ":<br>")
            delim = "<br>"
        else:
            delim = "&nbsp;"
        with yp_cell_card:
            link_html = ui.html()
            link_html.content = f"{link}{delim}"
            debug_div = ui.html()
            debug_div.content = f"{yp_cell.statusMsg}"
            debug_div.visible = not self.cell_hide_size_info
            status_div = ui.html()
            status_div.content = yp_cell.status
            checkbox.status_div = status_div
            self.cell_debug_msg_divs.append(debug_div)
        # link ypCell with check box via a unique identifier
        yp_cell.checkbox_id = checkbox.id
        self.ypcell_by_id[checkbox.id] = checkbox.id
        self.checkbox_by_id[checkbox.id] = checkbox
        yp_cell.ui_ready = True
        return checkbox

    def add_topic_cell(self, topic: Topic):
        """
        add an icon for the given topic
        """
        topic_cell = self.add_header_cell(topic.name)
        icon_url = None
        if hasattr(topic, "iconUrl"):
            if topic.iconUrl.startswith("http"):
                icon_url = f"{topic.iconUrl}"
            if icon_url is None and self.solution.mw_context is not None:
                icon_url = f"{self.solution.mw_context.wiki_url}{topic.iconUrl}"
        if icon_url is None:
            icon_url = "?"
        style = f"width: {self.iconSize}; height: {self.iconSize};"
        with topic_cell:
            topic_icon = ui.image(
                source=icon_url,
            )
        topic_icon.style(style)
        return topic_icon

    def resetProgress(self, desc: str, total: int):
        self.solution.progressBar.desc = desc
        self.solution.progressBar.total = total
        self.solution.progressBar.reset()

    def updateProgress(self):
        """
        update the progress
        """
        self.solution.progressBar.update(1)
        self.grid.update()

    def add_yp_cell(self, parent, ypCell: YpCell) -> "ui.checkbox":
        """
        add the given ypCell
        """
        if len(ypCell.subCells) > 0:
            checkbox = None
            with parent:
                # content_div=ui.row()
                hide_show = ui.expansion("", icon="format_list_bulleted").classes(
                    "w-full"
                )
                # hide_show = HideShow(
                #    show_content=False,
                #    hide_show_label=("properties", "properties"),
                #    content_div=content_div
                # )
            for _subcell_name, subCell in ypCell.subCells.items():
                checkbox = self.create_check_box_for_cell(subCell, parent=hide_show)
                self.updateProgress()
                pass
        else:
            checkbox = self.create_check_box_for_cell(ypCell, parent=self.grid)
            self.updateProgress()
        return checkbox

    def add_topic_rows(self, context: Context):
        """
        add the topic rows for the given context

        Args:
            context(Context): the context for which do add topic rows
        """
        total_steps = 0
        for topic_name, topic in context.topics.items():
            total_steps += len(self.displayTargets()) - 1
            total_steps += len(topic.properties)
        self.resetProgress("preparing", total=total_steps)
        for topic_name, topic in context.topics.items():
            self.checkboxes[topic_name] = {}
            checkbox_row = self.checkboxes[topic_name]
            with self.grid:
                self.add_topic_cell(topic)
                checkbox = self.create_simple_checkbox(
                    parent=self.grid,
                    label_text="→",
                    title=f"select all {topic_name}",
                    on_change=self.on_select_row,
                )
            for target in self.displayTargets():
                ypCell = YpCell.createYpCell(target=target, topic=topic)
                checkbox = self.add_yp_cell(parent=self.grid, ypCell=ypCell)
                if checkbox:
                    checkbox_row[target.name] = (checkbox, ypCell)
            pass

    def set_hide_show_status_of_cell_debug_msg(self, hidden: bool = False):
        """
        Sets the hidden status of all cell debug messages
        Args:
            hidden: If True hide debug messages else show them
        """
        try:
            self.cell_hide_size_info = hidden
            for div in self.cell_debug_msg_divs:
                div.visible = not hidden
            self.grid.update()
        except Exception as ex:
            self.solution.handle_exception(ex)

__init__(targets, parent, solution, iconSize='32px')

constructor

Parameters:

Name Type Description Default
targets(dict)

a list of targets

required
parent

the parent element

required
solution(WebSolution)

the solution

required
Source code in yprinciple/gengrid.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def __init__(
    self, targets: dict, parent, solution: WebSolution, iconSize: str = "32px"
):
    """
    constructor

    Args:
        targets(dict): a list of targets
        parent: the parent element
        solution(WebSolution): the solution

    """
    self.parent = parent
    self.solution = solution
    self.color_schema = solution.config.color_schema
    self.iconSize = iconSize
    self.cell_hide_size_info = True
    self.checkboxes = {}
    self.ypcell_by_id = {}
    self.checkbox_by_id = {}
    self.header_checkbox_by_id = {}
    self.cell_debug_msg_divs = []
    self.targets = targets
    self.setup_styles()
    self.setup_ui()

add_header_cell(title)

add a header cell with the given title

Parameters:

Name Type Description Default
title(str)

the title of the cell

required
Source code in yprinciple/gengrid.py
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def add_header_cell(self, title: str):
    """
    add a header cell with the given title

    Args:
        title(str): the title of the cell
    """
    with self.grid:
        classes = self.header_classes + self.center_classes
        header_cell = ui.row().classes(classes).style(self.header_style)
        with header_cell:
            header_div = (
                ui.html().classes(self.header_classes).style(self.header_style)
            )
            header_div.content = f"<strong>{title}</strong>"
    return header_cell

add_topic_cell(topic)

add an icon for the given topic

Source code in yprinciple/gengrid.py
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
def add_topic_cell(self, topic: Topic):
    """
    add an icon for the given topic
    """
    topic_cell = self.add_header_cell(topic.name)
    icon_url = None
    if hasattr(topic, "iconUrl"):
        if topic.iconUrl.startswith("http"):
            icon_url = f"{topic.iconUrl}"
        if icon_url is None and self.solution.mw_context is not None:
            icon_url = f"{self.solution.mw_context.wiki_url}{topic.iconUrl}"
    if icon_url is None:
        icon_url = "?"
    style = f"width: {self.iconSize}; height: {self.iconSize};"
    with topic_cell:
        topic_icon = ui.image(
            source=icon_url,
        )
    topic_icon.style(style)
    return topic_icon

add_topic_rows(context)

add the topic rows for the given context

Parameters:

Name Type Description Default
context(Context)

the context for which do add topic rows

required
Source code in yprinciple/gengrid.py
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
def add_topic_rows(self, context: Context):
    """
    add the topic rows for the given context

    Args:
        context(Context): the context for which do add topic rows
    """
    total_steps = 0
    for topic_name, topic in context.topics.items():
        total_steps += len(self.displayTargets()) - 1
        total_steps += len(topic.properties)
    self.resetProgress("preparing", total=total_steps)
    for topic_name, topic in context.topics.items():
        self.checkboxes[topic_name] = {}
        checkbox_row = self.checkboxes[topic_name]
        with self.grid:
            self.add_topic_cell(topic)
            checkbox = self.create_simple_checkbox(
                parent=self.grid,
                label_text="→",
                title=f"select all {topic_name}",
                on_change=self.on_select_row,
            )
        for target in self.displayTargets():
            ypCell = YpCell.createYpCell(target=target, topic=topic)
            checkbox = self.add_yp_cell(parent=self.grid, ypCell=ypCell)
            if checkbox:
                checkbox_row[target.name] = (checkbox, ypCell)
        pass

add_yp_cell(parent, ypCell)

add the given ypCell

Source code in yprinciple/gengrid.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
def add_yp_cell(self, parent, ypCell: YpCell) -> "ui.checkbox":
    """
    add the given ypCell
    """
    if len(ypCell.subCells) > 0:
        checkbox = None
        with parent:
            # content_div=ui.row()
            hide_show = ui.expansion("", icon="format_list_bulleted").classes(
                "w-full"
            )
            # hide_show = HideShow(
            #    show_content=False,
            #    hide_show_label=("properties", "properties"),
            #    content_div=content_div
            # )
        for _subcell_name, subCell in ypCell.subCells.items():
            checkbox = self.create_check_box_for_cell(subCell, parent=hide_show)
            self.updateProgress()
            pass
    else:
        checkbox = self.create_check_box_for_cell(ypCell, parent=self.grid)
        self.updateProgress()
    return checkbox

check_ypcell_box(checkbox, ypCell, checked)

check the given checkbox and the ypCell belonging to it

Source code in yprinciple/gengrid.py
215
216
217
218
219
220
def check_ypcell_box(self, checkbox, ypCell, checked: bool):
    """
    check the given checkbox and the ypCell belonging to it
    """
    checkbox.value = checked
    self.checkSubCells(ypCell, checked)

create_check_box_for_cell(yp_cell, parent, columns=1)

create a nicegui CheckBox for the given YpCell

Parameters:

Name Type Description Default
yp_cell YpCell

YpCell - the YpCell to create a checkbox for

required
parent

the nicegui parent element

required

Returns:

Type Description
checkbox

ui.checkbox: The created NiceGUI checkbox element.

Source code in yprinciple/gengrid.py
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def create_check_box_for_cell(
    self, yp_cell: YpCell, parent, columns: int = 1
) -> ui.checkbox:
    """
    create a nicegui CheckBox for the given YpCell

    Args:
        yp_cell: YpCell - the YpCell to create a checkbox for
        parent: the nicegui parent element
        columns(int) the number of columns

    Returns:
        ui.checkbox: The created NiceGUI checkbox element.
    """
    with parent:
        yp_cell_card = ui.card()
    label_text = yp_cell.getLabelText()
    checkbox = self.create_simple_checkbox(
        parent=yp_cell_card, label_text=label_text, title=label_text
    )
    yp_cell.getPage(self.solution.smwAccess)
    color = "blue" if yp_cell.status == "✅" else "red"
    link = f"<a href='{yp_cell.pageUrl}' style='color:{color}'>{label_text}<a>"
    if yp_cell.status == "ⓘ":
        link = f"{label_text}"
    # in a one column setting we need to break link and status message
    if columns == 1:
        label_text = label_text.replace(":", ":<br>")
        delim = "<br>"
    else:
        delim = "&nbsp;"
    with yp_cell_card:
        link_html = ui.html()
        link_html.content = f"{link}{delim}"
        debug_div = ui.html()
        debug_div.content = f"{yp_cell.statusMsg}"
        debug_div.visible = not self.cell_hide_size_info
        status_div = ui.html()
        status_div.content = yp_cell.status
        checkbox.status_div = status_div
        self.cell_debug_msg_divs.append(debug_div)
    # link ypCell with check box via a unique identifier
    yp_cell.checkbox_id = checkbox.id
    self.ypcell_by_id[checkbox.id] = checkbox.id
    self.checkbox_by_id[checkbox.id] = checkbox
    yp_cell.ui_ready = True
    return checkbox

create_simple_checkbox(parent, label_text, title, classes=None, on_change=None, **kwargs)

Create a NiceGUI checkbox with a label and optional tooltip, adding it to the specified parent container.

Parameters:

Name Type Description Default
parent

The parent UI element to attach the checkbox to. Must be a NiceGUI container.

required
label_text str

The text label to display next to the checkbox.

required
title str

The tooltip text to display when hovering over the checkbox.

required
classes str

CSS classes for additional styling. If None, uses a default.

None
**kwargs

Additional keyword arguments to pass to the checkbox.

{}

Returns:

Type Description

ui.checkbox: The created checkbox instance.

Source code in yprinciple/gengrid.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
def create_simple_checkbox(
    self,
    parent,
    label_text: str,
    title: str,
    classes: str = None,
    on_change: Callable = None,
    **kwargs,
):
    """
    Create a NiceGUI checkbox with a label and optional tooltip, adding it to the specified parent container.

    Args:
        parent: The parent UI element to attach the checkbox to. Must be a NiceGUI container.
        label_text (str): The text label to display next to the checkbox.
        title (str): The tooltip text to display when hovering over the checkbox.
        classes (str, optional): CSS classes for additional styling. If None, uses a default.
        **kwargs: Additional keyword arguments to pass to the checkbox.

    Returns:
        ui.checkbox: The created checkbox instance.
    """
    if classes is None:
        classes = self.header_classes
    with parent:
        checkbox = ui.checkbox(
            text=label_text,
            on_change=on_change,
            **kwargs,
        )
        checkbox.classes(classes)
        checkbox.style(self.light_header_style)
        checkbox.tooltip(title)
    return checkbox

getCheckedYpCells()

get all checked YpCells

Source code in yprinciple/gengrid.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def getCheckedYpCells(self) -> List[YpCell]:
    """
    get all checked YpCells
    """
    checkedYpCells = []
    # generate in order of rows
    for checkbox_row in self.checkboxes.values():
        for checkbox, ypCell in checkbox_row.values():
            if checkbox.value and ypCell.ui_ready:
                checkedYpCells.append(ypCell)
            for subCell in ypCell.subCells.values():
                if subCell.ui_ready:
                    checkbox = self.checkbox_by_id[subCell.checkbox_id]
                    if checkbox.value:
                        checkedYpCells.append(subCell)
    return checkedYpCells

get_colums(target)

get the number of columns for the given target

Parameters:

Name Type Description Default
target(Target)

the target

required

Returns:

Name Type Description
int int

the number of columns to be displayed

Source code in yprinciple/gengrid.py
306
307
308
309
310
311
312
313
314
315
316
317
def get_colums(self, target: Target) -> int:
    """
    get the number of columns for the given target

    Args:
        target(Target): the target

    Returns:
        int: the number of columns to be displayed
    """
    cols = 2 if target.is_multi else 1
    return cols

get_select(args)

get the select from the sender's tooltip

Source code in yprinciple/gengrid.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def get_select(self, args) -> str:
    """
    get the select from the sender's tooltip
    """
    select = None
    slots = args.sender.slots.get("default")
    if slots:
        children = slots.children
        if len(children) > 0:
            tooltip = children[0]
            if isinstance(tooltip, Tooltip):
                title = tooltip.text
                select = title.replace("select all", "").strip()
    return select

onGenerateButtonClick(_msg) async

react on the generate button having been clicked

Source code in yprinciple/gengrid.py
205
206
207
208
209
210
211
212
213
async def onGenerateButtonClick(self, _msg):
    """
    react on the generate button having been clicked
    """
    cellsToGen = self.getCheckedYpCells()
    total = len(cellsToGen)
    ui.notify(f"running {total} generator tasks")
    self.resetProgress("generating", total)
    await run.io_bound(self.generateCheckedCells, cellsToGen)

onParentCheckboxClick(args) async

a ypCell checkbox has been clicked for a ypCell that has subCells

Source code in yprinciple/gengrid.py
287
288
289
290
291
292
293
294
295
296
async def onParentCheckboxClick(self, args):
    """
    a ypCell checkbox has been clicked for a ypCell that has subCells
    """
    # get the parent checkbox
    checkbox = args.sender
    checked = args.value
    # lookup the ypCell
    ypCell = self.ypcell_by_id[checkbox.id]
    self.checkSubCells(ypCell, checked)

on_select_all(args) async

react on "select all" being clicked

Source code in yprinciple/gengrid.py
233
234
235
236
237
238
239
240
241
242
243
async def on_select_all(self, args):
    """
    react on "select all" being clicked
    """
    try:
        checked = args.value
        for checkbox_row in self.checkboxes.values():
            self.check_row(checkbox_row, checked)
    except BaseException as ex:
        self.solution.handle_exception(ex)
    pass

on_select_column(args) async

react on "select all " for a column being clicked

Source code in yprinciple/gengrid.py
260
261
262
263
264
265
266
267
268
269
270
271
272
async def on_select_column(self, args):
    """
    react on "select all " for a column being clicked
    """
    try:
        checked = args.value
        target_name = self.get_select(args)
        if target_name:
            for checkboxes in self.checkboxes.values():
                checkbox, ypCell = checkboxes[target_name]
                self.check_ypcell_box(checkbox, ypCell, checked)
    except BaseException as ex:
        self.solution.handle_exception(ex)

on_select_row(args) async

react on "select all " for a row being clicked

Source code in yprinciple/gengrid.py
274
275
276
277
278
279
280
281
282
283
284
285
async def on_select_row(self, args):
    """
    react on "select all " for a row being clicked
    """
    try:
        checked = args.value
        topic_name = self.get_select(args)
        if topic_name:
            checkbox_row = self.checkboxes[topic_name]
            self.check_row(checkbox_row, checked)
    except BaseException as ex:
        self.solution.handle_exception(ex)

set_hide_show_status_of_cell_debug_msg(hidden=False)

Sets the hidden status of all cell debug messages Args: hidden: If True hide debug messages else show them

Source code in yprinciple/gengrid.py
490
491
492
493
494
495
496
497
498
499
500
501
502
def set_hide_show_status_of_cell_debug_msg(self, hidden: bool = False):
    """
    Sets the hidden status of all cell debug messages
    Args:
        hidden: If True hide debug messages else show them
    """
    try:
        self.cell_hide_size_info = hidden
        for div in self.cell_debug_msg_divs:
            div.visible = not hidden
        self.grid.update()
    except Exception as ex:
        self.solution.handle_exception(ex)

setup_styles()

setup the styles for the ui

Source code in yprinciple/gengrid.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def setup_styles(self):
    """
    setup the styles for the ui
    """
    self.header_classes = "text-center"
    # centering
    self.center_classes = "flex flex-col items-center justify-center"
    # see https://www.materialpalette.com/indigo/indigo
    # and https://github.com/WolfgangFahl/nicegui_widgets/blob/main/ngwidgets/color_schema.py
    # light primary color
    self.header_background = "#c5cae9"
    #
    self.light_header_background = "#f5f5f5"
    self.bs_secondary = "#6c757d"
    self.header_style = (
        f"font-size: 1.0rem;background-color: {self.header_background}"
    )
    self.light_header_style = f"background-color: {self.light_header_background}"

setup_target_header_row()

setup the header row

Source code in yprinciple/gengrid.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def setup_target_header_row(self):
    """
    setup the header row
    """
    with self.grid:
        self.generateButton = ui.button(
            icon="play_circle", on_click=self.onGenerateButtonClick
        )
    self.targets_column_header = self.add_header_cell("Target")
    for target in self.displayTargets():
        target_header_cell = self.add_header_cell(target.name)
        with target_header_cell:
            ui.icon(target.icon_name, size=self.iconSize, color=self.bs_secondary)
            # <i class="mdi mdi-archive" style="color: rgb(108, 117, 125); font-size: 32px;"></i>
            # markup+=f"""<i class="mdi mdi-{target.icon_name}" style="color: {self.bs_secondary};font-size:{self.iconSize};"></i>"""
            # <i class="q-icon notranslate material-icons" aria-hidden="true" role="presentation" id="c48">archive</i>
            # markup+=f"""<i class="q-icon notranslate material-icons" aria-hidden="true" role="presentation">{target.icon_name}</i>"""
            pass

setup_topic_header_row()

setup the second header row

Source code in yprinciple/gengrid.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def setup_topic_header_row(self):
    """
    setup the second header row
    """
    with self.grid:
        self.topics_column_header = (
            ui.html().classes(self.header_classes).style(self.header_style)
        )
        self.topics_column_header.content = "<strong>Topics</strong>"
    self.header_checkboxes = {}
    self.header_checkboxes["all"] = self.create_simple_checkbox(
        parent=self.grid,
        label_text="↘",
        title="select all",
        classes=self.center_classes,
        on_change=self.on_select_all,
    )
    for target in self.displayTargets():
        self.header_checkboxes[target.name] = self.create_simple_checkbox(
            parent=self.grid,
            label_text="↓",
            title=f"select all {target.name}",
            classes=self.center_classes,
            on_change=self.on_select_column,
        )

setup_ui()

setup the user interface

Source code in yprinciple/gengrid.py
52
53
54
55
56
57
58
59
60
61
62
def setup_ui(self):
    """
    setup the user interface
    """
    with self.parent:
        target_columns = len(self.displayTargets())
        # two more for the Topics and check box columns
        target_columns += 2
        self.grid = ui.grid(columns=target_columns).classes("w-full gap-0")
        self.setup_target_header_row()
        self.setup_topic_header_row()

updateProgress()

update the progress

Source code in yprinciple/gengrid.py
428
429
430
431
432
433
def updateProgress(self):
    """
    update the progress
    """
    self.solution.progressBar.update(1)
    self.grid.update()

profiler

Created on 2022-11-18

@author: wf

Profiler

simple profiler

Source code in yprinciple/profiler.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Profiler:
    """
    simple profiler
    """

    def __init__(self, msg, profile=True):
        """
        construct me with the given msg and profile active flag

        Args:
            msg(str): the message to show if profiling is active
            profile(bool): True if messages should be shown
        """
        self.msg = msg
        self.profile = profile
        self.starttime = time.time()
        if profile:
            print(f"Starting {msg} ...")

    def time(self, extraMsg=""):
        """
        time the action and print if profile is active
        """
        elapsed = time.time() - self.starttime
        if self.profile:
            print(f"{self.msg}{extraMsg} took {elapsed:5.1f} s")
        return elapsed

__init__(msg, profile=True)

construct me with the given msg and profile active flag

Parameters:

Name Type Description Default
msg(str)

the message to show if profiling is active

required
profile(bool)

True if messages should be shown

required
Source code in yprinciple/profiler.py
15
16
17
18
19
20
21
22
23
24
25
26
27
def __init__(self, msg, profile=True):
    """
    construct me with the given msg and profile active flag

    Args:
        msg(str): the message to show if profiling is active
        profile(bool): True if messages should be shown
    """
    self.msg = msg
    self.profile = profile
    self.starttime = time.time()
    if profile:
        print(f"Starting {msg} ...")

time(extraMsg='')

time the action and print if profile is active

Source code in yprinciple/profiler.py
29
30
31
32
33
34
35
36
def time(self, extraMsg=""):
    """
    time the action and print if profile is active
    """
    elapsed = time.time() - self.starttime
    if self.profile:
        print(f"{self.msg}{extraMsg} took {elapsed:5.1f} s")
    return elapsed

smw_targets

Created on 2022-11-26

@author: wf

CategoryTarget

Bases: SMWTarget

the target to generate "Category" pages

see https://wiki.bitplan.com/index.php/SiDIFTemplates#category

Source code in yprinciple/smw_targets.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
class CategoryTarget(SMWTarget):
    """
    the target to generate "Category" pages

    see https://wiki.bitplan.com/index.php/SiDIFTemplates#category
    """

    def generate(self, topic: Topic) -> str:
        """
        generate a category page for the given topic

        e.g. https://wiki.bitplan.com/index.php/Category:Topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#category

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""__NOTOC__
{{{{#ask: [[Topic name::{topic.name}]] | ?Topic wikiDocumentation= | mainlabel=-}}}}
{topic.getPluralName()} may be added and edited with the form [[Form:{topic.name}]]
* [[List of {topic.getPluralName()}]]
<div class="toccolours mw-collapsible mw-collapsed" style="width:1024px">
{topic.name} {self.i18n("description")}
<div class="mw-collapsible-content">
{self.uml("uml",topic)}
* [[Help:{topic.name}]]
* [[Concept:{topic.name}]]
* [[:Template:{topic.name}]]
* [[:Form:{topic.name}]]
=== Properties ===
"""
        for prop in topic.properties.values():
            markup += f"* [[Property:{topic.name} {prop.name}]]\n"
        markup += """</div>
</div>
"""
        if hasattr(topic, "extends") and topic.extends:
            markup+=f"""[[Category:{topic.extends}]]\n"""
        return markup

generate(topic)

generate a category page for the given topic

e.g. https://wiki.bitplan.com/index.php/Category:Topic

see https://wiki.bitplan.com/index.php/SiDIFTemplates#category

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
    def generate(self, topic: Topic) -> str:
        """
        generate a category page for the given topic

        e.g. https://wiki.bitplan.com/index.php/Category:Topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#category

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""__NOTOC__
{{{{#ask: [[Topic name::{topic.name}]] | ?Topic wikiDocumentation= | mainlabel=-}}}}
{topic.getPluralName()} may be added and edited with the form [[Form:{topic.name}]]
* [[List of {topic.getPluralName()}]]
<div class="toccolours mw-collapsible mw-collapsed" style="width:1024px">
{topic.name} {self.i18n("description")}
<div class="mw-collapsible-content">
{self.uml("uml",topic)}
* [[Help:{topic.name}]]
* [[Concept:{topic.name}]]
* [[:Template:{topic.name}]]
* [[:Form:{topic.name}]]
=== Properties ===
"""
        for prop in topic.properties.values():
            markup += f"* [[Property:{topic.name} {prop.name}]]\n"
        markup += """</div>
</div>
"""
        if hasattr(topic, "extends") and topic.extends:
            markup+=f"""[[Category:{topic.extends}]]\n"""
        return markup

ConceptTarget

Bases: SMWTarget

the target to generate "Concept" pages

see https://wiki.bitplan.com/index.php/SiDIFTemplates#concept

Source code in yprinciple/smw_targets.py
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
class ConceptTarget(SMWTarget):
    """
    the target to generate "Concept" pages

    see https://wiki.bitplan.com/index.php/SiDIFTemplates#concept
    """

    def generate(self, topic: "Topic") -> str:
        """
        generate a result for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#concept

        Args:
            topic(Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        extends_markup=f" extends {topic.extends} " if hasattr(topic,"extends") else ""
        markup = f"""{{{{Topic
|name={topic.name}
|pluralName={topic.getPluralName()}
|extends={extends_markup}
|icon={topic.icon}
|iconUrl={topic.iconUrl}
|documentation={topic.documentation}
|wikiDocumentation={topic.wikiDocumentation}
|defaultstoremode={topic.defaultstoremode}
|listLimit={topic.getListLimit()}
|cargo={getattr(topic,"cargo","false")}
|context={topic.context}
|storemode=property
}}}}
{{{{Topic
|viewmode=masterdetail
|storemode=none
}}}}
{{{{#forminput:form=Property|button text=add Property}}}}
=== Documentation ===
{topic.wikiDocumentation}
{self.uml("uml",topic)}

{{{{#concept:[[isA::{topic.name}]]
|{topic.wikiDocumentation}
{self.seealso(topic)}
}}}}
[[Category:{topic.name}]]
"""
        return markup

generate(topic)

generate a result for the given topic

see https://wiki.bitplan.com/index.php/SiDIFTemplates#concept

Parameters:

Name Type Description Default
topic(Topic)

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
    def generate(self, topic: "Topic") -> str:
        """
        generate a result for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#concept

        Args:
            topic(Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        extends_markup=f" extends {topic.extends} " if hasattr(topic,"extends") else ""
        markup = f"""{{{{Topic
|name={topic.name}
|pluralName={topic.getPluralName()}
|extends={extends_markup}
|icon={topic.icon}
|iconUrl={topic.iconUrl}
|documentation={topic.documentation}
|wikiDocumentation={topic.wikiDocumentation}
|defaultstoremode={topic.defaultstoremode}
|listLimit={topic.getListLimit()}
|cargo={getattr(topic,"cargo","false")}
|context={topic.context}
|storemode=property
}}}}
{{{{Topic
|viewmode=masterdetail
|storemode=none
}}}}
{{{{#forminput:form=Property|button text=add Property}}}}
=== Documentation ===
{topic.wikiDocumentation}
{self.uml("uml",topic)}

{{{{#concept:[[isA::{topic.name}]]
|{topic.wikiDocumentation}
{self.seealso(topic)}
}}}}
[[Category:{topic.name}]]
"""
        return markup

FormTarget

Bases: SMWTarget

the target to generate "Form" pages e.g. https://wiki.bitplan.com/index.php/Form:Topic

see https://wiki.bitplan.com/index.php/SiDIFTemplates#form

Source code in yprinciple/smw_targets.py
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
class FormTarget(SMWTarget):
    """
    the target to generate "Form" pages
    e.g. https://wiki.bitplan.com/index.php/Form:Topic

    see https://wiki.bitplan.com/index.php/SiDIFTemplates#form

    """

    def formTemplate(self, topic: Topic, isMultiple: bool):
        """
        create the SMW pagefomrs markups for the given topic

        Args:
            topic (Topic): the topic to create the markup for
            isMultiple (bool): True if there are multiple values allowed
        """
        multiple = "|multiple" if isMultiple else ""
        markup = f"""<div id="wikiPreview" style="display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA;"></div>
{{{{{{section|{topic.name}|level=1|hidden}}}}}}
={topic.name}=
{{{{{{for template|{topic.name}{multiple}}}}}}}
{{| class="wikitable"
! colspan='2' | {topic.name}
|-
"""
        for prop in topic.propertiesByIndex():
            values_from_key = "values from="
            if prop.isLink:
                prop.values_from = f"{prop.topicLink.source}"
                prop.inputType = "dropdown"
                values_from_key = "values from concept="
                pass
            prop_type = getattr(prop, "type", "Text")
            markup += f"""! {prop.label}:
<!-- {prop_type} {prop.name} -->\n"""
            inputType = (
                f"|input type={prop.inputType}"
                if getattr(prop, "inputType", None)
                else ""
            )
            if "textarea" == getattr(prop, "inputType", None):
                inputType += "|editor=wikieditor"
            size = f"|size={prop.size}" if getattr(prop, "size", None) else ""
            mandatory = f"|mandatory" if getattr(prop, "mandatory", None) else ""
            uploadable = f"|uploadable" if getattr(prop, "uploadable", None) else ""
            size = f"|size={prop.size}" if getattr(prop, "size", None) else ""
            values_from = (
                f"|{values_from_key}{prop.values_from}"
                if getattr(prop, "values_from", None)
                else ""
            )
            defaultValue = (
                f"|default={prop.defaultValue}"
                if getattr(prop, "defaultValue", None)
                else ""
            )
            allowedValues = (
                f"|values={prop.allowedValues}"
                if getattr(prop, "allowedValues", None)
                else ""
            )
            markup += f"""|{{{{{{field|{prop.name}|property={topic.name} {prop.name}{inputType}{size}{mandatory}{uploadable}{values_from}{allowedValues}{defaultValue}}}}}}}
|-
"""
        markup += f"""|-
|}}
{{{{{{field|storemode|default={topic.defaultstoremode}|hidden}}}}}}
{{{{{{end template}}}}}}
<!-- {topic.name} -->
        """
        return markup

    def generate(self, topic: Topic) -> str:
        """
        generate the form page for the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        multiple = "subobject" == topic.defaultstoremode
        markup = f"""<noinclude>
This is the {self.profiWiki()}-Form for "{topic.name}".

Create a new {topic.name} by entering a new pagetitle for a {topic.name}
into the field below.

If you enter an existing {topic.name} pagetitle - you will edit the {topic.name}
with that pagetitle.
{{{{#forminput:form={topic.name}|values from concept={topic.name}}}}}

=== see also ===
{self.seealso(topic)}
</noinclude><includeonly>{self.formTemplate(topic,multiple)}

{{{{{{section|Freitext|level=1|hidden}}}}}}
=Freitext=
{{{{{{standard input|free text|rows=10}}}}}}

{{{{{{standard input|summary}}}}}}
{{{{{{standard input|changes}}}}}}

{{{{{{standard input|save}}}}}}
{{{{{{standard input|cancel}}}}}}
</includeonly>
        """
        return markup

formTemplate(topic, isMultiple)

create the SMW pagefomrs markups for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to create the markup for

required
isMultiple bool

True if there are multiple values allowed

required
Source code in yprinciple/smw_targets.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
    def formTemplate(self, topic: Topic, isMultiple: bool):
        """
        create the SMW pagefomrs markups for the given topic

        Args:
            topic (Topic): the topic to create the markup for
            isMultiple (bool): True if there are multiple values allowed
        """
        multiple = "|multiple" if isMultiple else ""
        markup = f"""<div id="wikiPreview" style="display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA;"></div>
{{{{{{section|{topic.name}|level=1|hidden}}}}}}
={topic.name}=
{{{{{{for template|{topic.name}{multiple}}}}}}}
{{| class="wikitable"
! colspan='2' | {topic.name}
|-
"""
        for prop in topic.propertiesByIndex():
            values_from_key = "values from="
            if prop.isLink:
                prop.values_from = f"{prop.topicLink.source}"
                prop.inputType = "dropdown"
                values_from_key = "values from concept="
                pass
            prop_type = getattr(prop, "type", "Text")
            markup += f"""! {prop.label}:
<!-- {prop_type} {prop.name} -->\n"""
            inputType = (
                f"|input type={prop.inputType}"
                if getattr(prop, "inputType", None)
                else ""
            )
            if "textarea" == getattr(prop, "inputType", None):
                inputType += "|editor=wikieditor"
            size = f"|size={prop.size}" if getattr(prop, "size", None) else ""
            mandatory = f"|mandatory" if getattr(prop, "mandatory", None) else ""
            uploadable = f"|uploadable" if getattr(prop, "uploadable", None) else ""
            size = f"|size={prop.size}" if getattr(prop, "size", None) else ""
            values_from = (
                f"|{values_from_key}{prop.values_from}"
                if getattr(prop, "values_from", None)
                else ""
            )
            defaultValue = (
                f"|default={prop.defaultValue}"
                if getattr(prop, "defaultValue", None)
                else ""
            )
            allowedValues = (
                f"|values={prop.allowedValues}"
                if getattr(prop, "allowedValues", None)
                else ""
            )
            markup += f"""|{{{{{{field|{prop.name}|property={topic.name} {prop.name}{inputType}{size}{mandatory}{uploadable}{values_from}{allowedValues}{defaultValue}}}}}}}
|-
"""
        markup += f"""|-
|}}
{{{{{{field|storemode|default={topic.defaultstoremode}|hidden}}}}}}
{{{{{{end template}}}}}}
<!-- {topic.name} -->
        """
        return markup

generate(topic)

generate the form page for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
    def generate(self, topic: Topic) -> str:
        """
        generate the form page for the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        multiple = "subobject" == topic.defaultstoremode
        markup = f"""<noinclude>
This is the {self.profiWiki()}-Form for "{topic.name}".

Create a new {topic.name} by entering a new pagetitle for a {topic.name}
into the field below.

If you enter an existing {topic.name} pagetitle - you will edit the {topic.name}
with that pagetitle.
{{{{#forminput:form={topic.name}|values from concept={topic.name}}}}}

=== see also ===
{self.seealso(topic)}
</noinclude><includeonly>{self.formTemplate(topic,multiple)}

{{{{{{section|Freitext|level=1|hidden}}}}}}
=Freitext=
{{{{{{standard input|free text|rows=10}}}}}}

{{{{{{standard input|summary}}}}}}
{{{{{{standard input|changes}}}}}}

{{{{{{standard input|save}}}}}}
{{{{{{standard input|cancel}}}}}}
</includeonly>
        """
        return markup

HelpTarget

Bases: SMWTarget

the help Target

Source code in yprinciple/smw_targets.py
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
class HelpTarget(SMWTarget):
    """
    the help Target
    """

    def generate(self, topic: "Topic") -> str:
        """
        generate a result for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#help

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""[[File:Help_Icon.png|right]]
== Help for {topic.name} ==
{self.topicHeader(topic)}
=== Documentation ===
{topic.wikiDocumentation}
=== Example {topic.pluralName} ===
{{{{#ask: [[Concept:{topic.name}]]
}}}}
=== Properties ===
{{{{#ask: [[Concept:Property]][[Property topic::Concept:{topic.name}]]
| ?Property documentation = documentation
| ?Property type = type
| ?Property name = name
| ?Property label = label
| ?Property allowedValues = allowedValues
| ?Property mandatory = mandatory
| ?Property uploadable = uploadable
|format=table
}}}}
{self.uml("uml",topic)}
{self.seealso(topic)}
[[Category:{topic.name}]]
"""
        return markup

generate(topic)

generate a result for the given topic

see https://wiki.bitplan.com/index.php/SiDIFTemplates#help

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    def generate(self, topic: "Topic") -> str:
        """
        generate a result for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#help

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""[[File:Help_Icon.png|right]]
== Help for {topic.name} ==
{self.topicHeader(topic)}
=== Documentation ===
{topic.wikiDocumentation}
=== Example {topic.pluralName} ===
{{{{#ask: [[Concept:{topic.name}]]
}}}}
=== Properties ===
{{{{#ask: [[Concept:Property]][[Property topic::Concept:{topic.name}]]
| ?Property documentation = documentation
| ?Property type = type
| ?Property name = name
| ?Property label = label
| ?Property allowedValues = allowedValues
| ?Property mandatory = mandatory
| ?Property uploadable = uploadable
|format=table
}}}}
{self.uml("uml",topic)}
{self.seealso(topic)}
[[Category:{topic.name}]]
"""
        return markup

ListOfTarget

Bases: SMWTarget

the target to generate "List of" pages e.g. https://wiki.bitplan.com/index.php/List_of_Topics

see https://wiki.bitplan.com/index.php/SiDIFTemplates#listof

Source code in yprinciple/smw_targets.py
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
class ListOfTarget(SMWTarget):
    """
    the target to generate "List of" pages
    e.g. https://wiki.bitplan.com/index.php/List_of_Topics

    see https://wiki.bitplan.com/index.php/SiDIFTemplates#listof

    """

    def getPageTitle(self, modelElement) -> str:
        pageTitle = f"List of {modelElement.pluralName}"
        return pageTitle

    def generate(self, topic: Topic) -> str:
        """
        generate the list of page for the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""__NOCACHE__
{self.topicHeader(topic)}
== {topic.getPluralName()} ==
{{{{#ask: [[Concept:{topic.name}]]|format=count}}}}
{{{{#forminput:form={topic.name}|button text=add {topic.name}}}}}
{topic.askQuery()}
[[:Category:{topic.name}]]
    """
        return markup

generate(topic)

generate the list of page for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
    def generate(self, topic: Topic) -> str:
        """
        generate the list of page for the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""__NOCACHE__
{self.topicHeader(topic)}
== {topic.getPluralName()} ==
{{{{#ask: [[Concept:{topic.name}]]|format=count}}}}
{{{{#forminput:form={topic.name}|button text=add {topic.name}}}}}
{topic.askQuery()}
[[:Category:{topic.name}]]
    """
        return markup

PropertyMultiTarget

Bases: SMWTarget

the Property Multi Target

Source code in yprinciple/smw_targets.py
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
class PropertyMultiTarget(SMWTarget):
    """
    the Property Multi Target
    """

    def addSubCells(self, ypCell: "ypcell.YpCell", topic: Topic, debug: bool = False):
        """
        add the subcells for the given ypCell and topic

        Args:
            ypCell: the ypCell
            topic (Topic): the topic to add subcells for
        """
        for prop in topic.properties.values():
            subCell = ypcell.YpCell(
                modelElement=prop, target=self.subTarget, debug=debug
            )
            ypCell.subCells[prop.name] = subCell

addSubCells(ypCell, topic, debug=False)

add the subcells for the given ypCell and topic

Parameters:

Name Type Description Default
ypCell YpCell

the ypCell

required
topic Topic

the topic to add subcells for

required
Source code in yprinciple/smw_targets.py
666
667
668
669
670
671
672
673
674
675
676
677
678
def addSubCells(self, ypCell: "ypcell.YpCell", topic: Topic, debug: bool = False):
    """
    add the subcells for the given ypCell and topic

    Args:
        ypCell: the ypCell
        topic (Topic): the topic to add subcells for
    """
    for prop in topic.properties.values():
        subCell = ypcell.YpCell(
            modelElement=prop, target=self.subTarget, debug=debug
        )
        ypCell.subCells[prop.name] = subCell

PropertyTarget

Bases: SMWTarget

the Property Target

Source code in yprinciple/smw_targets.py
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
class PropertyTarget(SMWTarget):
    """
    the Property Target
    """

    def getPageTitle(self, prop) -> str:
        """
        get the page title for the given property

        Args:
            prop: the Property to get the page title for

        Returns:
            str: the markup
        """
        pageTitle = f"{self.name}:{prop.topic} {prop.name}"
        return pageTitle

    def generate(self, prop: Property) -> str:
        """
        generate wiki markup for the given property

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#propertiesdefs

        Returns:
            str: the wiki markup for the given property
        """
        topic_name = prop.topic
        topicWithConcept = f"Concept:{topic_name}"
        markup = f"""{{{{Property
|name={prop.name}
|label={prop.label}
        """
        if hasattr(prop, "documentation"):
            markup += f"""|documentation={prop.documentation}\n"""
        prop_type = getattr(prop, "type", "Text")
        markup += f"""|type=Special:Types/{prop_type}
"""
        # @TODO read from metamodel
        for prop_name in [
            "index",
            "sortPos",
            "primaryKey",
            "mandatory",
            "namespace",
            "size",
            "uploadable",
            "defaultValue",
            "inputType",
            "allowedValues",
            "values_from",
            "formatterURI",
            "showInGrid",
            "isLink",
        ]:
            if hasattr(prop, prop_name):
                value = getattr(prop, prop_name, None)
                if value is not None:
                    markup += f"|{prop_name}={value}\n"
                    # e.g. |index={prop.index}
        markup += f"""|topic={(topicWithConcept)}
|storemode=prop
}}}}
* [[Has type::{prop.type}]]
"""
        if hasattr(prop, "formatterURI"):
            markup += f"""* External formatter uri: [[External formatter uri::{prop.formatterURI}]]
"""
        markup += f"""
This is a Property with type {{{{#show: {{{{FULLPAGENAMEE}}}} | ?Property type#- }}}}
"""
        return markup

generate(prop)

generate wiki markup for the given property

see https://wiki.bitplan.com/index.php/SiDIFTemplates#propertiesdefs

Returns:

Name Type Description
str str

the wiki markup for the given property

Source code in yprinciple/smw_targets.py
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
    def generate(self, prop: Property) -> str:
        """
        generate wiki markup for the given property

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#propertiesdefs

        Returns:
            str: the wiki markup for the given property
        """
        topic_name = prop.topic
        topicWithConcept = f"Concept:{topic_name}"
        markup = f"""{{{{Property
|name={prop.name}
|label={prop.label}
        """
        if hasattr(prop, "documentation"):
            markup += f"""|documentation={prop.documentation}\n"""
        prop_type = getattr(prop, "type", "Text")
        markup += f"""|type=Special:Types/{prop_type}
"""
        # @TODO read from metamodel
        for prop_name in [
            "index",
            "sortPos",
            "primaryKey",
            "mandatory",
            "namespace",
            "size",
            "uploadable",
            "defaultValue",
            "inputType",
            "allowedValues",
            "values_from",
            "formatterURI",
            "showInGrid",
            "isLink",
        ]:
            if hasattr(prop, prop_name):
                value = getattr(prop, prop_name, None)
                if value is not None:
                    markup += f"|{prop_name}={value}\n"
                    # e.g. |index={prop.index}
        markup += f"""|topic={(topicWithConcept)}
|storemode=prop
}}}}
* [[Has type::{prop.type}]]
"""
        if hasattr(prop, "formatterURI"):
            markup += f"""* External formatter uri: [[External formatter uri::{prop.formatterURI}]]
"""
        markup += f"""
This is a Property with type {{{{#show: {{{{FULLPAGENAMEE}}}} | ?Property type#- }}}}
"""
        return markup

getPageTitle(prop)

get the page title for the given property

Parameters:

Name Type Description Default
prop

the Property to get the page title for

required

Returns:

Name Type Description
str str

the markup

Source code in yprinciple/smw_targets.py
686
687
688
689
690
691
692
693
694
695
696
697
def getPageTitle(self, prop) -> str:
    """
    get the page title for the given property

    Args:
        prop: the Property to get the page title for

    Returns:
        str: the markup
    """
    pageTitle = f"{self.name}:{prop.topic} {prop.name}"
    return pageTitle

PythonTarget

Bases: SMWTarget

generator for Python Code for a Topic

Source code in yprinciple/smw_targets.py
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
class PythonTarget(SMWTarget):
    """
    generator for Python Code for a Topic
    """

    def pythonPropType(self, prop: Property) -> str:
        """
        get the python property type for the given Property
        """
        ptype = "str"
        typestr = prop.type
        typestr = typestr.replace("Types/", "")
        if typestr == "Boolean":
            ptype = "bool"
        elif typestr == "Number":
            ptype = "float"
        return ptype

    def generate(self, topic: "Topic") -> str:
        """
        generate python code for the given topic
        """
        markup = f'''from dataclasses import dataclass
from typing import Optional
import dacite
@dataclass
class {topic.name}:
    """
    {topic.documentation}
    """
    pageTitle:str
'''

        for prop in topic.propertiesByIndex():
            markup += f"""    {prop.name}:Optional[{self.pythonPropType(prop)}] # {getattr(prop,"documentation","")}\n"""
        markup += f'''
    @classmethod
    def askQuery(cls):
        """
        get the ask Query for {topic.name}

        Returns:
            str: the mediawiki markup for the ask query
        """
        ask="""{topic.askQuery(mainlabel="pageTitle",filterShowInGrid=False,listLimit=10000)}"""
        return ask

    @classmethod
    def fromDict(cls,data:dict):
        """
        create a {topic.name} from the given dict

        Args:
            data(dict): the dict to create the {topic.name} from

        Returns:
            {topic.name}: the freshly created {topic.name}
        """
        {topic.name.lower()}=dacite.from_dict(data_class=cls,data=data)
        return {topic.name.lower()}
        '''

        return markup

generate(topic)

generate python code for the given topic

Source code in yprinciple/smw_targets.py
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
    def generate(self, topic: "Topic") -> str:
        """
        generate python code for the given topic
        """
        markup = f'''from dataclasses import dataclass
from typing import Optional
import dacite
@dataclass
class {topic.name}:
    """
    {topic.documentation}
    """
    pageTitle:str
'''

        for prop in topic.propertiesByIndex():
            markup += f"""    {prop.name}:Optional[{self.pythonPropType(prop)}] # {getattr(prop,"documentation","")}\n"""
        markup += f'''
    @classmethod
    def askQuery(cls):
        """
        get the ask Query for {topic.name}

        Returns:
            str: the mediawiki markup for the ask query
        """
        ask="""{topic.askQuery(mainlabel="pageTitle",filterShowInGrid=False,listLimit=10000)}"""
        return ask

    @classmethod
    def fromDict(cls,data:dict):
        """
        create a {topic.name} from the given dict

        Args:
            data(dict): the dict to create the {topic.name} from

        Returns:
            {topic.name}: the freshly created {topic.name}
        """
        {topic.name.lower()}=dacite.from_dict(data_class=cls,data=data)
        return {topic.name.lower()}
        '''

        return markup

pythonPropType(prop)

get the python property type for the given Property

Source code in yprinciple/smw_targets.py
760
761
762
763
764
765
766
767
768
769
770
771
def pythonPropType(self, prop: Property) -> str:
    """
    get the python property type for the given Property
    """
    ptype = "str"
    typestr = prop.type
    typestr = typestr.replace("Types/", "")
    if typestr == "Boolean":
        ptype = "bool"
    elif typestr == "Number":
        ptype = "float"
    return ptype

SMWTarget

Bases: Target

a specialized generation target for Semantic MediaWiki

Source code in yprinciple/smw_targets.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class SMWTarget(Target):
    """
    a specialized generation target for Semantic MediaWiki
    """

    @classmethod
    def getSMWTargets(cls):
        """
        define the targets
        """
        targets = {
            "category": CategoryTarget("Category", "archive"),
            "concept": ConceptTarget("Concept", "extension"),
            "form": FormTarget("Form", "note_alt"),
            "help": HelpTarget("Help", "help_center"),
            "listOf": ListOfTarget("List of", "format_list_bulleted"),
            "template": TemplateTarget("Template", "description"),
            "properties": PropertyMultiTarget(
                "Properties", "local_parking", is_multi=True
            ),
            "property": PropertyTarget("Property", showInGrid=False),
            "python": PythonTarget("Python", "code"),
        }
        for target_key, target in targets.items():
            target.target_key = target_key
        targets["properties"].subTarget = targets["property"]
        return targets

    def i18n(self, text: str) -> str:
        """
        return the internationalized version of the given text

        Args:
            text (str): the text to internationalize

        Returns:
            str: the internationalized version of the text
        """
        # @TODO implement language support for non english wikis
        return text

    def topicHeader(self, topic: "Topic") -> str:
        """
        get the topic header for the given topic

        Args:
            topic (Topic): the topic to generate a header for

        Returns:
            str: the markup to be generated
        """
        markup = f"""{{{{#ask: [[Topic name::{topic.name}]]
|mainlabel=-
|?Topic icon = icon
|? = Topic
|?Topic name = name
|?Topic pluralName = pluralName
|?Topic documentation = documentation
}}}}
"""
        return markup

    def seealso(self, topic: Topic) -> str:
        """
        generate wiki markup to to show relevant links for a topic
        """
        markup = f"""see also
* [[List of {topic.pluralName}]]
* [[Help:{topic.name}]]
* [[Concept:{topic.name}]]
* [[:Category:{topic.name}]]
* [[:Template:{topic.name}]]
* [[:Form:{topic.name}]]
"""
        topicLinks = topic.targetTopicLinks.values()
        extends_topics=topic.get_extends_topics()
        if len(topicLinks)+len(extends_topics) > 0:
            markup += "related topics:\n" ""
            for extends_topic in extends_topics:
                markup += f"* [[Concept:{extends_topic.name}]]\n"
            for topicLink in topicLinks:
                markup += f"* [[Concept:{topicLink.targetTopic.name}]]\n"
        return markup

    def copyright(self) -> str:
        """
        get the copyright markup

        Returns:
            str: the copyright markup
        """
        currentYear = datetime.now().year
        markup = f"""<!--
  --     Copyright (C) 2015-{currentYear} BITPlan GmbH
  --
  --     Pater-Delp-Str. -- 1
  --     D-47877 -- Willich-Schiefbahn
  --
  --     http://www.bitplan.com
  --
  --
-->"""
        return markup

    def profiWiki(self) -> str:
        """
        markup for profiWiki
        """
        markup = (
            "[https://wiki.bitplan.com/index.php/ProfiWiki BITPlan Y-Prinzip ProfiWiki]"
        )
        return markup

    def bitplanumlci(self, fontSize: int = 12) -> str:
        """
        create plantuml skin params for BITPlan corporate identity

        Args:
            fontSize (int): the font size to use

        Returns:
            str: the wiki markup to be generated
        """
        currentYear = datetime.now().year
        markup = f"""' BITPlan Corporate identity skin params
' Copyright (c) 2015-{currentYear} BITPlan GmbH
' see https://wiki.bitplan.com/index.php/PlantUmlSkinParams#BITPlanCI
' skinparams generated by {Version.name}
"""
        skinparams = [
            "note",
            "component",
            "package",
            "usecase",
            "activity",
            "classAttribute",
            "interface",
            "class",
            "object",
        ]
        for skinparam in skinparams:
            markup += f"""skinparam {skinparam} {{
  BackGroundColor #FFFFFF
  FontSize {fontSize}
  ArrowColor #FF8000
  BorderColor #FF8000
  FontColor black
  FontName Technical
}}
"""
        markup += """hide Circle
' end of skinparams '"""
        return markup

    def plantUmlRelation(self, topicLink: TopicLink) -> str:
        """
        generate wiki markup for a TopicLink/relation

        Args:
            topicLink (TopicLink): the topicLink to generate the relation for

        Returns:
            str: the wiki markup to generate
        """
        sourceMany = "*" if topicLink.sourceMultiple else "1"
        targetMany = "*" if topicLink.targetMultiple else "1"
        markup = f"""{topicLink.source} "{topicLink.sourceRole} ({sourceMany})" -- "{topicLink.targetRole}({targetMany})" {topicLink.target}\n"""
        return markup

    def plantUmlClass(self,topic: "Topic")->str:
        """
          get the plantuml markup for the given topic

          Args:
            topic (Topic): the topic to generate uml for

          Returns:
            str: the plantuml markup to be generated

        """
        markup=""
        extends=getattr(topic, "extends",None)
        extends_markup=f" extends {extends} " if extends else ""
        # recursive inheritance
        if extends:
            extends_topic=topic.context_obj.lookupTopic(extends,purpose=extends_markup)
            if extends_topic:
                markup+=self.plantUmlClass(extends_topic)

        markup += f"""note as {topic.name}Note
{topic.documentation}
end note
class {topic.name}{extends_markup} {{
"""
        for prop in topic.properties.values():
            prop_type = getattr(prop, "type", "Text")
            markup += f"  {prop_type} {prop.name}\n"
        markup += f"""}}
{topic.name}Note .. {topic.name}
"""
        # Relations/Topic Links
        for topicLink in topic.sourceTopicLinks.values():
            markup += f"{self.plantUmlRelation(topicLink)}"
        for topicLink in topic.targetTopicLinks.values():
            markup += f"{self.plantUmlRelation(topicLink)}"
        return markup

    def uml(self, title: str, topic: "Topic", output_format: str = "svg") -> str:
        """
        get the full uml (plantuml) markup for  the given topic

        Args:
            topic (Topic): the topic to generate uml for
            output_format (str): the output format to use - default: svg

        Returns:
            str: the plantuml markup to be generated

        """
        currentYear = datetime.now().year

        markup = f"""=== {title} ===
<uml format='{output_format}'>
title {topic.name}
note as {topic.name}DiagramNote
Copyright (c) 2015-{currentYear} BITPlan GmbH
[[http://www.bitplan.com]]
end note
"""
        markup+=self.plantUmlClass(topic)
        markup += f"""{self.bitplanumlci(12)}
</uml>"""
        return markup

bitplanumlci(fontSize=12)

create plantuml skin params for BITPlan corporate identity

Parameters:

Name Type Description Default
fontSize int

the font size to use

12

Returns:

Name Type Description
str str

the wiki markup to be generated

Source code in yprinciple/smw_targets.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    def bitplanumlci(self, fontSize: int = 12) -> str:
        """
        create plantuml skin params for BITPlan corporate identity

        Args:
            fontSize (int): the font size to use

        Returns:
            str: the wiki markup to be generated
        """
        currentYear = datetime.now().year
        markup = f"""' BITPlan Corporate identity skin params
' Copyright (c) 2015-{currentYear} BITPlan GmbH
' see https://wiki.bitplan.com/index.php/PlantUmlSkinParams#BITPlanCI
' skinparams generated by {Version.name}
"""
        skinparams = [
            "note",
            "component",
            "package",
            "usecase",
            "activity",
            "classAttribute",
            "interface",
            "class",
            "object",
        ]
        for skinparam in skinparams:
            markup += f"""skinparam {skinparam} {{
  BackGroundColor #FFFFFF
  FontSize {fontSize}
  ArrowColor #FF8000
  BorderColor #FF8000
  FontColor black
  FontName Technical
}}
"""
        markup += """hide Circle
' end of skinparams '"""
        return markup

copyright()

get the copyright markup

Returns:

Name Type Description
str str

the copyright markup

Source code in yprinciple/smw_targets.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    def copyright(self) -> str:
        """
        get the copyright markup

        Returns:
            str: the copyright markup
        """
        currentYear = datetime.now().year
        markup = f"""<!--
  --     Copyright (C) 2015-{currentYear} BITPlan GmbH
  --
  --     Pater-Delp-Str. -- 1
  --     D-47877 -- Willich-Schiefbahn
  --
  --     http://www.bitplan.com
  --
  --
-->"""
        return markup

getSMWTargets() classmethod

define the targets

Source code in yprinciple/smw_targets.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@classmethod
def getSMWTargets(cls):
    """
    define the targets
    """
    targets = {
        "category": CategoryTarget("Category", "archive"),
        "concept": ConceptTarget("Concept", "extension"),
        "form": FormTarget("Form", "note_alt"),
        "help": HelpTarget("Help", "help_center"),
        "listOf": ListOfTarget("List of", "format_list_bulleted"),
        "template": TemplateTarget("Template", "description"),
        "properties": PropertyMultiTarget(
            "Properties", "local_parking", is_multi=True
        ),
        "property": PropertyTarget("Property", showInGrid=False),
        "python": PythonTarget("Python", "code"),
    }
    for target_key, target in targets.items():
        target.target_key = target_key
    targets["properties"].subTarget = targets["property"]
    return targets

i18n(text)

return the internationalized version of the given text

Parameters:

Name Type Description Default
text str

the text to internationalize

required

Returns:

Name Type Description
str str

the internationalized version of the text

Source code in yprinciple/smw_targets.py
44
45
46
47
48
49
50
51
52
53
54
55
def i18n(self, text: str) -> str:
    """
    return the internationalized version of the given text

    Args:
        text (str): the text to internationalize

    Returns:
        str: the internationalized version of the text
    """
    # @TODO implement language support for non english wikis
    return text

plantUmlClass(topic)

get the plantuml markup for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate uml for

required

Returns:

Name Type Description
str str

the plantuml markup to be generated

Source code in yprinciple/smw_targets.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    def plantUmlClass(self,topic: "Topic")->str:
        """
          get the plantuml markup for the given topic

          Args:
            topic (Topic): the topic to generate uml for

          Returns:
            str: the plantuml markup to be generated

        """
        markup=""
        extends=getattr(topic, "extends",None)
        extends_markup=f" extends {extends} " if extends else ""
        # recursive inheritance
        if extends:
            extends_topic=topic.context_obj.lookupTopic(extends,purpose=extends_markup)
            if extends_topic:
                markup+=self.plantUmlClass(extends_topic)

        markup += f"""note as {topic.name}Note
{topic.documentation}
end note
class {topic.name}{extends_markup} {{
"""
        for prop in topic.properties.values():
            prop_type = getattr(prop, "type", "Text")
            markup += f"  {prop_type} {prop.name}\n"
        markup += f"""}}
{topic.name}Note .. {topic.name}
"""
        # Relations/Topic Links
        for topicLink in topic.sourceTopicLinks.values():
            markup += f"{self.plantUmlRelation(topicLink)}"
        for topicLink in topic.targetTopicLinks.values():
            markup += f"{self.plantUmlRelation(topicLink)}"
        return markup

plantUmlRelation(topicLink)

generate wiki markup for a TopicLink/relation

Parameters:

Name Type Description Default
topicLink TopicLink

the topicLink to generate the relation for

required

Returns:

Name Type Description
str str

the wiki markup to generate

Source code in yprinciple/smw_targets.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def plantUmlRelation(self, topicLink: TopicLink) -> str:
    """
    generate wiki markup for a TopicLink/relation

    Args:
        topicLink (TopicLink): the topicLink to generate the relation for

    Returns:
        str: the wiki markup to generate
    """
    sourceMany = "*" if topicLink.sourceMultiple else "1"
    targetMany = "*" if topicLink.targetMultiple else "1"
    markup = f"""{topicLink.source} "{topicLink.sourceRole} ({sourceMany})" -- "{topicLink.targetRole}({targetMany})" {topicLink.target}\n"""
    return markup

profiWiki()

markup for profiWiki

Source code in yprinciple/smw_targets.py
120
121
122
123
124
125
126
127
def profiWiki(self) -> str:
    """
    markup for profiWiki
    """
    markup = (
        "[https://wiki.bitplan.com/index.php/ProfiWiki BITPlan Y-Prinzip ProfiWiki]"
    )
    return markup

seealso(topic)

generate wiki markup to to show relevant links for a topic

Source code in yprinciple/smw_targets.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    def seealso(self, topic: Topic) -> str:
        """
        generate wiki markup to to show relevant links for a topic
        """
        markup = f"""see also
* [[List of {topic.pluralName}]]
* [[Help:{topic.name}]]
* [[Concept:{topic.name}]]
* [[:Category:{topic.name}]]
* [[:Template:{topic.name}]]
* [[:Form:{topic.name}]]
"""
        topicLinks = topic.targetTopicLinks.values()
        extends_topics=topic.get_extends_topics()
        if len(topicLinks)+len(extends_topics) > 0:
            markup += "related topics:\n" ""
            for extends_topic in extends_topics:
                markup += f"* [[Concept:{extends_topic.name}]]\n"
            for topicLink in topicLinks:
                markup += f"* [[Concept:{topicLink.targetTopic.name}]]\n"
        return markup

topicHeader(topic)

get the topic header for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate a header for

required

Returns:

Name Type Description
str str

the markup to be generated

Source code in yprinciple/smw_targets.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    def topicHeader(self, topic: "Topic") -> str:
        """
        get the topic header for the given topic

        Args:
            topic (Topic): the topic to generate a header for

        Returns:
            str: the markup to be generated
        """
        markup = f"""{{{{#ask: [[Topic name::{topic.name}]]
|mainlabel=-
|?Topic icon = icon
|? = Topic
|?Topic name = name
|?Topic pluralName = pluralName
|?Topic documentation = documentation
}}}}
"""
        return markup

uml(title, topic, output_format='svg')

get the full uml (plantuml) markup for the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate uml for

required
output_format str

the output format to use - default: svg

'svg'

Returns:

Name Type Description
str str

the plantuml markup to be generated

Source code in yprinciple/smw_targets.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
    def uml(self, title: str, topic: "Topic", output_format: str = "svg") -> str:
        """
        get the full uml (plantuml) markup for  the given topic

        Args:
            topic (Topic): the topic to generate uml for
            output_format (str): the output format to use - default: svg

        Returns:
            str: the plantuml markup to be generated

        """
        currentYear = datetime.now().year

        markup = f"""=== {title} ===
<uml format='{output_format}'>
title {topic.name}
note as {topic.name}DiagramNote
Copyright (c) 2015-{currentYear} BITPlan GmbH
[[http://www.bitplan.com]]
end note
"""
        markup+=self.plantUmlClass(topic)
        markup += f"""{self.bitplanumlci(12)}
</uml>"""
        return markup

TemplateTarget

Bases: SMWTarget

the template Target

Source code in yprinciple/smw_targets.py
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
class TemplateTarget(SMWTarget):
    """
    the template Target
    """

    def generateTopicCall(self,topic:Topic)->str:
        """
        generate the markup for the template call of the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for
        """
        markup=f"""{{{{{topic.name}\n"""
        for prop in topic.properties.values():
            markup+=f"""|{prop.name}={{{{{{{prop.name}|}}}}}}\n"""
        markup+="""|storemode={{{storemode|}}}
|viewmode={{{viewmode|}}}
}}"""
        return markup

    def generate(self, topic: Topic) -> str:
        """
        generate a template for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#template

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""<noinclude>{self.copyright()}
This is the {self.profiWiki()}-Template for "{topic.name}".
=== see also ===
{self.seealso(topic)}
=== Usage ===
<pre>{{{{{topic.name}
"""
        all_properties=topic.get_all_properties()
        for prop in all_properties:
            markup += f"|{prop.name}=\n"
        markup += f"""|storemode=property or subobject or none"
}}}}
</pre>
[[Category:Template]]
</noinclude><includeonly>"""
        extends_topics=topic.get_extends_topics()
        for extends_topic in extends_topics:
            markup+=self.generateTopicCall(extends_topic)
        markup+=f"""{{{{#switch:{{{{{{storemode|}}}}}}
|none=
|subobject={{{{#subobject:-
|isA={topic.name}
"""
        for prop in topic.properties.values():
            markup += f"|{topic.name} {prop.name}={{{{{{{prop.name}|}}}}}}\n"
        markup += f"""}}}}
|#default={{{{#set:
|isA={topic.name}
"""
        for prop in topic.properties.values():
            # separator handling
            sep = ""
            if prop.isLink:
                tl = prop.topicLink
                if hasattr(tl, "separator"):
                    sep = f"|+sep={tl.separator}"
            markup += f"|{topic.name} {prop.name}={{{{{{{prop.name}|}}}}}}{sep}\n"
        markup += f"""}}}}\n"""  # end of #set
        markup += f"""}}}}\n"""  # end of #switch
        markup += f"""{{{{#switch:{{{{{{viewmode|}}}}}}"""
        markup += "|hidden="
        markup += "|masterdetail=\n"
        for topicLink in topic.sourceTopicLinks.values():
            if topicLink.targetTopic:
                markup += f"= {topicLink.targetRole} =\n"
                markup += f"{{{{#ask:[[Concept:{topicLink.targetTopic.name}]]"
                markup += f"[[{topicLink.targetTopic.name} {topicLink.sourceRole}::{{{{FULLPAGENAME}}}}]]\n"
                for prop in topicLink.targetTopic.propertiesByIndex():
                    markup += (
                        f"| ?{topicLink.targetTopic.name} {prop.name} = {prop.name}\n"
                    )
                    pass
                markup += f"}}}}"  # end #ask
                pass
        markup += "|#default="
        markup += f"""{{{{{{!}}}} class='wikitable'
! colspan='2' {{{{!}}}}{topic.name}
{{{{!}}}}-
{{{{#switch:{{{{{{storemode|}}}}}}|property=
! colspan='2' style='text-align:left' {{{{!}}}} {{{{Icon|name=edit|size=24}}}}{{{{Link|target=Special:FormEdit/{topic.name}/{{{{FULLPAGENAME}}}}|title=edit}}}}
{{{{!}}}}-
}}}}
"""
        for prop in topic.properties.values():
            # https://github.com/WolfgangFahl/py-yprinciple-gen/issues/13
            # show Links for external Identifiers in templates
            prop_type = getattr(prop, "type", "Text")
            if prop_type == "External identifier" or prop.isLink:
                link_markup = (
                    "→{{#show: {{FULLPAGENAME}}|" + f"?{topic.name} {prop.name}" + "}}"
                )
                pass
            elif prop_type == "Page":
                link_markup = f"→[[{{{{{{{prop.name}|}}}}}}]]"
            else:
                link_markup = ""
            markup += f"""![[Property:{topic.name} {prop.name}|{prop.name}]]
{{{{!}}}}&nbsp;{{{{#if:{{{{{{{prop.name}|}}}}}}|{{{{{{{prop.name}}}}}}}|}}}}{link_markup}
{{{{!}}}}-\n"""
        markup += f"{{{{!}}}}}}\n"  # end of table
        markup += f"""}}}}\n"""  # end of #switch viewmode

        if hasattr(topic, "defaultstoremode"):
            if topic.defaultstoremode == "property":
                markup += (
                    f"[[Category:{topic.name}]]{{{{#default_form:{topic.name}}}}}\n"
                )

        markup += """</includeonly>"""
        return markup

generate(topic)

generate a template for the given topic

see https://wiki.bitplan.com/index.php/SiDIFTemplates#template

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required

Returns:

Name Type Description
str str

the generated wiki markup

Source code in yprinciple/smw_targets.py
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
    def generate(self, topic: Topic) -> str:
        """
        generate a template for the given topic

        see https://wiki.bitplan.com/index.php/SiDIFTemplates#template

        Args:
            topic (Topic): the topic to generate wiki markup for

        Returns:
            str: the generated wiki markup
        """
        markup = f"""<noinclude>{self.copyright()}
This is the {self.profiWiki()}-Template for "{topic.name}".
=== see also ===
{self.seealso(topic)}
=== Usage ===
<pre>{{{{{topic.name}
"""
        all_properties=topic.get_all_properties()
        for prop in all_properties:
            markup += f"|{prop.name}=\n"
        markup += f"""|storemode=property or subobject or none"
}}}}
</pre>
[[Category:Template]]
</noinclude><includeonly>"""
        extends_topics=topic.get_extends_topics()
        for extends_topic in extends_topics:
            markup+=self.generateTopicCall(extends_topic)
        markup+=f"""{{{{#switch:{{{{{{storemode|}}}}}}
|none=
|subobject={{{{#subobject:-
|isA={topic.name}
"""
        for prop in topic.properties.values():
            markup += f"|{topic.name} {prop.name}={{{{{{{prop.name}|}}}}}}\n"
        markup += f"""}}}}
|#default={{{{#set:
|isA={topic.name}
"""
        for prop in topic.properties.values():
            # separator handling
            sep = ""
            if prop.isLink:
                tl = prop.topicLink
                if hasattr(tl, "separator"):
                    sep = f"|+sep={tl.separator}"
            markup += f"|{topic.name} {prop.name}={{{{{{{prop.name}|}}}}}}{sep}\n"
        markup += f"""}}}}\n"""  # end of #set
        markup += f"""}}}}\n"""  # end of #switch
        markup += f"""{{{{#switch:{{{{{{viewmode|}}}}}}"""
        markup += "|hidden="
        markup += "|masterdetail=\n"
        for topicLink in topic.sourceTopicLinks.values():
            if topicLink.targetTopic:
                markup += f"= {topicLink.targetRole} =\n"
                markup += f"{{{{#ask:[[Concept:{topicLink.targetTopic.name}]]"
                markup += f"[[{topicLink.targetTopic.name} {topicLink.sourceRole}::{{{{FULLPAGENAME}}}}]]\n"
                for prop in topicLink.targetTopic.propertiesByIndex():
                    markup += (
                        f"| ?{topicLink.targetTopic.name} {prop.name} = {prop.name}\n"
                    )
                    pass
                markup += f"}}}}"  # end #ask
                pass
        markup += "|#default="
        markup += f"""{{{{{{!}}}} class='wikitable'
! colspan='2' {{{{!}}}}{topic.name}
{{{{!}}}}-
{{{{#switch:{{{{{{storemode|}}}}}}|property=
! colspan='2' style='text-align:left' {{{{!}}}} {{{{Icon|name=edit|size=24}}}}{{{{Link|target=Special:FormEdit/{topic.name}/{{{{FULLPAGENAME}}}}|title=edit}}}}
{{{{!}}}}-
}}}}
"""
        for prop in topic.properties.values():
            # https://github.com/WolfgangFahl/py-yprinciple-gen/issues/13
            # show Links for external Identifiers in templates
            prop_type = getattr(prop, "type", "Text")
            if prop_type == "External identifier" or prop.isLink:
                link_markup = (
                    "→{{#show: {{FULLPAGENAME}}|" + f"?{topic.name} {prop.name}" + "}}"
                )
                pass
            elif prop_type == "Page":
                link_markup = f"→[[{{{{{{{prop.name}|}}}}}}]]"
            else:
                link_markup = ""
            markup += f"""![[Property:{topic.name} {prop.name}|{prop.name}]]
{{{{!}}}}&nbsp;{{{{#if:{{{{{{{prop.name}|}}}}}}|{{{{{{{prop.name}}}}}}}|}}}}{link_markup}
{{{{!}}}}-\n"""
        markup += f"{{{{!}}}}}}\n"  # end of table
        markup += f"""}}}}\n"""  # end of #switch viewmode

        if hasattr(topic, "defaultstoremode"):
            if topic.defaultstoremode == "property":
                markup += (
                    f"[[Category:{topic.name}]]{{{{#default_form:{topic.name}}}}}\n"
                )

        markup += """</includeonly>"""
        return markup

generateTopicCall(topic)

generate the markup for the template call of the given topic

Parameters:

Name Type Description Default
topic Topic

the topic to generate wiki markup for

required
Source code in yprinciple/smw_targets.py
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    def generateTopicCall(self,topic:Topic)->str:
        """
        generate the markup for the template call of the given topic

        Args:
            topic (Topic): the topic to generate wiki markup for
        """
        markup=f"""{{{{{topic.name}\n"""
        for prop in topic.properties.values():
            markup+=f"""|{prop.name}={{{{{{{prop.name}|}}}}}}\n"""
        markup+="""|storemode={{{storemode|}}}
|viewmode={{{viewmode|}}}
}}"""
        return markup

target

Created on 2022-11-25

@author: wf

Target

a generator Target on the technical side of the Y-Principle

Source code in yprinciple/target.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Target:
    """
    a generator Target on the technical side of the Y-Principle
    """

    def __init__(
        self,
        name: str,
        icon_name: str = "bullseye",
        is_multi: bool = False,
        is_subtarget: bool = False,
        showInGrid: bool = True,
    ):
        """
        constructor
        name (str): the name of the target
        icon_name (str): the icon_name of the target
        is_multi(bool): if True this target creates a list of results (has subtargets)
        showInGrid (bool): if True this target is to be shown in the generator Grid

        """
        self.name = name
        self.icon_name = icon_name
        self.is_multi = is_multi
        self.is_subtarget = is_subtarget
        self.showInGrid = showInGrid
        self.subTarget = None

    def getLabelText(self, modelElement) -> str:
        return self.getPageTitle(modelElement)

    def getPageTitle(self, modelElement) -> str:
        pageTitle = f"{self.name}:{modelElement.name}"
        return pageTitle

    def getFileName(self, modelElement, purpose: str, fixcolon: bool = False) -> str:
        """
        get the filename for the given modelElement and purpose

        Args:
            modelElement:
            purpose (str): the purpose e.g. Help/Category ...

        Returns:
            str: a file name
        """
        prefix = self.getPageTitle(modelElement)
        # workaround for macos
        # https://apple.stackexchange.com/questions/173529/when-did-the-colon-character-become-an-allowed-character-in-the-filesystem
        if fixcolon:
            prefix = prefix.replace(":", "|")
        prefix = prefix.replace(" ", "_")
        filename = f"{prefix}{purpose}.wiki"
        return filename

    def generate(self, topic: "Topic") -> str:
        raise Exception(f"No generator available for target {self.name}")

__init__(name, icon_name='bullseye', is_multi=False, is_subtarget=False, showInGrid=True)

constructor name (str): the name of the target icon_name (str): the icon_name of the target is_multi(bool): if True this target creates a list of results (has subtargets) showInGrid (bool): if True this target is to be shown in the generator Grid

Source code in yprinciple/target.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def __init__(
    self,
    name: str,
    icon_name: str = "bullseye",
    is_multi: bool = False,
    is_subtarget: bool = False,
    showInGrid: bool = True,
):
    """
    constructor
    name (str): the name of the target
    icon_name (str): the icon_name of the target
    is_multi(bool): if True this target creates a list of results (has subtargets)
    showInGrid (bool): if True this target is to be shown in the generator Grid

    """
    self.name = name
    self.icon_name = icon_name
    self.is_multi = is_multi
    self.is_subtarget = is_subtarget
    self.showInGrid = showInGrid
    self.subTarget = None

getFileName(modelElement, purpose, fixcolon=False)

get the filename for the given modelElement and purpose

Parameters:

Name Type Description Default
modelElement
required
purpose str

the purpose e.g. Help/Category ...

required

Returns:

Name Type Description
str str

a file name

Source code in yprinciple/target.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def getFileName(self, modelElement, purpose: str, fixcolon: bool = False) -> str:
    """
    get the filename for the given modelElement and purpose

    Args:
        modelElement:
        purpose (str): the purpose e.g. Help/Category ...

    Returns:
        str: a file name
    """
    prefix = self.getPageTitle(modelElement)
    # workaround for macos
    # https://apple.stackexchange.com/questions/173529/when-did-the-colon-character-become-an-allowed-character-in-the-filesystem
    if fixcolon:
        prefix = prefix.replace(":", "|")
    prefix = prefix.replace(" ", "_")
    filename = f"{prefix}{purpose}.wiki"
    return filename

version

Created on 2022-04-01

@author: wf

Version dataclass

Bases: object

Version handling for pysotsog

Source code in yprinciple/version.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@dataclass
class Version(object):
    """
    Version handling for pysotsog
    """

    name = "py-yprinciple-gen"
    description = "python Y-Principle generator"
    version = yprinciple.__version__
    date = "2022-11-24"
    updated = "2024-08-05"
    authors = "Wolfgang Fahl"
    doc_url = "https://wiki.bitplan.com/index.php/Py-yprinciple-gen"
    chat_url = "https://github.com/WolfgangFahl/py-yprinciple-gen/discussions"
    cm_url = "https://github.com/WolfgangFahl/py-yprinciple-gen"
    license = f"""Copyright 2022-2024 contributors. All rights reserved.
  Licensed under the Apache License 2.0
  http://www.apache.org/licenses/LICENSE-2.0
  Distributed on an "AS IS" basis without warranties
  or conditions of any kind, either express or implied."""
    longDescription = f"""{name} version {version}
{description}
  Created by {authors} on {date} last updated {updated}"""

ypcell

Created on 2022-11-25

@author: wf

GenResult dataclass

generator Result

Source code in yprinciple/ypcell.py
20
21
22
23
24
25
26
27
@dataclass
class GenResult:
    """
    generator Result
    """

    # markup for new page
    markup: str

MwGenResult dataclass

Bases: GenResult

Source code in yprinciple/ypcell.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@dataclass
class MwGenResult(GenResult):
    # changes made
    markup_diff: str
    # @TODO use correct typing for MwClient Page object (pywikibot compatible?)
    old_page: object
    new_page: object

    def getDiffUrl(self) -> typing.Union[str, None]:
        """
        get the diff url of the two pages (if any)

        Returns:
            str: the url of the diff
        """
        diff_url = None
        if self.old_page and self.new_page:
            oldid = self.old_page.revision
            newid = self.new_page.revision
            site = self.new_page.site
            diff_url = f"{site.scheme}://{site.host}{site.path}index.php?title={self.new_page.page_title}&type=revision&diff={newid}&oldid={oldid}"
            pass
        return diff_url

    def page_changed(self) -> bool:
        """
        Check if changes were applied to the new page
        Returns:
            bool: True if the content of the page changed otherwise False
        """
        old_revision_id = getattr(self.old_page, "revision", None)
        new_revision_id = getattr(self.new_page, "revision", None)
        return old_revision_id != new_revision_id

getDiffUrl()

get the diff url of the two pages (if any)

Returns:

Name Type Description
str Union[str, None]

the url of the diff

Source code in yprinciple/ypcell.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def getDiffUrl(self) -> typing.Union[str, None]:
    """
    get the diff url of the two pages (if any)

    Returns:
        str: the url of the diff
    """
    diff_url = None
    if self.old_page and self.new_page:
        oldid = self.old_page.revision
        newid = self.new_page.revision
        site = self.new_page.site
        diff_url = f"{site.scheme}://{site.host}{site.path}index.php?title={self.new_page.page_title}&type=revision&diff={newid}&oldid={oldid}"
        pass
    return diff_url

page_changed()

Check if changes were applied to the new page Returns: bool: True if the content of the page changed otherwise False

Source code in yprinciple/ypcell.py
54
55
56
57
58
59
60
61
62
def page_changed(self) -> bool:
    """
    Check if changes were applied to the new page
    Returns:
        bool: True if the content of the page changed otherwise False
    """
    old_revision_id = getattr(self.old_page, "revision", None)
    new_revision_id = getattr(self.new_page, "revision", None)
    return old_revision_id != new_revision_id

YpCell

a Y-Principle cell

Source code in yprinciple/ypcell.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
class YpCell:
    """
    a Y-Principle cell
    """

    def __init__(
        self, modelElement: MetaModelElement, target: Target, debug: bool = False
    ):
        """
        constructor

        Args:
            modelElement (modelElement): the modelElement to generate for
            target (Target): the target to generate for
            debug (bool): if True - enable debugging
        """
        self.modelElement = modelElement
        self.target = target
        self.smwAccess = None
        self.debug = debug
        self.subCells = {}
        self.ui_ready = False

    @classmethod
    def createYpCell(
        cls, target: Target, topic: "Topic", debug: bool = False
    ) -> "YpCell":
        """
        add a ypCell for the given target and topic
        """
        ypCell = YpCell(modelElement=topic, target=target, debug=debug)
        if target.is_multi:
            target.addSubCells(ypCell=ypCell, topic=topic, debug=debug)
        return ypCell

    def generateToFile(
        self, target_dir: str, dryRun: bool = True, withEditor: bool = False
    ) -> FileGenResult:
        """
        generate the given cell and store the result to a file in the given target directory

        Args:
            target_dir (str): path to the target directory
            dryRun (bool): if True do not push the result
            withEditor (bool): if True open Editor when in dry Run mode

        Returns:
            FileGenResult: the generated result
        """
        # ignore multi targets
        if self.target.is_multi:
            return None
        markup = self.generateMarkup(withEditor=withEditor)
        path = None
        if not dryRun:
            filename = self.target.getFileName(self.modelElement, "")
            path = f"{target_dir}/{filename}"
            if not os.path.isdir(target_dir):
                os.makedirs(target_dir, exist_ok=True)
            with open(path, "w") as markup_file:
                markup_file.write(markup)
            pass
        genResult = FileGenResult(markup=markup, path=path)
        return genResult

    def generateMarkup(self, withEditor: bool = False):
        """
        generate the markup

        Args:
            withEditor (bool): if True open Editor when in dry Run mode

        Returns:
            str: the markup
        """
        markup = self.target.generate(self.modelElement)
        if withEditor:
            Editor.open_tmp_text(
                markup,
                file_name=self.target.getFileName(
                    self.modelElement, "wiki_gen", fixcolon=True
                ),
            )
        return markup

    def generateViaMwApi(
        self, smwAccess=None, dryRun: bool = True, withEditor: bool = False
    ) -> typing.Union[MwGenResult, None]:
        """
        generate the given cell and upload the result via the given
        Semantic MediaWiki Access

        Args:
            smwAccess (SMWAccess): the access to use
            dryRun (bool): if True do not push the result
            withEditor (bool): if True open Editor when in dry Run mode

        Returns:
            MwGenResult:
            None: if target is multi
        """
        markup_diff = ""
        # ignore multi targets
        if self.target.is_multi:
            return None
        markup = self.generateMarkup(withEditor=withEditor)
        old_page = self.getPage(smwAccess)
        new_page = None
        if self.pageText:
            markup_diff = WikiPush.getDiff(self.pageText, markup)
            if withEditor:
                Editor.open_tmp_text(
                    self.pageText,
                    file_name=self.target.getFileName(self.modelElement, "wiki_page"),
                )
                Editor.open_tmp_text(
                    markup_diff,
                    file_name=self.target.getFileName(self.modelElement, "wiki_diff"),
                )
        if not dryRun and self.page:
            self.page.edit(markup, f"modified by {Version.name} {Version.version}")
            # update status
            # @TODO make diff/status available see https://github.com/WolfgangFahl/py-yprinciple-gen/issues/15
            new_page = self.getPage(smwAccess)
        else:
            markup_diff = markup
        genResult = MwGenResult(
            markup=markup, markup_diff=markup_diff, old_page=old_page, new_page=new_page
        )
        return genResult

    def getLabelText(self) -> str:
        """
        get my label Text

        Returns:
            str: a label in the generator grid for my modelElement
        """
        return self.target.getLabelText(self.modelElement)

    def getPageTitle(self):
        """
        get the page title for my modelElement
        """
        return self.target.getPageTitle(self.modelElement)

    def getPage(self, smwAccess: SMWAccess) -> str:
        """
        get the pageText and status for the given smwAccess

        Args:
            smwAccess(SMWAccess): the Semantic Mediawiki access to use

        Returns:
            str: the wiki markup for this cell (if any)
        """
        self.smwAccess = smwAccess
        self.pageUrl = None
        self.page = None
        self.pageText = None
        self.pageTitle = None
        if self.target.name == "Python" or self.target.is_multi:
            self.status = "ⓘ"
            self.statusMsg = f"{self.status}"
        else:
            wikiClient = smwAccess.wikiClient
            self.pageTitle = self.getPageTitle()
            self.page = wikiClient.getPage(self.pageTitle)
            baseurl = wikiClient.wikiUser.getWikiUrl()
            # assumes simple PageTitle without special chars
            # see https://www.mediawiki.org/wiki/Manual:Page_title for the more comples
            # rules that could apply
            self.pageUrl = f"{baseurl}/index.php/{self.pageTitle}"
            if self.page.exists:
                self.pageText = self.page.text()
            else:
                self.pageText = None
            self.status = f"✅" if self.pageText else "❌"
            self.statusMsg = f"{len(self.pageText)}" if self.pageText else ""
        return self.page

__init__(modelElement, target, debug=False)

constructor

Parameters:

Name Type Description Default
modelElement modelElement

the modelElement to generate for

required
target Target

the target to generate for

required
debug bool

if True - enable debugging

False
Source code in yprinciple/ypcell.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def __init__(
    self, modelElement: MetaModelElement, target: Target, debug: bool = False
):
    """
    constructor

    Args:
        modelElement (modelElement): the modelElement to generate for
        target (Target): the target to generate for
        debug (bool): if True - enable debugging
    """
    self.modelElement = modelElement
    self.target = target
    self.smwAccess = None
    self.debug = debug
    self.subCells = {}
    self.ui_ready = False

createYpCell(target, topic, debug=False) classmethod

add a ypCell for the given target and topic

Source code in yprinciple/ypcell.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
@classmethod
def createYpCell(
    cls, target: Target, topic: "Topic", debug: bool = False
) -> "YpCell":
    """
    add a ypCell for the given target and topic
    """
    ypCell = YpCell(modelElement=topic, target=target, debug=debug)
    if target.is_multi:
        target.addSubCells(ypCell=ypCell, topic=topic, debug=debug)
    return ypCell

generateMarkup(withEditor=False)

generate the markup

Parameters:

Name Type Description Default
withEditor bool

if True open Editor when in dry Run mode

False

Returns:

Name Type Description
str

the markup

Source code in yprinciple/ypcell.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def generateMarkup(self, withEditor: bool = False):
    """
    generate the markup

    Args:
        withEditor (bool): if True open Editor when in dry Run mode

    Returns:
        str: the markup
    """
    markup = self.target.generate(self.modelElement)
    if withEditor:
        Editor.open_tmp_text(
            markup,
            file_name=self.target.getFileName(
                self.modelElement, "wiki_gen", fixcolon=True
            ),
        )
    return markup

generateToFile(target_dir, dryRun=True, withEditor=False)

generate the given cell and store the result to a file in the given target directory

Parameters:

Name Type Description Default
target_dir str

path to the target directory

required
dryRun bool

if True do not push the result

True
withEditor bool

if True open Editor when in dry Run mode

False

Returns:

Name Type Description
FileGenResult FileGenResult

the generated result

Source code in yprinciple/ypcell.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def generateToFile(
    self, target_dir: str, dryRun: bool = True, withEditor: bool = False
) -> FileGenResult:
    """
    generate the given cell and store the result to a file in the given target directory

    Args:
        target_dir (str): path to the target directory
        dryRun (bool): if True do not push the result
        withEditor (bool): if True open Editor when in dry Run mode

    Returns:
        FileGenResult: the generated result
    """
    # ignore multi targets
    if self.target.is_multi:
        return None
    markup = self.generateMarkup(withEditor=withEditor)
    path = None
    if not dryRun:
        filename = self.target.getFileName(self.modelElement, "")
        path = f"{target_dir}/{filename}"
        if not os.path.isdir(target_dir):
            os.makedirs(target_dir, exist_ok=True)
        with open(path, "w") as markup_file:
            markup_file.write(markup)
        pass
    genResult = FileGenResult(markup=markup, path=path)
    return genResult

generateViaMwApi(smwAccess=None, dryRun=True, withEditor=False)

generate the given cell and upload the result via the given Semantic MediaWiki Access

Parameters:

Name Type Description Default
smwAccess SMWAccess

the access to use

None
dryRun bool

if True do not push the result

True
withEditor bool

if True open Editor when in dry Run mode

False

Returns:

Name Type Description
MwGenResult Union[MwGenResult, None]
None Union[MwGenResult, None]

if target is multi

Source code in yprinciple/ypcell.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def generateViaMwApi(
    self, smwAccess=None, dryRun: bool = True, withEditor: bool = False
) -> typing.Union[MwGenResult, None]:
    """
    generate the given cell and upload the result via the given
    Semantic MediaWiki Access

    Args:
        smwAccess (SMWAccess): the access to use
        dryRun (bool): if True do not push the result
        withEditor (bool): if True open Editor when in dry Run mode

    Returns:
        MwGenResult:
        None: if target is multi
    """
    markup_diff = ""
    # ignore multi targets
    if self.target.is_multi:
        return None
    markup = self.generateMarkup(withEditor=withEditor)
    old_page = self.getPage(smwAccess)
    new_page = None
    if self.pageText:
        markup_diff = WikiPush.getDiff(self.pageText, markup)
        if withEditor:
            Editor.open_tmp_text(
                self.pageText,
                file_name=self.target.getFileName(self.modelElement, "wiki_page"),
            )
            Editor.open_tmp_text(
                markup_diff,
                file_name=self.target.getFileName(self.modelElement, "wiki_diff"),
            )
    if not dryRun and self.page:
        self.page.edit(markup, f"modified by {Version.name} {Version.version}")
        # update status
        # @TODO make diff/status available see https://github.com/WolfgangFahl/py-yprinciple-gen/issues/15
        new_page = self.getPage(smwAccess)
    else:
        markup_diff = markup
    genResult = MwGenResult(
        markup=markup, markup_diff=markup_diff, old_page=old_page, new_page=new_page
    )
    return genResult

getLabelText()

get my label Text

Returns:

Name Type Description
str str

a label in the generator grid for my modelElement

Source code in yprinciple/ypcell.py
201
202
203
204
205
206
207
208
def getLabelText(self) -> str:
    """
    get my label Text

    Returns:
        str: a label in the generator grid for my modelElement
    """
    return self.target.getLabelText(self.modelElement)

getPage(smwAccess)

get the pageText and status for the given smwAccess

Parameters:

Name Type Description Default
smwAccess(SMWAccess)

the Semantic Mediawiki access to use

required

Returns:

Name Type Description
str str

the wiki markup for this cell (if any)

Source code in yprinciple/ypcell.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
def getPage(self, smwAccess: SMWAccess) -> str:
    """
    get the pageText and status for the given smwAccess

    Args:
        smwAccess(SMWAccess): the Semantic Mediawiki access to use

    Returns:
        str: the wiki markup for this cell (if any)
    """
    self.smwAccess = smwAccess
    self.pageUrl = None
    self.page = None
    self.pageText = None
    self.pageTitle = None
    if self.target.name == "Python" or self.target.is_multi:
        self.status = "ⓘ"
        self.statusMsg = f"{self.status}"
    else:
        wikiClient = smwAccess.wikiClient
        self.pageTitle = self.getPageTitle()
        self.page = wikiClient.getPage(self.pageTitle)
        baseurl = wikiClient.wikiUser.getWikiUrl()
        # assumes simple PageTitle without special chars
        # see https://www.mediawiki.org/wiki/Manual:Page_title for the more comples
        # rules that could apply
        self.pageUrl = f"{baseurl}/index.php/{self.pageTitle}"
        if self.page.exists:
            self.pageText = self.page.text()
        else:
            self.pageText = None
        self.status = f"✅" if self.pageText else "❌"
        self.statusMsg = f"{len(self.pageText)}" if self.pageText else ""
    return self.page

getPageTitle()

get the page title for my modelElement

Source code in yprinciple/ypcell.py
210
211
212
213
214
def getPageTitle(self):
    """
    get the page title for my modelElement
    """
    return self.target.getPageTitle(self.modelElement)

ypgen

Created on 2022-11-24

@author: wf

YPGen

Bases: WebserverCmd

Y-Principle Generator Command Line

Source code in yprinciple/ypgen.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class YPGen(WebserverCmd):
    """
    Y-Principle Generator Command Line
    """

    def getArgParser(self, description: str, version_msg) -> ArgumentParser:
        """
        Setup command line argument parser

        Args:
            description (str): the description
            version_msg (str): the version message

        Returns:
            ArgumentParser: the argument parser
        """
        parser = super().getArgParser(description, version_msg)
        parser.add_argument(
            "--context",
            default="MetaModel",
            help="context to generate from [default: %(default)s]",
        )
        parser.add_argument(
            "--topics", nargs="*", help="list of topic names\n[default: %(default)s]"
        )
        parser.add_argument(
            "--targets", nargs="*", help="list of target names\n[default: %(default)s]"
        )
        parser.add_argument(
            "-ga",
            "--genViaMwApi",
            action="store_true",
            help="generate elements via Api",
        )
        parser.add_argument(
            "-gf", "--genToFile", action="store_true", help="generate elements to files"
        )
        parser.add_argument(
            "--targetPath",
            dest="targetPath",
            help="path for the files to be generated - uses wikibackup default path for wikiId if not specified",
            required=False,
        )
        parser.add_argument("--sidif", help="path to SiDIF input file")
        parser.add_argument(
            "-nd",
            "--noDry",
            action="store_true",
            help="switch off dry run [default: %(default)s]",
        )
        parser.add_argument(
            "--editor",
            action="store_true",
            help="open editor for results [default: %(default)s]",
        )
        parser.add_argument(
            "--push",
            action="store_true",
            help="push from source to target [default: %(default)s]",
        )
        parser.add_argument(
            "--wikiId",
            "--target",
            default="wiki",
            help="id of the wiki to generate for [default: %(default)s]",
        )
        parser.add_argument(
            "--source",
            default="profiwiki",
            help="id of the wiki to get concept and contexts (schemas) from [default: %(default)s]",
        )
        parser.add_argument(
            "--login",
            dest="login",
            action="store_true",
            help="login to source wiki for access permission",
        )
        parser.add_argument(
            "-f",
            "--force",
            dest="force",
            action="store_true",
            help="force to overwrite existing pages",
        )
        parser.add_argument("-q", "--quiet", help="not verbose [default: %(default)s]")
        return parser

    def handle_args(self):
        """
        work on the arguments
        """
        handled = super().handle_args()
        args = self.args
        if args.genToFile or args.genViaMwApi or args.push:
            gen = GeneratorAPI.fromArgs(args)
            if gen.error:
                print(f"{gen.errmsg}", file=sys.stderr)
                return 3
            dryRun = not args.noDry
            if args.genViaMwApi:
                gen.generateViaMwApi(
                    target_names=args.targets,
                    topic_names=args.topics,
                    dryRun=dryRun,
                    withEditor=args.editor,
                )
            if args.genToFile:
                gen.generateToFile(
                    target_dir=args.targetPath,
                    target_names=args.targets,
                    topic_names=args.topics,
                    dryRun=dryRun,
                    withEditor=args.editor,
                )
            if args.push:
                gen.push()
            handled = True
        return handled

getArgParser(description, version_msg)

Setup command line argument parser

Parameters:

Name Type Description Default
description str

the description

required
version_msg str

the version message

required

Returns:

Name Type Description
ArgumentParser ArgumentParser

the argument parser

Source code in yprinciple/ypgen.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def getArgParser(self, description: str, version_msg) -> ArgumentParser:
    """
    Setup command line argument parser

    Args:
        description (str): the description
        version_msg (str): the version message

    Returns:
        ArgumentParser: the argument parser
    """
    parser = super().getArgParser(description, version_msg)
    parser.add_argument(
        "--context",
        default="MetaModel",
        help="context to generate from [default: %(default)s]",
    )
    parser.add_argument(
        "--topics", nargs="*", help="list of topic names\n[default: %(default)s]"
    )
    parser.add_argument(
        "--targets", nargs="*", help="list of target names\n[default: %(default)s]"
    )
    parser.add_argument(
        "-ga",
        "--genViaMwApi",
        action="store_true",
        help="generate elements via Api",
    )
    parser.add_argument(
        "-gf", "--genToFile", action="store_true", help="generate elements to files"
    )
    parser.add_argument(
        "--targetPath",
        dest="targetPath",
        help="path for the files to be generated - uses wikibackup default path for wikiId if not specified",
        required=False,
    )
    parser.add_argument("--sidif", help="path to SiDIF input file")
    parser.add_argument(
        "-nd",
        "--noDry",
        action="store_true",
        help="switch off dry run [default: %(default)s]",
    )
    parser.add_argument(
        "--editor",
        action="store_true",
        help="open editor for results [default: %(default)s]",
    )
    parser.add_argument(
        "--push",
        action="store_true",
        help="push from source to target [default: %(default)s]",
    )
    parser.add_argument(
        "--wikiId",
        "--target",
        default="wiki",
        help="id of the wiki to generate for [default: %(default)s]",
    )
    parser.add_argument(
        "--source",
        default="profiwiki",
        help="id of the wiki to get concept and contexts (schemas) from [default: %(default)s]",
    )
    parser.add_argument(
        "--login",
        dest="login",
        action="store_true",
        help="login to source wiki for access permission",
    )
    parser.add_argument(
        "-f",
        "--force",
        dest="force",
        action="store_true",
        help="force to overwrite existing pages",
    )
    parser.add_argument("-q", "--quiet", help="not verbose [default: %(default)s]")
    return parser

handle_args()

work on the arguments

Source code in yprinciple/ypgen.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def handle_args(self):
    """
    work on the arguments
    """
    handled = super().handle_args()
    args = self.args
    if args.genToFile or args.genViaMwApi or args.push:
        gen = GeneratorAPI.fromArgs(args)
        if gen.error:
            print(f"{gen.errmsg}", file=sys.stderr)
            return 3
        dryRun = not args.noDry
        if args.genViaMwApi:
            gen.generateViaMwApi(
                target_names=args.targets,
                topic_names=args.topics,
                dryRun=dryRun,
                withEditor=args.editor,
            )
        if args.genToFile:
            gen.generateToFile(
                target_dir=args.targetPath,
                target_names=args.targets,
                topic_names=args.topics,
                dryRun=dryRun,
                withEditor=args.editor,
            )
        if args.push:
            gen.push()
        handled = True
    return handled

main(argv=None)

main call

Source code in yprinciple/ypgen.py
136
137
138
139
140
141
142
143
144
145
def main(argv: list = None):
    """
    main call
    """
    cmd = YPGen(
        config=YPGenServer.get_config(),
        webserver_cls=YPGenServer,
    )
    exit_code = cmd.cmd_main(argv)
    return exit_code

ypgenapp

Created on 2022-11-24

@author: wf

YPGenApp

Bases: InputWebSolution

Y-Principle Generator Web Application / Solution

Source code in yprinciple/ypgenapp.py
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
class YPGenApp(InputWebSolution):
    """
    Y-Principle Generator Web Application / Solution
    """

    def __init__(self, webserver: YPGenServer, client: Client):
        """
        Initializes the InputWebSolution instance with the webserver and client.

        Args:
            webserver (NiceGuiWebserver): The webserver instance this solution is part of.
            client (Client): The client interacting with this solution.
        """
        super().__init__(webserver, client)

    def clearErrors(self):
        """
        clear log entries
        """
        self.log_view.clear()

    def prepare_ui(self):
        """
        prepare the user interface
        """
        super().prepare_ui()
        args = self.args
        profile = Profiler("prepare_ui", profile=args.debug)
        self.wikiUsers = self.webserver.wikiUsers
        # see https://wiki.bitplan.com/index.php/Y-Prinzip#Example
        smw_profile = Profiler("get SMW targets", profile=args.debug)
        self.targets = SMWTarget.getSMWTargets()
        smw_profile.time()
        self.wikiId = args.wikiId
        self.context_name = args.context

        self.wikiLink = None
        self.mw_context = None
        self.mw_contexts = {}
        self.contextLink = None

        # states
        self.useSidif = True
        self.dryRun = True
        self.openEditor = False
        self.explainDepth = 0
        profile.time()

    def setWikiLink(self, url, text, tooltip):
        if self.wikiLink is not None:
            link = Link.create(url, text, tooltip)
            self.wikiLink.content = link

    def setGenApiFromArgs(self, args=None):
        """
        set the semantic MediaWiki
        """
        if args is None:
            args = self.args
        with self.content_div:
            ui.notify("preparing Generator")
        self.setGeneratorEnvironmentFromArgs(args)

    def setGeneratorEnvironmentFromArgs(self, args):
        """
        set my generator environment from the given command line arguments
        """
        gen_profile = Profiler("prepare gen API and mwcontext", profile=self.args.debug)
        self.genapi = GeneratorAPI.fromArgs(args)
        self.genapi.setWikiAndGetContexts(args)
        self.smwAccess = self.genapi.smwAccess
        wikiUser = self.smwAccess.wikiClient.wikiUser
        if wikiUser:
            self.setWikiLink(wikiUser.getWikiUrl(), args.wikiId, args.wikiId)
        self.setMWContext()
        gen_profile.time()

    def setMWContext(self):
        """
        set my context
        """
        self.mw_contexts = self.genapi.mw_contexts
        mw_context = self.mw_contexts.get(self.context_name, None)
        self.setContext(mw_context)

    def setContext(self, mw_context):
        """
        set the Context
        """
        self.mw_context = mw_context
        if self.contextLink is not None:
            if mw_context is not None:
                self.contextLink.title = (
                    f"{mw_context.context}({mw_context.since} at {mw_context.master}"
                )
                self.contextLink.text = f"{mw_context.wikiId}:{mw_context.context}"
                self.contextLink.href = mw_context.sidif_url()
            else:
                self.contextLink.title = "?"
                self.contextLink.text = "?"
                self.contextLink.href = (
                    "https://wiki.bitplan.com/index.php/Concept:Context"
                )

    async def async_showGenerateGrid(self):
        """
        run setup in background
        """
        await run.io_bound(self.show_GenerateGrid)

    def show_GenerateGrid(self):
        """
        show the grid for generating code
        """
        try:
            self.grid_container.clear()
            with self.grid_container:
                # start with a new generatorGrid
                self.generatorGrid = GeneratorGrid(
                    self.targets, parent=self.grid_container, solution=self
                )
            if self.useSidif:
                if self.mw_context is not None:
                    context, error, errMsg = Context.fromWikiContext(
                        self.mw_context, debug=self.args.debug, depth=self.explainDepth
                    )
                    if error is not None:
                        self.log_view.push(errMsg)
                    else:
                        self.generatorGrid.add_topic_rows(context)
        except Exception as ex:
            self.handle_exception(ex)

    async def onChangeLanguage(self, msg):
        """
        react on language being changed via Select control
        """
        self.language = msg.value

    async def onChangeWiki(self, e):
        """
        react on a the wiki being changed
        via a Select control
        """
        try:
            wikiId = e.value
            self.clearErrors()
            # change wikiUser
            self.args.wikiId = wikiId
            await run.io_bound(self.setGeneratorEnvironmentFromArgs, self.args)
            await self.update_context_select()
        except BaseException as ex:
            self.handle_exception(ex)

    async def onChangeContext(self, msg):
        """
        react on the context
        being changed via a Select control
        """
        try:
            self.clearErrors()
            self.context_name = msg.value
            self.mw_context = self.mw_contexts.get(self.context_name, None)
            self.setContext(self.mw_context)
            self.update_context_link()
            await self.async_showGenerateGrid()
        except BaseException as ex:
            self.handle_exception(ex)

    def addLanguageSelect(self):
        """
        add a language selector
        """
        self.languageSelect = self.createSelect(
            "Language", "en", a=self.colB1, change=self.onChangeLanguage
        )
        for language in self.getLanguages():
            lang = language[0]
            desc = language[1]
            desc = html.unescape(desc)
            self.languageSelect.add(self.jp.Option(value=lang, text=desc))

    def addWikiSelect(self):
        """
        add a wiki selector
        """
        if len(self.wikiUsers) > 0:
            self.wiki_select = self.add_select(
                title="wikiId",
                selection=sorted(self.wikiUsers),
                value=self.wikiId,
                on_change=self.onChangeWiki,
            )
            if self.mw_context is not None:
                self.wikiLink = ui.html()
                url = self.mw_context.wiki_url
                self.setWikiLink(url=url, text=self.wikiId, tooltip=self.wikiId)

    def onExplainDepthChange(self, msg):
        self.explainDepth = int(msg.value)

    def addExplainDepthSelect(self):
        self.explainDepthSelect = self.createSelect(
            "explain depth", value=0, change=self.onExplainDepthChange, a=self.colB1
        )
        for depth in range(17):
            self.explainDepthSelect.add(self.jp.Option(value=depth, text=str(depth)))

    def handleHideShowSizeInfo(self, msg):
        """
        handles switching visibility of size information
        """
        show_size = msg.sender.value
        self.generatorGrid.set_hide_show_status_of_cell_debug_msg(hidden=not show_size)

    def add_context_select(self):
        """
        add a selection of possible contexts for the given wiki
        """
        try:
            selection = list(self.mw_contexts.keys())
            self.context_select = self.add_select(
                "Context",
                selection=selection,
                value=self.context_name,
                on_change=self.onChangeContext,
            )
            self.context_link = ui.html()
            self.update_context_link()
        except BaseException as ex:
            self.handle_exception(ex)

    def update_context_link(self):
        """
        update the context link
        """
        if self.mw_context is not None:
            url = self.mw_context.sidif_url()
            link = Link.create(url, text=self.context_name)
            self.context_link.content = link
        else:
            self.context_link.content = "?"

        self.context_link.update()
        pass

    async def update_context_select(self):
        """
        react on update of context select
        """
        self.update_context_link()
        context_selection = list(self.mw_contexts.keys())
        self.context_select.options = context_selection
        if self.context_name in context_selection:
            self.context_select.value = self.context_name
        self.context_select.update()
        self.grid_container.clear()
        pass

    def configure_settings(self):
        """
        override settings
        """
        self.addLanguageSelect()
        self.addWikiSelect()
        self.addExplainDepthSelect()

    async def show_all(self):
        """
        show settings and generator grid
        """
        with self.content_div:
            self.content_div.clear()
            with ui.card() as self.settings_area:
                with ui.grid(columns=2):
                    self.addWikiSelect()
                    self.add_context_select()
            with ui.row() as self.button_bar:
                self.tool_button(
                    tooltip="reload",
                    icon="refresh",
                    handler=self.async_showGenerateGrid,
                )
                self.useSidifButton = ui.switch("use SiDIF").bind_value(
                    self, "useSidif"
                )
                self.dryRunButton = ui.switch("dry Run").bind_value(self, "dryRun")
                self.openEditorButton = ui.switch("open Editor").bind_value(
                    self, "openEditor"
                )
                self.hideShowSizeInfo = ui.switch("size info").on(
                    "click", self.handleHideShowSizeInfo
                )
            with ui.row() as self.progress_container:
                self.progressBar = NiceguiProgressbar(
                    total=100, desc="preparing", unit="steps"
                )
            with ui.row() as self.grid_container:
                pass

    async def load_home_page(self):
        """
        back ground preparation
        """
        with self.content_div:
            # show a spinner while loading
            ui.spinner()
        await run.io_bound(self.setGenApiFromArgs)
        self.content_div.clear()
        await self.show_all()

    async def home(self):
        """
        provide the main content / home page

        home page
        """

        def show():
            """
            show the ui
            """
            try:
                # run view
                background_tasks.create(self.load_home_page())
            except Exception as ex:
                self.handle_exception(ex)

        await self.setup_content_div(show)

__init__(webserver, client)

Initializes the InputWebSolution instance with the webserver and client.

Parameters:

Name Type Description Default
webserver NiceGuiWebserver

The webserver instance this solution is part of.

required
client Client

The client interacting with this solution.

required
Source code in yprinciple/ypgenapp.py
63
64
65
66
67
68
69
70
71
def __init__(self, webserver: YPGenServer, client: Client):
    """
    Initializes the InputWebSolution instance with the webserver and client.

    Args:
        webserver (NiceGuiWebserver): The webserver instance this solution is part of.
        client (Client): The client interacting with this solution.
    """
    super().__init__(webserver, client)

addLanguageSelect()

add a language selector

Source code in yprinciple/ypgenapp.py
227
228
229
230
231
232
233
234
235
236
237
238
def addLanguageSelect(self):
    """
    add a language selector
    """
    self.languageSelect = self.createSelect(
        "Language", "en", a=self.colB1, change=self.onChangeLanguage
    )
    for language in self.getLanguages():
        lang = language[0]
        desc = language[1]
        desc = html.unescape(desc)
        self.languageSelect.add(self.jp.Option(value=lang, text=desc))

addWikiSelect()

add a wiki selector

Source code in yprinciple/ypgenapp.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def addWikiSelect(self):
    """
    add a wiki selector
    """
    if len(self.wikiUsers) > 0:
        self.wiki_select = self.add_select(
            title="wikiId",
            selection=sorted(self.wikiUsers),
            value=self.wikiId,
            on_change=self.onChangeWiki,
        )
        if self.mw_context is not None:
            self.wikiLink = ui.html()
            url = self.mw_context.wiki_url
            self.setWikiLink(url=url, text=self.wikiId, tooltip=self.wikiId)

add_context_select()

add a selection of possible contexts for the given wiki

Source code in yprinciple/ypgenapp.py
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def add_context_select(self):
    """
    add a selection of possible contexts for the given wiki
    """
    try:
        selection = list(self.mw_contexts.keys())
        self.context_select = self.add_select(
            "Context",
            selection=selection,
            value=self.context_name,
            on_change=self.onChangeContext,
        )
        self.context_link = ui.html()
        self.update_context_link()
    except BaseException as ex:
        self.handle_exception(ex)

async_showGenerateGrid() async

run setup in background

Source code in yprinciple/ypgenapp.py
162
163
164
165
166
async def async_showGenerateGrid(self):
    """
    run setup in background
    """
    await run.io_bound(self.show_GenerateGrid)

clearErrors()

clear log entries

Source code in yprinciple/ypgenapp.py
73
74
75
76
77
def clearErrors(self):
    """
    clear log entries
    """
    self.log_view.clear()

configure_settings()

override settings

Source code in yprinciple/ypgenapp.py
317
318
319
320
321
322
323
def configure_settings(self):
    """
    override settings
    """
    self.addLanguageSelect()
    self.addWikiSelect()
    self.addExplainDepthSelect()

handleHideShowSizeInfo(msg)

handles switching visibility of size information

Source code in yprinciple/ypgenapp.py
266
267
268
269
270
271
def handleHideShowSizeInfo(self, msg):
    """
    handles switching visibility of size information
    """
    show_size = msg.sender.value
    self.generatorGrid.set_hide_show_status_of_cell_debug_msg(hidden=not show_size)

home() async

provide the main content / home page

home page

Source code in yprinciple/ypgenapp.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
async def home(self):
    """
    provide the main content / home page

    home page
    """

    def show():
        """
        show the ui
        """
        try:
            # run view
            background_tasks.create(self.load_home_page())
        except Exception as ex:
            self.handle_exception(ex)

    await self.setup_content_div(show)

load_home_page() async

back ground preparation

Source code in yprinciple/ypgenapp.py
358
359
360
361
362
363
364
365
366
367
async def load_home_page(self):
    """
    back ground preparation
    """
    with self.content_div:
        # show a spinner while loading
        ui.spinner()
    await run.io_bound(self.setGenApiFromArgs)
    self.content_div.clear()
    await self.show_all()

onChangeContext(msg) async

react on the context being changed via a Select control

Source code in yprinciple/ypgenapp.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
async def onChangeContext(self, msg):
    """
    react on the context
    being changed via a Select control
    """
    try:
        self.clearErrors()
        self.context_name = msg.value
        self.mw_context = self.mw_contexts.get(self.context_name, None)
        self.setContext(self.mw_context)
        self.update_context_link()
        await self.async_showGenerateGrid()
    except BaseException as ex:
        self.handle_exception(ex)

onChangeLanguage(msg) async

react on language being changed via Select control

Source code in yprinciple/ypgenapp.py
191
192
193
194
195
async def onChangeLanguage(self, msg):
    """
    react on language being changed via Select control
    """
    self.language = msg.value

onChangeWiki(e) async

react on a the wiki being changed via a Select control

Source code in yprinciple/ypgenapp.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
async def onChangeWiki(self, e):
    """
    react on a the wiki being changed
    via a Select control
    """
    try:
        wikiId = e.value
        self.clearErrors()
        # change wikiUser
        self.args.wikiId = wikiId
        await run.io_bound(self.setGeneratorEnvironmentFromArgs, self.args)
        await self.update_context_select()
    except BaseException as ex:
        self.handle_exception(ex)

prepare_ui()

prepare the user interface

Source code in yprinciple/ypgenapp.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def prepare_ui(self):
    """
    prepare the user interface
    """
    super().prepare_ui()
    args = self.args
    profile = Profiler("prepare_ui", profile=args.debug)
    self.wikiUsers = self.webserver.wikiUsers
    # see https://wiki.bitplan.com/index.php/Y-Prinzip#Example
    smw_profile = Profiler("get SMW targets", profile=args.debug)
    self.targets = SMWTarget.getSMWTargets()
    smw_profile.time()
    self.wikiId = args.wikiId
    self.context_name = args.context

    self.wikiLink = None
    self.mw_context = None
    self.mw_contexts = {}
    self.contextLink = None

    # states
    self.useSidif = True
    self.dryRun = True
    self.openEditor = False
    self.explainDepth = 0
    profile.time()

setContext(mw_context)

set the Context

Source code in yprinciple/ypgenapp.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def setContext(self, mw_context):
    """
    set the Context
    """
    self.mw_context = mw_context
    if self.contextLink is not None:
        if mw_context is not None:
            self.contextLink.title = (
                f"{mw_context.context}({mw_context.since} at {mw_context.master}"
            )
            self.contextLink.text = f"{mw_context.wikiId}:{mw_context.context}"
            self.contextLink.href = mw_context.sidif_url()
        else:
            self.contextLink.title = "?"
            self.contextLink.text = "?"
            self.contextLink.href = (
                "https://wiki.bitplan.com/index.php/Concept:Context"
            )

setGenApiFromArgs(args=None)

set the semantic MediaWiki

Source code in yprinciple/ypgenapp.py
111
112
113
114
115
116
117
118
119
def setGenApiFromArgs(self, args=None):
    """
    set the semantic MediaWiki
    """
    if args is None:
        args = self.args
    with self.content_div:
        ui.notify("preparing Generator")
    self.setGeneratorEnvironmentFromArgs(args)

setGeneratorEnvironmentFromArgs(args)

set my generator environment from the given command line arguments

Source code in yprinciple/ypgenapp.py
121
122
123
124
125
126
127
128
129
130
131
132
133
def setGeneratorEnvironmentFromArgs(self, args):
    """
    set my generator environment from the given command line arguments
    """
    gen_profile = Profiler("prepare gen API and mwcontext", profile=self.args.debug)
    self.genapi = GeneratorAPI.fromArgs(args)
    self.genapi.setWikiAndGetContexts(args)
    self.smwAccess = self.genapi.smwAccess
    wikiUser = self.smwAccess.wikiClient.wikiUser
    if wikiUser:
        self.setWikiLink(wikiUser.getWikiUrl(), args.wikiId, args.wikiId)
    self.setMWContext()
    gen_profile.time()

setMWContext()

set my context

Source code in yprinciple/ypgenapp.py
135
136
137
138
139
140
141
def setMWContext(self):
    """
    set my context
    """
    self.mw_contexts = self.genapi.mw_contexts
    mw_context = self.mw_contexts.get(self.context_name, None)
    self.setContext(mw_context)

show_GenerateGrid()

show the grid for generating code

Source code in yprinciple/ypgenapp.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def show_GenerateGrid(self):
    """
    show the grid for generating code
    """
    try:
        self.grid_container.clear()
        with self.grid_container:
            # start with a new generatorGrid
            self.generatorGrid = GeneratorGrid(
                self.targets, parent=self.grid_container, solution=self
            )
        if self.useSidif:
            if self.mw_context is not None:
                context, error, errMsg = Context.fromWikiContext(
                    self.mw_context, debug=self.args.debug, depth=self.explainDepth
                )
                if error is not None:
                    self.log_view.push(errMsg)
                else:
                    self.generatorGrid.add_topic_rows(context)
    except Exception as ex:
        self.handle_exception(ex)

show_all() async

show settings and generator grid

Source code in yprinciple/ypgenapp.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
async def show_all(self):
    """
    show settings and generator grid
    """
    with self.content_div:
        self.content_div.clear()
        with ui.card() as self.settings_area:
            with ui.grid(columns=2):
                self.addWikiSelect()
                self.add_context_select()
        with ui.row() as self.button_bar:
            self.tool_button(
                tooltip="reload",
                icon="refresh",
                handler=self.async_showGenerateGrid,
            )
            self.useSidifButton = ui.switch("use SiDIF").bind_value(
                self, "useSidif"
            )
            self.dryRunButton = ui.switch("dry Run").bind_value(self, "dryRun")
            self.openEditorButton = ui.switch("open Editor").bind_value(
                self, "openEditor"
            )
            self.hideShowSizeInfo = ui.switch("size info").on(
                "click", self.handleHideShowSizeInfo
            )
        with ui.row() as self.progress_container:
            self.progressBar = NiceguiProgressbar(
                total=100, desc="preparing", unit="steps"
            )
        with ui.row() as self.grid_container:
            pass

update the context link

Source code in yprinciple/ypgenapp.py
290
291
292
293
294
295
296
297
298
299
300
301
302
def update_context_link(self):
    """
    update the context link
    """
    if self.mw_context is not None:
        url = self.mw_context.sidif_url()
        link = Link.create(url, text=self.context_name)
        self.context_link.content = link
    else:
        self.context_link.content = "?"

    self.context_link.update()
    pass

update_context_select() async

react on update of context select

Source code in yprinciple/ypgenapp.py
304
305
306
307
308
309
310
311
312
313
314
315
async def update_context_select(self):
    """
    react on update of context select
    """
    self.update_context_link()
    context_selection = list(self.mw_contexts.keys())
    self.context_select.options = context_selection
    if self.context_name in context_selection:
        self.context_select.value = self.context_name
    self.context_select.update()
    self.grid_container.clear()
    pass

YPGenServer

Bases: InputWebserver

Y-Principle Generator webserver

Source code in yprinciple/ypgenapp.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class YPGenServer(InputWebserver):
    """
    Y-Principle Generator webserver
    """

    @classmethod
    def get_config(cls) -> WebserverConfig:
        """
        get the configuration for this Webserver
        """
        copy_right = ""
        config = WebserverConfig(
            short_name="ypgen",
            copy_right=copy_right,
            version=Version(),
            default_port=8778,
            timeout=7.5,
        )
        server_config = WebserverConfig.get(config)
        server_config.solution_class = YPGenApp
        return server_config

    def __init__(self):
        """Constructs all the necessary attributes for the WebServer object."""
        InputWebserver.__init__(self, config=YPGenServer.get_config())

    def configure_run(self):
        """ """
        InputWebserver.configure_run(self)
        # wiki users
        self.wikiUsers = WikiUser.getWikiUsers()

__init__()

Constructs all the necessary attributes for the WebServer object.

Source code in yprinciple/ypgenapp.py
47
48
49
def __init__(self):
    """Constructs all the necessary attributes for the WebServer object."""
    InputWebserver.__init__(self, config=YPGenServer.get_config())

configure_run()

Source code in yprinciple/ypgenapp.py
51
52
53
54
55
def configure_run(self):
    """ """
    InputWebserver.configure_run(self)
    # wiki users
    self.wikiUsers = WikiUser.getWikiUsers()

get_config() classmethod

get the configuration for this Webserver

Source code in yprinciple/ypgenapp.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@classmethod
def get_config(cls) -> WebserverConfig:
    """
    get the configuration for this Webserver
    """
    copy_right = ""
    config = WebserverConfig(
        short_name="ypgen",
        copy_right=copy_right,
        version=Version(),
        default_port=8778,
        timeout=7.5,
    )
    server_config = WebserverConfig.get(config)
    server_config.solution_class = YPGenApp
    return server_config