Spaces:
Running
Running
freemt
commited on
Commit
·
2c2081e
1
Parent(s):
e55a136
Bump version from 0.1.0-alpha.0 to 0.1.0-alpha.1, pendulum diff_for_humans eta
Browse files- .stignore +101 -0
- app_mlbee.py +164 -0
- data/king-later01-de.txt +107 -0
- data/king-later01-en.txt +106 -0
- data/sternstunden04-de.txt +57 -0
- data/sternstunden04-en.txt +61 -0
- install-sw.sh +24 -0
- mlbee/__init__.py +1 -1
- mlbee/app-litbee.py- +202 -0
- mlbee/color_map.py +49 -0
- mlbee/cos_matrix2.py +47 -0
- mlbee/fetch_paste.py +48 -0
- mlbee/fetch_upload.py +129 -0
- mlbee/fetch_urls.py +173 -0
- mlbee/gen_cmat.py +75 -0
- mlbee/home.py +288 -0
- mlbee/info.py +16 -0
- mlbee/loadtext.py +126 -0
- mlbee/mlbee.py +49 -3
- mlbee/multipage.py +68 -0
- mlbee/settings.py +98 -0
- mlbee/text2lists.py +167 -0
- mlbee/url2txt.py +97 -0
- mlbee/utils.py +321 -0
- okteto.yml +44 -0
- poetry.lock +0 -0
- pyproject.toml +14 -1
- requirements.txt +50 -0
- run-flake8.sh +1 -0
- run-nodemon-streamlit-run-app_mlbee.sh +1 -0
- tests/test_gen_cmat_aset2pairs.py +57 -0
- tests/test_time.py- +37 -0
.stignore
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.git
|
2 |
+
# Byte-compiled / optimized / DLL files
|
3 |
+
__pycache__
|
4 |
+
*.py[cod]
|
5 |
+
*$py.class
|
6 |
+
|
7 |
+
# C extensions
|
8 |
+
*.so
|
9 |
+
|
10 |
+
# Distribution / packaging
|
11 |
+
.Python
|
12 |
+
build
|
13 |
+
develop-eggs
|
14 |
+
dist
|
15 |
+
downloads
|
16 |
+
eggs
|
17 |
+
.eggs
|
18 |
+
lib
|
19 |
+
lib64
|
20 |
+
parts
|
21 |
+
sdist
|
22 |
+
var
|
23 |
+
wheels
|
24 |
+
pip-wheel-metadata
|
25 |
+
share/python-wheels
|
26 |
+
*.egg-info
|
27 |
+
.installed.cfg
|
28 |
+
*.egg
|
29 |
+
MANIFEST
|
30 |
+
|
31 |
+
# PyInstaller
|
32 |
+
# Usually these files are written by a python script from a template
|
33 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
34 |
+
*.manifest
|
35 |
+
*.spec
|
36 |
+
|
37 |
+
# Installer logs
|
38 |
+
pip-log.txt
|
39 |
+
pip-delete-this-directory.txt
|
40 |
+
|
41 |
+
# Translations
|
42 |
+
*.mo
|
43 |
+
*.pot
|
44 |
+
|
45 |
+
# Django stuff:
|
46 |
+
*.log
|
47 |
+
local_settings.py
|
48 |
+
db.sqlite3
|
49 |
+
|
50 |
+
# Flask stuff:
|
51 |
+
instance
|
52 |
+
.webassets-cache
|
53 |
+
|
54 |
+
# Scrapy stuff:
|
55 |
+
.scrapy
|
56 |
+
|
57 |
+
# Sphinx documentation
|
58 |
+
docs/_build
|
59 |
+
|
60 |
+
# PyBuilder
|
61 |
+
target
|
62 |
+
|
63 |
+
# Jupyter Notebook
|
64 |
+
.ipynb_checkpoints
|
65 |
+
|
66 |
+
# IPython
|
67 |
+
profile_default
|
68 |
+
ipython_config.py
|
69 |
+
|
70 |
+
# pyenv
|
71 |
+
.python-version
|
72 |
+
|
73 |
+
# celery beat schedule file
|
74 |
+
celerybeat-schedule
|
75 |
+
|
76 |
+
# SageMath parsed files
|
77 |
+
*.sage.py
|
78 |
+
|
79 |
+
# Environments
|
80 |
+
.env
|
81 |
+
.venv
|
82 |
+
env
|
83 |
+
venv
|
84 |
+
ENV
|
85 |
+
env.bak
|
86 |
+
venv.bak
|
87 |
+
|
88 |
+
# Spyder project settings
|
89 |
+
.spyderproject
|
90 |
+
.spyproject
|
91 |
+
|
92 |
+
# Rope project settings
|
93 |
+
.ropeproject
|
94 |
+
|
95 |
+
# mypy
|
96 |
+
.mypy_cache
|
97 |
+
.dmypy.json
|
98 |
+
dmypy.json
|
99 |
+
|
100 |
+
# Pyre type checker
|
101 |
+
.pyre
|
app_mlbee.py
ADDED
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Prep for streamlit run app_mlbee.py.
|
2 |
+
|
3 |
+
Based on app.py in lit-bee
|
4 |
+
|
5 |
+
https://docs.streamlit.io/knowledge-base/using-streamlit/hide-row-indices-displaying-dataframe
|
6 |
+
Hide row indices when displaying a dataframe
|
7 |
+
# CSS to inject contained in a string
|
8 |
+
hide_table_row_index = '''
|
9 |
+
<style>
|
10 |
+
tbody th {display:none}
|
11 |
+
.blank {display:none}
|
12 |
+
</style>
|
13 |
+
'''
|
14 |
+
# Inject CSS with Markdown
|
15 |
+
st.markdown(hide_table_row_index, unsafe_allow_html=True)
|
16 |
+
|
17 |
+
# Display a static table
|
18 |
+
st.table(df)
|
19 |
+
|
20 |
+
# Hide row indices with st.dataframe
|
21 |
+
# CSS to inject contained in a string
|
22 |
+
hide_dataframe_row_index = '''
|
23 |
+
<style>
|
24 |
+
.row_heading.level0 {display:none}
|
25 |
+
.blank {display:none}
|
26 |
+
</style>
|
27 |
+
'''
|
28 |
+
# Inject CSS with Markdown
|
29 |
+
st.markdown(hide_dataframe_row_index, unsafe_allow_html=True)
|
30 |
+
|
31 |
+
# Display an interactive table
|
32 |
+
st.dataframe(df)
|
33 |
+
|
34 |
+
https://medium.com/@avra42/streamlit-python-cool-tricks-to-make-your-web-application-look-better-8abfc3763a5b
|
35 |
+
hide_menu_style = '''
|
36 |
+
<style>
|
37 |
+
#MainMenu {visibility: hidden; }
|
38 |
+
footer {visibility: hidden;}
|
39 |
+
</style>
|
40 |
+
'''
|
41 |
+
st.markdown(hide_menu_style, unsafe_allow_html=True)
|
42 |
+
"""
|
43 |
+
# pylint: disable=invalid-name
|
44 |
+
import os
|
45 |
+
import sys
|
46 |
+
import time
|
47 |
+
from pathlib import Path
|
48 |
+
from types import SimpleNamespace
|
49 |
+
from typing import Optional
|
50 |
+
|
51 |
+
import loguru
|
52 |
+
import logzero
|
53 |
+
import pandas as pd
|
54 |
+
|
55 |
+
import streamlit as st
|
56 |
+
from loguru import logger as loggu
|
57 |
+
from logzero import logger
|
58 |
+
from set_loglevel import set_loglevel
|
59 |
+
from streamlit import session_state as state
|
60 |
+
|
61 |
+
from mlbee import __version__
|
62 |
+
from mlbee.utils import menu_items
|
63 |
+
from mlbee.multipage import Multipage
|
64 |
+
|
65 |
+
from mlbee.home import home
|
66 |
+
from mlbee.settings import settings
|
67 |
+
from mlbee.info import info
|
68 |
+
from mlbee.utils import style_css
|
69 |
+
|
70 |
+
# curr_py = sys.version[:3]
|
71 |
+
# msg = f"Some packages mlbee depends on can only run with Python 3.8, current python is **{curr_py}**, sorry..."
|
72 |
+
# assert curr_py == "3.8", msg
|
73 |
+
|
74 |
+
os.environ["TZ"] = "Asia/Shanghai"
|
75 |
+
try:
|
76 |
+
time.tzset() # type: ignore
|
77 |
+
except Exception as _:
|
78 |
+
logger.warning("time.tzset() error: %s. Probably running Windows, we let it pass.", _)
|
79 |
+
|
80 |
+
# uncomment this in dev oe set/export LOGLEVEL=10
|
81 |
+
# os.environ["LOGLEVEL"] = "10"
|
82 |
+
|
83 |
+
logzero.loglevel(set_loglevel())
|
84 |
+
|
85 |
+
loggu.remove()
|
86 |
+
_ = (
|
87 |
+
"<green>{time:YY-MM-DD HH:mm:ss}</green> | "
|
88 |
+
"<level>{level: <5}</level> | <level>{message}</level> "
|
89 |
+
"<cyan>{module}.{name}</cyan>:<cyan>{line}</cyan>"
|
90 |
+
)
|
91 |
+
loggu.add(
|
92 |
+
sys.stderr,
|
93 |
+
format=_,
|
94 |
+
level=set_loglevel(),
|
95 |
+
colorize=True,
|
96 |
+
)
|
97 |
+
|
98 |
+
# from PIL import Image
|
99 |
+
# page_icon=Image.open("icon.ico"),
|
100 |
+
st.set_page_config( # type: ignore
|
101 |
+
page_title=f"mlbee v{__version__}",
|
102 |
+
# page_icon="🧊",
|
103 |
+
page_icon="🐝",
|
104 |
+
# layout="wide",
|
105 |
+
initial_sidebar_state="auto", # "auto" or "expanded" or "collapsed",
|
106 |
+
menu_items=menu_items,
|
107 |
+
)
|
108 |
+
|
109 |
+
# pd.set_option("precision", 2)
|
110 |
+
pd.set_option("display.precision", 2)
|
111 |
+
pd.options.display.float_format = "{:,.2f}".format
|
112 |
+
|
113 |
+
beetype = "mlbee"
|
114 |
+
sourcetype = "upload"
|
115 |
+
if set_loglevel() <= 10:
|
116 |
+
sourcetype = "urls"
|
117 |
+
|
118 |
+
_ = dict(
|
119 |
+
beetype=beetype,
|
120 |
+
sourcetype=sourcetype,
|
121 |
+
sourcecount=2,
|
122 |
+
sentali=None,
|
123 |
+
src_filename="",
|
124 |
+
tgt_filename="",
|
125 |
+
src_fileio=b"",
|
126 |
+
tgt_fileio=b"",
|
127 |
+
src_file="",
|
128 |
+
tgt_file="",
|
129 |
+
list1=[""],
|
130 |
+
list2=[""],
|
131 |
+
df=None,
|
132 |
+
df_a=None,
|
133 |
+
df_s_a=None,
|
134 |
+
count=1,
|
135 |
+
updated=False,
|
136 |
+
)
|
137 |
+
if "ns" not in state:
|
138 |
+
state.ns = SimpleNamespace(**_)
|
139 |
+
state.ns.list = [*_]
|
140 |
+
|
141 |
+
|
142 |
+
def main():
|
143 |
+
"""Bootstrap."""
|
144 |
+
# options()
|
145 |
+
|
146 |
+
st.markdown(f"<style>{style_css}</style>", unsafe_allow_html=True)
|
147 |
+
|
148 |
+
app = Multipage()
|
149 |
+
|
150 |
+
app.add_page("Home", "house", home)
|
151 |
+
app.add_page("Settings", "gear", settings)
|
152 |
+
app.add_page("Info", "info", info)
|
153 |
+
|
154 |
+
app.run()
|
155 |
+
|
156 |
+
if set_loglevel() <= 10:
|
157 |
+
st.markdown(state.ns.count)
|
158 |
+
loggu.debug(f" run: {state.ns.count}")
|
159 |
+
logger.debug(f" run: {state.ns.count}")
|
160 |
+
state.ns.count += 1
|
161 |
+
state.ns.updated = False
|
162 |
+
|
163 |
+
|
164 |
+
main()
|
data/king-later01-de.txt
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
1
|
4 |
+
|
5 |
+
|
6 |
+
Ich war gerade mit meiner Mutter auf dem Heimweg von der Schule. Sie hatte mich an die Hand genommen, und in der anderen hielt ich meinen Truthahn, den wir Erstklässler in der Woche vor Thanksgiving angefertigt hatten. Ich war stolz wie Oskar. Wir machten das folgendermaßen: Man legte eine Hand auf ein Blatt Tonpapier und umfuhr sie mit einem Wachsmalstift. So entstanden der Schwanz und der Rumpf. Was den Kopf anging, war man auf sich selbst gestellt.
|
7 |
+
|
8 |
+
Als ich Mama meinen Truthahn zeigte, sagte sie ja, ja, ja, genau, genau, genau, total super, aber ich glaube nicht, dass sie ihn sich richtig angesehen hat. Wahrscheinlich dachte sie stattdessen an eines der Bücher, die sie verkaufen wollte. »Das Produkt an den Mann bringen«, wie sie es nannte. Mama war nämlich Literaturagentin. Früher hat das ihr Bruder gemacht, mein Onkel Harry, aber ein Jahr vor der Zeit, von der ich hier erzähle, hatte Mama seine Agentur übernommen. Das ist eine lange, ziemlich traurige Geschichte.
|
9 |
+
|
10 |
+
»Ich hab Dunkelgrün genommen, weil das meine Lieblingsfarbe ist«, sagte ich. »Das weißt du doch, oder?« Inzwischen waren wir fast da. Unsere Wohnung lag nur drei Straßen von meiner Schule entfernt.
|
11 |
+
|
12 |
+
Ja, ja, ja, hat Mama gesagt. Außerdem: »Wenn wir zu Hause sind, spielst du was oder schaust dir Barney und den Zauberschulbus an, Kleiner. Ich muss massenhaft Anrufe erledigen.«
|
13 |
+
|
14 |
+
Worauf ich ja, ja, ja von mir gegeben habe, was mir einen Knuff in die Seite und ein Grinsen einbrachte. Ich fand es immer toll, wenn ich meine Mutter zum Grinsen bringen konnte. Schon mit sechs wusste ich nämlich, dass sie das Leben äußerst ernst nahm. Später stellte ich fest, dass das teilweise an mir lag. Sie ging irgendwie davon aus, dass sie ein ziemlich gestörtes Kind aufziehen würde. Der Tag, von dem ich gerade erzähle, war jedenfalls der, wo sie definitiv zu dem Schluss kam, dass ich doch nicht gestört war. Was für sie einerseits eine Erleichterung und andererseits keine gewesen sein muss.
|
15 |
+
|
16 |
+
»Du sprichst mit keinem darüber, ja?«, sagte sie später an jenem Tag zu mir. »Außer mit mir. Und vielleicht nicht mal mit mir, Kleiner. Okay?«
|
17 |
+
|
18 |
+
Ich sagte okay. Wenn man klein ist und es sich um die eigene Mama handelt, sagt man zu allem okay. Außer natürlich, sie erklärt einem, dass es Zeit fürs Bett ist. Oder sie befiehlt, bloß ja den ganzen Brokkoli aufzuessen.
|
19 |
+
|
20 |
+
Als wir zu Hause ankamen, war der Aufzug immer noch kaputt. Theoretisch könnte man sagen, das Ganze wäre vielleicht anders gelaufen, wenn das Ding funktioniert hätte, aber das glaube ich nicht. Leute, die behaupten, im Leben gehe es nur um die Entscheidungen, die wir treffen, und um die Wege, die wir wählen, haben meiner Meinung nach keinen blassen Schimmer. Egal ob Treppe oder Aufzug, wir wären in jedem Fall im zweiten Stock gelandet. Wenn der flatterhafte Finger des Schicksals auf einen zeigt, führen alle Wege zum selben Ort, meiner Meinung nach jedenfalls. Eventuell werde ich meine Meinung ändern, wenn ich älter bin, aber das glaube ich eigentlich nicht.
|
21 |
+
|
22 |
+
»Was für ein Scheißaufzug«, sagte Mama. Und dann: »Das hast du nicht gehört, Kleiner.«
|
23 |
+
|
24 |
+
»Was denn?«, sagte ich, was mir ein weiteres Grinsen einbrachte. Es sollte das letzte an jenem Nachmittag sein, so viel kann ich verraten. Ich fragte, ob ich ihre Tasche tragen solle, in der wie immer ein Manuskript steckte. An jenem Tag war es ein dickes, das nach circa fünfhundert Seiten aussah (wenn das Wetter gut war, saß Mama immer auf einer Bank und las, während sie darauf wartete, dass ich aus der Schule kam). »Lieb von dir«, sagte sie. »Aber was erkläre ich dir immer?«
|
25 |
+
|
26 |
+
»Jeder muss im Leben seine eigene Last tragen«, sagte ich.
|
27 |
+
|
28 |
+
»Haargenau.«
|
29 |
+
|
30 |
+
»Ist es von Regis Thomas?«, fragte ich.
|
31 |
+
|
32 |
+
»Richtig geraten. Von dem guten alten Regis, der unsere Miete bezahlt.«
|
33 |
+
|
34 |
+
»Geht es um Roanoke?«
|
35 |
+
|
36 |
+
»Musst du das wirklich fragen, Jamie?« Was mich zum Kichern brachte. Alles, was der gute alte Regis schrieb, spielte in Roanoke. Das war die Last, die er im Leben trug.
|
37 |
+
|
38 |
+
Wir stiegen die Treppe hinauf in den zweiten Stock, wo sich außer unserer noch zwei weitere Wohnungen befanden. Unsere hinten im Flur war die schickste. Vor der Tür von 3A standen Mr. und Mrs. Burkett, und mir war sofort klar, dass etwas nicht stimmte. Mr. Burkett rauchte eine Zigarette, was ich bei ihm noch nie gesehen hatte und was in unserem Haus außerdem verboten war. Seine Augen waren blutunterlaufen, und die Haare standen ihm in wirren grauen Büscheln vom Kopf ab. Ich sagte immer Mister zu ihm, aber eigentlich war er Professor Burkett und lehrte an der New York University irgendetwas Kluges. Englische und europäische Dichtung, wie ich später erfuhr. Mrs. Burkett stand barfuß nur im Nachthemd da. Das Nachthemd war ziemlich dünn. Ich konnte durch es hindurch fast alles von ihr sehen.
|
39 |
+
|
40 |
+
»Was ist denn passiert, Marty?«, fragte meine Mutter.
|
41 |
+
|
42 |
+
Bevor er etwas erwidern konnte, zeigte ich ihm meinen Truthahn. Weil Mr. Burkett traurig wirkte und ich ihn aufmuntern wollte, aber auch weil ich so stolz darauf war. »Schauen Sie mal, Mr. Burkett! Ich hab einen Truthahn gemalt! Schauen Sie mal, Mrs. Burkett!« Beim Hinhalten hob ich ihn vors Gesicht, weil sie nicht denken sollte, dass ich auf ihren Busen glotzte.
|
43 |
+
|
44 |
+
Mr. Burkett achtete nicht auf mich. Ich glaube, er hatte mich nicht mal gehört. »Tia, ich habe eine schreckliche Nachricht. Heute Morgen ist Mona gestorben.«
|
45 |
+
|
46 |
+
Meine Mutter ließ die Tasche mit dem Manuskript zwischen ihre Füße fallen und schlug sich die Hand vor den Mund. »O nein! Das ist nicht wahr!«
|
47 |
+
|
48 |
+
Er brach in Tränen aus. »Sie ist nachts aufgestanden und hat gesagt, sie will ein Glas Wasser trinken. Ich bin wieder eingeschlafen, und heute Morgen lag sie auf dem Sofa und hatte eine Steppdecke bis zum Kinn gezogen. Da bin ich auf Zehenspitzen in die Küche gegangen und hab Kaffee aufgesetzt, weil ich dachte, der Duft würde sie auf-auf-w-w-w… würde sie aufwecken …«
|
49 |
+
|
50 |
+
Dann brach er wirklich zusammen. Mama nahm ihn in die Arme, wie sie es mit mir tat, wenn ich mir wehgetan hatte, obwohl Mr. Burkett ungefähr hundert Jahre alt war (vierundsiebzig, wie ich später erfuhr).
|
51 |
+
|
52 |
+
Und da sagte Mrs. Burkett etwas zu mir. Sie war schwer zu verstehen, wenn auch nicht so schwer wie manche anderen, weil sie noch ziemlich frisch war. »Truthähne sind nicht grün, James«, sagte sie.
|
53 |
+
|
54 |
+
»Meiner schon«, sagte ich.
|
55 |
+
|
56 |
+
Meine Mutter hielt Mr. Burkett immer noch in den Armen und wiegte ihn irgendwie hin und her. Die beiden hörten Mrs. Burkett nicht, und mich hörten sie auch nicht, weil sie erwachsene Dinge taten: Mama spendete Trost, Mr. Burkett heulte.
|
57 |
+
|
58 |
+
»Ich hab Doktor Allen angerufen«, sagte Mr. Burkett. »Als er dann da war, meinte er, dass sie wahrscheinlich einen Schlag hatte.« Wenigstens glaube ich, dass er das gesagt hat. Er weinte so sehr, dass er nicht gut zu verstehen war. »Er hat für mich beim Bestattungsinstitut angerufen. Die haben sie weggebracht. Ich weiß gar nicht, was ich ohne sie tun soll.«
|
59 |
+
|
60 |
+
Mrs. Burkett sagte: »Wenn mein Mann nicht aufpasst, wird er deiner Mutter mit seiner Zigarette noch das Haar versengen.«
|
61 |
+
|
62 |
+
Was er tatsächlich tat. Ich konnte die schmorenden Haare riechen, ein Gestank wie beim Damenfriseur. Mama war zu höflich, etwas zu sagen, aber sie sorgte dafür, dass er sie losließ, und dann nahm sie ihm die Zigarette ab, warf sie auf den Boden und trat darauf. Das fand ich richtig eklig, eine echte Schweinerei, aber ich hielt den Mund. Ich kapierte, dass es sich um eine besondere Situation handelte.
|
63 |
+
|
64 |
+
Außerdem kapierte ich, dass er durchdrehen würde, wenn ich weiter mit Mrs. Burkett sprach. Mama ebenfalls. Selbst ein kleiner Junge weiß gewisse grundlegende Dinge, wenn er nicht weich in der Birne ist. Man sagt bitte, man sagt danke, man wedelt in der Öffentlichkeit nicht mit dem Schniedel herum, man kaut nicht mit offenem Mund, und man spricht nicht mit toten Leuten, wenn sie neben lebenden Leuten stehen, die gerade erst anfangen, sie zu vermissen. Zu meiner Verteidigung will ich nur sagen: Beim ersten Anblick war mir noch nicht bewusst gewesen, dass sie tot war. Später wurde ich besser darin, den Unterschied zu beurteilen, aber damals musste ich das erst noch lernen. Durch das Nachthemd von Mrs. Burkett konnte ich hindurchschauen, durch sie selbst nicht. Tote sehen genauso aus wie Lebende, nur dass sie immer die Kleidung wie beim Sterben tragen.
|
65 |
+
|
66 |
+
Währenddessen kaute Mr. Burkett die ganze Sache noch einmal durch. Er erzählte meiner Mutter, wie er neben dem Sofa auf dem Boden gesessen und die Hand seiner Frau gehalten hatte, bis der Arzt kam, und dann wieder, bis die Leute vom Bestattungsinstitut gekommen waren, um sie wegzuschaffen. Eigentlich sagte er: »Um sie von dannen zu bringen«, was ich nicht verstand, bis Mama es mir erklärte. Das Weinen hatte zwischendurch nachgelassen, nahm jetzt jedoch wieder Fahrt auf. »Ihre Ringe sind weg«, sagte er unter Tränen. »Nicht nur ihr Hochzeitsring, sondern auch der Verlobungsring mit dem großen Diamanten. Ich hab auf dem Nachttisch auf ihrer Bettseite nachgeschaut, wo sie die Dinger immer hinlegt, wenn sie sich die Hände mit der scheußlich riechenden Arthritissalbe einreibt …«
|
67 |
+
|
68 |
+
»Die riecht tatsächlich erbärmlich«, bestätigte Mrs. Burkett. »Das Lanolin da drin ist im Grunde nur Schaffett, aber irgendwie hilft’s.«
|
69 |
+
|
70 |
+
Ich nickte, um auszudrücken, dass ich verstanden hätte, sagte aber nichts.
|
71 |
+
|
72 |
+
»… und auf dem Waschbecken im Bad, weil sie die Ringe manchmal da liegen lässt … Überall hab ich nachgeschaut.«
|
73 |
+
|
74 |
+
»Die werden schon wieder auftauchen«, sagte meine Mutter besänftigend, und da ihre Haare jetzt nicht mehr gefährdet waren, nahm sie Mr. Burkett wieder in die Arme. »Sie tauchen bestimmt wieder auf, Marty, mach dir darüber keine Sorgen.«
|
75 |
+
|
76 |
+
»Ich vermisse sie so sehr! Ich vermisse sie jetzt schon!«
|
77 |
+
|
78 |
+
Mrs. Burkett wedelte mit der Hand vor dem Gesicht. »Wahrscheinlich dauert es nicht mal sechs Wochen, bis er Dolores Magowan zum Mittagessen ausführt.«
|
79 |
+
|
80 |
+
Mr. Burkett flennte, und meine Mutter tröstete ihn, so wie sie mich tröstete, wenn ich mir mal das Knie aufschürfte oder wie damals, als ich ihr eine Tasse Tee machen wollte und mir heißes Wasser auf die Hand geschwappt ist. Es herrschte also irgendwie ein ziemlich hoher Lärmpegel, weshalb ich es riskierte, wenn auch nur mit leiser Stimme.
|
81 |
+
|
82 |
+
»Wo sind Ihre Ringe denn, Mrs. Burkett? Wissen Sie das?«
|
83 |
+
|
84 |
+
Wenn sie tot sind, müssen sie die Wahrheit sagen. Im Alter von sechs Jahren wusste ich das allerdings noch nicht; ich nahm einfach an, dass alle Erwachsenen, ob nun lebendig oder tot, stets die Wahrheit sagten. Natürlich glaubte ich damals auch, Goldlöckchen wäre ein echtes Mädchen. Man darf mich gern als dämlich bezeichnen, aber immerhin glaubte ich wenigstens nicht, dass die drei Bären auch wirklich sprechen konnten.
|
85 |
+
|
86 |
+
»Im obersten Fach vom Flurschrank«, sagte sie. »Ganz hinten, hinter den Fotoalben.«
|
87 |
+
|
88 |
+
»Warum da?«, fragte ich, worauf meine Mutter mir einen seltsamen Blick zuwarf. Soweit sie sehen konnte, unterhielt ich mich mit dem leeren Wohnungseingang … obwohl sie eigentlich längst wusste, dass ich ein bisschen anders als andere Kinder tickte. Nach einem nicht gerade schönen Vorfall im Central Park – dazu bald mehr – bekam ich mit, wie sie einer von ihren Verlagsfreundinnen am Telefon erzählte, ich hätte eine besondere Beziehung zur Geisterwelt. Was mir einen gewaltigen Schrecken einjagte. Geister setzte ich nämlich mit Gespenstern gleich, und vor denen hatte ich furchtbare Angst.
|
89 |
+
|
90 |
+
»Ich habe nicht die leiseste Ahnung«, sagte Mrs. Burkett. »Aber da hatte ich wahrscheinlich schon den Schlaganfall. Das heißt, meine Gedanken sind in Blut ertrunken.«
|
91 |
+
|
92 |
+
In Blut ertrunkene Gedanken. Das habe ich nie vergessen.
|
93 |
+
|
94 |
+
Mama fragte Mr. Burkett, ob er mit in unsere Wohnung kommen wolle, zu einer Tasse Tee (»oder etwas Stärkerem«), aber er lehnte ab, weil er noch einmal nach den verschwundenen Ringen seiner Frau suchen wollte. Daraufhin fragte sie ihn, ob wir ihm was vom Chinesen mitbringen sollten, wenn wir dort unser Abendessen holten, und er sagte: »Das wäre schön, vielen Dank, Tia.«
|
95 |
+
|
96 |
+
»De nada«, sagte meine Mutter (das verwendete sie beinah so oft wie ja, ja, ja und genau, genau, genau), und dann erklärte sie ihm, wir würden ihm das Essen gegen sechs in die Wohnung bringen, falls er nicht bei uns essen wolle, wozu er gern eingeladen sei. Nein, sagte er, er würde lieber bei sich essen, aber er fände es nett, wenn wir uns dann zu ihm gesellen würden. Nur dass er zu uns sagte, als wäre Mrs. Burkett noch am Leben. Was sie ja nicht war, obwohl sie dastand.
|
97 |
+
|
98 |
+
»Bis dahin hast du die Ringe bestimmt gefunden«, sagte Mama. Sie nahm mich an die Hand. »Komm, Jamie. Wir besuchen Mr. Burkett später wieder. Jetzt lassen wir ihn erst mal lieber für sich allein.«
|
99 |
+
|
100 |
+
»Truthähne sind nicht grün, Jamie«, sagte Mrs. Burkett. »Und das da sieht sowieso nicht wie ein Truthahn aus. Das sieht aus wie ein Klecks, aus dem Finger ragen. Ein Rembrandt bist du nicht gerade.«
|
101 |
+
|
102 |
+
Tote Leute mussten die Wahrheit sagen, was okay war, solange man die Antwort auf eine Frage erfahren wollte, aber wie schon gesagt, die Wahrheit war halt manchmal echt beschissen. Weshalb ich irgendwie wütend auf Mrs. Burkett wurde, wenn auch nur kurz, weil sie auf einmal zu weinen anfing. Sie wandte sich Mr. Burkett zu und sagte: »Wer wird jetzt dafür sorgen, dass du die Gürtelschlaufe hinten an deiner Hose nicht vergisst? Dolores Magowan? Da fress ich doch ’nen Besen!« Sie küsste seine Wange … oder küsste in deren Richtung, das war mir nicht ganz klar. »Ich habe dich geliebt, Marty. Das tu ich immer noch.«
|
103 |
+
|
104 |
+
Mr. Burkett hob die Hand und kratzte sich an der Stelle, wo ihre Lippen ihn berührt hatten, als würde es ihn da jucken. Das hat er wohl jedenfalls gedacht.
|
105 |
+
|
106 |
+
|
107 |
+
|
data/king-later01-en.txt
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
1
|
4 |
+
|
5 |
+
|
6 |
+
I was coming home from school with my mother. She was holding my hand. In the other hand I clutched my turkey, the ones we made in first grade the week before Thanksgiving. I was so proud of mine I was practically shitting nickels. What you did, see, was put your hand on a piece of construction paper and then trace around it with a crayon. That made the tail and body. When it came to the head, you were on your own.
|
7 |
+
|
8 |
+
I showed mine to Mom and she’s all yeah yeah yeah, right right right, totally great, but I don’t think she ever really saw it. She was probably thinking about one of the books she was trying to sell. “Flogging the product,” she called it. Mom was a literary agent, see. It used to be her brother, my Uncle Harry, but Mom took over his business a year before the time I’m telling you about. It’s a long story and kind of a bummer.
|
9 |
+
|
10 |
+
I said, “I used Forest Green because it’s my favorite color. You knew that, right?” We were almost to our building by then. It was only three blocks from my school.
|
11 |
+
|
12 |
+
She’s all yeah yeah yeah. Also, “You play or watch Barney and The Magic Schoolbus when we get home, kiddo, I’ve got like a zillion calls to make.”
|
13 |
+
|
14 |
+
So I go yeah yeah yeah, which earned me a poke and a grin. I loved it when I could make my mother grin because even at six I knew that she took the world very serious. Later on I found out part of the reason was me. She thought she might be raising a crazy kid. The day I’m telling you about was the one when she decided for sure I wasn’t crazy after all. Which must have been sort of a relief and sort of not.
|
15 |
+
|
16 |
+
“You don’t talk to anybody about this,” she said to me later that day. “Except to me. And maybe not even me, kiddo. Okay?”
|
17 |
+
|
18 |
+
I said okay. When you’re little and it’s your mom, you say okay to everything. Unless she says it’s bedtime, of course. Or to finish your broccoli.
|
19 |
+
|
20 |
+
We got to our building and the elevator was still broken. You could say things might have been different if it had been working, but I don’t think so. I think that people who say life is all about the choices we make and the roads we go down are full of shit. Because check it, stairs or elevator, we still would have come out on the third floor. When the fickle finger of fate points at you, all roads lead to the same place, that’s what I think. I may change my mind when I’m older, but I really don’t think so.
|
21 |
+
|
22 |
+
“Fuck this elevator,” Mom said. Then, “You didn’t hear that, kiddo.”
|
23 |
+
|
24 |
+
“Hear what?” I said, which got me another grin. Last grin for her that afternoon, I can tell you. I asked her if she wanted me to carry her bag, which had a manuscript in it like always, that day a big one, looked like a five-hundred-pager (Mom always sat on a bench reading while she waited for me to get out of school, if the weather was nice). She said, “Sweet offer, but what do I always tell you?”
|
25 |
+
|
26 |
+
“You have to tote your own burden in life,” I said.
|
27 |
+
|
28 |
+
“Correctamundo.”
|
29 |
+
|
30 |
+
“Is it Regis Thomas?” I asked.
|
31 |
+
|
32 |
+
“Yes indeed. Good old Regis, who pays our rent.”
|
33 |
+
|
34 |
+
“Is it about Roanoke?”
|
35 |
+
|
36 |
+
“Do you even have to ask, Jamie?” Which made me snicker. Everything good old Regis wrote was about Roanoke. That was the burden he toted in life.
|
37 |
+
|
38 |
+
We went up the stairs to the third floor, where there were two other apartments plus ours at the end of the hall. Ours was the fanciest one. Mr. and Mrs. Burkett were standing outside 3A, and I knew right away something was wrong because Mr. Burkett was smoking a cigarette, which I hadn’t seen him do before and was illegal in our building anyway. His eyes were bloodshot and his hair was all crazied up in gray spikes. I always called him mister, but he was actually Professor Burkett, and taught something smart at NYU. English and European Literature, I later found out. Mrs. Burkett was dressed in a nightgown and her feet were bare. That nightgown was pretty thin. I could see most of her stuff right through it.
|
39 |
+
|
40 |
+
My mother said, “Marty, what’s wrong?”
|
41 |
+
|
42 |
+
Before he could say anything back, I showed him my turkey. Because he looked sad and I wanted to cheer him up, but also because I was so proud of it. “Look, Mr. Burkett! I made a turkey! Look, Mrs. Burkett!” I held it up for her in front of my face because I didn’t want her to think I was looking at her stuff.
|
43 |
+
|
44 |
+
Mr. Burkett paid no attention. I don’t think he even heard me. “Tia, I have some awful news. Mona died this morning.”
|
45 |
+
|
46 |
+
My mother dropped her bag with the manuscript inside it between her feet and put her hand over her mouth. “Oh, no! Tell me that’s not true!”
|
47 |
+
|
48 |
+
He began to cry. “She got up in the night and said she wanted a drink of water. I went back to sleep and she was on the couch this morning with a comforter pulled up to her chin and so I tiptoed to the kitchen and put on the coffee because I thought the pleasant smell would w-w-wake… would wake…”
|
49 |
+
|
50 |
+
He really broke down then. Mom took him in her arms the way she did me when I hurt myself, even though Mr. Burkett was about a hundred (seventy-four, I found out later).
|
51 |
+
|
52 |
+
That was when Mrs. Burkett spoke to me. She was hard to hear, but not as hard as some of them because she was still pretty fresh. She said, “Turkeys aren’t green, James.”
|
53 |
+
|
54 |
+
“Well mine is,” I said.
|
55 |
+
|
56 |
+
My mother was still holding Mr. Burkett and kind of rocking him. They didn’t hear her because they couldn’t, and they didn’t hear me because they were doing adult things: comforting for Mom, blubbering for Mr. Burkett.
|
57 |
+
|
58 |
+
Mr. Burkett said, “I called Dr. Allen and he came and said she probably had a soak.” At least that’s what I thought he said. He was crying so much it was hard to tell. “He called the funeral parlor. They took her away. I don’t know what I’ll do without her.”
|
59 |
+
|
60 |
+
Mrs. Burkett said, “My husband is going to burn your mother’s hair with his cigarette if he doesn’t look out.”
|
61 |
+
|
62 |
+
And sure enough, he did. I could smell the singeing hair, a kind of beauty shop smell. Mom was too polite to say anything about it, but she made him let go of her, and then she took the cigarette from him and dropped it on the floor and stepped on it. I thought that was a groady thing to do, extreme litterbugging, but I didn’t say anything. I got that it was a special situation.
|
63 |
+
|
64 |
+
I also knew that talking to Mrs. Burkett any more would freak him out. Mom, too. Even a little kid knows certain basic things if he’s not soft in the attic. You said please, you said thank you, you didn’t flap your weenie around in public or chew with your mouth open, and you didn’t talk to dead folks when they were standing next to living folks who were just starting to miss them. I only want to say, in my own defense, that when I saw her I didn’t know she was dead. Later on I got better at telling the difference, but back then I was just learning. It was her nightgown I could see through, not her. Dead people look just like living people, except they’re always wearing the clothes they died in.
|
65 |
+
|
66 |
+
Meanwhile, Mr. Burkett was rehashing the whole thing. He told my mother how he sat on the floor beside the couch and held his wife’s hand till that doctor guy came and again till the mortician guy came to take her away. “Conveyed her hence” was what he actually said, which I didn’t understand until Mom explained it to me. And at first I thought he said beautician, maybe because of the smell when he burned Mom’s hair. His crying had tapered off, but now it ramped up again. “Her rings are gone,” he said through his tears. “Both her wedding ring and her engagement ring, that big diamond. I looked on the night table by her side of the bed, where she puts them when she rubs that awful-smelling arthritis cream into her hands—”
|
67 |
+
|
68 |
+
“It does smell bad,” Mrs. Burkett admitted. “Lanolin is basically sheep dip, but it really helps.”
|
69 |
+
|
70 |
+
I nodded to show I understood but didn’t say anything.
|
71 |
+
|
72 |
+
“—and on the bathroom sink, because sometimes she leaves them there…I’ve looked everywhere.”
|
73 |
+
|
74 |
+
“They’ll turn up,” my mother soothed, and now that her hair was safe, she took Mr. Burkett in her arms again. “They’ll turn up, Marty, don’t you worry about that.”
|
75 |
+
|
76 |
+
“I miss her so much! I miss her already!”
|
77 |
+
|
78 |
+
Mrs. Burkett flapped a hand in front of her face. “I give him six weeks before he’s asking Dolores Magowan out to lunch.”
|
79 |
+
|
80 |
+
Mr. Burkett was blubbing, and my mother was doing her soothing thing like she did to me whenever I scraped my knee or this one time when I tried to make her a cup of tea and slopped hot water on my hand. Lots of noise, in other words, so I took a chance but kept my voice low.
|
81 |
+
|
82 |
+
“Where are your rings, Mrs. Burkett? Do you know?”
|
83 |
+
|
84 |
+
They have to tell you the truth when they’re dead. I didn’t know that at the age of six; I just assumed all grownups told the truth, living or dead. Of course back then I also believed Goldilocks was a real girl. Call me stupid if you want to. At least I didn’t believe the three bears actually talked.
|
85 |
+
|
86 |
+
“Top shelf of the hall closet,” she said. “Way in the back, behind the scrapbooks.”
|
87 |
+
|
88 |
+
“Why there?” I asked, and my mother gave me a strange look. As far as she could see, I was talking to the empty doorway…although by then she knew I wasn’t quite the same as other kids. After a thing that happened in Central Park, not a nice thing—I’ll get to it—I overheard her telling one of her editor friends on the phone that I was “fey.” That scared the shit out of me, because I thought she meant she was changing my name to Fay, which is a girl’s name.
|
89 |
+
|
90 |
+
“I don’t have the slightest idea,” Mrs. Burkett said. “By then I suppose I was having the stroke. My thoughts would have been drowning in blood.”
|
91 |
+
|
92 |
+
Thoughts drowning in blood. I never forgot that.
|
93 |
+
|
94 |
+
Mom asked Mr. Burkett if he wanted to come down to our apartment for a cup of tea (“or something stronger”), but he said no, he was going to have another hunt for his wife’s missing rings. She asked him if he would like us to bring him some Chinese take-out, which my mother was planning for dinner, and he said that would be good, thank you Tia.
|
95 |
+
|
96 |
+
My mother said de nada (which she used almost as much as yeah yeah yeah and right right right), then said we’d bring it to his apartment around six, unless he wanted to eat with us in ours, which he was welcome to do. He said no, he’d like to eat in his place but he would like us to eat with him. Except what he actually said was our place, like Mrs. Burkett was still alive. Which she wasn’t, even though she was there.
|
97 |
+
|
98 |
+
“By then you’ll have found her rings,” Mom said. She took my hand. “Come on, Jamie. We’ll see Mr. Burkett later, but for now let’s leave him alone.”
|
99 |
+
|
100 |
+
Mrs. Burkett said, “Turkeys aren’t green, Jamie, and that doesn’t look like a turkey anyway. It looks like a blob with fingers sticking out of it. You’re no Rembrandt.”
|
101 |
+
|
102 |
+
Dead people have to tell the truth, which is okay when you want to know the answer to a question, but as I said, the truth can really suck. I started to be mad at her, but just then she started to cry and I couldn’t be. She turned to Mr. Burkett and said, “Who’ll make sure you don’t miss the belt loop in the back of your pants now? Dolores Magowan? I should smile and kiss a pig.” She kissed his cheek…or kissed at it, I couldn’t really tell which. “I loved you, Marty. Still do.”
|
103 |
+
|
104 |
+
Mr. Burkett raised his hand and scratched the spot where her lips had touched him, as if he had an itch. I suppose that’s what he thought it was.
|
105 |
+
|
106 |
+
|
data/sternstunden04-de.txt
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Das Genie einer Nacht
|
2 |
+
|
3 |
+
Die Marseillaise, 25. April 1792
|
4 |
+
|
5 |
+
Das Genie einer Nacht
|
6 |
+
|
7 |
+
1792. Zwei Monate, drei Monate schon schwankt in der französischen Nationalversammlung die Entscheidung: Krieg gegen die Koalition der Kaiser und Könige oder Frieden. Ludwig XVI. ist selbst unentschlossen; er ahnt die Gefahr eines Sieges der Revolutionäre, er ahnt die Gefahr ihrer Niederlage. Ungewiß sind auch die Parteien. Die Girondisten drängen zum Kriege, um die Macht zu behalten, Robespierre und die Jakobiner fechten für den Frieden, um inzwischen selbst die Macht an sich zu reißen. Von Tag zu Tag wird die Lage gespannter, die Journale lärmen, die Klubs diskutieren, immer wilder schwirren die Gerüchte, und immer mehr wird die öffentliche Meinung durch sie erregt. Wie immer eine Entscheidung, wird es darum eine Art von Befreiung, wie am 20. April der König von Frankreich endlich den Krieg an den Kaiser von Österreich und den König von Preußen erklärt.
|
8 |
+
|
9 |
+
Lastend und seelenverstörend hat in diesen Wochen und Wochen die elektrische Spannung über Paris gelegen; aber noch drückender, noch drohender schwült die Erregung in den Grenzstädten. In allen Biwaks sind schon die Truppen versammelt, in jedem Dorf, in jeder Stadt werden Freiwillige und Nationalgarden ausgerüstet, überall die Festungen instand gesetzt, und vor allem im Elsaß weiß man, daß auf seiner Scholle, wie immer zwischen Frankreich und Deutschland, die erste Entscheidung fallen wird. An den Ufern des Rheins ist der Feind, der Gegner, nicht wie in Paris ein verschwommener, ein pathetisch-rhetorischer Begriff, sondern sichtbare, sinnliche Gegenwart; denn an dem befestigten Brückenkopf, von dem Turm der Kathedrale, kann man die heranrückenden Regimenter der Preußen mit freiem Auge wahrnehmen. Nachts trägt der Wind das Rollen der feindlichen Artilleriewagen, das Klirren der Waffen, die Trompetensignale über den gleichgültig im Mondlicht glitzernden Strom. Und alle wissen: nur ein einziges Wort, nur ein einziges Dekret ist vonnöten, und aus dem schweigenden Mund der preußischen Kanonen fährt Donner und Blitz, und der tausendjährige Kampf zwischen Deutschland und Frankreich hat abermals begonnen – diesmal im Namen der neuen Freiheit auf der einen Seite und im Namen der alten Ordnung auf der andern.
|
10 |
+
|
11 |
+
Unvergleichlicher Tag darum, da am 25. April 1792 Stafetten die Nachricht der erfolgten Kriegserklärung aus Paris nach Straßburg bringen. Sofort strömt aus allen Gassen und Häusern das Volk auf die offenen Plätze, kriegsbereit marschiert die ganze Garnison zur letzten Parade, Regiment nach Regiment. Auf dem Hauptplatz erwartet sie der Bürgermeister Dietrich, die dreifarbige Schärpe um den Leib, die Kokarde auf dem Hut, den er grüßend den Soldaten entgegenschwenkt. Fanfarenruf und Trommelwirbel mahnt zur Stille. Mit lauter Stimme liest Dietrich an diesem und allen andern Plätzen der Stadt französisch und deutsch den Wortlaut der Kriegserklärung vor. Nach seinen letzten Worten intonieren die Regimentsmusiker das erste, das vorläufige Kriegslied der Revolution, das »Ça ira«, eigentlich eine prickelnde, übermütige, spöttische Tanzmelodie, aber die klirrenden, die donnernden Schritte der ausmarschierenden Regimenter geben ihr martialischen Takt. Dann zerstreut sich die Menge und trägt die angefachte Begeisterung in alle Gassen und Häuser; in den Cafés, in den Klubs werden zündende Ansprachen gehalten und Proklamationen verteilt. »Aux armes, citoyens! L'étendard de la guerre est déployé! Le signal est donné!«; so und mit ähnlichen Anrufen beginnen sie, und überall, in allen Reden, in allen Zeitungen, auf allen Plakaten, auf allen Lippen wiederholen sich solche schlagkräftige, rhythmische Rufe wie »Aux armes, citoyens! Qu'ils tremblent donc, les despotes couronnés! Marchons, enfants de la liberté!«, und jedesmal jubelt und jubelt die Masse den feurigen Worten zu.
|
12 |
+
|
13 |
+
Immer jubelt die große Masse auf den Straßen und Plätzen bei einer Kriegserklärung, immer aber regen sich in solchen Augenblicken des Straßenjubels auch andere Stimmen, leisere, abseitige; auch die Angst, auch die Sorge wacht auf bei einer Kriegserklärung, nur daß sie heimlich in den Stuben flüstert oder mit blasser Lippe schweigt. Ewig und überall sind Mütter, die sich sagen: Werden die fremden Soldaten nicht meine Kinder hinmorden, in allen Ländern sind die Bauern, die um ihre Habe sorgen, ihre Felder, ihre Hütten, ihr Vieh und ihre Ernte. Wird ihre Saat nicht zerstampft werden, ihr Haus nicht geplündert von den brutalen Horden, nicht mit Blut die Felder ihrer Arbeit gedüngt? Aber der Bürgermeister von Straßburg, Friedrich Baron Dietrich, eigentlich ein Aristokrat, aber wie die beste Aristokratie Frankreichs damals der Sache der neuen Freiheit mit ganzer Seele hingegeben, will nur die lauten, die klingenden Stimmen der Zuversicht zu Wort kommen lassen; bewußt verwandelt er den Tag der Kriegserklärung in ein öffentliches Fest. Die Schärpe quer um die Brust, eilt er von einer Versammlung zur andern, um die Bevölkerung anzufeuern. Er läßt Wein und Zehrung an die abmarschierenden Soldaten verteilen, und am Abend versammelt er in seinem geräumigen Hause auf der Place de Broglie die Generalität, die Offiziere und wichtigsten Amtspersonen zu einem Abschiedsfest, dem Begeisterung schon im vornherein den Charakter eines Siegesfestes gibt. Die Generäle, siegessicher wie immer Generäle, präsidieren, die jungen Offiziere, die im Krieg den Sinn ihres Lebens erfüllt sehen, haben freies Wort. Einer feuert den andern an. Man schwingt die Säbel, man umarmt sich, man trinkt sich zu, man hält bei gutem Wein leidenschaftliche und immer leidenschaftlichere Reden. Und abermals kehren dieselben stimulierenden Worte der Journale und Proklamationen in allen Ansprachen wieder: »Auf zu den Waffen, Bürger! Marschieren wir! Retten wir das Vaterland! Bald werden sie zittern, die gekrönten Despoten. Jetzt, da sich die Fahne des Sieges entrollt hat, ist der Tag gekommen, die Trikolore über die Welt zu tragen! Jeder muß jetzt sein Bestes geben, für den König, für die Fahne, für die Freiheit!« Das ganze Volk, das ganze Land will in solchen Augenblicken eine heilige Einheit werden durch den Glauben an den Sieg und durch Begeisterung für die Sache der Freiheit.
|
14 |
+
|
15 |
+
Plötzlich, mitten im Reden und Toastieren, wendet sich der Bürgermeister Dietrich einem jungen Hauptmann vom Festungskorps, namens Rouget, zu, der an seiner Seite sitzt. Er hat sich erinnert, daß dieser nette, nicht gerade hübsche, aber sympathische Offizier vor einem halben Jahr anläßlich der Proklamierung der Konstitution eine recht nette Hymne an die Freiheit geschrieben hat, die der Regimentsmusikus Pleyel gleich vertonte. Die anspruchslose Arbeit hatte sich sangbar erwiesen, die Militärkapelle hatte sie eingelernt, man hatte sie am öffentlichen Platz gespielt und im Chor gesungen. Wären jetzt die Kriegserklärung und der Abmarsch nicht gegebener Anlaß, eine ähnliche Feier zu inszenieren? So fragt Bürgermeister Dietrich ganz lässig, wie man eben einen guten Bekannten um eine Gefälligkeit bittet, den Kapitän Rouget (der sich völlig unberechtigterweise selbst geadelt hat und Rouget de Lisle nennt), ob er nicht den patriotischen Anlaß wahrnehmen wolle und für die ausmarschierenden Truppen etwas dichten, ein Kriegslied für die Rheinarmee, die morgen gegen den Feind ausrücken soll.
|
16 |
+
|
17 |
+
Rouget, ein bescheidener, unbedeutender Mann, der sich nie für einen großen Komponisten hielt – seine Verse wurden nie gedruckt, seine Opern refüsiert –, weiß, daß ihm Gelegenheitsverse leicht in die Feder fließen. Um der hohen Amtsperson und dem guten Freunde gefällig zu sein, erklärt er sich bereit. Ja, er wolle es versuchen. »Bravo, Rouget«, trinkt ein General von gegenüber ihm zu und mahnt ihn, er solle ihm dann gleich das Lied ins Feld nachschicken; irgendeinen schrittbeflügelnden, patriotischen Marschgesang könne die Rheinarmee wirklich brauchen. Inzwischen beginnt ein anderer eine Rede zu schwingen. Wieder wird toastiert, gelärmt, getrunken. Mit starker Woge geht die allgemeine Begeisterung über die kleine zufällige Zwiesprache hinweg. Immer ekstatischer, immer lauter, immer frenetischer wird das Gelage, und die Stunde steht schon bedenklich spät nach Mitternacht, da die Gäste das Haus des Bürgermeisters verlassen.
|
18 |
+
|
19 |
+
Es ist spät nach Mitternacht. Der 25. April, der für Straßburg so erregende Tag der Kriegserklärung, ist zu Ende, eigentlich hat der 26. April schon begonnen. Nächtliches Dunkel liegt über den Häusern; aber trügerisch ist dieses Dunkel, denn noch fiebert die Stadt vor Erregung. In den Kasernen rüsten die Soldaten zum Ausmarsch und manche der Vorsichtigen hinter verschlossenen Läden vielleicht schon heimlich zur Flucht. Auf den Straßen marschieren einzelne Peletons, dazwischen jagen die klappernden Hufe der Meldereiter, dann rasselt wieder ein schwerer Zug Artillerie heran, und immer wieder hallt monoton der Ruf der Schildwache von Posten zu Posten. Zu nahe ist der Feind, zu unsicher und zu erregt die Seele der Stadt, als daß sie Schlaf fände in so entscheidendem Augenblick.
|
20 |
+
|
21 |
+
Auch Rouget, der jetzt in sein bescheidenes Zimmerchen in der Grande Rue 126 die runde Treppe hinaufgeklettert ist, fühlt sich merkwürdig erregt. Er hat sein Versprechen nicht vergessen, möglichst rasch ein Marschlied, ein Kriegslied für die Rheinarmee zu versuchen. Unruhig stapft er in seinem engen Zimmer auf und nieder. Wie beginnen? Wie beginnen? Noch schwirren ihm alle die anfeuernden Rufe der Proklamationen, der Reden, der Toaste chaotisch durch den Sinn. »Aux armes, citoyens! ... Marchons, enfants de la liberté! ... Ecrasons la tyrannie! ... L'étendard de la guerre est déployé! ...« Aber auch der andern Worte entsinnt er sich, die er im Vorübergehen gehört, die Stimmen der Frauen, die um ihre Söhne zittern, die Sorge der Bauern, Frankreichs Felder könnten zerstampft werden und mit Blut gedüngt von den fremden Kohorten. Halb unbewußt schreibt er die ersten beiden Zeilen hin, die nur Widerhall, Widerklang, Wiederholung sind jener Anrufe.
|
22 |
+
|
23 |
+
»Allons, enfants de la patrie,
|
24 |
+
|
25 |
+
Le jour de gloire est arrivé!«
|
26 |
+
|
27 |
+
Dann hält er inne und stutzt. Das sitzt. Der Ansatz ist gut. Jetzt nur gleich den rechten Rhythmus finden, die Melodie zu den Worten. Er nimmt seine Geige vom Schrank, er probiert. Und wunderbar: gleich in den ersten Takten paßt sich der Rhythmus vollkommen den Worten an. Hastig schreibt er weiter, nun schon getragen, nun schon mitgerissen von der Kraft, die in ihn gefahren ist. Und mit einemmal strömt alles zusammen: alle die Gefühle, die sich in dieser Stunde entladen, alle die Worte, die er auf der Straße, die er bei dem Bankett gehört, der Haß gegen die Tyrannen, die Angst um die Heimaterde, das Vertrauen zum Siege, die Liebe zur Freiheit. Rouget braucht gar nicht zu dichten, zu erfinden, er braucht nur in Reime zu bringen, in den hinreißenden Rhythmus seiner Melodie die Worte zu setzen, die heute, an diesem einzigen Tage, von Mund zu Mund gegangen, und er hat alles ausgesprochen, alles ausgesagt, alles ausgesungen, was die Nation in innerster Seele empfand. Und er braucht nicht zu komponieren, denn durch die verschlossenen Fensterläden dringt der Rhythmus der Straße, der Stunde herein, dieser Rhythmus des Trotzes und der Herausforderung, der in dem Marschtritt der Soldaten, dem Schmettern der Trompeten, dem Rasseln der Kanonen liegt. Vielleicht vernimmt er ihn nicht selbst, nicht sein eigenes waches Ohr, aber der Genius der Stunde, der für diese einzige Nacht Hausung genommen hat in seinem sterblichen Leibe, hat ihn vernommen. Und immer fügsamer gehorcht die Melodie dem hämmernden, dem jubelnden Takt, der Herzschlag eines ganzen Volkes ist. Wie unter fremdem Diktat schreibt hastig und immer hastiger Rouget die Worte, die Noten hin – ein Sturm ist über ihn gekommen, wie er nie seine enge bürgerliche Seele durchbrauste. Eine Exaltation, eine Begeisterung, die nicht die seine ist, sondern magische Gewalt, zusammengeballt in eine einzige explosive Sekunde, reißt den armen Dilettanten hunderttausendfach über sein eigenes Maß hinaus und schleudert ihn wie eine Rakete – eine Sekunde lang Licht und strahlende Flamme – bis zu den Sternen. Eine Nacht ist es dem Kapitänleutnant Rouget de Lisle gegönnt, Bruder der Unsterblichen zu sein: aus den übernommenen, der Straße, den Journalen abgeborgten Rufen des Anfangs formt sich ihm schöpferisches Wort und steigt empor zu einer Strophe, die in ihrer dichterischen Formulierung so unvergänglich ist wie die Melodie unsterblich.
|
28 |
+
|
29 |
+
»Amour sacré de la patrie,
|
30 |
+
|
31 |
+
Conduis, soutiens nos bras vengeurs,
|
32 |
+
|
33 |
+
Liberté, liberté chérie,
|
34 |
+
|
35 |
+
Combats avec tes défenseurs.«
|
36 |
+
|
37 |
+
Dann noch eine fünfte Strophe, die letzte, und aus einer Erregung und in einem Guß gestaltet, vollkommen das Wort der Melodie verbindend, ist noch vor dem Morgengrauen das unsterbliche Lied vollendet. Rouget löscht das Licht und wirft sich hin auf sein Bett. Irgend etwas, er weiß nicht was, hat ihn aufgehoben in eine nie gefühlte Helligkeit seiner Sinne, irgend etwas schleudert ihn jetzt nieder in eine dumpfe Erschöpfung. Er schläft einen abgründigen Schlaf, der wie ein Tod ist. Und tatsächlich ist schon wieder der Schöpfer, der Dichter, der Genius in ihm gestorben. Auf dem Tische aber liegt, losgelöst von dem Schlafenden, den dies Wunder wahrhaft im heiligen Rausch überkommen, das vollendete Werk. Kaum ein zweites Mal in der Geschichte aller Völker und Zeiten ist ein Lied so rasch und so vollkommen gleichzeitig Wort und Musik geworden.
|
38 |
+
|
39 |
+
Dieselben Glocken vom Münster wie immer verkünden den neuen Morgen. Ab und zu trägt der Wind vom Rhein her Schüsse herüber, die ersten Geplänkel haben begonnen. Rouget erwacht. Mit Mühe tastet er sich aus dem Abgrund seines Schlafes empor. Etwas ist geschehen, fühlt er dumpf, etwas mit ihm geschehen, an das er nur dumpf sich erinnert. Dann erst bemerkt er auf dem Tisch das frischbeschriebene Blatt. Verse? Wann habe ich die geschrieben? Musik, in meiner eigenen Handschrift? Wann habe ich das komponiert? Ach ja, das Lied, das Freund Dietrich gestern erbeten, das Marschlied für die Rheinarmee! Rouget liest seine Verse, summt dazu die Melodie, fühlt aber, wie immer der Schöpfer vor dem eben geschaffenen Werk, sich völlig ungewiß. Aber nebenan wohnt ein Regimentskamerad, dem zeigt und singt er es vor. Der Freund scheint zufrieden und schlägt nur einige kleine Änderungen vor. An dieser ersten Zustimmung gewinnt Rouget ein gewisses Zutrauen. Mit der ganzen Ungeduld eines Autors und stolz auf sein rasch erfülltes Versprechen, fällt er gleich dem Bürgermeister Dietrich ins Haus, der im Garten seinen Morgenspaziergang macht und dabei eine neue Rede meditiert. Wie, Rouget? Schon fertig? Nun, da wollen wir gleich eine Probe abhalten. Die beiden gehen aus dem Garten in den Salon des Hauses, Dietrich setzt sich ans Klavier und spielt die Begleitung, Rouget singt den Text. Angelockt von der unerwarteten morgendlichen Musik kommt die Frau des Bürgermeisters ins Zimmer und verspricht, Kopien von dem neuen Lied zu machen und als gelernte Musikerin, die sie ist, gleich die Begleitung auszuarbeiten, damit man schon heute nacht bei der Abendgesellschaft es den Freunden des Hauses zwischen allerhand andern Liedern vorsingen könne. Der Bürgermeister Dietrich, stolz auf seine nette Tenorstimme, übernimmt es, das Lied nun gründlicher zu studieren, und am 26. April, am Abend desselben Tages, in dessen Morgenstunden das Lied gedichtet und komponiert war, wird es zum erstenmal einer zufällig gewählten Gesellschaft im Salon des Bürgermeisters vorgesungen.
|
40 |
+
|
41 |
+
Die Zuhörer scheinen freundlich applaudiert zu haben, und wahrscheinlich hat es an allerhand höflichen Komplimenten für den anwesenden Autor nicht gefehlt. Aber selbstverständlich hat nicht die leiseste Ahnung die Gäste des Hôtel de Broglie an dem großen Platz von Straßburg überkommen, daß mit unsichtbaren Flügeln eine ewige Melodie sich niedergeschwungen in ihre irdische Gegenwart. Selten begreifen die Zeitgenossen auf den ersten Blick die Größe eines Menschen oder die Größe eines Werkes, und wie wenig sich die Frau Bürgermeisterin jenes erstaunlichen Augenblicks bewußt wurde, beweist sie mit dem Brief an ihren Bruder, in dem sie ein Wunder zu einem gesellschaftlichen Ereignis banalisiert. »Du weißt, daß wir viele Leute im Haus empfangen und man immer etwas erfinden muß, um Abwechslung in die Unterhaltung zu bringen. Und so hat mein Mann die Idee gehabt, irgendein Gelegenheitslied komponieren zu lassen. Der Kapitän vom Ingenieurkorps, Rouget de Lisle, ein liebenswürdiger Dichter und Kompositeur, hat sehr schnell diese Musik eines Kriegsliedes gemacht. Mein Mann, der eine gute Tenorstimme hat, hat das Stück gleich gesungen, das sehr anziehend ist und eine gewisse Eigenart zeigt. Es ist ein besserer Gluck, lebendiger und belebter. Ich für mein Teil habe meine Begabung für Orchestrierung dabei angewandt und arrangierte die Partitur für Klavier und andere Instrumente, so daß ich viel zu arbeiten habe. Das Stück ist bei uns gespielt worden, zur großen Zufriedenheit der ganzen Gesellschaft.«
|
42 |
+
|
43 |
+
»Zur großen Zufriedenheit der ganzen Gesellschaft« – das scheint uns heute überraschend kühl. Aber der bloß freundliche Eindruck, die bloß laue Zustimmung ist verständlich, denn noch hat sich bei dieser ersten Darbietung die Marseillaise nicht wahrhaft in ihrer Kraft enthüllen können. Die Marseillaise ist kein Vortragsstück für eine behagliche Tenorstimme und nicht bestimmt, in einem kleinbürgerlichen Salon zwischen Romanzen und italienischen Arien mit einer einzelnen Singstimme vorgetragen zu werden. Ein Lied, das aufrauscht zu den hämmernden, federnden, fordernden Takten »Aux armes, citoyens«, wendet sich an eine Masse, eine Menge, und seine wahre Orchestrierung sind klirrende Waffen, schmetternde Fanfaren, marschierende Regimenter. Nicht für Zuhörer, für kühl sitzende und behaglich genießende, war sie gedacht, sondern für Mittäter, Mitkämpfer. Nicht einem einzelnen Sopran, einem Tenor ist sie zugesungen, sondern der tausendkehligen Masse, das vorbildliche Marschlied, Siegeslied, Todeslied, Heimatlied, Nationallied eines ganzen Volkes. Erst Begeisterung, aus der es zuerst geboren ward, wird dem Lied Rougets die begeisternde Gewalt verleihen. Noch hat das Lied nicht gezündet, noch haben in magischer Resonanz nicht die Worte, nicht die Melodie die Seele der Nation erreicht, noch kennt die Armee nicht ihr Marschlied, ihr Siegeslied, noch kennt die Revolution nicht ihren ewigen Päan.
|
44 |
+
|
45 |
+
Auch er selbst, dem über Nacht dieses Wunder geschehen, Rouget de Lisle, ahnt ebensowenig wie die andern, was er traumwandlerisch und von einem treulosen Genius geführt, in jener einen Nacht geschaffen. Er freut sich natürlich herzhaft, der brave, liebenswürdige Dilettant, daß die eingeladenen Gäste kräftig applaudieren, daß man ihn als Autor höflich komplimentiert. Mit der kleinen Eitelkeit eines kleinen Menschen sucht er diesen kleinen Erfolg in seinem kleinen Provinzkreise fleißig auszunützen. Er singt in den Kaffeehäusern seinen Kameraden die neue Melodie vor, er läßt Abschriften herstellen und schickt sie an die Generäle der Rheinarmee. Inzwischen hat auf Befehl des Bürgermeisters und Empfehlung der Militärbehörden das Straßburger Musikkorps das »Kriegslied für die Rheinarmee« einstudiert, und vier Tage später, beim Abmarsch der Truppen, spielt das Musikkorps der Straßburger Nationalgarde auf dem großen Platz den neuen Marsch. In patriotischer Weise erklärt sich auch der Straßburger Verleger bereit, den »Chant de guerre pour l'armée du Rhin« zu drucken, der respektvoll dem General Luckner von seinem militärischen Untergebenen gewidmet wird. Aber nicht ein einziger der Generäle der Rheinarmee denkt daran, die neue Weise beim Vormarsch wirklich spielen oder singen zu lassen, und so scheint, wie alle bisherigen Versuche Rougets, der Salonerfolg des »Allons, enfants de la patrie« ein Eintagserfolg, eine Provinzangelegenheit zu bleiben und als solche vergessen zu werden.
|
46 |
+
|
47 |
+
Aber nie läßt sich die eingeborene Kraft eines Werkes auf die Dauer verbergen oder verschließen. Ein Kunstwerk kann vergessen werden von der Zeit, es kann verboten werden und versargt, immer aber erzwingt sich das Elementare den Sieg über das Ephemere. Einen Monat, zwei Monate hört man nichts vom Kriegslied der Rheinarmee. Die gedruckten und handgeschriebenen Exemplare liegen und wandern herum in gleichgültigen Händen. Aber immer genügt es, wenn ein Werk auch nur einen einzigen Menschen wirklich begeistert, denn jede echte Begeisterung wird selber schöpferisch. Am andern Ende von Frankreich, in Marseille, gibt der Klub der Verfassungsfreunde am 22. Juni ein Bankett für die abmarschierenden Freiwilligen. An langer Tafel sitzen fünfhundert junge, feurige Menschen in ihren neuen Uniformen der Nationalgarde; in ihrem Kreise fiebert genau die gleiche Stimmung wie an dem 25. April in Straßburg, nur noch heißer, hitziger und leidenschaftlicher, dank dem südlichen Temperament der Marseiller, und nicht mehr so eitel siegesgewiß wie in jener ersten Stunde der Kriegserklärung. Denn nicht wie jene Generäle flunkerten, sind die revolutionären französischen Truppen gleich über den Rhein marschiert und überall mit offenen Armen empfangen worden. Im Gegenteil, der Feind ist tief ins französische Land vorgestoßen, die Freiheit ist bedroht, die Sache der Freiheit in Gefahr.
|
48 |
+
|
49 |
+
Plötzlich, inmitten des Festmahls, schlägt einer – er heißt Mireur und ist ein Medizinstudent von der Universität in Montpellier – an sein Glas und erhebt sich. Alle verstummen und blicken auf ihn. Man erwartet eine Rede und eine Ansprache. Aber statt dessen schwingt der junge Mensch die Rechte empor und stimmt ein Lied an, ein neues Lied, das sie alle nicht kennen und von dem niemand weiß, wie es in seine Hand geraten ist, »Allons, enfants de la patrie«. Und nun zündet der Funke, als wäre er in ein Pulverfaß gefallen. Gefühl und Gefühl, die ewigen Pole, haben sich berührt. Alle diese jungen Menschen, die morgen ausrücken, die für die Freiheit kämpfen wollen und für das Vaterland zu sterben bereit sind, empfinden ihren innersten Willen, ihren ureigensten Gedanken in diesen Worten ausgedrückt; unwiderstehlich reißt der Rhythmus sie auf in eine einhellige, ekstatische Begeisterung. Strophe um Strophe wird bejubelt, noch einmal, noch ein zweites Mal muß das Lied wiederholt werden, und schon ist die Melodie ihr eigen geworden, schon singen sie, erregt aufgesprungen, die Gläser erhoben, den Refrain donnernd mit. »Aux armes, citoyens! Formez vos bataillons!« Neugierig drängen von der Straße Menschen heran, um zu hören, was hier mit solcher Begeisterung gesungen wird, und schon singen sie selber mit; am nächsten Tage ist die Melodie auf tausend und zehntausend Lippen. Ein Neudruck verbreitet sie, und wie die fünfhundert Freiwilligen am 2. Juli abmarschieren, wandert das Lied mit ihnen. Wenn sie müde werden auf der Landstraße, wenn ihr Schritt schlapp wird, braucht nur einer die Hymne anzustimmen, und schon gibt ihr mitreißender, ihr vorwärtsreißender Takt ihnen allen erneuten Schwung. Wenn sie durch ein Dorf marschieren und staunend die Bauern, neugierig die Einwohner sich versammeln, stimmen sie es im Chore an. Es ist ihr Lied geworden, sie haben, ohne es zu wissen, daß es für die Rheinarmee bestimmt war, ohne zu ahnen, von wem und wann es gedichtet war, den Hymnus als den ihres Bataillons, als Bekenntnis ihres Lebens und Sterbens übernommen. Es gehört zu ihnen wie die Fahne, und in leidenschaftlichem Vormarsch wollen sie ihn über die Welt tragen.
|
50 |
+
|
51 |
+
Der erste große Sieg der Marseillaise – denn so wird die Hymne Rougets bald sich nennen – ist Paris. Am 30. Juni marschiert das Bataillon durch die Faubourgs ein, die Fahne voran und das Lied. Tausende und Zehntausende stehen und warten in den Straßen, um sie festlich zu empfangen, und wie die Marseiller nun anrücken, fünfhundert Männer, gleichsam aus einer Kehle zum Taktschritt das Lied singend und immer wieder singend, horcht die Menge auf. Was ist das für eine herrliche, hinreißende Hymne, welche die Marseiller da singen? Was für ein Fanfarenruf dies, der in alle Herzen fährt, begleitet vom prasselnden Trommelschlag, dies »Aux armes, citoyens!« Zwei Stunden später, drei Stunden später, und schon klingt der Refrain in allen Gassen wieder. Vergessen ist das »Ça ira«, vergessen die alten Märsche, die abgebrauchten Couplets: die Revolution hat ihre eigne Stimme erkannt, die Revolution hat ihr Lied gefunden.
|
52 |
+
|
53 |
+
Lawinenhaft wird nun die Verbreitung, unaufhaltsam der Siegeslauf. Auf den Banketten wird die Hymne gesungen, in den Theatern und Klubs, dann sogar in den Kirchen nach dem Tedeum und bald anstatt des Tedeum. In einem, in zwei Monaten ist die Marseillaise das Lied des Volkes geworden und der ganzen Armee. Servan, der erste republikanische Kriegsminister, erkennt mit klugem Blick die tonische, die exaltierende Kraft eines so einzigartigen nationalen Schlachtgesanges. In eiliger Ordre befiehlt er, daß hunderttausend Exemplare an alle Kommandos überstellt werden sollen, und in zwei oder drei Nächten ist das Lied des Unbekannten mehr verbreitet als alle Werke Moli��res, Racines und Voltaires. Kein Fest, das nicht mit der Marseillaise schließt, keine Schlacht, wo nicht vorerst die Regimentsmusiker das Kriegslied der Freiheit intonieren. Bei Jemappes und Nerwinden ordnen sich die Regimenter im Chorgesang zum entscheidenden Sturme, und die feindlichen Generäle, die nur mit dem alten Rezept der verdoppelten Branntweinration ihre Soldaten stimulieren können, sehen erschreckt, daß sie nichts der explosiven Kraft dieser »fürchterlichen« Hymne entgegenzusetzen haben, wenn sie, gleichzeitig von Tausenden und Tausenden gesungen, wie eine klingende, klirrende Welle gegen ihre eigenen Reihen stürmt. Über allen Schlachten Frankreichs schwebt nun, Unzählige mitreißend in Begeisterung und Tod, die Marseillaise, wie Nike, die geflügelte Göttin des Sieges.
|
54 |
+
|
55 |
+
Unterdessen sitzt in der kleinen Garnison von Hüningen ein höchst unbekannter Hauptmann des Festungswesens, Rouget, und entwirft brav Wälle und Verschanzungen. Vielleicht hat er schon das »Kriegslied der Rheinarmee« vergessen, das er in jener verschollenen Nacht des 26. April 1792 geschaffen, und wagt gar nicht zu ahnen, wenn er in den Gazetten von jener andern Hymne, jenem andern Kriegslied liest, das im Sturm Paris erobert, daß dieses sieghafte »Lied der Marseiller« Wort für Wort und Takt für Takt nichts anderes ist als das in ihm und an ihm geschehene Wunder jener Nacht. Denn grausame Ironie des Schicksals – in alle Himmel rauschend, zu den Sternen brausend, trägt diese Melodie nur einen einzigen Menschen nicht hoch, nämlich den Menschen, der sie ersonnen. Niemand in ganz Frankreich kümmert sich um den Hauptmann Rouget de Lisle, der riesigste Ruhm, den je ein Lied gekannt, bleibt dem Liede, und nicht ein Schatten davon fällt auf seinen Schöpfer Rouget. Sein Name wird nicht mitgedruckt auf den Texten, und er selbst bliebe völlig unbeachtet bei den Herren der Stunde, brächte er sich nicht selbst in ärgerliche Erinnerung. Denn – geniale Paradoxie, wie sie nur die Geschichte erfinden kann – der Schöpfer der Revolutionshymne ist kein Revolutionär; im Gegenteil: der wie kein anderer die Revolution durch sein unsterbliches Lied fortgetrieben, möchte sie mit allen Kräften nun wieder zurückdämmen. Als die Marseiller und der Pariser Pöbel – seinen Gesang auf den Lippen – die Tuilerien stürmen und man den König absetzt, hat Rouget de Lisle genug von der Revolution. Er weigert sich, den Eid auf die Republik zu leisten, und quittiert lieber seinen Dienst, als den Jakobinern zu dienen. Das Wort von der »liberté chérie«, der geliebten Freiheit, in seiner Hymne ist diesem aufrechten Manne kein leeres Wort: er verabscheut die neuen Tyrannen und Despoten im Konvent nicht minder, als er die gekrönten und gesalbten jenseits der Grenzen haßte. Offen macht er seinem Unmut gegen den Wohlfahrtsausschuß Luft, als sein Freund, der Bürgermeister Dietrich, der Pate der Marseillaise, als der General Luckner, dem sie gewidmet war, als alle die Offiziere und Adeligen, die an jenem Abend ihre ersten Zuhörer waren, auf die Guillotine geschleppt werden, und bald ereignet sich die groteske Situation, daß der Dichter der Revolution als Konterrevolutionär gefangengesetzt wird, daß man ihm, und gerade ihm den Prozeß macht mit der Anschuldigung, sein Vaterland verraten zu haben. Nur der 9. Thermidor, der mit dem Sturz Robespierres die Gefängnisse öffnet, hat der Französischen Revolution die Schmach erspart, den Dichter ihres unsterblichsten Liedes dem »nationalen Rasiermesser« überantwortet zu haben.
|
56 |
+
|
57 |
+
Immerhin, es wäre ein heldischer Tod gewesen und nicht ein so klägliches Verdämmern im Dunkel, wie es Rouget verhängt ist. Denn um mehr als vierzig Jahre, um Tausende und Tausende von Tagen überlebt der unglückliche Rouget den einzigen wirklich schöpferischen Tag seines Lebens. Man hat ihm die Uniform ausgezogen, man hat ihm die Pension gestrichen; die Gedichte, die Opern, die Texte, die er schreibt, werden nicht gedruckt, nicht gespielt. Das Schicksal verzeiht es dem Dilettanten nicht, sich unberufen in die Reihe der Unsterblichen eingedrängt zu haben. Mit allerhand kleinen und nicht immer sauberen Geschäften fristet der kleine Mann sein kleines Leben. Vergebens versuchen aus Mitleid Carnot und später Bonaparte, ihm zu helfen. Aber etwas in dem Charakter Rougets ist rettungslos vergiftet und verschroben geworden durch die Grausamkeit jenes Zufalls, der ihn Gott und Genius sein ließ drei Stunden lang und dann verächtlich wieder zurückwarf in die eigene Nichtigkeit. Er zankt und queruliert mit allen Mächten, er schreibt an Bonaparte, der ihm helfen wollte, freche und pathetische Briefe, er rühmt sich öffentlich, bei der Volksabstimmung gegen ihn gestimmt zu haben. Seine Geschäfte verwickeln ihn in dunkle Affären, und wegen eines unbezahlten Wechsels muß er sogar mit dem Schuldgefängnis St. Pelargie Bekanntschaft machen. Unbeliebt an allen Stellen, von Schuldnern gejagt, von der Polizei ständig bespitzelt, verkriecht er sich schließlich irgendwo in der Provinz, und wie aus einem Grabe, abgeschieden und vergessen, lauscht er von dort dem Schicksal seines unsterblichen Liedes; er erlebt es noch, daß die Marseillaise mit den siegreichen Armeen über alle Länder Europas stürmt, dann noch, daß Napoleon, kaum Kaiser geworden, sie als zu revolutionär aus allen Programmen streichen läßt, daß die Bourbonen sie dann gänzlich verbieten. Nur ein Staunen kommt den verbitterten Greis an, wie nach einem Menschenalter die Julirevolution 1830 seine Worte, seine Melodie in alter Kraft auferstehen läßt an den Barrikaden von Paris und der Bürgerkönig Louis Philippe ihm als dem Dichter ein kleines Pensiönchen verleiht. Wie ein Traum scheint es dem Verschollenen, dem Vergessenen, daß man sich seiner überhaupt noch erinnert, aber es ist nur ein kleines Erinnern mehr, und als der Sechsundsiebzigjährige endlich in Choisy-le-Roi 1836 stirbt, nennt und kennt niemand seinen Namen mehr. Abermals muß ein Menschenalter vergehen: erst im Weltkrieg, da die Marseillaise, längst Nationalhymnus geworden, an allen Fronten Frankreichs wieder kriegerisch erklingt, wird angeordnet, daß der Leichnam des kleinen Hauptmanns Rouget an derselben Stelle im Invalidendom bestattet werde wie der des kleinen Leutnants Bonaparte, und so ruht endlich der höchst unberühmte Schöpfer eines ewigen Liedes in der Ruhmeskrypta seines Vaterlandes von der Enttäuschung aus, nichts gewesen zu sein als der Dichter einer einzigen Nacht.
|
data/sternstunden04-en.txt
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
https://www.deepl.com/translator machine translated
|
2 |
+
|
3 |
+
The genius of a night
|
4 |
+
|
5 |
+
The Marseillaise, April 25, 1792
|
6 |
+
|
7 |
+
The genius of one night
|
8 |
+
|
9 |
+
In 1792, two months, three months already, the French National Assembly is wavering over the decision: war against the coalition of emperors and kings or peace. Louis XVI is himself undecided; he senses the danger of a victory for the revolutionaries, he senses the danger of their defeat. The parties are also uncertain. The Girondists are pushing for war to retain power, Robespierre and the Jacobins are fighting for peace to seize power themselves in the meantime. Day by day the situation becomes more tense, the journals clamor, the clubs discuss, ever wilder the rumors buzz, and ever more public opinion is aroused by them. As always a decision, it therefore becomes a kind of liberation, as on April 20 the King of France finally declares war on the Emperor of Austria and the King of Prussia.
|
10 |
+
|
11 |
+
During these weeks and weeks the electric tension has lain over Paris, weighing heavily and disturbing the soul; but the excitement in the border towns is even more oppressive, even more threatening. Troops are already assembled in all bivouacs, volunteers and national guards are being equipped in every village, in every town, fortifications are being repaired everywhere, and especially in Alsace it is known that on its soil, as always between France and Germany, the first decision will be made. On the banks of the Rhine, the enemy, the adversary, is not, as in Paris, a blurred, a pathetic-rhetorical concept, but a visible, sensuous presence; for at the fortified bridgehead, from the tower of the cathedral, the approaching Prussian regiments can be perceived with the naked eye. At night the wind carries the rolling of the enemy artillery wagons, the clanking of weapons, the trumpet signals across the stream glittering indifferently in the moonlight. And everyone knows: only one word, only one decree is needed, and from the silent mouth of the Prussian cannons thunder and lightning will fly, and the thousand-year struggle between Germany and France will have begun once again - this time in the name of the new freedom on the one side and in the name of the old order on the other.
|
12 |
+
|
13 |
+
It was an incomparable day when, on April 25, 1792, the news of the declaration of war was brought from Paris to Strasbourg by convoys. Immediately the people streamed out of all the streets and houses into the open squares, ready for war the whole garrison marched to the last parade, regiment after regiment. On the main square, Mayor Dietrich awaits them, the tricolor sash around his body, the cockade on his hat, which he waves in greeting to the soldiers. The call of the fanfare and the roll of the drums call for silence. In a loud voice, Dietrich reads the text of the declaration of war in French and German in this and all other places in the city. After his last words the regimental musicians intoned the first, the preliminary war song of the revolution, the "Ça ira", actually a sparkling, high-spirited, mocking dance melody, but the clashing, the thundering steps of the marching out regiments gave it a martial beat. Then the crowd disperses, carrying the fanned enthusiasm into all the alleys and houses; in the cafes, in the clubs, inflammatory speeches are made and proclamations distributed. "Aux armes, citoyens! L'étendard de la guerre est déployé! Le signal est donné!"; thus and with similar calls they begin, and everywhere, in all the speeches, in all the newspapers, on all the posters, on all the lips, are repeated such punchy, rhythmic cries as "Aux armes, citoyens!". Qu'ils tremblent donc, les despotes couronnés! Marchons, enfants de la liberté!", and each time the masses cheer and cheer to the fiery words.
|
14 |
+
|
15 |
+
The great masses in the streets and squares always cheer when war is declared, but other voices, quieter, more distant voices, always stir in such moments of street jubilation; fear, too, worry, too, wakes up at a declaration of war, only it whispers secretly in the parlors or remains silent with a pale lip. Eternally and everywhere are mothers who say to themselves: Will the foreign soldiers not murder my children; in all countries are the peasants who worry about their possessions, their fields, their huts, their cattle and their harvest. Will not their seed be trampled, their house plundered by the brutal hordes, not fertilized with blood the fields of their labors? But the mayor of Strasbourg, Frederick Baron Dietrich, actually an aristocrat, but like the best aristocracy of France at that time devoted to the cause of the new freedom with all his soul, wants only the loud, the ringing voices of confidence to speak; consciously he turns the day of the declaration of war into a public celebration. With his sash across his chest, he hurried from one meeting to another to cheer the people. He distributes wine and food to the departing soldiers, and in the evening he gathers the generals, the officers and the most important officials in his spacious house on the Place de Broglie for a farewell party, to which the enthusiasm already gives the character of a victory celebration. The generals, confident of victory as always generals, preside, the young officers, who see in the war the meaning of their lives fulfilled, have free word. One cheers the other. They swing their sabers, they embrace, they drink to each other, they make passionate and increasingly passionate speeches over good wine. And again the same stimulating words of the journals and proclamations recur in all the speeches: "To arms, citizens! Let us march! Let us save the fatherland! Soon they will tremble, the crowned despots. Now that the flag of victory has unfurled, the day has come to carry the tricolor over the world! Everyone must give his best now, for the king, for the flag, for freedom!" The whole people, the whole country at such moments wants to become a sacred unity through faith in victory and enthusiasm for the cause of freedom.
|
16 |
+
|
17 |
+
Suddenly, in the midst of talking and toasting, Mayor Dietrich turned to a young captain from the fortress corps, named Rouget, sitting at his side. He remembered that half a year ago, on the occasion of the proclamation of the Constitution, this nice officer, not exactly handsome but sympathetic, had written a rather nice hymn to liberty, which the regimental musician Pleyel immediately set to music. The unpretentious work had proved to be singable, the military band had learned it, it had been played in the public square and sung in the choir. Wouldn't the declaration of war and the departure be a good occasion to stage a similar celebration? So Mayor Dietrich casually asks, as one would ask a good acquaintance for a favor, Captain Rouget (who has quite unjustifiably ennobled himself and calls himself Rouget de Lisle), whether he would not like to take advantage of the patriotic occasion and compose something for the marching troops, a war song for the Rhine army, which is to march out against the enemy tomorrow.
|
18 |
+
|
19 |
+
Rouget, a modest, insignificant man who never considered himself a great composer - his verses were never printed, his operas refüsiert - knows that occasional verses flow easily into his pen. To please the high official and the good friend, he declares himself ready. Yes, he wants to try. "Bravo, Rouget," a general from across the street drinks to him and admonishes him that he should then immediately send the song after him into the field; the Rhine Army could really use some step-winging, patriotic marching song. In the meantime, someone else begins to make a speech. Again there is toasting, noise, drinking. With a strong wave the general enthusiasm goes over the small coincidental dialogue. The revelry becomes more and more ecstatic, louder and louder, more and more frenetic, and the hour is already precariously late after midnight, when the guests leave the mayor's house.
|
20 |
+
|
21 |
+
It is late after midnight. April 25, the day of the declaration of war, which was so exciting for Strasbourg, has come to an end; in fact, April 26 has already begun. Nightly darkness lies over the houses; but this darkness is deceptive, for the city is still feverish with excitement. In the barracks, the soldiers are preparing to march out, and some of the cautious ones are perhaps already secretly fleeing behind closed shutters. In the streets, individual peletons march, the clattering hooves of the dispatch riders hunt between them, then another heavy platoon of artillery rattles up, and again and again the call of the shield guard echoes monotonously from post to post. The enemy is too close, the soul of the city too uncertain and too excited to find sleep at such a decisive moment.
|
22 |
+
|
23 |
+
Rouget, who has now climbed the circular staircase to his modest little room at 126 Grande Rue, also feels strangely aroused. He has not forgotten his promise to attempt as quickly as possible a marching song, a war song for the Rhine Army. Restlessly he trudges up and down his narrow room. How to begin? How to begin? All the cheering shouts of the proclamations, the speeches, the toasts are still whirring chaotically through his mind. "Aux armes, citoyens! ... Marchons, enfants de la liberté! ... Ecrasons la tyrannie! ... L'étendard de la guerre est déployé! ..." But he also remembers the other words he heard in passing, the voices of women trembling for their sons, the concern of the peasants that France's fields might be trampled and fertilized with blood by the foreign cohorts. Half unconsciously, he writes down the first two lines, which are only echoes, reverberations, repetitions of those calls.
|
24 |
+
|
25 |
+
"Allons, enfants de la patrie,
|
26 |
+
|
27 |
+
Le jour de gloire est arrivé!"
|
28 |
+
|
29 |
+
Then he pauses and pauses. It sits. The approach is good. Now just find the right rhythm, the melody to the words. He takes his violin from the cabinet, tries it out. And wonderful: in the very first bars, the rhythm adapts perfectly to the words. Hastily he continues to write, now already carried along, now already carried away by the power that has entered him. And all at once everything flows together: all the feelings that are discharged in this hour, all the words that he heard on the street, that he heard at the banquet, the hatred against the tyrants, the fear for the native soil, the confidence for victory, the love for freedom. Rouget does not even need to write poetry, to invent, he only needs to put into rhyme, into the ravishing rhythm of his melody, the words that today, on this single day, have passed from mouth to mouth, and he has spoken everything, said everything, sung everything that the nation felt in its innermost soul. And he does not need to compose, because through the closed shutters penetrates the rhythm of the street, of the hour, this rhythm of defiance and challenge, which lies in the marching of the soldiers, the blare of the trumpets, the rattle of the cannons. Perhaps he does not hear it himself, not his own alert ear, but the genius of the hour, who has taken up residence in his mortal body for this single night, has heard it. And more and more docilely the melody obeys the hammering, the jubilant beat, which is the heartbeat of a whole people. As if under foreign dictation, Rouget hastily and ever more hastily writes down the words, the notes - a storm has come over him, such as has never raged through his narrow bourgeois soul. An exaltation, an enthusiasm that is not his own, but magical force, concentrated into a single explosive second, sweeps the poor dilettante a hundred thousand times beyond his own measure and hurls him like a rocket - for a second light and radiant flame - to the stars. One night it is granted to Lieutenant-Captain Rouget de Lisle to be brother of the immortals: from the adopted shouts of the beginning, borrowed from the street, from the journals, creative word forms itself to him and rises to a stanza which in its poetic formulation is as immortal as the melody is immortal.
|
30 |
+
|
31 |
+
"Amour sacré de la patrie,
|
32 |
+
|
33 |
+
Conduis, soutiens nos bras vengeurs,
|
34 |
+
|
35 |
+
Liberté, liberté chérie,
|
36 |
+
|
37 |
+
Combats avec tes défenseurs."
|
38 |
+
|
39 |
+
Then a fifth stanza, the last, and formed from one excitement and in one cast, perfectly uniting the word of the melody, the immortal song is completed before dawn. Rouget extinguishes the light and lies down on his bed. Something, he does not know what, has lifted him up into a never felt brightness of his senses, something now hurls him down into a dull exhaustion. He sleeps an abysmal sleep, which is like a death. And indeed the creator, the poet, the genius in him has died again. But on the table, detached from the sleeper, whom this miracle truly overcomes in holy intoxication, lies the completed work. Hardly a second time in the history of all peoples and times has a song become word and music so quickly and so perfectly at the same time.
|
40 |
+
|
41 |
+
The same bells from the minster as always announce the new morning. Now and then the wind carries shots from the Rhine, the first skirmishes have begun. Rouget wakes up. With difficulty he gropes his way up from the abyss of his sleep. Something has happened, he feels dully, something has happened to him that he remembers only dully. Only then does he notice the freshly written page on the table. Verses? When did I write these? Music, in my own handwriting? When did I compose that? Oh yes, the song that friend Dietrich asked for yesterday, the marching song for the Rhine army! Rouget reads his verses, hums the melody to it, but feels, as always the creator before the just created work, completely uncertain. But next door lives a regimental comrade, to whom he shows and sings it. The friend seems satisfied and suggests only a few minor changes. Rouget gains a certain confidence from this first approval. With all the impatience of an author and proud of his quickly fulfilled promise, he immediately falls in with Mayor Dietrich, who is taking his morning walk in the garden, meditating on a new speech. How, Rouget? Already ready? Well, let's have a rehearsal right away. The two walk out of the garden into the parlor of the house, Dietrich sits down at the piano and plays the accompaniment, Rouget sings the text. Attracted by the unexpected morning music, the mayor's wife comes into the room and promises to make copies of the new song and, being a trained musician, to work out the accompaniment right away, so that it can be sung to the friends of the house among all sorts of other songs at the evening party tonight. The mayor Dietrich, proud of his nice tenor voice, took it upon himself to study the song more thoroughly, and on April 26, in the evening of the same day, in the morning hours of which the song was composed, it was sung for the first time to a randomly chosen company in the mayor's salon.
|
42 |
+
|
43 |
+
The audience seems to have applauded kindly, and probably there was no lack of all kinds of polite compliments for the present author. But, of course, not the slightest inkling came over the guests of the Hôtel de Broglie in the great square of Strasbourg that with invisible wings an eternal melody had swooped down into their earthly presence. Rarely do contemporaries comprehend at first sight the greatness of a man or the greatness of a work, and how little the Lady Mayoress became aware of that amazing moment she proves with the letter to her brother in which she trivializes a miracle into a social event. "You know that we receive many people in the house and it is always necessary to invent something to bring variety to the entertainment. And so my husband had the idea of having some occasional song composed. The captain of the Corps of Engineers, Rouget de Lisle, a kind poet and composer, very quickly made this music of a war song. My husband, who has a good tenor voice, immediately sang the piece, which is very attractive and shows a certain peculiarity. It is a better Gluck, more lively and animated. For my part, I applied my talent for orchestration to it and arranged the score for piano and other instruments, so I have a lot to work with. The piece has been played in our house, to the great satisfaction of the whole company."
|
44 |
+
|
45 |
+
"To the great satisfaction of the whole society" - that seems surprisingly cool to us today. But the merely friendly impression, the merely lukewarm approval is understandable, for the Marseillaise has not yet been able to truly reveal its power in this first performance. The Marseillaise is not a recital piece for a comfortable tenor voice and is not meant to be performed in a petty bourgeois salon between romances and Italian arias with a single singing voice. A song that roars to the pounding, bouncing, demanding bars of "Aux armes, citoyens" addresses a mass, a crowd, and its true orchestration is clashing arms, blaring fanfares, marching regiments. It was not meant for listeners, for the coolly seated and comfortably enjoying, but for accomplices, fellow fighters. It was not sung to a single soprano or tenor, but to the thousand-throated masses, the exemplary marching song, victory song, death song, homeland song, national song of an entire people. Only enthusiasm, from which it was first born, will give Rouget's song its inspiring power. The song has not yet ignited, the words have not yet magically resonated, the melody has not yet reached the soul of the nation, the army does not yet know its marching song, its victory song, the revolution does not yet know its eternal paean.
|
46 |
+
|
47 |
+
Even he himself, to whom this miracle happened overnight, Rouget de Lisle, has as little idea as the others what he created in that one night, somnambulistically and guided by a faithless genius. He is of course heartily pleased, the good, amiable dilettante, that the invited guests applaud vigorously, that he is politely complimented as an author. With the small vanity of a small person, he seeks to make diligent use of this small success in his small provincial circle. He sings the new melody to his comrades in the coffee houses, he has copies made and sends them to the generals of the Rhine army. In the meantime, on the order of the mayor and the recommendation of the military authorities, the Strasbourg music corps rehearsed the "War Song for the Army of the Rhine", and four days later, at the departure of the troops, the music corps of the Strasbourg National Guard played the new march in the large square. In patriotic fashion, the Strasbourg publisher also agrees to print the "Chant de guerre pour l'armée du Rhin", which is respectfully dedicated to General Luckner by his military subordinate. But not a single one of the generals of the Rhine army thinks of actually having the new tune played or sung during the advance, and so, like all Rouget's previous attempts, the salon success of "Allons, enfants de la patrie" seems to remain a one-day success, a provincial affair, and to be forgotten as such.
|
48 |
+
|
49 |
+
But the innate power of a work can never be concealed or closed off in the long run. A work of art can be forgotten by time, it can be forbidden and become stale, but the elemental always forces its way to victory over the ephemeral. One month, two months nothing is heard of the war song of the Rhine army. The printed and handwritten copies lie and wander around in indifferent hands. But it is always enough if a work really inspires even a single person, for every genuine enthusiasm itself becomes creative. At the other end of France, in Marseille, the Club of the Friends of the Constitution held a banquet for the marching volunteers on June 22. Five hundred young, fiery people in their new uniforms of the National Guard were seated at a long table; in their circle exactly the same mood as on April 25 in Strasbourg, only even hotter, more heated and more passionate, thanks to the southern temperament of the people of Marseilles, and no longer so vainly confident of victory as in that first hour of the declaration of war. For not, as those generals fibbed, have the revolutionary French troops marched straight across the Rhine and been welcomed everywhere with open arms. On the contrary, the enemy has advanced deep into the French countryside, freedom is threatened, the cause of liberty is in danger.
|
50 |
+
|
51 |
+
Suddenly, in the midst of the feast, one - his name is Mireur and he is a medical student from the University of Montpellier - strikes his glass and rises. Everyone falls silent and looks at him. One expects a speech and an address. But instead, the young man swings up his right hand and intones a song, a new song that they all don't know and no one knows how it got into his hand, "Allons, enfants de la patrie." And now the spark ignites as if it had fallen into a powder keg. Feeling and emotion, the eternal poles, have touched. All these young people who are going out tomorrow, who want to fight for freedom and are ready to die for the fatherland, feel their innermost will, their very own thoughts expressed in these words; irresistibly the rhythm sweeps them up into a unanimous, ecstatic enthusiasm. Verse after verse is cheered, once again, a second time the song must be repeated, and already the melody has become their own, already they sing, excitedly jumping up, glasses raised, the refrain thundering along. "Aux armes, citoyens! Formez vos bataillons!" Curious people crowd in from the street to hear what is being sung here with such enthusiasm, and already they are singing along themselves; the next day the melody is on a thousand and ten thousand lips. A reprint spreads it, and as the five hundred volunteers march off on July 2, the song travels with them. When they get tired on the road, when their step becomes slack, only one person needs to sing the hymn, and its rousing, its forward-rushing beat gives them all renewed momentum. When they march through a village and the peasants gather in amazement, the inhabitants curious, they sing it in chorus. It has become their song, they have, without knowing that it was intended for the Rhine Army, without suspecting by whom and when it was composed, adopted the hymn as that of their battalion, as a confession of their life and death. It belongs to them like the flag, and in passionate advance they want to carry it over the world.
|
52 |
+
|
53 |
+
The first great victory of the Marseillaise - for that is what Rouget's anthem will soon call itself - is Paris. On June 30, the battalion marches in through the faubourgs, flag in front and song. Thousands and tens of thousands stand and wait in the streets to receive them festively, and as the Marseillers now approach, five hundred men, singing as it were from one throat to the beat of the song and singing it again and again, the crowd listens. What is this magnificent, ravishing hymn that the people of Marseilles are singing? What a fanfare this, driving into all hearts, accompanied by the pattering drumbeat, this "Aux armes, citoyens!" Two hours later, three hours later, and already the refrain resounds in all alleys again. Forgotten is the "Ça ira," forgotten the old marches, the worn-out couplets: the revolution has recognized its own voice, the revolution has found its song.
|
54 |
+
|
55 |
+
|
56 |
+
The spread of the hymn became avalanche-like, the triumphant run unstoppable. The hymn is sung at banquets, in theaters and clubs, then even in churches after the Tedeum and soon instead of the Tedeum. In one, in two months the Marseillaise has become the song of the people and of the whole army. Servan, the first republican minister of war, recognizes with a wise eye the tonic, the exalting power of such a unique national battle song. In hasty order he commands that a hundred thousand copies be sent to all commands, and in two or three nights the Song of the Unknown is more widespread than all the works of Molière, Racine, and Voltaire. No feast that does not close with the Marseillaise, no battle where regimental musicians do not for the time being intone the war song of freedom. At Jemappes and Nerwinden, the regiments arrange themselves in choral song for the decisive storm, and the enemy generals, who can only stimulate their soldiers with the old recipe of doubled brandy rations, see with fright that they have nothing to oppose the explosive force of this "terrible" hymn when, sung simultaneously by thousands and thousands, it rushes against their own ranks like a ringing, clashing wave. Over all the battles of France now floats, sweeping countless into enthusiasm and death, the Marseillaise, like Nike, the winged goddess of victory.
|
57 |
+
|
58 |
+
Meanwhile, in the small garrison of Hüningen, a most unknown captain of fortifications, Rouget, sits dutifully designing ramparts and entrenchments. Perhaps he has already forgotten the "War Song of the Rhine Army" that he created on that lost night of April 26, 1792, and does not even dare to suspect, when he reads in the gazettes about that other hymn, that other war song that takes Paris by storm, that this victorious "Song of the Marseilles" is word for word and measure for measure nothing other than the miracle of that night that happened in him and to him. For cruel irony of fate - rushing to all heavens, roaring to the stars, this melody does not carry high only one person, namely the person who devised it. Nobody in all France cares about Captain Rouget de Lisle, the most enormous fame ever known to a song remains to the song, and not a shadow of it falls on its creator Rouget. His name is not printed on the texts, and he himself would remain completely unnoticed by the masters of the hour, if he did not bring himself into annoying memory. For - ingenious paradox as only history can invent it - the creator of the revolutionary hymn is not a revolutionary; on the contrary: he who, like no other, drove the revolution on through his immortal song, now wants to curb it back with all his might. When the people of Marseilles and the Parisian mob - his song on their lips - storm the Tuileries and the king is deposed, Rouget de Lisle has had enough of the revolution. He refuses to take the oath of allegiance to the Republic and resigns rather than serve the Jacobins. The word of "liberté chérie", the beloved liberty, in his hymn is not an empty word to this upright man: he detests the new tyrants and despots in the Convention no less than he hated those crowned and anointed beyond the borders. He openly gives vent to his displeasure against the Welfare Committee when his friend, Mayor Dietrich, the godfather of the Marseillaise, when General Luckner, to whom it was dedicated, when all the officers and nobles who were their first listeners that evening are dragged to the guillotine, and soon the grotesque situation occurs that the poet of the Revolution is imprisoned as a counter-revolutionary, that he, and he of all people, is put on trial with the accusation of having betrayed his fatherland. Only the 9th Thermidor, which opened the prisons with the fall of Robespierre, spared the French Revolution the ignominy of having handed over the poet of its most immortal song to the "national razor".
|
59 |
+
|
60 |
+
At least, it would have been a heroic death and not such a miserable twilight in the darkness as Rouget is imposed. For by more than forty years, by thousands and thousands of days, the unfortunate Rouget survives the only truly creative day of his life. His uniform has been taken off, his pension has been cancelled; the poems, the operas, the texts he writes are not printed, not performed. The fate does not forgive the dilettante for having entered the ranks of the immortals without being called. With all kinds of small and not always clean business the little man ekes out his small life. In vain, Carnot and later Bonaparte try to help him out of pity. But something in Rouget's character has become irredeemably poisoned and twisted by the cruelty of that coincidence which let him be God and genius for three hours and then contemptuously threw him back into his own nothingness. He quarrels and crosses with all powers, he writes insolent and pathetic letters to Bonaparte, who wanted to help him, he boasts publicly that he voted against him in the referendum. His business entangles him in dark affairs, and because of an unpaid bill of exchange he even has to make acquaintance with the debtors' prison St. Pelargie. Unpopular in all places, hounded by debtors, constantly spied on by the police, he finally holes up somewhere in the provinces, and as if from a grave, secluded and forgotten, he listens from there to the fate of his immortal song; he still experiences that the Marseillaise storms with the victorious armies over all the countries of Europe, then still that Napoleon, hardly emperor, has it deleted from all programs as too revolutionary, that the Bourbons then ban it completely. The embittered old man is only amazed when, after an age, the July Revolution of 1830 resurrects his words and his melody in their old strength at the barricades of Paris, and the bourgeois king Louis Philippe grants him a small pensium as a poet. It seems like a dream to the lost, the forgotten, that he is remembered at all, but it is only a small remembrance, and when the seventy-six-year-old finally dies in Choisy-le-Roi in 1836, no one calls or knows his name anymore. Once again, an age must pass: Only in the World War, when the Marseillaise, which had long since become the national anthem, resounds again in war on all fronts of France, is it ordered that the body of the little Captain Rouget be buried in the same place in the Invalides Cathedral as that of the little Lieutenant Bonaparte, and so at last the most unfamous creator of an eternal song rests in the crypt of glory of his fatherland from the disappointment of having been nothing but the poet of a single night.
|
61 |
+
|
install-sw.sh
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# pip install pipx
|
2 |
+
# pipx install poetry
|
3 |
+
# pipx ensurepath
|
4 |
+
# source ~/.bashrc
|
5 |
+
|
6 |
+
# curl -sSL https://install.python-poetry.org | python3 -
|
7 |
+
# -C- continue -S show error -o output
|
8 |
+
curl -sSL -C- -o install-poetry.py https://install.python-poetry.org
|
9 |
+
python install-poetry.py
|
10 |
+
rm install-poetry.py
|
11 |
+
echo export PATH=~/.local/bin:$PATH > ~/.bashrc
|
12 |
+
source ~/.bashrc
|
13 |
+
# ~/.local/bin/poetry install
|
14 |
+
|
15 |
+
wget -c https://deb.nodesource.com/setup_12.x
|
16 |
+
bash setup_12.x
|
17 |
+
apt-get install -y nodejs
|
18 |
+
npm install -g npm@latest
|
19 |
+
npm install -g nodemon
|
20 |
+
rm setup_12.x
|
21 |
+
|
22 |
+
# apt upate # alerady done in apt-get install -y nodejs
|
23 |
+
apt install byobu -y > /dev/null 2>&1
|
24 |
+
|
mlbee/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
"""Init."""
|
2 |
-
__version__ = "0.1.
|
3 |
from .mlbee import mlbee
|
4 |
|
5 |
__all__ = ("mlbee",)
|
|
|
1 |
"""Init."""
|
2 |
+
__version__ = "0.1.0a1"
|
3 |
from .mlbee import mlbee
|
4 |
|
5 |
__all__ = ("mlbee",)
|
mlbee/app-litbee.py-
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Prep __main__.py.
|
2 |
+
|
3 |
+
https://share.streamlit.io/deploy
|
4 |
+
Advanced settings...
|
5 |
+
Python version
|
6 |
+
3.7
|
7 |
+
3.8
|
8 |
+
3.9*
|
9 |
+
3.10
|
10 |
+
|
11 |
+
https://docs.streamlit.io/knowledge-base/using-streamlit/hide-row-indices-displaying-dataframe
|
12 |
+
Hide row indices when displaying a dataframe
|
13 |
+
# CSS to inject contained in a string
|
14 |
+
hide_table_row_index = '''
|
15 |
+
<style>
|
16 |
+
tbody th {display:none}
|
17 |
+
.blank {display:none}
|
18 |
+
</style>
|
19 |
+
'''
|
20 |
+
# Inject CSS with Markdown
|
21 |
+
st.markdown(hide_table_row_index, unsafe_allow_html=True)
|
22 |
+
|
23 |
+
# Display a static table
|
24 |
+
st.table(df)
|
25 |
+
|
26 |
+
# Hide row indices with st.dataframe
|
27 |
+
# CSS to inject contained in a string
|
28 |
+
hide_dataframe_row_index = '''
|
29 |
+
<style>
|
30 |
+
.row_heading.level0 {display:none}
|
31 |
+
.blank {display:none}
|
32 |
+
</style>
|
33 |
+
'''
|
34 |
+
# Inject CSS with Markdown
|
35 |
+
st.markdown(hide_dataframe_row_index, unsafe_allow_html=True)
|
36 |
+
|
37 |
+
# Display an interactive table
|
38 |
+
st.dataframe(df)
|
39 |
+
|
40 |
+
https://medium.com/@avra42/streamlit-python-cool-tricks-to-make-your-web-application-look-better-8abfc3763a5b
|
41 |
+
hide_menu_style = '''
|
42 |
+
<style>
|
43 |
+
#MainMenu {visibility: hidden; }
|
44 |
+
footer {visibility: hidden;}
|
45 |
+
</style>
|
46 |
+
'''
|
47 |
+
st.markdown(hide_menu_style, unsafe_allow_html=True)
|
48 |
+
|
49 |
+
"""
|
50 |
+
# pylint: disable=invalid-name
|
51 |
+
import os
|
52 |
+
import sys
|
53 |
+
import time
|
54 |
+
from pathlib import Path
|
55 |
+
from types import SimpleNamespace
|
56 |
+
from typing import Optional
|
57 |
+
|
58 |
+
import loguru
|
59 |
+
import logzero
|
60 |
+
import pandas as pd
|
61 |
+
import ezbee
|
62 |
+
import dzbee
|
63 |
+
import debee
|
64 |
+
|
65 |
+
import streamlit as st
|
66 |
+
from loguru import logger as loggu
|
67 |
+
from logzero import logger
|
68 |
+
from set_loglevel import set_loglevel
|
69 |
+
from streamlit import session_state as state
|
70 |
+
|
71 |
+
from litbee import __version__
|
72 |
+
# from litbee.options import options
|
73 |
+
|
74 |
+
# from litbee.files2df import files2df
|
75 |
+
# from litbee.utils import sb_front_cover, instructions, menu_items
|
76 |
+
# from litbee.ezbee_page import ezbee_page
|
77 |
+
# from litbee.dzbee_page import dzbee_page
|
78 |
+
# from litbee.xbee_page import xbee_page
|
79 |
+
from litbee.utils import menu_items
|
80 |
+
|
81 |
+
from litbee.multipage import Multipage
|
82 |
+
|
83 |
+
# from litbee.fetch_upload import fetch_upload
|
84 |
+
# from litbee.fetch_paste import fetch_paste
|
85 |
+
# from litbee.fetch_urls import fetch_urls
|
86 |
+
|
87 |
+
from litbee.home import home
|
88 |
+
from litbee.settings import settings
|
89 |
+
from litbee.info import info
|
90 |
+
from litbee.utils import style_css
|
91 |
+
|
92 |
+
# from ezbee import ezbee
|
93 |
+
|
94 |
+
curr_py = sys.version[:3]
|
95 |
+
msg = f"Some packages litbee depends on can only run with Python 3.8, current python is **{curr_py}**, sorry..."
|
96 |
+
assert curr_py == "3.8", msg
|
97 |
+
|
98 |
+
os.environ["TZ"] = "Asia/Shanghai"
|
99 |
+
try:
|
100 |
+
time.tzset() # type: ignore
|
101 |
+
except Exception as _:
|
102 |
+
logger.warning("time.tzset() error: %s. Probably running Windows, we let it pass.", _)
|
103 |
+
|
104 |
+
# uncomment this in dev oe set/export LOGLEVEL=10
|
105 |
+
# os.environ["LOGLEVEL"] = "10"
|
106 |
+
|
107 |
+
logzero.loglevel(set_loglevel())
|
108 |
+
|
109 |
+
loggu.remove()
|
110 |
+
_ = (
|
111 |
+
"<green>{time:YY-MM-DD HH:mm:ss}</green> | "
|
112 |
+
"<level>{level: <5}</level> | <level>{message}</level> "
|
113 |
+
"<cyan>{module}.{name}</cyan>:<cyan>{line}</cyan>"
|
114 |
+
)
|
115 |
+
loggu.add(
|
116 |
+
sys.stderr,
|
117 |
+
format=_,
|
118 |
+
level=set_loglevel(),
|
119 |
+
colorize=True,
|
120 |
+
)
|
121 |
+
|
122 |
+
# from PIL import Image
|
123 |
+
# page_icon=Image.open("icon.ico"),
|
124 |
+
st.set_page_config( # type: ignore
|
125 |
+
page_title=f"litbee v{__version__}",
|
126 |
+
# page_icon="🧊",
|
127 |
+
page_icon="🐝",
|
128 |
+
# layout="wide",
|
129 |
+
initial_sidebar_state="auto", # "auto" or "expanded" or "collapsed",
|
130 |
+
menu_items=menu_items,
|
131 |
+
)
|
132 |
+
|
133 |
+
# pd.set_option("precision", 2)
|
134 |
+
pd.set_option("display.precision", 2)
|
135 |
+
pd.options.display.float_format = "{:,.2f}".format
|
136 |
+
|
137 |
+
sourcetype = "upload"
|
138 |
+
if set_loglevel() <= 10:
|
139 |
+
sourcetype = "urls"
|
140 |
+
|
141 |
+
_ = dict(
|
142 |
+
beetype="ezbee",
|
143 |
+
sourcetype=sourcetype,
|
144 |
+
sourcecount=2,
|
145 |
+
sentali=None,
|
146 |
+
src_filename="",
|
147 |
+
tgt_filename="",
|
148 |
+
src_fileio=b"",
|
149 |
+
tgt_fileio=b"",
|
150 |
+
src_file="",
|
151 |
+
tgt_file="",
|
152 |
+
list1=[""],
|
153 |
+
list2=[""],
|
154 |
+
df=None,
|
155 |
+
df_a=None,
|
156 |
+
df_s_a=None,
|
157 |
+
count=1,
|
158 |
+
updated=False,
|
159 |
+
)
|
160 |
+
if "ns" not in state:
|
161 |
+
state.ns = SimpleNamespace(**_)
|
162 |
+
state.ns.list = [*_]
|
163 |
+
|
164 |
+
logger.info(
|
165 |
+
"versions ezbee dzbee debee: %s, %s, %s",
|
166 |
+
ezbee.__version__,
|
167 |
+
dzbee.__version__,
|
168 |
+
debee.__version__,
|
169 |
+
)
|
170 |
+
|
171 |
+
|
172 |
+
def main():
|
173 |
+
"""Bootstrap."""
|
174 |
+
# options()
|
175 |
+
|
176 |
+
st.markdown(f"<style>{style_css}</style>", unsafe_allow_html=True)
|
177 |
+
|
178 |
+
app = Multipage()
|
179 |
+
|
180 |
+
# app.add_page("Home", "house", ask.app)
|
181 |
+
# app.add_page("Settings", "gear", settings.app)
|
182 |
+
# app.add_page("Info", "info", info.app)
|
183 |
+
|
184 |
+
# app.add_page("Home", "house", fetch_upload)
|
185 |
+
app.add_page("Home", "house", home)
|
186 |
+
app.add_page("Settings", "gear", settings)
|
187 |
+
app.add_page("Info", "info", info)
|
188 |
+
|
189 |
+
# The main app
|
190 |
+
app.run()
|
191 |
+
|
192 |
+
# st.markdown(f"""<div class="text"> run: {state.ns.count}</div>""", unsafe_allow_html=True)
|
193 |
+
|
194 |
+
if set_loglevel() <= 10:
|
195 |
+
st.markdown(state.ns.count)
|
196 |
+
loggu.debug(f" run: {state.ns.count}")
|
197 |
+
logger.debug(f" run: {state.ns.count}")
|
198 |
+
state.ns.count += 1
|
199 |
+
state.ns.updated = False
|
200 |
+
|
201 |
+
|
202 |
+
main()
|
mlbee/color_map.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Map cell background color for pandas.DataFrame.
|
2 |
+
|
3 |
+
palette = sns.blend_palette(
|
4 |
+
# ["pink", "palegreen", 'green'], N_COLORS).as_hex()
|
5 |
+
# ["pink", "palegreen"], N_COLORS).as_hex()
|
6 |
+
["red", "palegreen"], N_COLORS).as_hex()
|
7 |
+
Refer to color_table_applymap.py
|
8 |
+
|
9 |
+
Taken from vizbee color_map
|
10 |
+
"""
|
11 |
+
# pylint: disable=invalid-name, broad-except
|
12 |
+
palette = [
|
13 |
+
# "#f00000",
|
14 |
+
# "#f02315",
|
15 |
+
"#e2482c",
|
16 |
+
"#d36b41",
|
17 |
+
"#c49057",
|
18 |
+
"#b5b36c",
|
19 |
+
"#a7d883",
|
20 |
+
"#98fb98",
|
21 |
+
]
|
22 |
+
ncolors = len(palette)
|
23 |
+
|
24 |
+
|
25 |
+
def color_map(v, min_: float = 0, max_: float = 1):
|
26 |
+
"""Map cell background color.
|
27 |
+
|
28 |
+
e.g. s_df = df.style.applymap(color_map, min_=min_, max_=max_, subset=["B"])
|
29 |
+
or s_df = df.style.applymap(color_map, subset=['likelihood'])
|
30 |
+
|
31 |
+
or
|
32 |
+
s_df = df.style.applymap(color_map, subset=[2,])
|
33 |
+
|
34 |
+
or
|
35 |
+
s_df = df.style.applymap(color_map, subset=[df.columns[2])
|
36 |
+
|
37 |
+
or
|
38 |
+
s_df = df.style.applymap(color_map, subset=[*df.columns[1:3]] + [*df.columns[0:1]])
|
39 |
+
"""
|
40 |
+
wd = (max_ - min_) / ncolors
|
41 |
+
try:
|
42 |
+
v = float(v) # !!!
|
43 |
+
pal = palette[min(ncolors - 1, int((v - min_) / wd))]
|
44 |
+
except Exception: # as e: # wont style str etc.
|
45 |
+
# logger.debug("%s", e)
|
46 |
+
# return None
|
47 |
+
return "wrap_text: true"
|
48 |
+
|
49 |
+
return f"background-color: {pal}"
|
mlbee/cos_matrix2.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Compute the cosine similarity matrix of A.
|
2 |
+
|
3 |
+
https://stackoverflow.com/questions/17627219/whats-the-fastest-way-in-python-to-calculate-cosine-similarity-given-sparse-mat
|
4 |
+
"""
|
5 |
+
from typing import Optional
|
6 |
+
import numpy as np
|
7 |
+
|
8 |
+
|
9 |
+
def cos_matrix2(mat1: np.ndarray, mat2: Optional[np.ndarray] = None) -> np.ndarray:
|
10 |
+
"""Compute the cosine similarity matrix of mat1, mat2: mat1 * mat2.T.
|
11 |
+
|
12 |
+
Args:
|
13 |
+
mat1: np.asarray
|
14 |
+
mat2: [Optional], if not present mat2 = mat1
|
15 |
+
Returns
|
16 |
+
cosine similarity
|
17 |
+
"""
|
18 |
+
if not isinstance(mat1, np.ndarray):
|
19 |
+
mat1 = np.asarray(mat1, dtype=np.float32)
|
20 |
+
|
21 |
+
if mat2 is None:
|
22 |
+
mat2 = mat1.copy()
|
23 |
+
|
24 |
+
if not isinstance(mat2, np.ndarray):
|
25 |
+
mat2 = np.asarray(mat2, dtype=np.float32)
|
26 |
+
|
27 |
+
if mat1.shape[1] != mat2.shape[1]:
|
28 |
+
print("shape mismatch: %s, %s", mat1.shape, mat2.shape)
|
29 |
+
raise SystemError(1)
|
30 |
+
cosine = np.dot(mat1, mat2.T)
|
31 |
+
|
32 |
+
norm1 = np.linalg.norm(mat1, axis=1)
|
33 |
+
norm2 = np.linalg.norm(mat2, axis=1)
|
34 |
+
|
35 |
+
# if not (norm1 and norm2): return 0
|
36 |
+
|
37 |
+
size1 = norm1.size
|
38 |
+
size2 = norm2.size
|
39 |
+
norm_mat = np.dot(norm1.reshape(size1, 1), norm2.reshape(1, size2))
|
40 |
+
|
41 |
+
for idx in range(size1):
|
42 |
+
for jdx in range(size2):
|
43 |
+
if norm_mat[idx, jdx] == 0:
|
44 |
+
cosine[idx, jdx] = 0.0
|
45 |
+
else:
|
46 |
+
cosine[idx, jdx] = cosine[idx, jdx] / norm_mat[idx, jdx]
|
47 |
+
return cosine
|
mlbee/fetch_paste.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Fetch pasted text and convert to state.ns.list1/list2."""
|
2 |
+
# pylint: disable=invalid-name
|
3 |
+
import streamlit as st
|
4 |
+
from logzero import logger
|
5 |
+
from streamlit import session_state as state
|
6 |
+
|
7 |
+
|
8 |
+
def fetch_paste():
|
9 |
+
"""Fetch from clipboard."""
|
10 |
+
# st.write("Coming soon")
|
11 |
+
text1 = ""
|
12 |
+
text2 = ""
|
13 |
+
with st.form(key="paste_in_form"):
|
14 |
+
_ = st.expander(f"{state.ns.beetype}: Paste text", expanded=True)
|
15 |
+
with _:
|
16 |
+
col1, col2 = st.columns(2)
|
17 |
+
with col1:
|
18 |
+
text1 = st.text_area(
|
19 |
+
label="Paste your stuff here",
|
20 |
+
key="paste_text1",
|
21 |
+
# help=""
|
22 |
+
height=500,
|
23 |
+
)
|
24 |
+
|
25 |
+
with col2:
|
26 |
+
text2 = st.text_area(
|
27 |
+
label="Paste your stuff here",
|
28 |
+
# help=""
|
29 |
+
key="paste_text2",
|
30 |
+
height=500,
|
31 |
+
)
|
32 |
+
|
33 |
+
submitted = st.form_submit_button("Submit")
|
34 |
+
|
35 |
+
logger.debug("text1[:10]: %s, text2[:10]: %s", text1[:10], text2[:10])
|
36 |
+
|
37 |
+
list1 = [_.strip() for _ in text1.splitlines() if _.strip()]
|
38 |
+
list2 = [_.strip() for _ in text2.splitlines() if _.strip()]
|
39 |
+
|
40 |
+
state.ns.list1 = list1[:]
|
41 |
+
state.ns.list2 = list2[:]
|
42 |
+
|
43 |
+
logger.debug("len(list1): %s, len(list2): %s", len(list1), len(list2))
|
44 |
+
|
45 |
+
logger.debug("state.ns.updated: %s", state.ns.updated)
|
46 |
+
|
47 |
+
state.ns.src_filename = ""
|
48 |
+
state.ns.updated = True
|
mlbee/fetch_upload.py
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Fetch upload and convert to list1/list2."""
|
2 |
+
import streamlit as st
|
3 |
+
from logzero import logger
|
4 |
+
from streamlit import session_state as state
|
5 |
+
|
6 |
+
|
7 |
+
def fetch_upload():
|
8 |
+
"""Fetch upload and convert to list1/list2."""
|
9 |
+
src_fileio = b""
|
10 |
+
tgt_fileio = b""
|
11 |
+
with st.form(key="upload_in_form"):
|
12 |
+
_ = st.expander(f"{state.ns.beetype}: Pick two files", expanded=True)
|
13 |
+
with _:
|
14 |
+
col1, col2 = st.columns(2)
|
15 |
+
with col1:
|
16 |
+
src_fileio = st.file_uploader(
|
17 |
+
"Choose source file (utf8 txt)",
|
18 |
+
type=[
|
19 |
+
"txt",
|
20 |
+
],
|
21 |
+
key="src_text",
|
22 |
+
# accept_multiple_files=True,
|
23 |
+
# accept_multiple_files=False,
|
24 |
+
)
|
25 |
+
|
26 |
+
with col2:
|
27 |
+
tgt_fileio = st.file_uploader(
|
28 |
+
"Choose target file (utf8 txt)",
|
29 |
+
type=[
|
30 |
+
"txt",
|
31 |
+
],
|
32 |
+
key="tgt_text",
|
33 |
+
# accept_multiple_files=True,
|
34 |
+
)
|
35 |
+
submitted = st.form_submit_button("Submit")
|
36 |
+
|
37 |
+
# logger.debug(" len(src_fileio): %s", len(src_fileio))
|
38 |
+
# logger.debug(" len(tgt_fileio): %s", len(tgt_fileio))
|
39 |
+
|
40 |
+
filename1 = ""
|
41 |
+
if src_fileio:
|
42 |
+
logger.debug(" type(src_fileio): %s", type(src_fileio))
|
43 |
+
|
44 |
+
# for st.file_uploade accept_multiple_files=True
|
45 |
+
if isinstance(src_fileio, list):
|
46 |
+
logger.debug(" len(src_fileio): %s", len(src_fileio))
|
47 |
+
filenames = []
|
48 |
+
try:
|
49 |
+
filenames = [elm.name for elm in src_fileio] # type: ignore
|
50 |
+
except Exception as exc:
|
51 |
+
logger.error(exc)
|
52 |
+
logger.debug("src_fileio names: *%s*", filenames)
|
53 |
+
|
54 |
+
# state.ns.src_fileio = src_fileio
|
55 |
+
state.ns.src_file = src_fileio[-1].getvalue().decode()
|
56 |
+
state.ns.src_filename = src_fileio[-1].name
|
57 |
+
else:
|
58 |
+
logger.debug("src_fileio.name: [%s]", src_fileio.name)
|
59 |
+
filenames = [src_fileio.name]
|
60 |
+
logger.debug("src_fileio names: %s", filenames)
|
61 |
+
|
62 |
+
# state.ns.src_fileio = src_fileio
|
63 |
+
state.ns.src_file = src_fileio.getvalue().decode()
|
64 |
+
state.ns.src_filename = src_fileio.name
|
65 |
+
filename1 = state.ns.src_filename
|
66 |
+
|
67 |
+
filename2 = ""
|
68 |
+
if tgt_fileio:
|
69 |
+
if isinstance(tgt_fileio, list):
|
70 |
+
logger.warning("not set to handle multiple files")
|
71 |
+
logger.warning("set accept_multiple_files=False in the meantime")
|
72 |
+
else:
|
73 |
+
state.ns.tgt_file = tgt_fileio.getvalue().decode()
|
74 |
+
state.ns.tgt_filename = tgt_fileio.name
|
75 |
+
filename2 = tgt_fileio.name
|
76 |
+
|
77 |
+
# proceed when Submit is clicked
|
78 |
+
msg1 = ""
|
79 |
+
if filename1:
|
80 |
+
msg1 += f" file1 {filename1}"
|
81 |
+
msg2 = ""
|
82 |
+
if filename2:
|
83 |
+
msg2 += f" file2 {filename2}"
|
84 |
+
glue = ""
|
85 |
+
if filename1 and filename2:
|
86 |
+
glue = ", "
|
87 |
+
|
88 |
+
upload_placeholder = st.empty()
|
89 |
+
prefix = f" Upload submitted: {msg1}{glue}{msg2}"
|
90 |
+
upload_placeholder.write(prefix)
|
91 |
+
|
92 |
+
# st.write(f" Submitted upload: {msg1}{glue}{msg2}")
|
93 |
+
if not submitted:
|
94 |
+
return None
|
95 |
+
|
96 |
+
if not (filename1 or filename2):
|
97 |
+
# st.write("| no file uploaded")
|
98 |
+
upload_placeholder.write(f"{prefix} no file uploaded")
|
99 |
+
return None
|
100 |
+
|
101 |
+
if not filename1:
|
102 |
+
# st.write("| file1 not ready")
|
103 |
+
upload_placeholder.write(f"{prefix}, file1 not ready")
|
104 |
+
return None
|
105 |
+
|
106 |
+
if not filename2:
|
107 |
+
# st.write("| file2 not ready")
|
108 |
+
upload_placeholder.write(f"{prefix}, file2 not ready")
|
109 |
+
return None
|
110 |
+
|
111 |
+
try:
|
112 |
+
_ = state.ns.src_file.splitlines()
|
113 |
+
list1 = [elm.strip() for elm in _ if elm.strip()]
|
114 |
+
_ = state.ns.tgt_file.splitlines()
|
115 |
+
list2 = [elm.strip() for elm in _ if elm.strip()]
|
116 |
+
except Exception as exc:
|
117 |
+
logger.error(exc)
|
118 |
+
list1 = [""]
|
119 |
+
list2 = [""]
|
120 |
+
|
121 |
+
logger.debug("len(list1): %s, len(list2): %s", len(list1), len(list2))
|
122 |
+
|
123 |
+
state.ns.list1 = list1[:]
|
124 |
+
state.ns.list2 = list2[:]
|
125 |
+
|
126 |
+
state.ns.updated = True
|
127 |
+
logger.debug("state.ns.updated: %s", state.ns.updated)
|
128 |
+
|
129 |
+
return None
|
mlbee/fetch_urls.py
ADDED
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Fetch text from urls and convert to state.ns.list1/list2."""
|
2 |
+
# pylint: disable=invalid-name
|
3 |
+
import streamlit as st
|
4 |
+
from icecream import ic
|
5 |
+
from logzero import logger
|
6 |
+
from streamlit import session_state as state
|
7 |
+
|
8 |
+
from mlbee.url2txt import url2txt
|
9 |
+
|
10 |
+
ic.configureOutput(
|
11 |
+
includeContext=True,
|
12 |
+
outputFunction=logger.debug, # outputFunction=logger.info,
|
13 |
+
)
|
14 |
+
|
15 |
+
|
16 |
+
def fetch_urls():
|
17 |
+
"""Fetch text from urls and convert to state.ns.list1/list2."""
|
18 |
+
beetype = state.ns.beetype
|
19 |
+
sourcecount = state.ns.sourcecount
|
20 |
+
value = ""
|
21 |
+
if beetype == "ezbee" or beetype == "mlbee":
|
22 |
+
url1 = (
|
23 |
+
"https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/test_en.txt"
|
24 |
+
)
|
25 |
+
url2 = (
|
26 |
+
"https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/test_zh.txt"
|
27 |
+
)
|
28 |
+
value = f"{url1} {url2}"
|
29 |
+
if beetype == "dzbee":
|
30 |
+
url1 = "https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/sternstunden04-de.txt"
|
31 |
+
url2 = "https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/sternstunden04-zh.txt"
|
32 |
+
value = f"{url1} {url2}"
|
33 |
+
if beetype == "debee":
|
34 |
+
url1 = "https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/sternstunden04-de.txt"
|
35 |
+
url2 = "https://raw.githubusercontent.com/ffreemt/en-de-zh-txt/master/sternstunden04-en.txt"
|
36 |
+
value = f"{url1} {url2}"
|
37 |
+
|
38 |
+
dict_ = dict(text1="", text2="")
|
39 |
+
|
40 |
+
def fetch_cb():
|
41 |
+
"""Fetch text (dict_["text1"|"text2"]) from urls."""
|
42 |
+
ic("fetch_cb")
|
43 |
+
urls = [elm.strip() for elm in text_inp.split(" ") if elm.strip()]
|
44 |
+
|
45 |
+
# supply http:// if not startswith http
|
46 |
+
urls = [elm if elm.startswith("http") else "http://" + elm for elm in urls]
|
47 |
+
|
48 |
+
_ = "\n\t"
|
49 |
+
# st.markdown(f" urls submitted: \n{_.join(urls)}")
|
50 |
+
ic(f" urls submitted: \n{_.join(urls)}")
|
51 |
+
|
52 |
+
# st.write(" TODO: fetch text from urls.")
|
53 |
+
|
54 |
+
if state.ns.sourcecount == 2: # 2-sep
|
55 |
+
for idx, url in enumerate(urls[:2]):
|
56 |
+
try:
|
57 |
+
_ = url2txt(url)
|
58 |
+
except Exception as e:
|
59 |
+
logger.error(e)
|
60 |
+
_ = str(e)
|
61 |
+
dict_[f"text{idx + 1}"] = _
|
62 |
+
ic(f"{idx + 1}: [{url}] {dict_['text' + str(idx + 1)][:100]}")
|
63 |
+
|
64 |
+
ic(dict_["text1"][:10])
|
65 |
+
ic(dict_["text2"][:10])
|
66 |
+
else: # 1-mix
|
67 |
+
text1 = ""
|
68 |
+
for url in urls:
|
69 |
+
try:
|
70 |
+
_ = url2txt(url)
|
71 |
+
except Exception as e:
|
72 |
+
logger.error(e)
|
73 |
+
_ = str(e)
|
74 |
+
text1 += _
|
75 |
+
ic(text1[:10])
|
76 |
+
dict_["text1"] = text1[:]
|
77 |
+
|
78 |
+
_ = [elm.strip() for elm in dict_["text1"].splitlines() if elm.strip()]
|
79 |
+
state.ns.list1 = _
|
80 |
+
_ = [elm.strip() for elm in dict_["text2"].splitlines() if elm.strip()]
|
81 |
+
state.ns.list2 = _
|
82 |
+
|
83 |
+
list1 = state.ns.list1
|
84 |
+
list2 = state.ns.list2
|
85 |
+
ic(len(list1), len(list2))
|
86 |
+
|
87 |
+
state.fetched_text1 = dict_["text1"]
|
88 |
+
state.fetched_text2 = dict_["text2"]
|
89 |
+
|
90 |
+
# streamlit complains if an initial value of
|
91 |
+
# a widget with this key is set
|
92 |
+
# state.text_area_urls = text_inp
|
93 |
+
|
94 |
+
# with st.form(key="urls_in_form"):
|
95 |
+
# _ = st.expander(f"{beetype}: Paste urls below and press Ctl+Enter or Space Ctl+Enter to testdrive", expanded=True)
|
96 |
+
# with _:
|
97 |
+
label = f"{beetype}: Paste urls below and press Ctl+Enter or Space Ctl+Enter to testdrive"
|
98 |
+
text_inp = st.text_area(
|
99 |
+
label=label,
|
100 |
+
value=value,
|
101 |
+
key="text_area_urls",
|
102 |
+
height=25,
|
103 |
+
help=" URLs separated by at least a space or a newline(贴网址,空格分开或另起一行, Ctrl+回车提交)",
|
104 |
+
on_change=fetch_cb,
|
105 |
+
# args=(text_inp,),
|
106 |
+
)
|
107 |
+
|
108 |
+
# st.button("Fetch", on_click=fetch_cb, args=(text_inp,))
|
109 |
+
|
110 |
+
def text2lists():
|
111 |
+
"""Convert text(s) to list(s)."""
|
112 |
+
if text1:
|
113 |
+
try:
|
114 |
+
list1 = [elm.strip() for elm in text1.splitlines() if elm.strip()]
|
115 |
+
state.ns.list1 = list1[:]
|
116 |
+
except Exception as e:
|
117 |
+
logger.warning("text1 to list1 errors: %s", e)
|
118 |
+
|
119 |
+
if text2:
|
120 |
+
try:
|
121 |
+
list2 = [elm.strip() for elm in text2.splitlines() if elm.strip()]
|
122 |
+
state.ns.list2 = list2[:]
|
123 |
+
except Exception as e:
|
124 |
+
logger.warning("text2 to list2 errors: %s", e)
|
125 |
+
|
126 |
+
# show fetch text(s)
|
127 |
+
text1 = dict_["text1"]
|
128 |
+
text2 = dict_["text2"]
|
129 |
+
if state.ns.sourcecount == 2: # 2-sep
|
130 |
+
with st.form(key="fetched_2texts_in_form"):
|
131 |
+
_ = st.expander(f"{state.ns.beetype}: fetched text", expanded=True)
|
132 |
+
with _:
|
133 |
+
col1, col2 = st.columns(2)
|
134 |
+
with col1:
|
135 |
+
text1 = st.text_area(
|
136 |
+
label="Edit when necessary, click Submit when ready",
|
137 |
+
key="fetched_text1",
|
138 |
+
# help=""
|
139 |
+
height=500,
|
140 |
+
value=text1,
|
141 |
+
)
|
142 |
+
|
143 |
+
with col2:
|
144 |
+
text2 = st.text_area(
|
145 |
+
label="Edit when necessary, click Submit when ready",
|
146 |
+
# help=""
|
147 |
+
key="fetched_text2",
|
148 |
+
height=500,
|
149 |
+
value=text2,
|
150 |
+
)
|
151 |
+
|
152 |
+
submitted = st.form_submit_button("Submit", on_click=text2lists)
|
153 |
+
|
154 |
+
else: # 1-mix
|
155 |
+
with st.form(key="fetched_1_text_in_form"):
|
156 |
+
_ = st.expander(f"{state.ns.beetype}: fetched mixed text", expanded=True)
|
157 |
+
with _:
|
158 |
+
text1 = st.text_area(
|
159 |
+
label="Edit when necessary, click Submit when ready",
|
160 |
+
key="fetched_mixed_text1",
|
161 |
+
height=500,
|
162 |
+
value=text1,
|
163 |
+
)
|
164 |
+
submitted = st.form_submit_button("Submit", on_click=text2lists)
|
165 |
+
|
166 |
+
# _ = """
|
167 |
+
if not submitted:
|
168 |
+
ic("Submit not yet clicked")
|
169 |
+
return
|
170 |
+
# """
|
171 |
+
|
172 |
+
state.ns.src_filename = ""
|
173 |
+
state.ns.updated = True
|
mlbee/gen_cmat.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Gen cmat for de/en text."""
|
2 |
+
# pylint: disable=invalid-name, too-many-branches
|
3 |
+
|
4 |
+
from typing import List, Optional
|
5 |
+
|
6 |
+
import more_itertools as mit
|
7 |
+
import numpy as np
|
8 |
+
|
9 |
+
from tqdm import tqdm
|
10 |
+
from model_pool import load_model_s
|
11 |
+
from logzero import logger
|
12 |
+
|
13 |
+
from mlbee.cos_matrix2 import cos_matrix2
|
14 |
+
|
15 |
+
try:
|
16 |
+
model_s = load_model_s()
|
17 |
+
except Exception as _:
|
18 |
+
logger.exception(_)
|
19 |
+
raise
|
20 |
+
|
21 |
+
|
22 |
+
def gen_cmat(
|
23 |
+
text1: List[str],
|
24 |
+
text2: List[str],
|
25 |
+
bsize: int = 50
|
26 |
+
) -> np.ndarray:
|
27 |
+
"""Gen corr matrix for texts.
|
28 |
+
|
29 |
+
Args:
|
30 |
+
text1: typically '''...''' splitlines()
|
31 |
+
text2: typically '''...''' splitlines()
|
32 |
+
bsize: batch size, default 50
|
33 |
+
text1 = 'this is a test'
|
34 |
+
text2 = 'another test'
|
35 |
+
"""
|
36 |
+
bsize = int(bsize)
|
37 |
+
if bsize <= 0:
|
38 |
+
bsize = 50
|
39 |
+
|
40 |
+
if isinstance(text1, str):
|
41 |
+
text1 = [text1]
|
42 |
+
if isinstance(text2, str):
|
43 |
+
text1 = [text2]
|
44 |
+
|
45 |
+
vec1 = []
|
46 |
+
vec2 = []
|
47 |
+
len1 = len(text1)
|
48 |
+
len2 = len(text2)
|
49 |
+
tot = len1 // bsize + bool(len1 % bsize)
|
50 |
+
tot += len2 // bsize + bool(len2 % bsize)
|
51 |
+
with tqdm(total=tot) as pbar:
|
52 |
+
for chunk in mit.chunked(text1, bsize):
|
53 |
+
try:
|
54 |
+
vec = model_s.encode(chunk)
|
55 |
+
except Exception as exc:
|
56 |
+
logger.error(exc)
|
57 |
+
raise
|
58 |
+
vec1.extend(vec)
|
59 |
+
pbar.update()
|
60 |
+
for chunk in mit.chunked(text2, bsize):
|
61 |
+
try:
|
62 |
+
vec = model_s.encode(chunk)
|
63 |
+
except Exception as exc:
|
64 |
+
logger.error(exc)
|
65 |
+
raise
|
66 |
+
vec2.extend(vec)
|
67 |
+
pbar.update()
|
68 |
+
try:
|
69 |
+
# note the order vec2, vec1
|
70 |
+
_ = cos_matrix2(np.array(vec2), np.array(vec1))
|
71 |
+
except Exception as exc:
|
72 |
+
logger.exception(exc)
|
73 |
+
raise
|
74 |
+
|
75 |
+
return _
|
mlbee/home.py
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Fetch content from upload.
|
2 |
+
|
3 |
+
org ezbee_page.py.
|
4 |
+
"""
|
5 |
+
# pylint: disable=invalid-name
|
6 |
+
# pylint: disable=too-many-locals, too-many-return-statements, too-many-branches, too-many-statements
|
7 |
+
import base64
|
8 |
+
import inspect
|
9 |
+
import io
|
10 |
+
|
11 |
+
# pylint: disable=invalid-name
|
12 |
+
from functools import partial
|
13 |
+
from itertools import zip_longest
|
14 |
+
|
15 |
+
import hanzidentifier
|
16 |
+
import logzero
|
17 |
+
import numpy as np
|
18 |
+
import pandas as pd
|
19 |
+
import pendulum
|
20 |
+
import streamlit as st
|
21 |
+
from about_time import about_time
|
22 |
+
|
23 |
+
# from ezbee.gen_pairs import gen_pairs # aset2pairs?
|
24 |
+
from aset2pairs import aset2pairs
|
25 |
+
from icecream import ic
|
26 |
+
from loguru import logger as loggu
|
27 |
+
from logzero import logger
|
28 |
+
from set_loglevel import set_loglevel
|
29 |
+
from st_aggrid import AgGrid, GridOptionsBuilder, GridUpdateMode
|
30 |
+
|
31 |
+
# from st_aggrid.grid_options_builder import GridOptionsBuilder
|
32 |
+
from streamlit import session_state as state
|
33 |
+
|
34 |
+
from mlbee.color_map import color_map
|
35 |
+
from mlbee.fetch_paste import fetch_paste
|
36 |
+
from mlbee.fetch_upload import fetch_upload
|
37 |
+
from mlbee.fetch_urls import fetch_urls
|
38 |
+
# from mlbee.t2s import t2s
|
39 |
+
from mlbee import mlbee
|
40 |
+
|
41 |
+
|
42 |
+
def home(): # noqa
|
43 |
+
"""Run tasks.
|
44 |
+
|
45 |
+
beetype
|
46 |
+
|
47 |
+
sourcetype
|
48 |
+
fetch_upload/fetch_paste, fetch_url
|
49 |
+
sourcecount
|
50 |
+
|
51 |
+
align: para-align/sent-align
|
52 |
+
|
53 |
+
save xlsx/tsv
|
54 |
+
"""
|
55 |
+
if state.ns.sourcetype not in ["upload", "paste", "urls"]:
|
56 |
+
st.write("Coming soooooooon...")
|
57 |
+
return None
|
58 |
+
|
59 |
+
# if state.ns.beetype not in ["ezbee", "dzbee", "debee"]:
|
60 |
+
if state.ns.beetype not in ["mlbee", ]:
|
61 |
+
st.write("Coming soon...")
|
62 |
+
return None
|
63 |
+
|
64 |
+
# process sourcetype and fetch list1/list2
|
65 |
+
list1 = list2 = []
|
66 |
+
# fetch_upload/fetch_paste
|
67 |
+
if state.ns.sourcetype in ["upload"]:
|
68 |
+
fetch_upload()
|
69 |
+
elif state.ns.sourcetype in ["paste"]:
|
70 |
+
fetch_paste()
|
71 |
+
elif state.ns.sourcetype in ["urls"]:
|
72 |
+
fetch_urls()
|
73 |
+
else:
|
74 |
+
st.warning(f"{state.ns.sourcetype}: Not implemented")
|
75 |
+
return None
|
76 |
+
|
77 |
+
logger.debug("state.ns.updated: %s", state.ns.updated)
|
78 |
+
|
79 |
+
# if not updated, quit: this does not quite work
|
80 |
+
# only prevents the first run/missing upload
|
81 |
+
if not state.ns.updated:
|
82 |
+
logger.debug(" not updated, early exit.")
|
83 |
+
return None
|
84 |
+
|
85 |
+
list1 = state.ns.list1[:]
|
86 |
+
list2 = state.ns.list2[:]
|
87 |
+
|
88 |
+
logger.debug("list1[:3]: %s", list1[:3])
|
89 |
+
logger.debug("list2[:3]: %s", list2[:3])
|
90 |
+
|
91 |
+
df = pd.DataFrame(zip_longest(list1, list2, fillvalue=""))
|
92 |
+
try:
|
93 |
+
# df.columns = ["text1", "text2"]
|
94 |
+
df.columns = [f"text{i + 1}" for i in range(len(df.columns))]
|
95 |
+
except Exception as exc:
|
96 |
+
logger.debug("df: \n%s", df)
|
97 |
+
logger.error("%s", exc)
|
98 |
+
|
99 |
+
state.ns.df = df
|
100 |
+
logger.debug("df: %s", df)
|
101 |
+
|
102 |
+
# st.table(df) # looks alright
|
103 |
+
# equiv to st.markdown(df.to_markdown())?
|
104 |
+
|
105 |
+
# stlyed pd dataframe?
|
106 |
+
# bigger, no pagination
|
107 |
+
# st.markdown(df.to_html(), unsafe_allow_html=True)
|
108 |
+
|
109 |
+
# ag_grid smallish, editable, probably slower
|
110 |
+
|
111 |
+
# if "df" not in globals() or "df" not in locals():
|
112 |
+
if "df" not in locals():
|
113 |
+
logger.debug(" df not defined, return")
|
114 |
+
|
115 |
+
if df.empty:
|
116 |
+
logger.debug(" df.empty, return")
|
117 |
+
return None
|
118 |
+
|
119 |
+
# only show this for upload
|
120 |
+
if state.ns.sourcetype in ["upload"]:
|
121 |
+
len1 = len([elm.strip() for elm in list1 if elm.strip()])
|
122 |
+
len2 = len([elm.strip() for elm in list2 if elm.strip()])
|
123 |
+
len12 = len1 + len2
|
124 |
+
time0 = len12 * 0.4
|
125 |
+
time1 = len12 * 1
|
126 |
+
eta = pendulum.now() + pendulum.duration(seconds=len12 * 0.66)
|
127 |
+
|
128 |
+
in_words0 = pendulum.duration(seconds=time0).in_words()
|
129 |
+
in_words1 = pendulum.duration(seconds=time1).in_words()
|
130 |
+
diff_for_humans = eta.diff_for_humans()
|
131 |
+
dt_str = eta.to_datetime_string()
|
132 |
+
timezone_name = eta.timezone_name
|
133 |
+
_ = (
|
134 |
+
f"Estimated time to complete: {in_words0} to {in_words1}; "
|
135 |
+
f"ETA: {diff_for_humans} ({dt_str} {timezone_name}) "
|
136 |
+
)
|
137 |
+
st.info(_)
|
138 |
+
_ = st.expander("to be aligned", expanded=False)
|
139 |
+
with _:
|
140 |
+
st.write(df)
|
141 |
+
|
142 |
+
logger.info("Processing data... %s", state.ns.beetype)
|
143 |
+
|
144 |
+
# if state.ns.beetype in ["ezbee", "dzbee", "debee"]:
|
145 |
+
if state.ns.beetype in ["mlbee"]:
|
146 |
+
with about_time() as t:
|
147 |
+
with st.spinner(" diggin..."):
|
148 |
+
try:
|
149 |
+
aset = globals()[state.ns.beetype](
|
150 |
+
list1,
|
151 |
+
list2,
|
152 |
+
# eps=eps,
|
153 |
+
# min_samples=min_samples,
|
154 |
+
)
|
155 |
+
except Exception as e:
|
156 |
+
logger.exception(
|
157 |
+
"aset = globals()[state.ns.beetype](...) exc: %s", e
|
158 |
+
)
|
159 |
+
aset = ""
|
160 |
+
st.write("Collecting inputs...")
|
161 |
+
logger.debug("Collecting inputs...")
|
162 |
+
return None
|
163 |
+
|
164 |
+
st.success(f"Done, took {t.duration_human}")
|
165 |
+
else:
|
166 |
+
try:
|
167 |
+
filename = inspect.currentframe().f_code.co_filename # type: ignore
|
168 |
+
except Exception as e:
|
169 |
+
logger.error(e)
|
170 |
+
filename = ""
|
171 |
+
try:
|
172 |
+
lineno = inspect.currentframe().f_lineno # type: ignore
|
173 |
+
except Exception as e:
|
174 |
+
logger.error(e)
|
175 |
+
lineno = ""
|
176 |
+
st.write(f"{state.ns.beetype} coming soon...{filename}:{lineno}")
|
177 |
+
return None
|
178 |
+
|
179 |
+
if aset:
|
180 |
+
logger.debug("aset: %s...%s", aset[:3], aset[-3:])
|
181 |
+
# logger.debug("aset[:10]: %s", aset[:10])
|
182 |
+
|
183 |
+
if set_loglevel() <= 10:
|
184 |
+
st.write(aset)
|
185 |
+
|
186 |
+
# aligned_pairs = gen_pairs(list1, list2, aset)
|
187 |
+
aligned_pairs = aset2pairs(list1, list2, aset)
|
188 |
+
if aligned_pairs:
|
189 |
+
# logger.debug("%s...%s", aligned_pairs[:1], aligned_pairs[-1:])
|
190 |
+
logger.debug("%s...s", aligned_pairs[:1])
|
191 |
+
|
192 |
+
df_a = pd.DataFrame(
|
193 |
+
aligned_pairs, columns=["text1", "text2", "llh"], dtype="object"
|
194 |
+
)
|
195 |
+
|
196 |
+
if set_loglevel() <= 10:
|
197 |
+
_ = st.expander("done aligned")
|
198 |
+
with _:
|
199 |
+
st.table(df_a.astype(str))
|
200 |
+
# st.markdown(df_a.astype(str).to_markdown())
|
201 |
+
# st.markdown(df_a.astype(str).to_numpy().tolist())
|
202 |
+
|
203 |
+
# insert seq no
|
204 |
+
df_a.insert(0, "sn", range(len(df_a)))
|
205 |
+
|
206 |
+
gb = GridOptionsBuilder.from_dataframe(df_a)
|
207 |
+
gb.configure_pagination(paginationAutoPageSize=True)
|
208 |
+
options = {
|
209 |
+
"resizable": True,
|
210 |
+
"autoHeight": True,
|
211 |
+
"wrapText": True,
|
212 |
+
"editable": True,
|
213 |
+
}
|
214 |
+
gb.configure_default_column(**options)
|
215 |
+
gridOptions = gb.build()
|
216 |
+
|
217 |
+
# st.write("editable aligned (double-click a cell to edit, drag column header to adjust widths)")
|
218 |
+
_ = "editable aligned (double-click a cell to edit, drag column header to adjust widths)"
|
219 |
+
with st.expander(_, expanded=False):
|
220 |
+
ag_df = AgGrid(
|
221 |
+
# df,
|
222 |
+
df_a,
|
223 |
+
gridOptions=gridOptions,
|
224 |
+
key="outside",
|
225 |
+
reload_data=True,
|
226 |
+
editable=True,
|
227 |
+
# width="100%", # width parameter is deprecated
|
228 |
+
height=750,
|
229 |
+
# fit_columns_on_grid_load=True,
|
230 |
+
update_mode=GridUpdateMode.MODEL_CHANGED,
|
231 |
+
)
|
232 |
+
|
233 |
+
# ### prep download
|
234 |
+
|
235 |
+
# taken from vizbee cb_save_xlsx
|
236 |
+
# subset = list(df_a.columns[2:3]) # 3rd col
|
237 |
+
subset = list(df_a.columns[2:]) # 3rd col
|
238 |
+
|
239 |
+
# pop("sn"): remove sn column
|
240 |
+
df_a.pop("sn")
|
241 |
+
s_df = df_a.astype(str).style.applymap(color_map, subset=subset)
|
242 |
+
|
243 |
+
if set_loglevel() <= 10:
|
244 |
+
logger.debug(" showing styled aligned")
|
245 |
+
with st.expander("styled aligned"):
|
246 |
+
# st.dataframe(s_df) # can't handle styleddf
|
247 |
+
st.table(s_df)
|
248 |
+
|
249 |
+
output = io.BytesIO()
|
250 |
+
with pd.ExcelWriter(
|
251 |
+
output, engine="xlsxwriter"
|
252 |
+
) as writer: # pylint: disable=abstract-class-instantiated
|
253 |
+
s_df.to_excel(writer, index=False, header=False, sheet_name="Sheet1")
|
254 |
+
writer.sheets["Sheet1"].set_column("A:A", 70)
|
255 |
+
writer.sheets["Sheet1"].set_column("B:B", 70)
|
256 |
+
output.seek(0)
|
257 |
+
|
258 |
+
val = output.getvalue()
|
259 |
+
b64 = base64.b64encode(val)
|
260 |
+
filename = ""
|
261 |
+
if state.ns.src_filename:
|
262 |
+
filename = f"{state.ns.src_filename}-"
|
263 |
+
|
264 |
+
dl_xlsx = f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="{filename}aligned_paras.xlsx">Download aligned paras xlsx</a>'
|
265 |
+
|
266 |
+
_ = """
|
267 |
+
output = io.BytesIO()
|
268 |
+
# df_a.astype(str).to_csv(output, sep="\t", index=False, header=False, encoding="gbk")
|
269 |
+
df_a.astype(object).to_csv(output, sep="\t", index=False, header=False, encoding="gbk")
|
270 |
+
output.seek(0)
|
271 |
+
|
272 |
+
val = output.getvalue()
|
273 |
+
b64 = base64.b64encode(val)
|
274 |
+
dl_tsv = f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="{filename}aligned_paras.tsv">Download aligned paras tsv</a>'
|
275 |
+
# """
|
276 |
+
|
277 |
+
col1_dl, col2_dl = st.columns(2)
|
278 |
+
with col1_dl:
|
279 |
+
st.markdown(dl_xlsx, unsafe_allow_html=True)
|
280 |
+
_ = """
|
281 |
+
with col2_dl:
|
282 |
+
st.markdown(dl_tsv, unsafe_allow_html=True)
|
283 |
+
# """
|
284 |
+
|
285 |
+
# reset
|
286 |
+
state.ns.updated = False
|
287 |
+
|
288 |
+
return None
|
mlbee/info.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Present info about mlbee."""
|
2 |
+
from textwrap import dedent
|
3 |
+
|
4 |
+
import streamlit as st
|
5 |
+
|
6 |
+
from mlbee import __version__
|
7 |
+
|
8 |
+
from mlbee.utils import msg
|
9 |
+
|
10 |
+
|
11 |
+
def info():
|
12 |
+
"""Prep info page."""
|
13 |
+
|
14 |
+
st.subheader(f"mlbee {__version__}")
|
15 |
+
|
16 |
+
st.markdown(msg, unsafe_allow_html=True)
|
mlbee/loadtext.py
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Load file content to text.
|
2 |
+
|
3 |
+
Check encoding and load a file to text.
|
4 |
+
|
5 |
+
Win
|
6 |
+
Linux
|
7 |
+
apt install libmagic1
|
8 |
+
|
9 |
+
py -3.8 -m pip install python-magic-bin
|
10 |
+
py -3.8 -m pip install python-magic
|
11 |
+
|
12 |
+
import magic
|
13 |
+
magic.from_file("testdata/test.pdf")
|
14 |
+
|
15 |
+
original load_textrev
|
16 |
+
refer to load_paras.py
|
17 |
+
"""
|
18 |
+
# pylint: disable=line-too-long, unused-variable, unused-import
|
19 |
+
|
20 |
+
from typing import List, Optional, Union # noqa
|
21 |
+
from pathlib import Path
|
22 |
+
import cchardet
|
23 |
+
|
24 |
+
import pytest
|
25 |
+
from logzero import logger
|
26 |
+
|
27 |
+
# from detect_file import detect_file
|
28 |
+
|
29 |
+
|
30 |
+
def loadtext(filepath: Union[Path, str] = "") -> str:
|
31 |
+
"""Load file context to text.
|
32 |
+
|
33 |
+
Check encoding and load a file to text.
|
34 |
+
"""
|
35 |
+
filepath = Path(filepath)
|
36 |
+
if not filepath.is_file():
|
37 |
+
logger.error(" file [%s] does not exist or is not a file.", filepath)
|
38 |
+
# return None
|
39 |
+
raise Exception(f" file [{filepath}] does not exist or is not a file.")
|
40 |
+
|
41 |
+
# encoding = detect_file(filepath)
|
42 |
+
encoding = cchardet.detect(filepath.read_bytes()).get("encoding", "utf8")
|
43 |
+
|
44 |
+
if encoding is None:
|
45 |
+
raise Exception("cchardet.detect says it's not a text file.")
|
46 |
+
|
47 |
+
# cchardet: 'GB18030', no need for errors="ignore"
|
48 |
+
try:
|
49 |
+
text = filepath.read_text(encoding=encoding, errors="ignore")
|
50 |
+
except Exception as exc:
|
51 |
+
logger.error(" Opening %s resulted in errors: %s", filepath, exc)
|
52 |
+
raise
|
53 |
+
|
54 |
+
return text
|
55 |
+
|
56 |
+
|
57 |
+
def text2paras(text: str) -> List[str]:
|
58 |
+
"""Convert text to list/paras, remove blank lines."""
|
59 |
+
_ = text.splitlines()
|
60 |
+
|
61 |
+
return [elm.strip() for elm in _ if elm.strip()]
|
62 |
+
|
63 |
+
|
64 |
+
def loadparas(filepath: Path) -> List[str]:
|
65 |
+
"""Load file as paras list."""
|
66 |
+
try:
|
67 |
+
_ = loadtext(filepath)
|
68 |
+
except Exception as exc:
|
69 |
+
logger.error(exc)
|
70 |
+
raise
|
71 |
+
|
72 |
+
try:
|
73 |
+
_ = text2paras(_)
|
74 |
+
except Exception as exc:
|
75 |
+
logger.error(exc)
|
76 |
+
raise
|
77 |
+
|
78 |
+
return _
|
79 |
+
|
80 |
+
|
81 |
+
@pytest.mark.xfail(reason="no file provided, trying to open ’.‘.")
|
82 |
+
def test1():
|
83 |
+
r"""Tests default file."""
|
84 |
+
text = loadtext()
|
85 |
+
# eq_(2283, len(text))
|
86 |
+
# eq_(2283, len(text))
|
87 |
+
|
88 |
+
# del text
|
89 |
+
if text:
|
90 |
+
assert len(text) == 86423
|
91 |
+
|
92 |
+
|
93 |
+
def testgb():
|
94 |
+
r"""Tests shuangyu_ku\txt-books\19部世界名著中英文对照版TXT."""
|
95 |
+
file = (
|
96 |
+
r"C:\dl\Dropbox\shuangyu_ku\txt-books\19部世界名著中英文对照版TXT"
|
97 |
+
r"\爱丽丝漫游奇境记.txt"
|
98 |
+
)
|
99 |
+
text = loadtext(file)
|
100 |
+
if text:
|
101 |
+
# assert len(text) == 190913
|
102 |
+
assert len(text) >= 188760
|
103 |
+
|
104 |
+
text0 = "ALICE'S ADVENTURES IN WONDERLAND\n CHAPTER 01 Down the Rabbit-Hole\n CHAPTER 02 The Pool of Tears\n CHAPTER 03 A Caucus-Race and a Long Tale\n CHAPTER 04 The Rabbit Sends in a Little Bill\n CHAPTER 05 Advice from a Caterpillar\n CHAPTER 06 Pig and Pepper\n CHAPTER 07 A Mad Tea-Party\n CHAPTER 08 The Queen's Croquet-Ground\n CHAPTER 09 The Mock Turtle's Story \n CHAPTER 10 The Lobster Quadrille\n CHAPTER 11 Who Stole the Tarts?\n CHAPTER 12 Alice's Evidence\n\n\n 爱 丽 丝 漫 游 奇 境 记 \n\n 第01章 " # NOQA
|
105 |
+
|
106 |
+
if text:
|
107 |
+
assert text0 == text[:500]
|
108 |
+
|
109 |
+
|
110 |
+
def test_utf_16le():
|
111 |
+
r"""Test 'E:\\beta_final_version\\build\\test_files\\files_for_testing_import\\Folding_Beijing_12.txt'."""
|
112 |
+
# file = 'E:\\beta_final_version\\build\\test_files\\files_for_testing_import\\Folding_Beijing_12.txt' # NOQA
|
113 |
+
file = r"C:\dl\Dropbox\mat-dir\snippets-mat\pyqt\Sandbox\hp_beta-version_files\test_files\files_for_testing_import\Folding_Beijing_12.txt" # NOQA
|
114 |
+
file = r"C:\dl\Dropbox\mat-dir\pyqt\Sandbox\hp_beta-version_files\test_files\files_for_testing_import\Folding_Beijing_12.txt"
|
115 |
+
|
116 |
+
text = loadtext(file)
|
117 |
+
if text:
|
118 |
+
assert len(text) == 117871
|
119 |
+
|
120 |
+
# text0 = '\ufeffFolding Beijing\t北京折叠\n"by Hao Jingfang, translated by Ken Liu"\t郝景芳\n# 1.\t# 1\n"At ten of five in the m' # NOQA
|
121 |
+
text0 = r'Folding Beijing\t北京折叠\n"by Hao Jingfang, translated by Ken Liu"\t郝景芳\n# 1.\t# 1\n"At ten of five in the mo' # NOQA
|
122 |
+
text2 = 'Folding Beijing\t\xe5\x8c\x97\xe4\xba\xac\xe6\x8a\x98\xe5\x8f\xa0\r\n"by Hao Jingfang, translated by Ken Liu"\t\xe9\x83\x9d\xe6\x99\xaf\xe8\x8a\xb3\r\n# 1.\t# 1\r\n"At ten of five in the mo'
|
123 |
+
|
124 |
+
del text2
|
125 |
+
|
126 |
+
# if text: assert text0 == text[:100]
|
mlbee/mlbee.py
CHANGED
@@ -1,7 +1,53 @@
|
|
1 |
"""Define mlbee."""
|
|
|
|
|
|
|
|
|
2 |
from logzero import logger
|
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
logger.debug(" entry ")
|
|
|
1 |
"""Define mlbee."""
|
2 |
+
# pylint: disable=invalid-name
|
3 |
+
from typing import List
|
4 |
+
|
5 |
+
from cmat2aset import cmat2aset
|
6 |
from logzero import logger
|
7 |
|
8 |
+
from mlbee.gen_cmat import gen_cmat
|
9 |
+
|
10 |
+
|
11 |
+
def mlbee(
|
12 |
+
text1: List[str],
|
13 |
+
text2: List[str],
|
14 |
+
eps: float = 10,
|
15 |
+
min_samples: int = 6,
|
16 |
+
# show_plot: bool = True,
|
17 |
+
):
|
18 |
+
"""Align multilingual texts.
|
19 |
+
|
20 |
+
Args:
|
21 |
+
text1: list of strings
|
22 |
+
text2: list of strings
|
23 |
+
eps: epsilon
|
24 |
+
min_samples: minimum number of points to be considered as a cluster
|
25 |
+
x show_plot: whether to show plots, refactored to cmat2html's show_plot
|
26 |
+
Returns:
|
27 |
+
Aligned text pairs.
|
28 |
+
"""
|
29 |
+
if not (
|
30 |
+
[elm for elm in text1 if elm.strip()] and [elm for elm in text2 if elm.strip()]
|
31 |
+
):
|
32 |
+
logger.warning("One or both inputs are empty")
|
33 |
+
raise Exception("Nothing to do...exiting")
|
34 |
+
|
35 |
+
try:
|
36 |
+
# from json_de2zh.gen_cmat import gen_cmat # noqa # pylint: disable=import-outside-toplevel
|
37 |
+
cmat = gen_cmat(text1, text2)
|
38 |
+
# logger.level is reset to 20 in fastlid
|
39 |
+
mlbee.cmat = cmat
|
40 |
+
except Exception as e:
|
41 |
+
logger.exception(e)
|
42 |
+
raise
|
43 |
+
|
44 |
+
try:
|
45 |
+
# aset = cmat2aset(cmat.T)
|
46 |
+
aset = cmat2aset(cmat, eps=eps, min_samples=min_samples)
|
47 |
+
mlbee.aset = aset
|
48 |
+
except Exception as e:
|
49 |
+
logger.exception(e)
|
50 |
+
raise
|
51 |
|
52 |
+
# paired texts or aset?
|
53 |
+
return aset
|
|
mlbee/multipage.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Credit to https://huggingface.co/spaces/lfqa/lfqa.
|
2 |
+
|
3 |
+
This file is the framework for generating multiple Streamlit
|
4 |
+
applications through an object oriented framework.
|
5 |
+
"""
|
6 |
+
# Import necessary libraries
|
7 |
+
import streamlit as st
|
8 |
+
from streamlit_option_menu import option_menu
|
9 |
+
|
10 |
+
|
11 |
+
# Define the multipage class to manage the multiple apps in our program
|
12 |
+
class Multipage:
|
13 |
+
"""Framework for combining multiple streamlit applications."""
|
14 |
+
|
15 |
+
def __init__(self) -> None:
|
16 |
+
"""Construct class to generate a list which will store all our applications as an instance variable.""" # noqa
|
17 |
+
self.pages = []
|
18 |
+
|
19 |
+
def add_page(self, title, icon, func) -> None:
|
20 |
+
"""Class Method to Add pages to the project.
|
21 |
+
|
22 |
+
Args:
|
23 |
+
title ([str]): The title of page which we are
|
24 |
+
adding to the list of apps
|
25 |
+
icon: icon from streamlit-menu-option
|
26 |
+
func: Python function to render this page in Streamlit
|
27 |
+
"""
|
28 |
+
self.pages.append({"title": title, "icon": icon, "function": func})
|
29 |
+
|
30 |
+
def run(self):
|
31 |
+
"""Dropdown to select the page to run."""
|
32 |
+
# Dropdown to select the page to run
|
33 |
+
st.markdown(
|
34 |
+
"""
|
35 |
+
<style>
|
36 |
+
section[data-testid="stSidebar"] > div:first-of-type {
|
37 |
+
background-color: var(--secondary-background-color);
|
38 |
+
background: var(--secondary-background-color);
|
39 |
+
width: 250px;
|
40 |
+
padding: 4rem 0;
|
41 |
+
box-shadow: -2rem 0px 2rem 2rem rgba(0,0,0,0.16);
|
42 |
+
}
|
43 |
+
section[aria-expanded="true"] > div:nth-of-type(2) {
|
44 |
+
display: none;
|
45 |
+
}
|
46 |
+
.main > div:first-of-type {
|
47 |
+
padding: 1rem 0;
|
48 |
+
}
|
49 |
+
</style>
|
50 |
+
""",
|
51 |
+
unsafe_allow_html=True,
|
52 |
+
)
|
53 |
+
|
54 |
+
selected = None
|
55 |
+
with st.sidebar:
|
56 |
+
selected = option_menu(
|
57 |
+
None,
|
58 |
+
[page["title"] for page in self.pages],
|
59 |
+
icons=[page["icon"] for page in self.pages],
|
60 |
+
menu_icon="cast",
|
61 |
+
default_index=0,
|
62 |
+
)
|
63 |
+
|
64 |
+
# Run the selected page
|
65 |
+
for index, item in enumerate(self.pages):
|
66 |
+
if item["title"] == selected:
|
67 |
+
self.pages[index]["function"]()
|
68 |
+
break
|
mlbee/settings.py
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Prep Settings/Options page."""
|
2 |
+
# pylint: disable=invalid-name
|
3 |
+
from functools import partial
|
4 |
+
|
5 |
+
import streamlit as st
|
6 |
+
from loguru import logger as loggu
|
7 |
+
from logzero import logger
|
8 |
+
from streamlit import session_state as state
|
9 |
+
|
10 |
+
|
11 |
+
def settings():
|
12 |
+
"""Prep Settings/Options page.
|
13 |
+
|
14 |
+
Refer to options.py
|
15 |
+
"""
|
16 |
+
# horizotal radio
|
17 |
+
st.write(
|
18 |
+
"<style>div.row-widget.stRadio > div{flex-direction:row;}</style>",
|
19 |
+
unsafe_allow_html=True,
|
20 |
+
)
|
21 |
+
|
22 |
+
sourcetype_list = ["upload", "paste", "urls"]
|
23 |
+
beetype_list = ["mlbee", "ezbee", "dzbee", "debee", "xbee"]
|
24 |
+
|
25 |
+
_ = """
|
26 |
+
_ = "ezbee: english-chinese; dzbee: german-chinese, debee: german-english; xbee: other language pairs (slow, approx.1000 pairs/3 min) | ezbee: 英/中; dzbee: 德/中, debee: 德/英; xbee: 其他语言对(慢, 约1000对/3分钟)"
|
27 |
+
try:
|
28 |
+
index = beetype_list.index(state.ns.beetype)
|
29 |
+
except Exception as e:
|
30 |
+
logger.error("beetype index error: %s, setting to 0", e)
|
31 |
+
index = 0
|
32 |
+
beetype = st.radio(
|
33 |
+
"Pick a bee",
|
34 |
+
beetype_list,
|
35 |
+
index=index,
|
36 |
+
format_func=lambda x: f"{x:<7} |",
|
37 |
+
help=_,
|
38 |
+
)
|
39 |
+
state.ns.beetype = beetype
|
40 |
+
# """
|
41 |
+
|
42 |
+
try:
|
43 |
+
index = sourcetype_list.index(state.ns.sourcetype)
|
44 |
+
except Exception as e:
|
45 |
+
logger.error("sourcetype index error: %s, setting to 0", e)
|
46 |
+
index = 0
|
47 |
+
sourcetype = st.radio(
|
48 |
+
"Source",
|
49 |
+
sourcetype_list,
|
50 |
+
index=index,
|
51 |
+
format_func=lambda x: f"{x:<8} |",
|
52 |
+
help="upload: one or two files; paste: from clipboard; urls: from the net",
|
53 |
+
# disabled=True,
|
54 |
+
)
|
55 |
+
state.ns.sourcetype = sourcetype
|
56 |
+
|
57 |
+
sourcecount_list = [2, 1]
|
58 |
+
try:
|
59 |
+
index = sourcecount_list.index(state.ns.sourcecount)
|
60 |
+
except Exception as e:
|
61 |
+
logger.error("sourcecount index error: %s, setting to 0", e)
|
62 |
+
index = 0
|
63 |
+
sourcecount = st.radio(
|
64 |
+
"Source Count",
|
65 |
+
sourcecount_list,
|
66 |
+
index=index,
|
67 |
+
format_func=lambda x: f"{x:<3} |",
|
68 |
+
help="2: two separate sources (files/pastes/urls), each containing one language; 1: one mixed source (file/paste/url) containing both languages",
|
69 |
+
disabled=True,
|
70 |
+
)
|
71 |
+
state.ns.sourcecount = sourcecount
|
72 |
+
|
73 |
+
# sentali used for sent split
|
74 |
+
sentali_list = [None, "yes"]
|
75 |
+
try:
|
76 |
+
index = sentali_list.index(state.ns.sentali)
|
77 |
+
except Exception as e:
|
78 |
+
logger.error("sentali sindex error: %s, setting to 0", e)
|
79 |
+
index = 0
|
80 |
+
sentali = st.radio(
|
81 |
+
"Split to Sents",
|
82 |
+
sentali_list,
|
83 |
+
index=index,
|
84 |
+
format_func=lambda x: f"{str(x):<4} |",
|
85 |
+
help="None: leave it as it is; yes: attempt to split to sents in a sensible manner.",
|
86 |
+
disabled=True,
|
87 |
+
)
|
88 |
+
state.ns.sentali = sentali
|
89 |
+
|
90 |
+
# show state.ns[:6]
|
91 |
+
loggu.debug(f" state.ns.list: {state.ns.list}")
|
92 |
+
|
93 |
+
# beetype, sourcetype, sourcecount, filename1, filename2
|
94 |
+
_ = map(partial(getattr, state.ns), state.ns.list[:6])
|
95 |
+
logger.debug(" state.ns.list[:3]: %s", str([*_]))
|
96 |
+
|
97 |
+
# st.write(f"run: {state.ns.count}")
|
98 |
+
# loggu.debug(f"run: {state.ns.count}")
|
mlbee/text2lists.py
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Separate text to zh en lists."""
|
2 |
+
# pylint: disable=unused-import, too-many-locals, invalid-name, too-many-branches, too-many-statements,
|
3 |
+
|
4 |
+
# from typing import Tuple,
|
5 |
+
from typing import Iterable, List, Optional, Tuple, Union # noqa
|
6 |
+
|
7 |
+
import numpy as np
|
8 |
+
from logzero import logger
|
9 |
+
|
10 |
+
# from fastlid import fastlid
|
11 |
+
from polyglot.text import Detector
|
12 |
+
|
13 |
+
# from dzbee.detect import detect
|
14 |
+
from debee.detect import detect
|
15 |
+
|
16 |
+
# from json_de2zh.gen_cmat import gen_cmat
|
17 |
+
from debee.gen_cmat import gen_cmat
|
18 |
+
|
19 |
+
# from radiobee.lists2cmat import lists2cmat # use fast_scores
|
20 |
+
# from radiobee.detect import detect
|
21 |
+
# from fast_scores.gen_cmat import gen_cmat # pylint: disable=import-error
|
22 |
+
|
23 |
+
|
24 |
+
def text2lists(
|
25 |
+
text: Union[Iterable[str], str],
|
26 |
+
set_languages: Optional[List[str]] = None,
|
27 |
+
) -> Tuple[List[str], List[str]]:
|
28 |
+
"""Separate text to zh en lists.
|
29 |
+
|
30 |
+
Args:
|
31 |
+
text: mixed text
|
32 |
+
set_languages: no default (open-end)
|
33 |
+
use polyglot.text.Detector to pick two languages
|
34 |
+
|
35 |
+
Attributes:
|
36 |
+
cmat: correlation matrix (len(list_l) x len(list_r))
|
37 |
+
before adjusting (shifting)
|
38 |
+
offset: plus, [""] * offset + list2
|
39 |
+
minus, [""] * (-offset) + list1
|
40 |
+
Returns:
|
41 |
+
two lists, best effort alignment
|
42 |
+
"""
|
43 |
+
if not isinstance(text, str) and isinstance(text, Iterable):
|
44 |
+
try:
|
45 |
+
text = "\n".join(text)
|
46 |
+
except Exception as e:
|
47 |
+
logger.error(e)
|
48 |
+
raise
|
49 |
+
|
50 |
+
# set_languages default to ["en", "zh"]
|
51 |
+
if set_languages is None:
|
52 |
+
lang12 = [elm.code for elm in Detector(text).languages]
|
53 |
+
|
54 |
+
# set_languages = ["en", "zh"]
|
55 |
+
|
56 |
+
# set 'un' to 'en'
|
57 |
+
# set_languages = ['en' if elm in ['un'] else elm for elm in lang12[:2]]
|
58 |
+
set_languages = []
|
59 |
+
for elm in lang12[:2]:
|
60 |
+
if elm in ["un"]:
|
61 |
+
logger.warning(" Unknown language, set to en")
|
62 |
+
set_languages.append("en")
|
63 |
+
else:
|
64 |
+
set_languages.append(elm)
|
65 |
+
|
66 |
+
# fastlid.set_languages = set_languages
|
67 |
+
|
68 |
+
list1 = []
|
69 |
+
list2 = []
|
70 |
+
|
71 |
+
# lang0, _ = fastlid(text[:15000])
|
72 |
+
lang0 = detect(text, set_languages)
|
73 |
+
|
74 |
+
res = []
|
75 |
+
left = True # start with left list1
|
76 |
+
|
77 |
+
for elm in [_ for _ in text.splitlines() if _.strip()]:
|
78 |
+
# lang, _ = fastlid(elm)
|
79 |
+
try:
|
80 |
+
lang = detect(elm, set_languages)
|
81 |
+
except Exception:
|
82 |
+
lang = "en"
|
83 |
+
logger.warning("Cant detect: %s[20]...setting to %s", elm[:20], lang)
|
84 |
+
|
85 |
+
if lang == lang0:
|
86 |
+
res.append(elm)
|
87 |
+
else:
|
88 |
+
if left:
|
89 |
+
# list1.append("\n".join(res))
|
90 |
+
list1.extend(res)
|
91 |
+
else:
|
92 |
+
# list2.append("\n".join(res))
|
93 |
+
list2.extend(res)
|
94 |
+
left = not left
|
95 |
+
|
96 |
+
res = [elm]
|
97 |
+
lang0 = lang
|
98 |
+
|
99 |
+
# process the last
|
100 |
+
if left:
|
101 |
+
list1.extend(res)
|
102 |
+
else:
|
103 |
+
list2.extend(res)
|
104 |
+
|
105 |
+
try:
|
106 |
+
# lang1, _ = fastlid(' '.join(list1))
|
107 |
+
lang1 = detect(" ".join(list1), set_languages)
|
108 |
+
except Exception as exc:
|
109 |
+
logger.error(exc)
|
110 |
+
lang1 = "en"
|
111 |
+
try:
|
112 |
+
# lang2, _ = fastlid(' '.join(list2))
|
113 |
+
lang2 = detect(" ".join(list2), set_languages)
|
114 |
+
except Exception as exc:
|
115 |
+
logger.error(exc)
|
116 |
+
lang2 = "en"
|
117 |
+
|
118 |
+
# find offset via diagonal(k),
|
119 |
+
len1, len2 = len(list1), len(list2)
|
120 |
+
|
121 |
+
# ylim, xlim = cmat.shape
|
122 |
+
ylim, xlim = len2, len1 # check
|
123 |
+
|
124 |
+
# cmat dim: len1 x len2 or ylim x xlim
|
125 |
+
# cmat = lists2cmat(list1, list2, lang1, lang2)
|
126 |
+
# cmat.shape: len(list1)xlen(list2) or ylim x xlim
|
127 |
+
cmat = gen_cmat(list1, list2)
|
128 |
+
|
129 |
+
# sq_mean_pair = [(elm, np.square(cmat.diagonal(elm)).mean()) for elm in range(2 - ylim, xlim + 1)]
|
130 |
+
# df = pd.DataFrame(sq_mean_pair, columns=['offset', 'sq_mean'])
|
131 |
+
# df.plot.scatter('offset', 'sq_mean')
|
132 |
+
# optimum_offset = df.offset[df.sq_mean.argmax()]
|
133 |
+
|
134 |
+
# equiv to np.argmax(sq_mean) - (ylim - 2)
|
135 |
+
# locate max, -ylim + 2 ...xlim: range(1 - ylim, xlim)
|
136 |
+
# sqare sum
|
137 |
+
|
138 |
+
sq_mean = [
|
139 |
+
np.square(cmat.diagonal(elm)).mean() for elm in range(1 - ylim, xlim - 1)
|
140 |
+
]
|
141 |
+
# tot: xlim + ylim - 1
|
142 |
+
|
143 |
+
# temp = [np.square(cmat.diagonal(elm)) for elm in range(2 - ylim, xlim + 1)]
|
144 |
+
# sq_mean = [elm.mean() if np.any(elm) else 0.0 for elm in temp]
|
145 |
+
|
146 |
+
# plt.figure()
|
147 |
+
# plt.scatter(range(1 - ylim, xlim), sq_mean)
|
148 |
+
|
149 |
+
offset = np.argmax(sq_mean) - (ylim - 1)
|
150 |
+
|
151 |
+
text2lists.cmat = cmat
|
152 |
+
text2lists.offset = offset
|
153 |
+
text2lists.lang1 = lang1
|
154 |
+
text2lists.lang2 = lang2
|
155 |
+
|
156 |
+
# shift list1 if offsset >= 0, else shift list2
|
157 |
+
if offset > -1:
|
158 |
+
# list1a = list1[:]
|
159 |
+
# list2a = [""] * offset + list2
|
160 |
+
list2 = [""] * offset + list2
|
161 |
+
else:
|
162 |
+
list1 = [""] * (-offset) + list1
|
163 |
+
# list1a = [""] * (-offset) + list1
|
164 |
+
# list2a = list2[:]
|
165 |
+
|
166 |
+
# return list1, list2
|
167 |
+
return [elm.strip() for elm in list1], [elm.strip() for elm in list2]
|
mlbee/url2txt.py
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Fetch text from url."""
|
2 |
+
from typing import Optional
|
3 |
+
from urllib.parse import urlparse
|
4 |
+
|
5 |
+
import html2text
|
6 |
+
import httpx
|
7 |
+
import streamlit as st
|
8 |
+
from logzero import logger
|
9 |
+
from readability import Document
|
10 |
+
|
11 |
+
|
12 |
+
# @st.cache
|
13 |
+
def url2txt(
|
14 |
+
url: str,
|
15 |
+
bodywidth: Optional[int] = 5000,
|
16 |
+
remove: bool = False,
|
17 |
+
show_url: bool = True,
|
18 |
+
ignore_links: bool = True,
|
19 |
+
) -> str:
|
20 |
+
"""Fetch text from url.
|
21 |
+
|
22 |
+
Args:
|
23 |
+
url: netloc from which to fetch text
|
24 |
+
bodywidth: if set to None, fall back to default bodywidth of
|
25 |
+
html2text.HTML2Text
|
26 |
+
remove: remove blank lines if set to True
|
27 |
+
show_url: prepend url if set to True
|
28 |
+
ignore_links: remove [ur](url)
|
29 |
+
|
30 |
+
Return:
|
31 |
+
main body in text
|
32 |
+
|
33 |
+
bodywidth: Optional[int] = 5000
|
34 |
+
remove: bool = False
|
35 |
+
show_url: bool = True
|
36 |
+
ignore_links: bool = True
|
37 |
+
"""
|
38 |
+
url = url.strip()
|
39 |
+
if not url.startswith("http"):
|
40 |
+
url = "http://" + url
|
41 |
+
|
42 |
+
logger.info("url: %s", url)
|
43 |
+
|
44 |
+
parsed = urlparse(url)
|
45 |
+
if not parsed.scheme or not parsed.netloc: # no scheme or netloc present
|
46 |
+
raise Exception(f"Invalid url: {url}")
|
47 |
+
|
48 |
+
try:
|
49 |
+
resp = httpx.get(url, timeout=30)
|
50 |
+
resp.raise_for_status()
|
51 |
+
except Exception as exc:
|
52 |
+
logger.error(exc)
|
53 |
+
raise
|
54 |
+
|
55 |
+
try:
|
56 |
+
content_type = resp.headers["content-type"]
|
57 |
+
except Exception as e:
|
58 |
+
logger.error(e)
|
59 |
+
content_type = ""
|
60 |
+
# output text if text/plain
|
61 |
+
if "text/plain" in content_type:
|
62 |
+
return resp.text
|
63 |
+
|
64 |
+
# handle html and the rest
|
65 |
+
try:
|
66 |
+
doc = Document(resp.text)
|
67 |
+
except Exception as exc:
|
68 |
+
logger.error(exc)
|
69 |
+
raise
|
70 |
+
|
71 |
+
if not doc.summary().strip():
|
72 |
+
raise Exception("No content for some reason...")
|
73 |
+
|
74 |
+
if bodywidth is not None:
|
75 |
+
handle = html2text.HTML2Text(bodywidth=bodywidth)
|
76 |
+
else:
|
77 |
+
handle = html2text.HTML2Text()
|
78 |
+
|
79 |
+
handle.ignore_links = ignore_links
|
80 |
+
|
81 |
+
try:
|
82 |
+
res = handle.handle(doc.summary())
|
83 |
+
except Exception as exc:
|
84 |
+
logger.error(exc)
|
85 |
+
raise
|
86 |
+
|
87 |
+
# remove double blank lines
|
88 |
+
if remove:
|
89 |
+
res = "\n".join(elm for elm in res.splitlines() if elm.strip())
|
90 |
+
|
91 |
+
if not res.strip(): # warn if empty output
|
92 |
+
logger.warning("Output seems to be empty...")
|
93 |
+
|
94 |
+
if show_url:
|
95 |
+
return f"{url}\n# {doc.title()}\n{res}"
|
96 |
+
|
97 |
+
return f"# {doc.title()}\n{res}"
|
mlbee/utils.py
ADDED
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Prep front cover for sidebar (based on st-bumblebee-st_app.py)."""
|
2 |
+
import base64
|
3 |
+
from io import BytesIO
|
4 |
+
from textwrap import dedent
|
5 |
+
|
6 |
+
import logzero
|
7 |
+
import pandas as pd
|
8 |
+
import streamlit as st
|
9 |
+
from logzero import logger
|
10 |
+
from set_loglevel import set_loglevel
|
11 |
+
|
12 |
+
from mlbee import __version__
|
13 |
+
|
14 |
+
logzero.loglevel(set_loglevel())
|
15 |
+
|
16 |
+
msg = dedent(
|
17 |
+
"""
|
18 |
+
What would you like to do?
|
19 |
+
The following alignment engines are available.
|
20 |
+
|
21 |
+
**UFast-Engine**: ultra-fast, based on a home-brewed algorithm, faster than blazing fast but can only process en-zh para/sent pairs, not as sophisticated as DL-Engine;
|
22 |
+
|
23 |
+
**SFast-Engine**: super-fast, based on machine translation;
|
24 |
+
|
25 |
+
**Fast-Engine**: based on yet another home-brewed algorithm, blazing fast but can only process en-zh para/sent pairs;
|
26 |
+
|
27 |
+
**DL-Engin**: based on machine learning, multilingual, one para/sent takes about 1s.
|
28 |
+
"""
|
29 |
+
).strip()
|
30 |
+
msg = dedent(
|
31 |
+
"""
|
32 |
+
a multilingual (about 100 luanguage pairs) dualtext
|
33 |
+
aligner based on machine learning
|
34 |
+
|
35 |
+
It takes about 1-2 s to process a pair of blocks (be it
|
36 |
+
sents, paras of docus).
|
37 |
+
Extremely long blocks will likely have a negative impact
|
38 |
+
on aligning.
|
39 |
+
"""
|
40 |
+
).strip()
|
41 |
+
|
42 |
+
|
43 |
+
def sb_front_cover():
|
44 |
+
"""Prep front cover for sidebar."""
|
45 |
+
st.sidebar.markdown(f"### mlbee {__version__} ")
|
46 |
+
|
47 |
+
_ = "More info (click to toggle)"
|
48 |
+
sb_tit_expander = st.sidebar.expander(_, expanded=False)
|
49 |
+
with sb_tit_expander:
|
50 |
+
st.markdown(msg)
|
51 |
+
|
52 |
+
|
53 |
+
intructins = dedent(
|
54 |
+
f"""
|
55 |
+
* Set up options in the left sidebar
|
56 |
+
|
57 |
+
* Click expanders / +: to reveal more details; -: to hide them
|
58 |
+
|
59 |
+
* Press '**Click to start aligning**' to get
|
60 |
+
the ball rolling. (The button will appear when
|
61 |
+
everything is ready.)
|
62 |
+
|
63 |
+
* mlbee v.{__version__} from mu@qq41947782's
|
64 |
+
keyboard in cyberspace. Join **qq group 316287378**
|
65 |
+
for feedback and questions or to be kept updated.
|
66 |
+
mlbee is a member of the bee family.
|
67 |
+
"""
|
68 |
+
).strip()
|
69 |
+
|
70 |
+
|
71 |
+
def instructions():
|
72 |
+
"""Prep msg."""
|
73 |
+
logger.debug("instructions entry")
|
74 |
+
back_cover_expander = st.expander("Instructions")
|
75 |
+
with back_cover_expander:
|
76 |
+
st.markdown(intructins)
|
77 |
+
|
78 |
+
logger.debug("instructions exit")
|
79 |
+
|
80 |
+
|
81 |
+
about_msg = dedent(
|
82 |
+
f"""
|
83 |
+
# mlbee {__version__}
|
84 |
+
|
85 |
+
https://bumblebee.freeforums.net/thread/6/mlbee
|
86 |
+
or head to 桃花元 (qq group 316287378)
|
87 |
+
"""
|
88 |
+
).strip()
|
89 |
+
|
90 |
+
menu_items = {
|
91 |
+
"Get Help": "https://bumblebee.freeforums.net/thread/6/mlbee",
|
92 |
+
"Report a bug": "https://github.com/ffreemt/mlbee/issues",
|
93 |
+
"About": about_msg,
|
94 |
+
}
|
95 |
+
|
96 |
+
style_css = """
|
97 |
+
.row-widget.stTextInput > div:first-of-type {
|
98 |
+
background: #fff;
|
99 |
+
display: flex;
|
100 |
+
border: 1px solid #dfe1e5;
|
101 |
+
box-shadow: none;
|
102 |
+
border-radius: 24px;
|
103 |
+
height: 50px;
|
104 |
+
width: auto;
|
105 |
+
margin: 10px auto 30px;
|
106 |
+
}
|
107 |
+
|
108 |
+
.row-widget.stTextInput > div:first-of-type:hover,
|
109 |
+
.row-widget.stTextInput > div:first-of-type:focus {
|
110 |
+
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2);
|
111 |
+
}
|
112 |
+
|
113 |
+
.row-widget.stTextInput .st-bq {
|
114 |
+
background-color: #fff;
|
115 |
+
}
|
116 |
+
|
117 |
+
.row-widget.stTextInput > label {
|
118 |
+
color: #b3b3b3;
|
119 |
+
}
|
120 |
+
|
121 |
+
.row-widget.stButton > button {
|
122 |
+
border-radius: 24px;
|
123 |
+
background-color: #B6C9B1;
|
124 |
+
color: #fff;
|
125 |
+
border: none;
|
126 |
+
padding: 6px 20px;
|
127 |
+
float: right;
|
128 |
+
background-image: none;
|
129 |
+
}
|
130 |
+
|
131 |
+
.row-widget.stButton > button:hover {
|
132 |
+
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2);
|
133 |
+
}
|
134 |
+
|
135 |
+
.row-widget.stButton > button:focus {
|
136 |
+
border: none;
|
137 |
+
color: #fff;
|
138 |
+
}
|
139 |
+
|
140 |
+
.footer-custom {
|
141 |
+
position: fixed;
|
142 |
+
bottom: 0;
|
143 |
+
width: 100%;
|
144 |
+
color: var(--text-color);
|
145 |
+
max-width: 698px;
|
146 |
+
font-size: 14px;
|
147 |
+
height: 50px;
|
148 |
+
padding: 10px 0;
|
149 |
+
z-index: 50;
|
150 |
+
}
|
151 |
+
|
152 |
+
.main {
|
153 |
+
padding: 20px;
|
154 |
+
}
|
155 |
+
|
156 |
+
footer {
|
157 |
+
display: none !important;
|
158 |
+
}
|
159 |
+
|
160 |
+
.footer-custom a {
|
161 |
+
color: var(--text-color);
|
162 |
+
}
|
163 |
+
|
164 |
+
#wikipedia-assistant {
|
165 |
+
font-size: 36px;
|
166 |
+
}
|
167 |
+
|
168 |
+
.generated-answer p {
|
169 |
+
font-size: 16px;
|
170 |
+
font-weight: bold;
|
171 |
+
}
|
172 |
+
|
173 |
+
.react-json-view {
|
174 |
+
margin: 40px 0 80px;
|
175 |
+
}
|
176 |
+
|
177 |
+
.tooltip {
|
178 |
+
text-align: center;
|
179 |
+
line-height: 20px;
|
180 |
+
display: table-caption;
|
181 |
+
font-size: 10px;
|
182 |
+
border-radius: 50%;
|
183 |
+
height: 20px;
|
184 |
+
width: 20px;
|
185 |
+
position: relative;
|
186 |
+
cursor: pointer;
|
187 |
+
color:#000;
|
188 |
+
}
|
189 |
+
|
190 |
+
.tooltip .tooltiptext {
|
191 |
+
visibility: hidden;
|
192 |
+
width: 280px;
|
193 |
+
text-align: center;
|
194 |
+
border-radius: 6px;
|
195 |
+
padding: 10px;
|
196 |
+
position: absolute;
|
197 |
+
z-index: 1;
|
198 |
+
top: 25px;
|
199 |
+
left: 50%;
|
200 |
+
margin-left: -140px;
|
201 |
+
font-size: 14px;
|
202 |
+
background-color: #fff;
|
203 |
+
border: 1px solid #ccc;
|
204 |
+
box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.16);
|
205 |
+
color: #000;
|
206 |
+
}
|
207 |
+
|
208 |
+
.tooltip:hover .tooltiptext {
|
209 |
+
visibility: visible;
|
210 |
+
}
|
211 |
+
|
212 |
+
.sentence-wrapper {
|
213 |
+
border-left: 4px solid #ffc423;
|
214 |
+
padding-left: 20px;
|
215 |
+
margin-bottom: 40px;
|
216 |
+
}
|
217 |
+
|
218 |
+
#context {
|
219 |
+
padding: 2rem 0 1rem;
|
220 |
+
}
|
221 |
+
|
222 |
+
hr {
|
223 |
+
margin: 2em 0 1em;
|
224 |
+
}
|
225 |
+
|
226 |
+
.technical-details-info {
|
227 |
+
margin-bottom: 100px;
|
228 |
+
}
|
229 |
+
|
230 |
+
.loader-wrapper {
|
231 |
+
display: flex;
|
232 |
+
align-items: center;
|
233 |
+
background-color: rgba(250, 202, 43, 0.2);
|
234 |
+
padding: 15px 20px;
|
235 |
+
border-radius: 6px;
|
236 |
+
}
|
237 |
+
|
238 |
+
.loader-wrapper p {
|
239 |
+
margin-bottom: 0;
|
240 |
+
margin-left: 20px;
|
241 |
+
}
|
242 |
+
|
243 |
+
.loader {
|
244 |
+
width: 30px;
|
245 |
+
height: 30px;
|
246 |
+
border: dotted 5px #868686;
|
247 |
+
border-radius: 100%;
|
248 |
+
animation: spin 1s linear infinite;
|
249 |
+
}
|
250 |
+
|
251 |
+
.loader-note {
|
252 |
+
font-size: 14px;
|
253 |
+
color: #b3b3b3;
|
254 |
+
margin-left: 5px;
|
255 |
+
}
|
256 |
+
|
257 |
+
@keyframes spin {
|
258 |
+
0% {
|
259 |
+
transform: rotate(0deg) scale(0.8);
|
260 |
+
border-top-color: transparent;
|
261 |
+
border-right-color: transparent;
|
262 |
+
}
|
263 |
+
50% { transform: rotate(180deg) scale(1.2);
|
264 |
+
border-color: #949494;
|
265 |
+
border-top-color: transparent;
|
266 |
+
border-right-color: transparent;
|
267 |
+
}
|
268 |
+
100% { transform: rotate(360deg) scale(0.8);
|
269 |
+
border-color: #bbbbbb;
|
270 |
+
border-top-color: transparent;
|
271 |
+
border-right-color: transparent;
|
272 |
+
}
|
273 |
+
}
|
274 |
+
"""
|
275 |
+
|
276 |
+
|
277 |
+
def to_excel(df):
|
278 |
+
"""Convert df to excel.
|
279 |
+
|
280 |
+
ref. st-bumblebee st_app.py
|
281 |
+
"""
|
282 |
+
output = BytesIO()
|
283 |
+
writer = pd.ExcelWriter(output, engine="xlsxwriter")
|
284 |
+
df.to_excel(writer, sheet_name="Sheet1")
|
285 |
+
writer.save()
|
286 |
+
processed_data = output.getvalue()
|
287 |
+
return processed_data
|
288 |
+
|
289 |
+
|
290 |
+
def get_table_download_link(df):
|
291 |
+
"""Generates a link.
|
292 |
+
|
293 |
+
Allowing the data in a given panda dataframe
|
294 |
+
to be downloaded.
|
295 |
+
|
296 |
+
Args:
|
297 |
+
df: pandas.dataframe
|
298 |
+
|
299 |
+
Returns:
|
300 |
+
href string
|
301 |
+
"""
|
302 |
+
val = to_excel(df)
|
303 |
+
b64 = base64.b64encode(val) # val looks like b'...'
|
304 |
+
return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="aligned_paras.xlsx">Download aligned paras xlsx file</a>' # decode b'abc' => abc
|
305 |
+
|
306 |
+
|
307 |
+
def get_table_download_link_sents(df):
|
308 |
+
"""Generates a link.
|
309 |
+
|
310 |
+
Allowing the data in a given panda dataframe to be
|
311 |
+
downloaded for sents aligned.
|
312 |
+
|
313 |
+
Args:
|
314 |
+
df: pandas.dataframe
|
315 |
+
|
316 |
+
Returns:
|
317 |
+
href string
|
318 |
+
"""
|
319 |
+
val = to_excel(df)
|
320 |
+
b64 = base64.b64encode(val) # val looks like b'...'
|
321 |
+
return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="aligned_sents.xlsx">Download aligned sents xlsx file</a>' # decode b'abc' => abc # noqa
|
okteto.yml
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: gradio-cmat
|
2 |
+
|
3 |
+
# The build section defines how to build the images of
|
4 |
+
# your development environment
|
5 |
+
# More info: https://www.okteto.com/docs/reference/manifest/#build
|
6 |
+
# build:
|
7 |
+
# my-service:
|
8 |
+
# context: .
|
9 |
+
|
10 |
+
# The deploy section defines how to deploy your development environment
|
11 |
+
# More info: https://www.okteto.com/docs/reference/manifest/#deploy
|
12 |
+
# deploy:
|
13 |
+
# commands:
|
14 |
+
# - name: Deploy
|
15 |
+
# command: echo 'Replace this line with the proper 'helm'
|
16 |
+
|
17 |
+
# or 'kubectl' commands to deploy your development environment'
|
18 |
+
|
19 |
+
# The dependencies section defines other git repositories to be
|
20 |
+
# deployed as part of your development environment
|
21 |
+
# More info: https://www.okteto.com/docs/reference/manifest/#dependencies
|
22 |
+
# dependencies:
|
23 |
+
# - https://github.com/okteto/sample
|
24 |
+
# The dev section defines how to activate a development container
|
25 |
+
# More info: https://www.okteto.com/docs/reference/manifest/#dev
|
26 |
+
dev:
|
27 |
+
gradio-cmat:
|
28 |
+
# image: okteto/dev:latest
|
29 |
+
# image: python:3.8.13-bullseye
|
30 |
+
# image: simbachain/poetry-3.8
|
31 |
+
image: python:3.8
|
32 |
+
command: bash
|
33 |
+
workdir: /usr/src/app
|
34 |
+
sync:
|
35 |
+
- .:/usr/src/app
|
36 |
+
environment:
|
37 |
+
- name=$USER
|
38 |
+
forward:
|
39 |
+
- 7861:7861
|
40 |
+
- 7860:7860
|
41 |
+
- 8501:8501
|
42 |
+
reverse:
|
43 |
+
- 9000:9000
|
44 |
+
autocreate: true
|
poetry.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|
pyproject.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
[tool.poetry]
|
2 |
name = "mlbee"
|
3 |
-
version = "0.1.0-alpha.
|
4 |
description = "mlbee"
|
5 |
authors = ["ffreemt"]
|
6 |
license = "MIT"
|
@@ -13,6 +13,19 @@ logzero = "^1.7.0"
|
|
13 |
set-loglevel = "^0.1.2"
|
14 |
icecream = "^2.1.1"
|
15 |
install = "^1.3.5"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
[tool.poe.executor]
|
18 |
type = "poetry"
|
|
|
1 |
[tool.poetry]
|
2 |
name = "mlbee"
|
3 |
+
version = "0.1.0-alpha.1"
|
4 |
description = "mlbee"
|
5 |
authors = ["ffreemt"]
|
6 |
license = "MIT"
|
|
|
13 |
set-loglevel = "^0.1.2"
|
14 |
icecream = "^2.1.1"
|
15 |
install = "^1.3.5"
|
16 |
+
model-pool = "^0.1.3"
|
17 |
+
tqdm = "^4.64.0"
|
18 |
+
about-time = "^3.1.1"
|
19 |
+
more-itertools = "^8.13.0"
|
20 |
+
cmat2aset = "^0.1.0-alpha.7"
|
21 |
+
aset2pairs = "^0.1.0"
|
22 |
+
pendulum = "^2.1.2"
|
23 |
+
streamlit = "^1.10.0"
|
24 |
+
loguru = "^0.6.0"
|
25 |
+
streamlit-option-menu = "^0.3.2"
|
26 |
+
httpx = "^0.23.0"
|
27 |
+
html2text = "^2020.1.16"
|
28 |
+
readability-lxml = "^0.8.1"
|
29 |
|
30 |
[tool.poe.executor]
|
31 |
type = "poetry"
|
requirements.txt
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
about-time==3.1.1
|
2 |
+
alive-progress==2.4.1; python_version >= "3.7" and python_version < "4" and python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
3 |
+
aset2pairs==0.1.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0"
|
4 |
+
asttokens==2.0.5; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version < "4.0"
|
5 |
+
certifi==2022.5.18.1; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.6"
|
6 |
+
charset-normalizer==2.0.12; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3"
|
7 |
+
click==8.1.3; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
8 |
+
cmat2aset==0.1.0a7; python_full_version >= "3.8.3" and python_version < "4.0"
|
9 |
+
colorama==0.4.4; python_full_version >= "3.8.3" and sys_platform == "win32" and platform_system == "Windows" and python_full_version < "4.0.0" and python_version < "4.0" and python_version >= "3.7"
|
10 |
+
environs==9.5.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.6"
|
11 |
+
executing==0.8.3; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version < "4.0"
|
12 |
+
filelock==3.7.1; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
13 |
+
grapheme==0.6.0; python_version >= "3.7" and python_version < "4" and python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
14 |
+
huggingface-hub==0.4.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
15 |
+
icecream==2.1.2
|
16 |
+
idna==3.3; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.5"
|
17 |
+
install==1.3.5; python_version >= "3.5"
|
18 |
+
joblib==1.1.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.8" and python_version < "4.0"
|
19 |
+
logzero==1.7.0
|
20 |
+
marshmallow==3.16.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.7"
|
21 |
+
model-pool==0.1.3; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
22 |
+
more-itertools==8.13.0; python_version >= "3.5"
|
23 |
+
nltk==3.7; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
24 |
+
numpy==1.22.4
|
25 |
+
packaging==21.3; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.7"
|
26 |
+
pandas==1.4.2; python_full_version >= "3.8.3" and python_version < "4.0" and python_version >= "3.8"
|
27 |
+
pillow==9.1.1; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
28 |
+
pygments==2.12.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version < "4.0" and python_version >= "3.6"
|
29 |
+
pyparsing==3.0.9; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.7"
|
30 |
+
python-dateutil==2.8.2; python_full_version >= "3.8.3" and python_version < "4.0" and python_version >= "3.8"
|
31 |
+
python-dotenv==0.20.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.6"
|
32 |
+
pytz==2022.1; python_full_version >= "3.8.3" and python_version < "4.0" and python_version >= "3.8"
|
33 |
+
pyyaml==6.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.6"
|
34 |
+
regex==2022.6.2; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
35 |
+
requests==2.27.1; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
36 |
+
scikit-learn==1.1.1; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.8" and python_version < "4.0"
|
37 |
+
scipy==1.6.1; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.8" and python_version < "4.0"
|
38 |
+
sentence-transformers==2.2.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
39 |
+
sentencepiece==0.1.96; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
40 |
+
set-loglevel==0.1.2; python_full_version >= "3.8.3" and python_full_version < "4.0.0"
|
41 |
+
six==1.16.0; python_full_version >= "3.8.3" and python_version < "4.0" and python_version >= "3.8"
|
42 |
+
sklearn==0.0; python_full_version >= "3.8.3" and python_version < "4.0"
|
43 |
+
threadpoolctl==3.1.0; python_full_version >= "3.8.3" and python_full_version < "4.0.0" and python_version >= "3.8" and python_version < "4.0"
|
44 |
+
tokenizers==0.12.1; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
45 |
+
torch==1.11.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
46 |
+
torchvision==0.12.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
47 |
+
tqdm==4.64.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
|
48 |
+
transformers==4.19.2; python_full_version >= "3.7.9" and python_full_version < "4.0.0"
|
49 |
+
typing-extensions==4.2.0; python_full_version >= "3.7.9" and python_full_version < "4.0.0" and python_version >= "3.7"
|
50 |
+
urllib3==1.26.9; python_full_version >= "3.7.9" and python_version < "4" and python_full_version < "4.0.0"
|
run-flake8.sh
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
flake8 --ignore F401,F841,E501 app_mlbee.py mlbee
|
run-nodemon-streamlit-run-app_mlbee.sh
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
nodemon -w app_mlbee.py -x python -m streamlit run app_mlbee.py
|
tests/test_gen_cmat_aset2pairs.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Test gen_cmat.
|
2 |
+
|
3 |
+
king
|
4 |
+
In [21]: len(de)
|
5 |
+
Out[21]: 51
|
6 |
+
|
7 |
+
In [22]: len(en)
|
8 |
+
Out[22]: 51
|
9 |
+
|
10 |
+
In [23]: len(" ".join(en))
|
11 |
+
Out[23]: 11208
|
12 |
+
|
13 |
+
In [24]: len(" ".join(de))
|
14 |
+
Out[24]: 13532
|
15 |
+
|
16 |
+
In [25]: %time en_vec = model_s.encode(en)
|
17 |
+
CPU times: user 22 s, sys: 436 ms, total: 22.4 s
|
18 |
+
Wall time: 22.4 s
|
19 |
+
|
20 |
+
In [26]: %time de_vec = model_s.encode(de)
|
21 |
+
CPU times: user 22.8 s, sys: 311 ms, total: 23.1 s
|
22 |
+
Wall time: 23.1 s
|
23 |
+
|
24 |
+
en1 = loadparas("data/sternstunden04-en.txt")
|
25 |
+
en2 = loadparas("data/sternstunden04-de.txt")
|
26 |
+
|
27 |
+
len(en1) # 30
|
28 |
+
|
29 |
+
len(" ".join(en1)) # 29718
|
30 |
+
len(" ".join(en2)) # 31478
|
31 |
+
"""
|
32 |
+
from cmat2aset import cmat2aset
|
33 |
+
from aset2pairs import aset2pairs
|
34 |
+
from mlbee.gen_cmat import gen_cmat
|
35 |
+
from mlbee.loadtext import loadparas
|
36 |
+
|
37 |
+
paras1 = loadparas("data/sternstunden04-en.txt")
|
38 |
+
paras2 = loadparas("data/sternstunden04-de.txt")
|
39 |
+
cmat = gen_cmat(paras1, paras2)
|
40 |
+
|
41 |
+
|
42 |
+
def test_gen_cmat_sternstunden04():
|
43 |
+
"""Test gen_cmat sternstunden04."""
|
44 |
+
len1, len2 = len(paras1), len(paras2)
|
45 |
+
|
46 |
+
# note the order
|
47 |
+
assert cmat.shape == (len2, len1)
|
48 |
+
|
49 |
+
|
50 |
+
def test_aset2pairs():
|
51 |
+
"""Test aset2pairs."""
|
52 |
+
aset = cmat2aset(cmat)
|
53 |
+
pairs = aset2pairs(paras1, paras2, aset)
|
54 |
+
|
55 |
+
assert "Marseillaise" in pairs[2][0]
|
56 |
+
assert "Marseillaise" in pairs[2][1]
|
57 |
+
assert pairs[2][2] > 0.95
|
tests/test_time.py-
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
king
|
3 |
+
In [21]: len(de)
|
4 |
+
Out[21]: 51
|
5 |
+
|
6 |
+
In [22]: len(en)
|
7 |
+
Out[22]: 51
|
8 |
+
|
9 |
+
In [23]: len(" ".join(en))
|
10 |
+
Out[23]: 11208
|
11 |
+
|
12 |
+
In [24]: len(" ".join(de))
|
13 |
+
Out[24]: 13532
|
14 |
+
|
15 |
+
In [25]: %time en_vec = model_s.encode(en)
|
16 |
+
CPU times: user 22 s, sys: 436 ms, total: 22.4 s
|
17 |
+
Wall time: 22.4 s
|
18 |
+
|
19 |
+
In [26]: %time de_vec = model_s.encode(de)
|
20 |
+
CPU times: user 22.8 s, sys: 311 ms, total: 23.1 s
|
21 |
+
Wall time: 23.1 s .5s/para
|
22 |
+
# .43-.45/paras
|
23 |
+
|
24 |
+
en1 = loadparas("data/sternstunden04-en.txt")
|
25 |
+
en2 = loadparas("data/sternstunden04-de.txt")
|
26 |
+
|
27 |
+
len(en1) # 30
|
28 |
+
|
29 |
+
len(" ".join(en1)) # 29718 990.6/para
|
30 |
+
len(" ".join(en1).split()) # 29718 990.6/para
|
31 |
+
len(" ".join(en2)) # 5148 words 171.6words/para
|
32 |
+
len(" ".join(en1)) # 29718 word.36words/para
|
33 |
+
|
34 |
+
%time en_vec1 = model_s.encode(en1) # 19 s .66s/para
|
35 |
+
%time en_vec2 = model_s.encode(en2) # 0.56/para
|
36 |
+
%time en_vec12 = model_s.encode(en1 + en2) # 38/60 .63s/paras
|
37 |
+
"""
|