codero 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
codero/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ #==============================================================================
2
+ # C O P Y R I G H T
3
+ #------------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ #==============================================================================
9
+
10
+ import os
11
+
12
+ __version__ = '0.6.3'
13
+
14
+ ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
15
+ PROCESS = 'codero'
codero/cli.py ADDED
@@ -0,0 +1,60 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import utilo
11
+
12
+ import codero
13
+
14
+ DESCRIPTION = 'TODO'
15
+
16
+ SOURCES = [
17
+ utilo.ResultFile('rawmaker', 'oneline_text_text'),
18
+ utilo.ResultFile('rawmaker', 'oneline_text_positions'),
19
+ utilo.ResultFile('rawmaker', 'oneline_fonts_header'),
20
+ utilo.ResultFile('rawmaker', 'oneline_fonts_content'),
21
+ utilo.ResultFile('rawmaker', 'border_pages'),
22
+ utilo.ResultFile('groupme', 'footer_footerheader'),
23
+ utilo.ResultFile('rawmaker', 'horizontals_horizontals'),
24
+ ]
25
+
26
+ WORKPLAN = [
27
+ utilo.create_step(
28
+ 'horizontal',
29
+ inputs=SOURCES,
30
+ output=('horizontal',),
31
+ ),
32
+ utilo.create_step(
33
+ 'text',
34
+ inputs=SOURCES,
35
+ output=('text',),
36
+ ),
37
+ utilo.create_step(
38
+ 'result',
39
+ inputs=[
40
+ utilo.ResultFile('codero', 'horizontal_horizontal'),
41
+ utilo.ResultFile('codero', 'text_text'),
42
+ ],
43
+ output=('result',),
44
+ ),
45
+ ]
46
+
47
+
48
+ def main():
49
+ utilo.featurepack(
50
+ root=codero.ROOT,
51
+ workplan=WORKPLAN,
52
+ featurepackage='codero.features',
53
+ config=utilo.FeaturePackConfig(
54
+ description=DESCRIPTION,
55
+ multiprocessed=True,
56
+ name=codero.PROCESS,
57
+ pages=True,
58
+ version=codero.__version__,
59
+ ),
60
+ )
codero/decider.py ADDED
@@ -0,0 +1,30 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import iamraw
11
+ import utilo
12
+
13
+
14
+ def decide(horizontal, text):
15
+ data = (horizontal, text)
16
+ result = []
17
+ for pagenumber, (horizontalx, textx) in utilo.sync_pages(iterators=data,):
18
+ horizontalx = horizontalx.content if horizontalx else []
19
+ textx = textx.content if textx else []
20
+ best = horizontalx if score(horizontalx) > score(textx) else textx
21
+ result.append(iamraw.PageContent(content=best, page=pagenumber))
22
+ return result
23
+
24
+
25
+ def score(items) -> int:
26
+ result = 0
27
+ for item in items:
28
+ result += len(item.caption)
29
+ result += len(item.tokens)
30
+ return result
@@ -0,0 +1,8 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
@@ -0,0 +1,49 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import serializeraw
11
+
12
+ import codero.finalize
13
+ import codero.strategy.lines
14
+
15
+
16
+ def work(
17
+ oneline_text: str,
18
+ oneline_textposition: str,
19
+ oneline_font_header: str,
20
+ oneline_font_content: str,
21
+ sizeandborder: str,
22
+ headerfooter: str,
23
+ horizontalx: str,
24
+ pages: tuple = None,
25
+ ) -> str:
26
+ ptcns = serializeraw.ptcn_fromfile(
27
+ oneline_text,
28
+ oneline_textposition,
29
+ sizeandborder,
30
+ headerfooter,
31
+ oneline_font_header,
32
+ oneline_font_content,
33
+ horizontals=horizontalx,
34
+ pages=pages,
35
+ )
36
+ parsed = run(ptcns)
37
+ dumped = serializeraw.dump_codes(parsed)
38
+ return dumped
39
+
40
+
41
+ def run(ptcns) -> list:
42
+ result = []
43
+ for ptn in ptcns:
44
+ parsed = codero.strategy.lines.parse(ptn)
45
+ if not parsed.content:
46
+ continue
47
+ result.append(parsed)
48
+ result = codero.finalize.run(result, ptcns)
49
+ return result
@@ -0,0 +1,30 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import serializeraw
11
+
12
+ import codero.decider
13
+
14
+
15
+ def work(
16
+ horizontalx: str,
17
+ textx: str,
18
+ pages: tuple = None,
19
+ ) -> str:
20
+ horizontalx = serializeraw.load_codes(
21
+ horizontalx,
22
+ pages=pages,
23
+ )
24
+ textx = serializeraw.load_codes(
25
+ textx,
26
+ pages=pages,
27
+ )
28
+ result = codero.decider.decide(horizontalx, textx)
29
+ dumped = serializeraw.dump_codes(result)
30
+ return dumped
@@ -0,0 +1,49 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import serializeraw
11
+
12
+ import codero.finalize
13
+ import codero.strategy.text
14
+
15
+
16
+ def work(
17
+ oneline_text: str,
18
+ oneline_textposition: str,
19
+ oneline_font_header: str,
20
+ oneline_font_content: str,
21
+ sizeandborder: str,
22
+ headerfooter: str,
23
+ horizontalx: str,
24
+ pages: tuple = None,
25
+ ) -> str:
26
+ ptcns = serializeraw.ptcn_fromfile(
27
+ oneline_text,
28
+ oneline_textposition,
29
+ sizeandborder,
30
+ headerfooter,
31
+ oneline_font_header,
32
+ oneline_font_content,
33
+ horizontals=horizontalx,
34
+ pages=pages,
35
+ )
36
+ parsed = run(ptcns)
37
+ dumped = serializeraw.dump_codes(parsed)
38
+ return dumped
39
+
40
+
41
+ def run(ptcns) -> list:
42
+ result = []
43
+ for ptn in ptcns:
44
+ parsed = codero.strategy.text.parse(ptn)
45
+ if not parsed.content:
46
+ continue
47
+ result.append(parsed)
48
+ result = codero.finalize.run(result, ptcns)
49
+ return result
codero/finalize.py ADDED
@@ -0,0 +1,50 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import iamraw
11
+ import texmex
12
+ import utilo
13
+
14
+
15
+ def run(codes: iamraw.PeaceOfCodes, ptns):
16
+ codes = insert_code_boundings(codes, ptns)
17
+ codes = horizontals_remove(codes, ptns)
18
+ return codes
19
+
20
+
21
+ def insert_code_boundings(codes: iamraw.PeaceOfCodes, ptns):
22
+ for page in codes:
23
+ ptn = utilo.select_page(ptns, page=page.page)
24
+ for code in page.content:
25
+ for caption in code.caption:
26
+ code.caption_bounding.append(tuple(ptn[caption].bounding))
27
+ for token in code.tokens:
28
+ code.tokens_bounding.append(tuple(ptn[token].bounding))
29
+ return codes
30
+
31
+
32
+ def horizontals_remove(codes: iamraw.PeaceOfCodes, ptns):
33
+ """Adjust indexes of caption and token to ptns without horizontals."""
34
+ for page in codes:
35
+ ptn = utilo.select_page(ptns, page=page.page)
36
+ before = horizontals_before(ptn)
37
+ for code in page.content:
38
+ code.caption = {item - before[item] for item in code.caption}
39
+ code.tokens = {item - before[item] for item in code.tokens}
40
+ return codes
41
+
42
+
43
+ def horizontals_before(ptn):
44
+ before = 0
45
+ result = []
46
+ for item in ptn:
47
+ result.append(before)
48
+ if item.text == texmex.HORIZONTAL:
49
+ before += 1
50
+ return result
codero/loc.py ADDED
@@ -0,0 +1,167 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import re
11
+
12
+ import utilo
13
+
14
+
15
+ def determine(text: str) -> bool:
16
+ """\
17
+ >>> determine('print("hello")')
18
+ True
19
+ """
20
+ text = text.lower().strip()
21
+ methods = [
22
+ isjs,
23
+ ispython,
24
+ isbash,
25
+ isobjectc,
26
+ ]
27
+ if any(method(text) for method in methods):
28
+ return True
29
+ return False
30
+
31
+
32
+ def prepare(keys, left=r'\b', right=r'\b', escape: bool = False):
33
+ if escape:
34
+ keys = (re.escape(item) for item in keys)
35
+ items = [left + item + right for item in keys]
36
+ result = utilo.compiles('(' + '|'.join(items) + ')')
37
+ return result
38
+
39
+
40
+ PYTHON_KEYWORDS = utilo.splititems("""assert async await break continue
41
+ del elif else except finally for global if lambda pass raise nonlocal
42
+ return try while yield yield from as with import""")
43
+ PYTHON_KEYWORDS = prepare(PYTHON_KEYWORDS)
44
+ PYTHON_METHODS = utilo.splititems("""
45
+ __import__ abs all any bin bool bytearray bytes chr classmethod compile
46
+ complex delattr dict dir divmod enumerate eval filter float format
47
+ frozenset getattr globals hasattr hash hex id input int isinstance
48
+ issubclass iter len list locals map max memoryview min next object oct
49
+ open ord pow print property range repr reversed round set setattr slice
50
+ sorted staticmethod str sum super tuple namedtuple
51
+ """)
52
+ PYTHON_METHODS = prepare(PYTHON_METHODS, right='\\(')
53
+
54
+
55
+ def ispython(text: str) -> bool:
56
+ """\
57
+ >>> ispython('Abc = collections.namedtuple("Abc a b c")')
58
+ True
59
+ """
60
+ if PYTHON_KEYWORDS.search(text):
61
+ return True
62
+ if PYTHON_METHODS.search(text):
63
+ return True
64
+ return False
65
+
66
+
67
+ JAVASCRIPT_KEYWORDS = utilo.splitlines("""for own in of while until loop
68
+ break return continue switch when then if unless else throw try catch
69
+ finally new delete typeof instanceof super true false yes no on off null
70
+ NaN Infinity undefined void val map apply length split sum""")
71
+ JAVASCRIPT_KEYWORDS = prepare(JAVASCRIPT_KEYWORDS)
72
+ # .reduce(
73
+ JAVASCRIPT_METHODS = utilo.compiles(r'\.[a-z]{3,15}\(')
74
+ JAVASCRIPT_START = utilo.splititems("""// /** /* * ) } )} val""")
75
+ JAVASCRIPT_START = prepare(
76
+ keys=JAVASCRIPT_START,
77
+ left=r'^',
78
+ right='',
79
+ escape=True,
80
+ )
81
+ # 2 val tfidf
82
+ JAVASCRIPT_ASSIGNMENT = utilo.compiles(r"""
83
+ ^
84
+ (var|val)
85
+ [ ]{0,3}
86
+ \w[\w\d]{1,40}
87
+ [\:\=]
88
+ """)
89
+
90
+
91
+ def isjs(text: str) -> bool:
92
+ """\
93
+ >>> isjs('.reduce((vector1, vector2) =>')
94
+ True
95
+ >>> isjs('}')
96
+ True
97
+ >>> isjs('var tfidf:')
98
+ True
99
+ >>> isjs('val tfidf=230')
100
+ True
101
+ """
102
+ if JAVASCRIPT_KEYWORDS.search(text):
103
+ return True
104
+ if JAVASCRIPT_METHODS.search(text):
105
+ return True
106
+ if JAVASCRIPT_START.search(text):
107
+ return True
108
+ if JAVASCRIPT_ASSIGNMENT.match(text):
109
+ return True
110
+ return False
111
+
112
+
113
+ BASH_KEYWORDS = utilo.splititems("""for do done in while screen quit ssh
114
+ #/bin/bash bin bash .sh csp scp
115
+ """)
116
+ BASH_KEYWORDS = prepare(BASH_KEYWORDS, escape=True)
117
+ BASH_ASSIGNMENT = utilo.compiles(r"""
118
+ ^
119
+ [\w|\_]{1,30}
120
+ \=
121
+ ["'‘]
122
+ """)
123
+ BASH_COMMENT = utilo.compiles(r"""
124
+ ^
125
+ \#{1,4}[ ]
126
+ """)
127
+ BASH_VAR = utilo.compiles(r"""
128
+ [$]
129
+ (
130
+ \d|
131
+ \w{1,10}
132
+ )
133
+ """)
134
+
135
+
136
+ def isbash(text: str) -> bool:
137
+ """\
138
+ >>> isbash('#/bin/bash')
139
+ True
140
+ """
141
+ if BASH_KEYWORDS.search(text):
142
+ return True
143
+ if BASH_ASSIGNMENT.search(text):
144
+ return True
145
+ if BASH_COMMENT.search(text):
146
+ return True
147
+ if BASH_VAR.search(text):
148
+ return True
149
+ return False
150
+
151
+
152
+ OBJECTC_START = utilo.splititems("""// /** /* * ) } )} ...""")
153
+ OBJECTC_START = prepare(OBJECTC_START, escape=True)
154
+ OBJECTC_KEYWORDS = utilo.splititems("""target << |= [[""")
155
+ OBJECTC_KEYWORDS = prepare(OBJECTC_KEYWORDS, escape=True)
156
+
157
+
158
+ def isobjectc(text: str) -> bool:
159
+ """\
160
+ >>> isobjectc('t.target_addr |= [[ target objectForKey:@"main"] intValue] << 11; ')
161
+ True
162
+ """
163
+ if OBJECTC_START.search(text):
164
+ return True
165
+ if OBJECTC_KEYWORDS.search(text):
166
+ return True
167
+ return False
@@ -0,0 +1,8 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
@@ -0,0 +1,170 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+ """Code Between Lines Detector
10
+ ===========================
11
+
12
+ 1. Strategy: Code and listings is detected inside 3 horizonals
13
+ 2. Backup: Code is detected inside 2 horizonals and caption is detected below
14
+ """
15
+
16
+ import elementae
17
+ import iamraw
18
+ import texmex
19
+ import utilo
20
+
21
+
22
+ def parse(ptn: texmex.PTCN) -> iamraw.PageContent:
23
+ content = all_inside_textual_horizontals(ptn)
24
+ if not content:
25
+ content = caption_below_code(ptn)
26
+ result = iamraw.PageContent(content=content, page=ptn.page)
27
+ return result
28
+
29
+
30
+ def all_inside_textual_horizontals(ptn):
31
+ """\
32
+ Example
33
+
34
+ ---------------------------------------------------------------------------
35
+ Listing 2.1 Eine Beschwerde aus dem NHTSA-Datensatz.
36
+ ---------------------------------------------------------------------------
37
+ WHEN REAR ENDED WITH FOOT ON BRAKE, DRIVERS SHOULDER BELT DIDN’T RESTRAIN
38
+ FROM MAKING CONTACT, RESULTING IN AN INJURY. ESTIMATED SPEED OF
39
+ IMPACT 30 MPH.
40
+ ---------------------------------------------------------------------------
41
+ """
42
+ code = []
43
+ collector = DetectorAllInside()
44
+ for index, line in enumerate(ptn):
45
+ done = collector.run(line, index)
46
+ if not done:
47
+ continue
48
+ if len(utilo.unique(done)) != 3:
49
+ # false posititve, a lot of lines with any content. We require
50
+ # content between detected lines to have valid code detection.
51
+ collector.reset()
52
+ continue
53
+ # detected
54
+ code.append(done)
55
+ collector.reset()
56
+ converted = [
57
+ iamraw.PeaceOfCode(
58
+ caption=utilo.rtuple(done[0] + 1, done[1]),
59
+ tokens=utilo.rtuple(done[1] + 1, done[2]),
60
+ ) for done in code
61
+ ]
62
+ content = remove_invalids(converted, ptn)
63
+ return content
64
+
65
+
66
+ def caption_below_code(ptn):
67
+ code = []
68
+ collector = DetectorBelow()
69
+ for index, line in enumerate(ptn):
70
+ done = collector.run(line, index)
71
+ if not done:
72
+ continue
73
+ # detected
74
+ code.append(done)
75
+ collector.reset()
76
+ content = [
77
+ iamraw.PeaceOfCode(
78
+ caption=utilo.rtuple(done[1] + 1, done[2] + 1),
79
+ tokens=utilo.rtuple(done[0] + 1, done[1]),
80
+ ) for done in code
81
+ ]
82
+ content = remove_invalids(content, ptn)
83
+ return content
84
+
85
+
86
+ def remove_invalids(content, navigator: texmex.PTCN):
87
+ result = []
88
+ for item in content:
89
+ if not item.tokens:
90
+ # skip three horizontals in a row
91
+ continue
92
+ if not item.caption:
93
+ # skip three horizontals in a row
94
+ continue
95
+ firstindex = item.caption[0]
96
+ if not elementae.iscaption_code(navigator[firstindex].text):
97
+ continue
98
+ result.append(item)
99
+ return result
100
+
101
+
102
+ class DetectorAllInside:
103
+ """\
104
+ ---------------------------------------------------------------------------
105
+ Listing 2.1 Eine Beschwerde aus dem NHTSA-Datensatz.
106
+ ---------------------------------------------------------------------------
107
+ WHEN REAR ENDED WITH FOOT ON BRAKE, DRIVERS SHOULDER BELT DIDN’T RESTRAIN
108
+ FROM MAKING CONTACT, RESULTING IN AN INJURY. ESTIMATED SPEED OF
109
+ IMPACT 30 MPH.
110
+ ---------------------------------------------------------------------------
111
+ """
112
+
113
+ def __init__(self):
114
+ self.start: int = None
115
+ self.middle: int = None
116
+ self.end: int = None
117
+
118
+ def run(self, line, index):
119
+ text = line.text
120
+ if self.start is None:
121
+ if text == texmex.HORIZONTAL:
122
+ self.start = index
123
+ return None
124
+ if self.middle is None:
125
+ if text == texmex.HORIZONTAL:
126
+ self.middle = index
127
+ return None
128
+ if text != texmex.HORIZONTAL:
129
+ return None
130
+ self.end = index
131
+ return self.start, self.middle, self.end
132
+
133
+ def reset(self):
134
+ self.start, self.middle, self.end = None, None, None
135
+
136
+
137
+ class DetectorBelow:
138
+ """\
139
+ ---------------------------------------------------------------------------
140
+ WHEN REAR ENDED WITH FOOT ON BRAKE, DRIVERS SHOULDER BELT DIDN’T RESTRAIN
141
+ FROM MAKING CONTACT, RESULTING IN AN INJURY. ESTIMATED SPEED OF
142
+ IMPACT 30 MPH.
143
+ ---------------------------------------------------------------------------
144
+ Listing 2.1 Eine Beschwerde aus dem NHTSA-Datensatz.
145
+ """
146
+
147
+ def __init__(self):
148
+ self.start: int = None
149
+ self.middle: int = None
150
+ self.end: int = None
151
+
152
+ def run(self, line, index):
153
+ text = line.text
154
+ if self.start is None:
155
+ if text == texmex.HORIZONTAL:
156
+ self.start = index
157
+ return None
158
+ if self.middle is None:
159
+ if text == texmex.HORIZONTAL:
160
+ self.middle = index
161
+ return None
162
+ if not elementae.iscaption_code(text):
163
+ # invalid parsing, reset Detector
164
+ self.reset()
165
+ return None
166
+ self.end = index
167
+ return self.start, self.middle, self.end
168
+
169
+ def reset(self):
170
+ self.start, self.middle, self.end = None, None, None
@@ -0,0 +1,140 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2021-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+ """Code Inside Text Detector
10
+ =========================
11
+
12
+ Detect code as a normal part of written text. There are no special
13
+ elementae which help detecting these lines of code.
14
+
15
+ 1. Strategy: Lines number and caption below
16
+ 2. Strategy: Specialized R-Code with headline detector
17
+ """
18
+
19
+ import re
20
+
21
+ import configos
22
+ import elementae
23
+ import iamraw
24
+ import texmex
25
+ import utilo
26
+
27
+ import codero.loc
28
+ import codero.strategy.text.numbers
29
+
30
+
31
+ def parse(ptn: texmex.PTCN) -> iamraw.PageContent:
32
+ pagenumber = ptn.page
33
+ collected = []
34
+ if content := codero.strategy.text.numbers.linenumbers_and_caption(ptn):
35
+ collected.extend(content)
36
+ ptn = hide_result(ptn, collected)
37
+ if content := codero.strategy.text.numbers.code_without_caption(ptn):
38
+ collected.extend(content)
39
+ ptn = hide_result(ptn, collected)
40
+ if not collected:
41
+ collected = raw_code(ptn)
42
+ pagecontent = iamraw.PageContent(
43
+ content=collected,
44
+ page=pagenumber,
45
+ )
46
+ return pagecontent
47
+
48
+
49
+ def hide_result(ptn, codes):
50
+ done = set()
51
+ for item in codes:
52
+ done |= item.caption
53
+ done |= item.tokens
54
+ ptn = ptn[:]
55
+ for item in done:
56
+ ptn[item] = texmex.TextInfo(text='')
57
+ return ptn
58
+
59
+
60
+ NOHEADLINE = '<<<NOHEADLINE>>>'
61
+
62
+ RAWCODE_LENGTH_MIN = configos.HV_INT_PLUS(default=5)
63
+
64
+
65
+ def raw_code(ptn):
66
+ code = []
67
+ collector = DetectorWithHeadline()
68
+ for index, line in enumerate(ptn):
69
+ done = collector.run(line.text, index)
70
+ if not done:
71
+ continue
72
+ # detected
73
+ code.append(done)
74
+ collector.reset()
75
+ if any((collector.start, collector.middle, collector.end)):
76
+ # raw code till page end
77
+ code.append((collector.start, collector.middle, len(ptn)))
78
+ content = [
79
+ iamraw.PeaceOfCode(tokens=set(utilo.rtuple(done[1], done[2])))
80
+ if done[0] == NOHEADLINE else iamraw.PeaceOfCode(
81
+ caption=set(utilo.rtuple(done[0], done[1])),
82
+ tokens=set(utilo.rtuple(done[1], done[2])),
83
+ ) for done in code
84
+ ]
85
+
86
+ def is_valid_raw(item):
87
+ if not item.caption:
88
+ if len(item.tokens) < RAWCODE_LENGTH_MIN:
89
+ # Too short code without any caption
90
+ return False
91
+ return True
92
+
93
+ content = [item for item in content if is_valid_raw(item)]
94
+ return content
95
+
96
+
97
+ RCODE = re.compile(r'^R-Code:')
98
+
99
+
100
+ class DetectorWithHeadline:
101
+ """\
102
+ R-Code:
103
+
104
+ > PROB_VOLLST <- NA
105
+ > PROB_VOLLST[PROB_VOLLST_s1 == 1 & PROB_VOLLST_s2 == 1] <- 1
106
+ > PROB_VOLLST[PROB_VOLLST_s1 != 1 | PROB_VOLLST_s2 != 1] <- 0
107
+ """
108
+
109
+ def __init__(self):
110
+ self.start: int = None
111
+ self.middle: int = None
112
+ self.end: int = None
113
+
114
+ def run(self, line: str, index: int):
115
+ text = line.strip()
116
+ if self.start is None:
117
+ if RCODE.search(text):
118
+ self.start = index
119
+ return None
120
+ if text and text[0] in '#>+':
121
+ # TODO: MAKE THIS MORE GENERAL
122
+ # R-Code start without headline
123
+ self.start = NOHEADLINE
124
+ self.middle = index
125
+ return None
126
+ if text and text[0] in '#>+':
127
+ if self.middle is None:
128
+ self.middle = index
129
+ else:
130
+ self.end = index + 1
131
+ return None
132
+ if self.middle is not None:
133
+ result = self.start, self.middle, index
134
+ self.reset()
135
+ return result
136
+ self.reset()
137
+ return None
138
+
139
+ def reset(self):
140
+ self.start, self.middle, self.end = None, None, None
@@ -0,0 +1,187 @@
1
+ # =============================================================================
2
+ # C O P Y R I G H T
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2022-2023 by Helmut Konrad Schewe. All rights reserved.
5
+ # This file is property of Helmut Konrad Schewe. Any unauthorized copy,
6
+ # use or distribution is an offensive act against international law and may
7
+ # be prosecuted under federal law. Its content is company confidential.
8
+ # =============================================================================
9
+
10
+ import re
11
+
12
+ import configos
13
+ import elementae
14
+ import iamraw
15
+ import utilo
16
+
17
+ import codero.loc
18
+
19
+
20
+ def linenumbers_and_caption(ptn):
21
+ code = []
22
+ collector = DetectorLineNumbers()
23
+ for index, line in enumerate(ptn):
24
+ done = collector.run(line.text, index)
25
+ if not done:
26
+ continue
27
+ # detected
28
+ code.append(done)
29
+ collector.reset()
30
+ content = [
31
+ iamraw.PeaceOfCode(
32
+ caption=set(utilo.rtuple(done[1], done[2])),
33
+ tokens=set(utilo.rtuple(done[0], done[1])),
34
+ ) for done in code
35
+ ]
36
+ return content
37
+
38
+
39
+ def code_without_caption(ptn):
40
+ code = []
41
+ collector = CodeWithoutCaption()
42
+ for index, line in enumerate(ptn):
43
+ done = collector.run(line.text, index)
44
+ if not done:
45
+ continue
46
+ # detected
47
+ code.append(done)
48
+ final = collector.final()
49
+ if final[0] is not None and final[1] is not None:
50
+ code.append(final)
51
+ content = [
52
+ iamraw.PeaceOfCode(
53
+ caption=set(),
54
+ tokens=set(utilo.rtuple(done[0], done[1])),
55
+ ) for done in code
56
+ ]
57
+ return content
58
+
59
+
60
+ class DetectorLineNumbers:
61
+ """\
62
+ 1 $ ./spark-shell
63
+ 2 [...]
64
+ 3 / __/__ ___ _____/ /__
65
+
66
+ Listing 2.1: Word Count in der Spark Konsole
67
+
68
+ >>> dtn = DetectorLineNumbers()
69
+ >>> dtn.run('1 import collections', 10)
70
+ >>> dtn.run('2 Abc = collections.namedtuple("Abc a b c")', 11)
71
+ >>> dtn.run('Listing 3: Simple python example', 12)
72
+ (10, 12, 13)
73
+ """
74
+
75
+ def __init__(self):
76
+ self.start: int = None
77
+ self.middle: int = None
78
+ self.end: int = None
79
+
80
+ def run(self, line: str, index: int):
81
+ text = line.strip()
82
+ if self.start is None:
83
+ if self.loc(text):
84
+ self.start = index
85
+ return None
86
+ if self.loc(text):
87
+ self.middle = index + 1
88
+ return None
89
+ if self.valid(text):
90
+ self.middle = index
91
+ self.end = index + 1
92
+ else:
93
+ self.reset()
94
+ return None
95
+ return self.final()
96
+
97
+ def loc(self, text: str) -> bool: # pylint:disable=R0201
98
+ """Determine if text can be a line of code."""
99
+ return CODE_NUMBER.search(text)
100
+
101
+ def valid(self, text: str) -> bool: # pylint:disable=R0201
102
+ return elementae.iscaption_code(text)
103
+
104
+ def final(self):
105
+ result = self.start, self.middle, self.end
106
+ self.reset()
107
+ return result
108
+
109
+ def reset(self):
110
+ self.start, self.middle, self.end = None, None, None
111
+
112
+
113
+ CWC_CODE_LENGTH_MIN = configos.HV_INT_PLUS(default=2)
114
+
115
+
116
+ class CodeWithoutCaption(DetectorLineNumbers):
117
+ """\
118
+ Code inside text
119
+ >>> cwc = CodeWithoutCaption()
120
+ >>> cwc.run('Text text text.', 9)
121
+ >>> cwc.run('1 import collections', 10)
122
+ >>> cwc.run('2 Abc = collections.namedtuple("Abc a b c")', 11)
123
+ >>> cwc.run('3 print("hello")', 12)
124
+ >>> cwc.run('Ich bin kein Quellcode.', 13)
125
+ (10, 13)
126
+
127
+ Normal List
128
+ >>> cwc = CodeWithoutCaption()
129
+ >>> cwc.run('1. Ich bin ein Listenpunkt', 3)
130
+ >>> cwc.run('2. Haus', 4)
131
+ >>> cwc.run('3. Hund', 5)
132
+ >>> cwc.run('Ich bin kein Quellcode.', 6)
133
+
134
+ TODO: ADD OPTION TO HAVE SOME LINES FALSE DETECTION
135
+ """
136
+
137
+ def __init__(self):
138
+ super().__init__()
139
+ self.empty_line_count = 0
140
+
141
+ def loc(self, text: str) -> bool:
142
+ """\
143
+ Bash Code
144
+ >>> cwc = CodeWithoutCaption()
145
+ >>> cwc.loc('1 #/bin/bash')
146
+ True
147
+ """
148
+ if not CODE_NUMBER.search(text):
149
+ return False
150
+ without_number = CODE_NUMBER.sub('', text).strip()
151
+ if not without_number:
152
+ self.empty_line_count += 1
153
+ return True
154
+ if not codero.loc.determine(without_number):
155
+ return False
156
+ return True
157
+
158
+ def valid(self, text: str) -> bool:
159
+ if elementae.iscaption_code(text):
160
+ # see DetectorLineNumbers
161
+ self.reset()
162
+ return False
163
+ if self.middle is None:
164
+ self.reset()
165
+ return False
166
+ length = self.middle - self.start
167
+ if length < CWC_CODE_LENGTH_MIN:
168
+ self.reset()
169
+ return False
170
+ empty_rate = self.empty_line_count / length
171
+ if empty_rate > 0.34: # TODO: HOLY VALUE
172
+ # skip too many empty lines which are false positive dectections
173
+ self.reset()
174
+ return False
175
+ return True
176
+
177
+ def reset(self):
178
+ super().reset()
179
+ self.empty_line_count = 0
180
+
181
+ def final(self):
182
+ result = self.start, self.middle
183
+ self.reset()
184
+ return result
185
+
186
+
187
+ CODE_NUMBER = re.compile(r'^\d{1,3}([ ]|(?!\.))')
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: codero
3
+ Version: 1.0.0
4
+ Author-email: Helmut Konrad Schewe <helmutus@outlook.com>
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/anaticulae/codero
7
+ Project-URL: Repository, https://github.com/anaticulae/codero
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Python: >=3.12
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: utilo<3.0.0,>=2.109.1
15
+ Requires-Dist: iamraw<5.0.0,>=4.93.0
16
+ Requires-Dist: elementae<2.0.0,>=1.0.1
17
+ Requires-Dist: configos<2.0.0,>=1.0.4
18
+ Provides-Extra: dev
19
+ Requires-Dist: gennex<2.0.0,>=1.1.0; extra == "dev"
20
+ Requires-Dist: groupmes<2.0.0,>=1.1.0; extra == "dev"
21
+ Requires-Dist: headnote<2.0.0,>=1.0.3; extra == "dev"
22
+ Requires-Dist: hoverpower<2.0.0,>=1.5.2; extra == "dev"
23
+ Requires-Dist: ibidem<2.0.0,>=1.0.2; extra == "dev"
24
+ Requires-Dist: mundare<2.0.0,>=1.0.2; extra == "dev"
25
+ Requires-Dist: pagenumber<2.0.0,>=1.0.0; extra == "dev"
26
+ Requires-Dist: utilotest<2.0.0,>=1.0.5; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # codero
@@ -0,0 +1,19 @@
1
+ codero/__init__.py,sha256=s_KoCWB3r1hcvs8_1rEwwErxaTbilHdZUYqB7kXVpkU,676
2
+ codero/cli.py,sha256=aSryeT2ecsJgcmbj4JQwd2ctV27yG0sowi0HudmZAYQ,1815
3
+ codero/decider.py,sha256=1HkO7o3nXrKDXQaAy3VAnig3OjKbIlCIPCDv3jW_Py0,1169
4
+ codero/finalize.py,sha256=2FYwkXFA5Z7gGbX9y-Iovupqn41y4yt1327apvWIlmE,1812
5
+ codero/loc.py,sha256=K6NAsKhIb4DrK9oG6dqel7ameEZ6Ap3n2UUa2OOJteE,4519
6
+ codero/features/__init__.py,sha256=ZnAmZoM8YS7O_aZREOZiaHHCsO1ctWvQVu__JGFNWCc,552
7
+ codero/features/horizontal.py,sha256=DWup-mikKt2GXLE6uwWxj-g4W3xXwdfR1-jVtJM0uLs,1462
8
+ codero/features/result.py,sha256=msRKBiTKk_A8BEXxGGjo0mEC17FVE97ZM1OWqfCmGOw,969
9
+ codero/features/text.py,sha256=k42-W10xwS4iv2XInR6EuD6OwJbkpT5RG30q8mbbXfc,1460
10
+ codero/strategy/__init__.py,sha256=ZnAmZoM8YS7O_aZREOZiaHHCsO1ctWvQVu__JGFNWCc,552
11
+ codero/strategy/lines.py,sha256=cB9BKQC8nnS80T9u9LZcqLfZonOWiqqNxryKgyBdUJE,5684
12
+ codero/strategy/text/__init__.py,sha256=17Q_RqcE3_33nNX11jk70bhDZwZ1CrekX6oJZUpB30c,4136
13
+ codero/strategy/text/numbers.py,sha256=dJWrqrxhtnFGVMiZwJGsMiHJOAoOcKPHdhe4eDs6qMk,5253
14
+ codero-1.0.0.dist-info/licenses/LICENSE,sha256=9jV_XivjSpyzpEYIFKZGcjDzX8invw3EHEJsGXmGJbA,1077
15
+ codero-1.0.0.dist-info/METADATA,sha256=mcP_rBFL7E-siYy6st8cQjCL1DI0XUbkw_WomsyNV2Q,1128
16
+ codero-1.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
+ codero-1.0.0.dist-info/entry_points.txt,sha256=EflcpnvitNuwY78jFrrd4SHQmNUeLR5X6rUUkee7-xk,43
18
+ codero-1.0.0.dist-info/top_level.txt,sha256=mSUG4msWnOzBsNhN_ABrRJ5BDfD749-YKOIwA2yJcoc,7
19
+ codero-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ codero = codero.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Helmut Konrad Schewe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ codero