Initial commit

This commit is contained in:
SebastianObi
2023-08-22 17:18:40 +02:00
commit ea549f6de3
22 changed files with 1506 additions and 0 deletions

0
CHANGELOG.md Normal file
View File

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2023 Sebastian Obele / obele.eu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25
Makefile Normal file
View File

@@ -0,0 +1,25 @@
clean:
@echo Cleaning...
@-rm -rf ./build
@-rm -rf ./dist
@-rm -rf ./__pycache__
@-rm -rf ./any2micronconverter/__pycache__
@-rm -rf ./*.egg-info
@echo Done
cleanall: clean
preparewheel:
pyclean .
build_wheel:
python3 setup.py sdist bdist_wheel
release: build_wheel
upload:
@echo Ready to publish release, hit enter to continue
@read VOID
@echo Uploading to PyPi...
twine upload dist/*
@echo Release published

5
Makefile.bat Normal file
View File

@@ -0,0 +1,5 @@
cd any2micronconverter
py -m PyInstaller main.spec
xcopy /e /v /Y dist ..\dist\
del version.rc
cd ..

125
README.md Normal file
View File

@@ -0,0 +1,125 @@
# Any2MicronConverter
This program converts any text/markup document into the micron format, which can then be used by a reticlum page server/node.
At the moment only the DokuWiki format is supported. It is only a very rudimentary functionality, which will be further developed in the future. It is not guaranteed that all formatting will be converted properly.
### Features
- Compatible with all Reticulum apps which can display micron pages.
- Compatible with the following source file formats:
- DokuWiki
- ...
- Relatively easy to extend to convert additional file formats
## Current Status
It should currently be considered beta software and still work in progress.
All core features are implemented and functioning, but additions will probably occur as real-world use is explored.
There may be errors or the compatibility after an update is no longer guaranteed.
The full documentation is not yet available. Due to lack of time I can also not say when this will be further processed.
## Installation manual
### Linux
Download the file `any2micronconverter-x.x.x-py3-none-any.whl`.
Install the dependencies. (CentOS)
```
yum -y install epel-release.noarch
yum -y install python3-pip
pip3 install pip --upgrade
pip3 install wheel --upgrade
```
Install the dependencies. (Debian/Mint/Raspi OS/Ubuntu)
```
apt install python3-pip
pip3 install pip --upgrade
```
Install the dependencies. (Fedora)
```
yum -y install make gcc
yum -y install python3-pip
pip3 install pip --upgrade
pip3 install wheel --upgrade
```
Install the dependencies. (Manjaro)
```
pacman -Sy python-pip
pip3 install pip --upgrade
```
Install the dependencies. (openSUSE)
```
zypper install python310 python310-pip
pip3 install pip --upgrade
```
Install the application.
`pip3 install any2micronconverter-x.x.x-py3-none-any.whl`
Done. Launch the application (as user).
`any2micronconverter`
or in case of an error
`./local/bin/any2micronconverter`
### Windows
Download the file `any2micronconverter-x.x.x.exe`.
Launch it.
### Startup parameters:
```bash
usage: main.py [-h] [-m MODE] [-s SRC] [-d DST] [-l LOGLEVEL] [-t] [--link_root LINK_ROOT] [--header HEADER]
[--footer FOOTER]
Any2MicronConverter - Convert any text/makrup document to micron format
options:
-h, --help show this help message and exit
-m MODE, --mode MODE Mode or type of source data for conversion (Folder name with the command files)
-s SRC, --src SRC Path to source directory (All subfolders are included)
-d DST, --dst DST Path to destination directory (Folder is overwritten/deleted)
-l LOGLEVEL, --loglevel LOGLEVEL
-t, --test Running in test mode (No files are deleted or overwritten)
--link_root LINK_ROOT
Root path of the links (Internal links in the documents)
--header HEADER Header string which will be added on all pages
--footer FOOTER Footer string which will be added on all pages
```
### Example:
The following command converts all files from the source folder `/root/wiki` to the destination folder `/root/.rns_server_page/pages/wiki`.
The source file format is `dw2mu`.
Internal links start with the text `619d97957a863c8e9d29e2449925fb7c:/page/wiki` to ensure accessibility of the links.
```bash
any2micronconverter -m dw2mu -s /root/wiki -d /root/.rns_server_page/pages/wiki --link_root 619d97957a863c8e9d29e2449925fb7c:/page/wiki
```
## Support / Donations
You can help support the continued development by donating via one of the following channels:
- PayPal: https://paypal.me/SebastianObi
- Liberapay: https://liberapay.com/SebastianObi/donate
## Support in another way?
You are welcome to participate in the development. Just create a pull request. Or just contact me for further clarifications.
## Do you need a special function or customization?
Then feel free to contact me. Customizations or tools developed specifically for you can be realized.
## FAQ
### How do I start with the software?
You should read the `Installation manual` section. There everything is explained briefly. Just work through everything from top to bottom :)

View File

@@ -0,0 +1,5 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@@ -0,0 +1,59 @@
##############################################################################################################
#
# Copyright (c) 2023 Sebastian Obele / obele.eu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This software uses the following software-parts:
# dokuwiki-to-hugo / Copyright (c) 2017 Wouter Groeneveld / https://git.brainbaking.com/wgroeneveld/dokuwiki-to-hugo / MIT License
#
##############################################################################################################
#################################################
# General version information #
#################################################
__version__ = "0.0.1"
__version_variant__ = "beta"
__version_date_time__ = "2023-08-21 00:00"
__copyright_short__ = "Copyright (c) 2023 Sebastian Obele / obele.eu / MIT License"
__copyright_url__ = "https://www.obele.eu"
__title__ = "Any2MicronConverter"
__description__ = "Convert any text/makrup document to micron format"
__author__ = "Sebastian Obele"
__author_email__ = "info@obele.eu"
__package_name__ = "any2micronconverter"
#################################################
# Configuration #
#################################################
__config__ = {}
__config__["file_extensions_auto"] = {"html": "html2mu", "txt": "dw2mu"}
__config__["file_replace"] = {"home.txt": "index.mu", "home.md": "index.mu"}
__config__["link_root"] = ":/pages"
__config__["header"] = ""
__config__["footer"] = ""

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
##############################################################################################################
#
# Copyright (c) 2023 Sebastian Obele / obele.eu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This software uses the following software-parts:
# dokuwiki-to-hugo / Copyright (c) 2017 Wouter Groeneveld / https://git.brainbaking.com/wgroeneveld/dokuwiki-to-hugo / MIT License
#
##############################################################################################################
from pathlib import Path
class Converter:
converters = []
@classmethod
def register(cls, converter_class):
cls.converters.append(converter_class())
return converter_class
def __init__(self, file, config, mode):
self.file = file
self.config = config
self.mode = mode
def convert(self):
text = Path(self.file).read_text()
for converter in Converter.converters:
if self.mode != None and self.mode != converter.mode:
continue
text = converter.convert(text, self.config)
return text

View File

@@ -0,0 +1,50 @@
import os
from sys import platform
from re import compile
from abc import ABC
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
def strip_lang(language):
if language == "":
return language
lang = language[1:len(language)]
if " " in lang:
lang = lang[0:lang.index(" ")]
return lang
class BaseCode(ABC):
dest = "```"
def __init__(self, tag):
self.mode = os.path.basename((os.path.dirname(__file__)))
self.tag = tag
self.pattern = compile("(<"+tag+"(.*?)>)")
def convert(self, text, config={}):
result = text
for match in self.pattern.findall(text):
language = strip_lang(match[1])
result = result.replace(match[0], BaseCode.dest + language)
return result.replace("</"+self.tag+">", BaseCode.dest)
@Converter.register
class File(BaseCode):
def __init__(self):
super().__init__("file")
@Converter.register
class Code(BaseCode):
def __init__(self):
super().__init__("code")

View File

@@ -0,0 +1,20 @@
import os
import re
from sys import platform
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Cleanup:
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
result = re.sub(r"^>+", "#", result, flags=re.MULTILINE)
return result

View File

@@ -0,0 +1,42 @@
import os
from sys import platform
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Emoji:
# http://www.webpagefx.com/tools/emoji-cheat-sheet/
config = {
"8-)": "sunglasses",
"8-O": "flushed",
":-(": "worried",
":-)": "simple_smile",
"=)": "simple_smile",
":-/": "confused",
":-\\": "confused",
":-?": "sweat",
":-D": "laughing",
":-P": "stuck_out_tongue",
":-O": "open_mouth",
":-X": "grimacing",
":-|": "expressionless",
";-)": "wink",
"^_^": "smile",
":?:": "question",
":!:": "exclamation",
"LOL": "laughing",
}
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
for key, value in Emoji.config.items():
result = result.replace(key, ":"+value+":")
return result

View File

@@ -0,0 +1,40 @@
import os
from sys import platform
from re import compile
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Header:
pattern = compile("(=+)(.*?)(=+)")
head = "="
config = {
"======": 1,
"=====": 2,
"====": 3,
"===": 3,
"==": 3,
"=": 3
}
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
for regex_head in Header.pattern.findall(text):
orig_header = "".join(regex_head)
src_header = regex_head[0]
if src_header in Header.config:
new_header = (">" * Header.config[src_header]) + regex_head[1]
result = result.replace(orig_header, new_header)
else:
new_header = (">" * 1) + regex_head[1]
result = result.replace(orig_header, new_header)
return result

View File

@@ -0,0 +1,62 @@
import os
from sys import platform
from re import compile
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Images:
pattern = compile("{{(\s?)(.*?)(\s?)}}")
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def parse_style(self, match):
style = []
left = match[0]
src = match[1]
right = match[2]
def parse_dimensions():
if not "?" in src:
return
dimensions = src.split("?")[1]
if "x" in dimensions:
(width, height) = dimensions.split("x")
style.append("width: " + width + "px;")
style.append("height: " + height + "px;")
else:
style.append("width: " + dimensions + "px;")
def parse_position():
if len(left) > 0 and len(right) > 0:
style.append("margin-left: auto; margin-right: auto;")
elif len(left) > 0:
style.append("float: left;")
elif len(right) > 0:
style.append("float: right;")
parse_position()
parse_dimensions()
return " ".join(style)
def parse_source(self, src):
source = src if not "?" in src else src.split("?")[0]
return source.replace(":", "/")
def convert(self, text, config={}):
result = text
for match in Images.pattern.findall(text):
#replaced = "<img style='%s' src='/img/%s'>" % (self.parse_style(match), self.parse_source(match[1]))
replaced = ""
result = result.replace("{{" + "".join(match) + "}}", replaced)
return result

View File

@@ -0,0 +1,99 @@
import os
import re
from os import walk
from sys import platform
from pathlib import Path
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Links:
pattern = re.compile('(\[\[)(.*?)(\]\])')
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
def starts_with_space(match):
return match[1][0] == " "
for regex_link in Links.pattern.findall(text):
if starts_with_space(regex_link):
continue
origlink = "".join(regex_link)
convertedlink = ""
if "http" in origlink or "www" in origlink:
convertedlink = self.convert_as_external_link(origlink)
elif ">" in origlink and not "<" in origlink:
convertedlink = self.convert_as_interwiki_link(origlink)
else:
convertedlink = self.convert_as_internal_link(origlink, config["file_replace"], config["link_root"])
result = result.replace(origlink, convertedlink)
return result
def parse_url(self, text):
return text[2:text.index('|')]
def parse_title(self, text):
return text[text.index('|') + 1: text.index(']]')]
def convert_as_interwiki_link(self, text):
interwiki_shortcode = text[2:text.index('>')]
#self.assert_interwiki_is_known(interwiki_shortcode)
interwiki_urlpart = text[text.index(">") + 1: len(text) - 2]
return """`[%s`%s]""" % (interwiki_shortcode, interwiki_urlpart)
def convert_as_internal_link(self, text, file_replace, link_root):
url = ""
title = ""
if "|" not in text:
url = text[2:len(text) - 2].replace(":", "/")
title = text[2:len(text) - 2].replace(":", "/")
else:
url = self.parse_url(text).replace(":", "/")
title = self.parse_title(text)
if "." not in url:
url = url + ".mu"
for key, value in file_replace.items():
url = url.replace(key, value)
if not url.endswith(".mu"):
url += ".mu"
if not url.startswith("/"):
url = "/"+url
return """`[%s`%s%s]""" % (title, link_root, url.replace(' ', '_'))
def convert_as_external_link(self, text):
if "|" in text:
url = self.parse_url(text)
title = self.parse_title(text)
return "`[" + title + "`" + url + "]"
url = text.replace('[', '').replace(']', '')
return "`[" + url + "`" + url + "]"
def assert_interwiki_is_known(self, shortcode):
shortcodes = []
shortcodes_path = Path(__file__).parents[2].joinpath('layouts/shortcodes')
for (dirpath, dirnames, filenames) in walk(shortcodes_path):
shortcodes.extend(filenames)
break
if not (shortcode in map(lambda x: x.replace(".html", ""), shortcodes)):
raise ValueError("Unknown Interwiki code " + shortcode + " - please add a shortcode in the layouts dir!")

View File

@@ -0,0 +1,70 @@
import os
import re
from sys import platform
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class OrderedList:
pattern = re.compile('(^(\s*)-\s)(.*)', re.MULTILINE)
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
lines = text.split('\n')
last_used_linenrs = []
index = 0
result = text
def deeper_depth(depth):
return list(filter(lambda x: x[0] > depth, last_used_linenrs))
def drop_in_depth_detected(depth):
return len(deeper_depth(depth)) > 0
def remove_deeper_depths(depth):
for itm in deeper_depth(depth):
last_used_linenrs.remove(itm)
def last_used_by_depth(depth):
return list(filter(lambda x: x[0] == depth, last_used_linenrs))
def last_used_index(depth):
return last_used_by_depth(depth)[0][2]
def last_used_linenr(depth):
linenr_result = last_used_by_depth(depth)
if len(linenr_result) == 0:
return 0
return linenr_result[0][1]
def set_last_used_linenr(depth, linenr, the_index):
last_used_result = list(filter(lambda x: x[0] == depth, last_used_linenrs))
if len(last_used_result) > 0:
last_used_linenrs.remove(last_used_result[0])
last_used_linenrs.append((depth, linenr, the_index))
for match in OrderedList.pattern.findall(text):
current_line = (match[0] + match[2]).replace('\n', '')
current_depth = len(match[1].replace('\n', ''))
current_linenr = lines.index(current_line)
if last_used_linenr(current_depth) + 1 is current_linenr:
index += 1
elif drop_in_depth_detected(current_depth):
index = last_used_index(current_depth) + 1
remove_deeper_depths(current_depth)
else:
index = 1
set_last_used_linenr(current_depth, current_linenr, index)
result = result.replace(current_line, match[1].replace('\n', '') + str(index) + '. ' + match[2])
return result

View File

@@ -0,0 +1,98 @@
import os
from sys import platform
from re import compile
from abc import ABC
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
class NopStyle(ABC):
def convert(self, text, config={}):
return text
class SimpleReplacementStyle(ABC):
def __init__(self, dst_style, src_style):
self.mode = os.path.basename((os.path.dirname(__file__)))
self.dst_style = dst_style
self.src_style = src_style
def convert(self, text, config={}):
return text.replace(self.src_style, self.dst_style)
class SimpleStyleBetweenTags(ABC):
def __init__(self, dst_style, src_style_begin, src_style_end=None):
self.mode = os.path.basename((os.path.dirname(__file__)))
if src_style_end is None:
src_style_end = src_style_begin
self.pattern = compile("("+src_style_begin+")(.*?)("+src_style_end+")")
self.dst_style = dst_style
def convert(self, text, config={}):
result = text
for regex_head in self.pattern.findall(text):
orig_header = "".join(regex_head)
new_header = self.dst_style + regex_head[1] + self.dst_style
result = result.replace(orig_header, new_header)
return result
@Converter.register
class InlineHtml:
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
return text.replace("<html>", "").replace("</html>", "")
@Converter.register
class LineBreak(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("\n", "\\\\ ?")
@Converter.register
class Bold(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("`!", "\*\*")
@Converter.register
class Italic(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("`*", "//")
@Converter.register
class Underline(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("`_", "__")
@Converter.register
class StrikeThrough(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("~", "<del>", "</del>")
@Converter.register
class Subscript(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("", "<sub>", "</sub>")
@Converter.register
class Superscript(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("", "<sup>", "</sup>")
@Converter.register
class InlineCode(SimpleStyleBetweenTags):
def __init__(self):
super().__init__("", "''", "''")

View File

@@ -0,0 +1,27 @@
import os
from sys import platform
from re import compile
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Todo:
pattern = compile("(<todo(\s#)?>)(.*?)(</todo>)")
todo = "- [ ] "
done = "- [x] "
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
for match in Todo.pattern.findall(text):
prefix = Todo.todo if match[1] == "" else Todo.done
result = result.replace(match[0]+match[2]+match[3], prefix+match[2])
return result

View File

@@ -0,0 +1,29 @@
import os
import re
from sys import platform
if platform.startswith("win"):
from converter import Converter
else:
from ..converter import Converter
@Converter.register
class Cleanup:
def __init__(self):
self.mode = os.path.basename((os.path.dirname(__file__)))
def convert(self, text, config={}):
result = text
# Lines with spaces and tabs only
result = re.sub(r"^[ \t]+$", "", result, flags=re.MULTILINE)
# Lines with tabs and spaces at the end
result = re.sub(r"[ \t]+$", "", result, flags=re.MULTILINE)
# Replace more than two consecutive empty lines
result = re.sub(r"\n\s*\n\s*\n+", "\n\n\n", result, flags=re.MULTILINE)
return result

291
any2micronconverter/main.py Executable file
View File

@@ -0,0 +1,291 @@
##############################################################################################################
#
# Copyright (c) 2023 Sebastian Obele / obele.eu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This software uses the following software-parts:
# dokuwiki-to-hugo / Copyright (c) 2017 Wouter Groeneveld / https://git.brainbaking.com/wgroeneveld/dokuwiki-to-hugo / MIT License
#
##############################################################################################################
##############################################################################################################
# Include
#### System ####
import sys
import os
import time
import argparse
import shutil
if sys.platform.startswith("win"):
#### Version ####
from _version import __version__, __version_variant__, __copyright_short__, __title__, __description__, __config__
#### Converter ####
from converter import Converter
#### Commands - dokuwiki ####
from dokuwiki.code import *
from dokuwiki.comments import *
from dokuwiki.emoji import *
from dokuwiki.headers import *
from dokuwiki.images import *
from dokuwiki.links import *
from dokuwiki.lists import *
from dokuwiki.simplestyle import *
from dokuwiki.todo import *
from dokuwiki.z_cleanup import *
else:
#### Version ####
from ._version import __version__, __version_variant__, __copyright_short__, __title__, __description__, __config__
#### Converter ####
from .converter import Converter
#### Commands - dokuwiki ####
from .dokuwiki.code import *
from .dokuwiki.comments import *
from .dokuwiki.emoji import *
from .dokuwiki.headers import *
from .dokuwiki.images import *
from .dokuwiki.links import *
from .dokuwiki.lists import *
from .dokuwiki.simplestyle import *
from .dokuwiki.todo import *
from .dokuwiki.z_cleanup import *
##############################################################################################################
# Globals
PATH = os.path.expanduser("~") + "/." + os.path.splitext(os.path.basename(__file__))[0]
##############################################################################################################
# Log
LOG_FORCE = -1
LOG_CRITICAL = 0
LOG_ERROR = 1
LOG_WARNING = 2
LOG_NOTICE = 3
LOG_INFO = 4
LOG_VERBOSE = 5
LOG_DEBUG = 6
LOG_EXTREME = 7
LOG_LEVEL = LOG_NOTICE
LOG_LEVEL_SERVICE = LOG_NOTICE
LOG_TIMEFMT = "%Y-%m-%d %H:%M:%S"
LOG_MAXSIZE = 5*1024*1024
LOG_PREFIX = ""
LOG_SUFFIX = ""
LOG_FILE = ""
def log(text, level=3, file=None):
if not LOG_LEVEL:
return
if LOG_LEVEL >= level:
name = "Unknown"
if (level == LOG_FORCE):
name = ""
if (level == LOG_CRITICAL):
name = "Critical"
if (level == LOG_ERROR):
name = "Error"
if (level == LOG_WARNING):
name = "Warning"
if (level == LOG_NOTICE):
name = "Notice"
if (level == LOG_INFO):
name = "Info"
if (level == LOG_VERBOSE):
name = "Verbose"
if (level == LOG_DEBUG):
name = "Debug"
if (level == LOG_EXTREME):
name = "Extra"
if not isinstance(text, str):
text = str(text)
text = "[" + time.strftime(LOG_TIMEFMT, time.localtime(time.time())) +"] [" + name + "] " + LOG_PREFIX + text + LOG_SUFFIX
if file == None and LOG_FILE != "":
file = LOG_FILE
if file == None:
print(text)
else:
try:
file_handle = open(file, "a")
file_handle.write(text + "\n")
file_handle.close()
if os.path.getsize(file) > LOG_MAXSIZE:
file_prev = file + ".1"
if os.path.isfile(file_prev):
os.unlink(file_prev)
os.rename(file, file_prev)
except:
return
##############################################################################################################
# System
#### Panic #####
def panic():
sys.exit(255)
#### Exit #####
def exit():
sys.exit(0)
##############################################################################################################
# Setup/Start
#### Setup #####
def setup(mode=None, src=None, dst=None, loglevel=None, test=False, link_root=None, header=None, footer=None):
global LOG_LEVEL
config = __config__
if loglevel is not None:
LOG_LEVEL = loglevel
log("...............................................................................", LOG_INFO)
log(" Name: " + __title__ + " - " + __description__, LOG_INFO)
log("Program File: " + __file__, LOG_INFO)
log(" Version: " + __version__ + " " + __version_variant__, LOG_INFO)
log(" Copyright: " + __copyright_short__, LOG_INFO)
log("...............................................................................", LOG_INFO)
if src == None or dst == None:
log("Missing parameters", LOG_ERROR)
return
if src.endswith("/"):
src = src[:-1]
if dst.endswith("/"):
dst = dst[:-1]
if test:
log(" Test mode: YES", LOG_NOTICE)
log(" Mode: " + str(mode), LOG_NOTICE)
log(" Source path: " + src, LOG_NOTICE)
log(" Dest path: " + dst, LOG_NOTICE)
if link_root:
config["link_root"] = link_root
if config["link_root"].endswith("/"):
config["link_root"] = config["link_root"][:-1]
if header:
config["header"] = header
config["header"] = config["header"].replace("\\n", '\n')
if footer:
config["footer"] = footer
config["footer"] = config["footer"].replace("\\n", '\n')
if os.path.exists(dst):
log("Deleting dest directory "+dst, LOG_DEBUG)
if not test:
shutil.rmtree(dst)
if not test:
os.makedirs(dst)
for root, subFolders, files in os.walk(src):
dst_dir = dst+root.replace(src, "", 1)
if dst_dir.endswith("/"):
dst_dir = dst_dir[:-1]
files = [f for f in files if not f[0] == '.']
for file in files:
try:
src_file = root+"/"+file
if file in config["file_replace"]:
dst_file = config["file_replace"][file]
else:
dst_file = os.path.splitext(file)[0] + ".mu"
log("Converting "+dst_dir+"/"+dst_file+" ("+str(mode)+")", LOG_DEBUG)
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
output = Converter(src_file, config, mode).convert()
except Exception as e:
log("Failed to convert "+file+": "+str(e), LOG_ERROR)
output = "Failed to convert: "+str(e)
try:
output = config["header"] + output + config["footer"]
if not test:
with open(dst_dir+"/"+dst_file, "w", newline="\n") as fh:
fh.write(output)
except Exception as e:
log("Failed to write "+file+": "+str(e), LOG_ERROR)
#### Start ####
def main():
try:
description = __title__ + " - " + __description__
parser = argparse.ArgumentParser(description=description)
parser.add_argument("-m", "--mode", action="store", type=str, default=None, help="Mode or type of source data for conversion (Folder name with the command files)")
parser.add_argument("-s", "--src", action="store", type=str, default=None, help="Path to source directory (All subfolders are included)")
parser.add_argument("-d", "--dst", action="store", type=str, default=None, help="Path to destination directory (Folder is overwritten/deleted)")
parser.add_argument("-l", "--loglevel", action="store", type=int, default=LOG_LEVEL)
parser.add_argument("-t", "--test", action="store_true", default=False, help="Running in test mode (No files are deleted or overwritten)")
parser.add_argument("--link_root", action="store", type=str, default=None, help="Root path of the links (Internal links in the documents)")
parser.add_argument("--header", action="store", type=str, default=None, help="Header string which will be added on all pages")
parser.add_argument("--footer", action="store", type=str, default=None, help="Footer string which will be added on all pages")
params = parser.parse_args()
setup(mode=params.mode, src=params.src, dst=params.dst, loglevel=params.loglevel, test=params.test, link_root=params.link_root, header=params.header, footer=params.footer)
except KeyboardInterrupt:
print("Terminated by CTRL-C")
exit()
##############################################################################################################
# Init
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,101 @@
##############################################################################################################
#
# Copyright (c) 2023 Sebastian Obele / obele.eu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This software uses the following software-parts:
# dokuwiki-to-hugo / Copyright (c) 2017 Wouter Groeneveld / https://git.brainbaking.com/wgroeneveld/dokuwiki-to-hugo / MIT License
#
##############################################################################################################
#### Import ####
import sys
import os
import re
from pathlib import Path
import pyinstaller_versionfile
#from kivy_deps import sdl2, glew
#from kivymd import hooks_path as kivymd_hooks_path
#### Get Var ####
def get_var(file, var) -> str:
content = open(file, "rt", encoding="utf-8").read()
try:
regex = r"(?<=^"+var+" = ['\"])[^'\"]+(?=['\"]$)"
value = re.findall(regex, content, re.M)[0]
return value
except IndexError:
raise ValueError(f"Unable to find string {var} in {file}.")
return ""
#### Build ####
path = os.path.abspath(".")
version_file = "_version.py"
main_file = "main.py"
pyinstaller_versionfile.create_versionfile(
output_file="version.rc",
version=get_var(version_file, "__version__")+".0",
company_name=get_var(version_file, "__author__"),
file_description=get_var(version_file, "__description__"),
internal_name=get_var(version_file, "__title__"),
legal_copyright=get_var(version_file, "__copyright_short__"),
original_filename=get_var(version_file, "__title__")+".exe",
product_name=get_var(version_file, "__title__")
)
added_files = [
# ("assets", "assets"),
]
a = Analysis(
[main_file],
pathex=[path],
#hiddenimports=["plyer.platforms.win.storagepath"],
#hookspath=[kivymd_hooks_path],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False,
datas=added_files,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
#*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
debug=False,
strip=False,
upx=True,
name=get_var(version_file, "__package_name__")+"-"+get_var(version_file, "__version__"),
#icon="assets\icon.ico",
console=True,
version="version.rc",
)

184
build_git.sh Executable file
View File

@@ -0,0 +1,184 @@
#!/bin/bash
##############################################################################################################
# Configuration
BRANCH="main"
ORIGIN="git@github.com:SebastianObi/Any2MicronConverter.git"
FILES_ADD=("*")
FILES_REMOVE=(".git/*")
COMMENT_COMMIT="$(date +%Y-%m-%d_%H:%M:%S)"
COMMENT_CLEAR="Removed history, due to sensitive data"
COMMENT_INIT="Initial commit"
##############################################################################################################
# Functions
_prompt() {
echo -e ""
echo -e "Select an option:"
options=("Commit/Push" "Clear History" "Init" "Init (Pull only)" "Init (Push only)" "Exit")
select opt in "${options[@]}"; do
case $opt in
"Commit/Push"*)
_commit
break;;
"Clear History"*)
_clear
break;;
"Init (Pull only)"*)
_init_pull
break;;
"Init (Push only)"*)
_init_push
break;;
"Init"*)
_init
break;;
"Exit"*)
echo -e ""
echo -e "Exit"
break;;
*)
echo -e "Invalid choice!"
break;;
esac
done
}
_define_files() {
for file in ${FILES_ADD[@]}; do
git add "${file}"
done
for file in ${FILES_REMOVE[@]}; do
git reset -- "${file}"
done
}
_commit() {
_define_files
git diff --numstat
echo -e ""
echo -e "Commit/Push to Git"
echo -e "Comment:"
read VAR
if [ -z "${VAR}" ]; then
VAR="${COMMENT_COMMIT}"
fi
git commit -a -m "${VAR}"
git push
}
_clear() {
echo -e ""
echo -e "Clear History"
echo -e "Comment:"
read VAR
if [ -z "${VAR}" ]; then
VAR="${COMMENT_CLEAR}"
fi
rm -rf .git
git init
_define_files
git commit -m "${VAR}"
git branch -M "${BRANCH}"
git remote add origin "${ORIGIN}"
git push -f -u origin "${BRANCH}"
}
_init() {
echo -e ""
echo -e "Init"
echo -e "Comment:"
read VAR
if [ -z "${VAR}" ]; then
VAR="${COMMENT_INIT}"
fi
rm -rf .git
git init
_define_files
git branch -M "${BRANCH}"
git remote add origin "${ORIGIN}"
git pull origin "${BRANCH}"
git commit -m "${VAR}"
git push -f -u origin "${BRANCH}"
}
_init_pull() {
echo -e ""
echo -e "Init (Pull only)"
read VAR
if [ -z "${VAR}" ]; then
VAR="${COMMENT_INIT}"
fi
rm -rf .git
git init
_define_files
git branch -M "${BRANCH}"
git remote add origin "${ORIGIN}"
git pull origin "${BRANCH}"
git push -f -u origin "${BRANCH}"
}
_init_push() {
echo -e ""
echo -e "Init (Push only)"
read VAR
if [ -z "${VAR}" ]; then
VAR="${COMMENT_INIT}"
fi
rm -rf .git
git init
_define_files
git branch -M "${BRANCH}"
git remote add origin "${ORIGIN}"
git push -f -u origin "${BRANCH}"
}
##############################################################################################################
# Setup/Start
_prompt

100
setup.py Normal file
View File

@@ -0,0 +1,100 @@
##############################################################################################################
#
# Copyright (c) 2023 Sebastian Obele / obele.eu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This software uses the following software-parts:
# dokuwiki-to-hugo / Copyright (c) 2017 Wouter Groeneveld / https://git.brainbaking.com/wgroeneveld/dokuwiki-to-hugo / MIT License
#
##############################################################################################################
#### Import ####
import os
import re
import setuptools
from pathlib import Path
#### Get Var ####
def get_var(file, var) -> str:
content = open(file, "rt", encoding="utf-8").read()
try:
regex = r"(?<=^"+var+" = ['\"])[^'\"]+(?=['\"]$)"
value = re.findall(regex, content, re.M)[0]
return value
except IndexError:
raise ValueError(f"Unable to find string {var} in {file}.")
return ""
#### Build ####
with open("README.md", "r") as fh:
long_description = fh.read()
version_file = os.path.join(os.path.dirname(__file__), "any2micronconverter", "_version.py")
def glob_converter():
out_files = []
src_path = os.path.join(os.path.dirname(__file__), "any2micronconverter")
for root, dirs, files in os.walk(src_path):
for file in files:
filepath = os.path.join(str(Path(*Path(root).parts[1:])), file)
print(filepath)
out_files.append(filepath.split(f"any2micronconverter{os.sep}")[1])
return out_files
package_data = {
"": [
"assets/*",
*glob_converter()
]
}
print("Packaging "+get_var(version_file, "__title__")+" "+get_var(version_file, "__version__")+" "+get_var(version_file, "__version_variant__"))
setuptools.setup(
name=get_var(version_file, "__package_name__"),
version=get_var(version_file, "__version__"),
author=get_var(version_file, "__author__"),
author_email=get_var(version_file, "__author_email__"),
description=get_var(version_file, "__description__"),
long_description=long_description,
long_description_content_type="text/markdown",
url=get_var(version_file, "__copyright_url__"),
packages=setuptools.find_packages(),
package_data=package_data,
include_package_data=True,
classifiers=[
"Programming Language :: Python :: 3",
"License :: Other/Proprietary License",
"Operating System :: OS Independent",
],
entry_points= {
'console_scripts': [
get_var(version_file, "__package_name__")+'=any2micronconverter.main:main',
]
},
install_requires=[],
extras_require={
"macos": ["pyobjus"],
},
python_requires='>=3.6',
)