From fef8c60aeab34c09be56cfa9b4c591101b3a3733 Mon Sep 17 00:00:00 2001 From: Wolfgang Fahl Date: Tue, 31 Oct 2023 17:15:39 +0100 Subject: [PATCH] heavily refactors --- frontend/cmsapp.py | 151 ----------------- frontend/cmsmain.py | 118 +++---------- frontend/server.py | 19 ++- frontend/version.py | 2 +- frontend/webserver.py | 346 ++++++++------------------------------- frontend/wikigrid.py | 88 +++------- pyproject.toml | 12 +- templates/base.html | 36 ---- templates/bootstrap.html | 6 - templates/design.html | 11 -- templates/generator.html | 67 -------- templates/index.html | 12 -- templates/login.html | 12 -- templates/macros.html | 94 ----------- templates/server.html | 8 - templates/welcome.html | 12 -- tests/test_family.py | 9 +- tests/test_frontend.py | 1 - tests/test_parser.py | 6 +- tests/test_server.py | 9 +- tests/test_sql.py | 48 ------ tests/test_webserver.py | 5 - tests/test_wikicms.py | 7 +- 23 files changed, 140 insertions(+), 939 deletions(-) delete mode 100644 frontend/cmsapp.py delete mode 100644 templates/base.html delete mode 100644 templates/bootstrap.html delete mode 100644 templates/design.html delete mode 100644 templates/generator.html delete mode 100644 templates/index.html delete mode 100644 templates/login.html delete mode 100644 templates/macros.html delete mode 100644 templates/server.html delete mode 100644 templates/welcome.html delete mode 100644 tests/test_sql.py diff --git a/frontend/cmsapp.py b/frontend/cmsapp.py deleted file mode 100644 index 572ccdd..0000000 --- a/frontend/cmsapp.py +++ /dev/null @@ -1,151 +0,0 @@ -''' -Created on 2022-11-03 - -@author: wf -''' -import os -from jpcore.compat import Compatibility;Compatibility(0,11,3) -from jpcore.justpy_config import JpConfig -script_dir = os.path.dirname(os.path.abspath(__file__)) -static_dir = script_dir+"/resources/static" -JpConfig.set("STATIC_DIRECTORY",static_dir) -# shut up justpy -JpConfig.set("VERBOSE","False") -JpConfig.setup() -from jpwidgets.bt5widgets import App,About,ProgressBar -from frontend.wikigrid import WikiGrid -import sys - -class CmsApp(App): - """ - Content Management System application - """ - - def __init__(self,version,title:str,args:None): - ''' - Constructor - - Args: - version(Version): the version info for the app - ''' - import justpy as jp - self.jp=jp - App.__init__(self, version=version,title=title) - self.args=args - self.addMenuLink(text='Home',icon='home', href="/") - self.addMenuLink(text='github',icon='github', href=version.cm_url) - self.addMenuLink(text='Chat',icon='chat',href=version.chat_url) - self.addMenuLink(text='Documentation',icon='file-document',href=version.doc_url) - self.addMenuLink(text='Settings',icon='cog',href="/settings") - self.addMenuLink(text='About',icon='information',href="/about") - - self.wikiGrid=None - - jp.Route('/settings',self.settings) - jp.Route('/about',self.about) - - async def onPageReady(self,_msg): - """ - react on page Ready - """ - try: - if self.wikiGrid is not None: - await self.wikiGrid.onPageReady(_msg) - except BaseException as ex: - self.handleException(ex) - - def setupRowsAndCols(self): - """ - setup the general layout - """ - head_html="""""" - self.wp=self.getWp(head_html) - self.button_classes = """btn btn-primary""" - # rows - self.rowA=self.jp.Div(classes="row",a=self.contentbox) - self.rowB=self.jp.Div(classes="row",a=self.contentbox) - self.rowC=self.jp.Div(classes="row",a=self.contentbox) - self.rowD=self.jp.Div(classes="row",a=self.contentbox) - self.rowE=self.jp.Div(classes="row",a=self.contentbox) - self.rowF=self.jp.Div(classes="row",a=self.contentbox) - # columns - self.colA1=self.jp.Div(classes="col-12",a=self.rowA) - self.colC1=self.jp.Div(classes="col-12",a=self.rowC) - self.colD1=self.jp.Div(classes="col-3",a=self.rowD) - self.colD2=self.jp.Div(classes="col-2",a=self.rowD) - self.colE1=self.jp.Div(classes="col-12",a=self.rowE) - self.colF1=self.jp.Div(classes="col-12",a=self.rowF) - # standard elements - self.errors=self.jp.Div(a=self.colA1,style='color:red') - self.messages=self.jp.Div(a=self.colE1,style='color:black') - self.progressBar = ProgressBar(a=self.rowD) - - async def settings(self)->"jp.WebPage": - ''' - settings - - Returns: - jp.WebPage: a justpy webpage renderer - ''' - self.setupRowsAndCols() - return self.wp - - async def about(self)->"jp.WebPage": - ''' - show about dialog - - Returns: - jp.WebPage: a justpy webpage renderer - ''' - self.setupRowsAndCols() - self.aboutDiv=About(a=self.colC1,version=self.version) - - return self.wp - - def logo_img(self,a,logo,logo_size): - markup=f"""""" - logo_div=self.jp.Div(a=a,classes="col-1")# - logo_div.inner_html=markup - - def addServerInfo(self,a): - """ - add ServerInfo - """ - infoDiv=self.jp.Div(a=a,classes='col-2 h4 align-middle') - if sys.platform == "linux" or sys.platform == "linux2": - # linux - os_logo="https://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png" - elif sys.platform == "darwin": - # OS X - os_logo="https://upload.wikimedia.org/wikipedia/commons/thumb/a/ab/Icon-Mac.svg/256px-Icon-Mac.svg.png" - elif sys.platform == "win32": - # Windows... - os_logo="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Windows_icon.svg/256px-Windows_icon.svg.png" - else: - os_logo="" - logo_size=64 - infoDiv.inner_html=f"Welcome to {self.args.host}" - self.logo_img(a,os_logo,logo_size) - self.logo_img(a,self.args.logo,logo_size) - pass - - async def content(self)->"jp.WebPage": - ''' - provide the main content page - - Returns: - jp.WebPage: a justpy webpage renderer - ''' - self.setupRowsAndCols() - self.addServerInfo(a=self.rowB) - self.wikiGrid=WikiGrid(a=self.colC1,app=self) - self.wp.on("page_ready", self.onPageReady) - return self.wp - - def start(self,host,port,debug): - """ - start the server - """ - self.debug=debug - import justpy as jp - jp.justpy(self.content,host=host,port=port) diff --git a/frontend/cmsmain.py b/frontend/cmsmain.py index 1c71201..f09b02a 100644 --- a/frontend/cmsmain.py +++ b/frontend/cmsmain.py @@ -1,117 +1,43 @@ ''' -Created on 2022-12-03 - -@author: wf -''' -''' Created on 2022-11-24 @author: wf ''' - +from ngwidgets.cmd import WebserverCmd +from frontend.webserver import WebServer from argparse import ArgumentParser -from argparse import RawDescriptionHelpFormatter -from frontend.version import Version -from frontend.cmsapp import CmsApp -import os import sys -import traceback -import webbrowser -# import after app! -from jpcore.justpy_app import JustpyServer -from frontend.server import Server - -__version__ = Version.version -__date__ = Version.date -__updated__ = Version.updated -class CmsMain(): +class CmsMain(WebserverCmd): """ ContentManagement System Main Program """ def __init__(self): """ - construct me + constructor """ - self.server = Server() - self.server.load() - - def getArgParser(self,program_license,program_version_message): - # Setup argument parser - parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) - parser.add_argument("-a","--about",help="show about info [default: %(default)s]",action="store_true") - parser.add_argument("-b","--browse",help="open browser", action="store_true") - parser.add_argument("-d", "--debug", dest="debug", action="store_true", help="show debug info [default: %(default)s]") - parser.add_argument('--host',default=JustpyServer.getDefaultHost(),help="the host to serve / listen from [default: %(default)s]") - parser.add_argument('--logo',help="the server logo [default: %(default)s]",default=self.server.logo) - parser.add_argument('--port',type=int,default=8252,help="the port to serve from [default: %(default)s]") - parser.add_argument("--serve",help="start webserver",action="store_true") - parser.add_argument('-V', '--version', action='version', version=program_version_message) - return parser - -def main(argv=None): # IGNORE:C0111 - '''main program.''' - - if argv is None: - argv=sys.argv[1:] + config=WebServer.get_config() + WebserverCmd.__init__(self, config, WebServer, DEBUG) + pass - program_name = os.path.basename(__file__) - program_shortdesc = Version.description + def getArgParser(self,description:str,version_msg)->ArgumentParser: + """ + override the default argparser call + """ + parser=super().getArgParser(description, version_msg) + parser.add_argument('--sites',nargs='+',required=False,help="the sites to enable") + return parser - program_version =f"v{__version__}" - program_build_date = str(__updated__) - program_version_message = f'{program_name} ({program_version},{program_build_date})' - - user_name="Wolfgang Fahl" - program_license = '''%s - - Created by %s on %s. - Copyright 2022-2023 Wolfgang Fahl. All rights reserved. - - Licensed under the Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - Distributed on an "AS IS" basis without warranties - or conditions of any kind, either express or implied. - -USAGE -''' % (program_shortdesc, user_name,str(__date__)) - try: - cmsMain=CmsMain() - parser=cmsMain.getArgParser(program_license, program_version_message) - args = parser.parse_args(argv) - if len(argv) < 1: - parser.print_usage() - sys.exit(1) - #ypgen=YPGen(args) - if args.about: - print(program_version_message) - print(f"see {Version.doc_url}") - webbrowser.open(Version.doc_url) - else: - app=CmsApp(version=Version, title="pyWikiCMS",args=args) - url=f"http://{args.host}:{args.port}" - if args.browse: - webbrowser.open(url) - if args.serve: - app.start(host=args.host, port=args.port,debug=args.debug) - pass - pass - except KeyboardInterrupt: - ### handle keyboard interrupt ### - return 1 - except Exception as e: - if DEBUG: - raise(e) - indent = len(program_name) * " " - sys.stderr.write(program_name + ": " + repr(e) + "\n") - sys.stderr.write(indent + " for help use --help") - if args.debug: - print(traceback.format_exc()) - return 2 +def main(argv:list=None): + """ + main call + """ + cmd=CmsMain() + exit_code=cmd.cmd_main(argv) + return exit_code -DEBUG = 1 +DEBUG = 0 if __name__ == "__main__": if DEBUG: sys.argv.append("-d") diff --git a/frontend/server.py b/frontend/server.py index 31a270b..843a223 100644 --- a/frontend/server.py +++ b/frontend/server.py @@ -7,7 +7,6 @@ import os import socket import datetime -from flask import render_template from lodstorage.jsonable import JSONAble from pathlib import Path from frontend.wikicms import Frontend @@ -26,6 +25,7 @@ def __init__(self,debug=False): Args: storePath(str): the path to load my configuration from (if any) ''' + self.storage_secret=None self.frontendConfigs=None self.logo="https://wiki.bitplan.com/images/wiki/6/63/Profiwikiicon.png" self.purpose="" @@ -250,10 +250,21 @@ def checkApacheConfiguration(self,conf,status='enabled')->str: stateSymbol=self.stateSymbol(confExists) return stateSymbol - def render(self): + def asHtml(self)->str: ''' - render me + render me as HTML code ''' - html=render_template('server.html',server=self) + server=self + logo_html="" + if server.logo is not None: + logo_html=f"""{server.name} logo""" + html=f""" + + + {logo_html} + +
{server.platform} logoWelcome to {server.name } ({ server.ip }) { server.purpose } +
+""" return html \ No newline at end of file diff --git a/frontend/version.py b/frontend/version.py index d1e66f8..4d42d67 100644 --- a/frontend/version.py +++ b/frontend/version.py @@ -13,7 +13,7 @@ class Version(object): description='pyWikiCMS: python implementation of a Mediawiki based Content Management System' version=frontend.__version__ date = '2022-11-16' - updated = '2023-02-25' + updated = '2023-10-30' authors='Wolfgang Fahl' doc_url="http://wiki.bitplan.com/index.php/PyWikiCMS" chat_url="https://github.com/BITPlan/pyWikiCMS/discussions" diff --git a/frontend/webserver.py b/frontend/webserver.py index 82bc478..5aa0f98 100644 --- a/frontend/webserver.py +++ b/frontend/webserver.py @@ -3,116 +3,60 @@ @author: wf ''' -from fb4.app import AppWrap -from fb4.login_bp import LoginBluePrint -from flask_login import current_user, login_user,logout_user, login_required -from flask import send_file, flash, request -#from frontend.generator import Generator, GenerateTopicForm +from nicegui import app,ui, Client +from ngwidgets.input_webserver import InputWebserver from frontend.server import Server -from frontend.family import WikiFamily, WikiBackup -from fb4.widgets import Link, Icon, Image, MenuItem -from flask import render_template -from wikibot3rd.wikiuser import WikiUser -from fb4.sqldb import db -import os +from ngwidgets.webserver import WebserverConfig +from frontend.version import Version +from frontend.wikigrid import WikiGrid +from ngwidgets.users import Users +from ngwidgets.login import Login +from fastapi.responses import RedirectResponse +from ngwidgets.lod_grid import ListOfDictsGrid -class WikiCMSWeb(AppWrap): - ''' - Wrapper for Flask Web Application - ''' +class WebServer(InputWebserver): + """ + WebServer class that manages the server - def __init__(self, host='0.0.0.0', port=8251, debug=False): + """ + @classmethod + def get_config(cls)->WebserverConfig: + copy_right="(c)2023 Wolfgang Fahl" + config=WebserverConfig(copy_right=copy_right,version=Version(),default_port=8252) + return config + + def __init__(self): ''' constructor - Args: - wikiId(str): id of the wiki to use as a CMS backend - host(str): flask host - port(int): the port to use for http connections - debug(bool): True if debugging should be switched on ''' - scriptdir = os.path.dirname(os.path.abspath(__file__)) - template_folder=scriptdir + '/../templates' - super().__init__(host=host,port=port,debug=debug,template_folder=template_folder) - self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' - db.init_app(self.app) - self.db=db - self.loginBluePrint=LoginBluePrint(self.app,'login') + InputWebserver.__init__(self,config=WebServer.get_config()) + users=Users("~/.wikicms/") + self.login=Login(self,users) self.server = Server() self.server.load() self.enabledSites = ['admin'] + self.wiki_grid=WikiGrid() - # - # setup global handlers - # - @self.app.before_first_request - def before_first_request_func(): - self.initDB() - loginMenuList=self.adminMenuList("Login") - self.loginBluePrint.setLoginArgs(menuList=loginMenuList) - - @self.app.route('/') - def index(): - return self.family() - - @self.app.route('/family') - def family(): - return wcw.family() - - @login_required - @self.app.route('/wikis') - def wikis(): - return self.wikis() + @ui.page('/') + async def home(client: Client): + return await self.home(client) - @login_required - @self.app.route('/frontends') - def frontends(): - return wcw.frontends() - - @login_required - @self.app.route('/generate/', methods=['GET', 'POST']) - def generate(siteName: str): - ''' - Handle wiki generator page request - Args: - siteName(str): wikiId of the wiki the generator should be returned for - ''' - return self.generate(siteName) - - def initDB(self): - ''' - initialize the database - ''' - self.db.drop_all() - self.db.create_all() - self.initUsers() - - def initUsers(self): - if hasattr(self.server,"adminUser"): - self.loginBluePrint.addUser(self.db,self.server.adminUser,self.server.adminPassword) - else: - self.loginBluePrint.hint="There is no adminUser configured yet" + @ui.page('/settings') + async def settings(): + return await self.settings() + + @ui.page('/login') + async def login(client:Client): + return await self.login(client) + + @ui.page('/wikis') + async def wikis(client:Client): + if not self.login.authenticated(): + return RedirectResponse('/login') + return await self.wikis() + - @staticmethod - def splitPath(path): - ''' - split the given path - Args: - path(str): the path to split - Returns: - str,str: the site of the path an the actual path - ''' - # https://stackoverflow.com/questions/2136556/in-python-how-do-i-split-a-string-and-keep-the-separators - parts = path.split(r"/") - site = "" - if len(parts) > 0: - site = parts[0] - path = "" - if len(parts) > 1: - for part in parts[1:]: - path = path + "/%s" % (part) - return site, path - def enableSites(self, siteNames): ''' enable the sites given in the sites list @@ -124,186 +68,32 @@ def enableSites(self, siteNames): for siteName in siteNames: self.server.enableFrontend(siteName,self) self.enabledSites.append(siteName) + + async def home(self, _client:Client): + """ + Generates the home page with a pdf view + """ + self.setup_menu() + with ui.element("div").classes("w-full h-full"): + self.server_html=ui.html(self.server.asHtml()) + self.show_wikis() + await self.setup_footer() - def adminMenuList(self,activeItem:str=None): - ''' - get the list of menu items for the admin menu - Args: - activeItem(str): the active menu item - Return: - list: the list of menu items - ''' - menuList=[ - MenuItem('/','Home'), - MenuItem('https://github.com/BITPlan/pyWikiCMS','github'), - MenuItem('/generate/orth', 'Generator') # ToDo: Test Values - ] - if current_user.is_anonymous: - menuList.append(MenuItem('/login','login')) - else: - menuList.append(MenuItem('/wikis','Wikis')), - menuList.append(MenuItem('/frontends','Frontends')), - menuList.append(MenuItem('/logout','logout')) - - if activeItem is not None: - for menuItem in menuList: - if menuItem.title==activeItem: - menuItem.active=True - if menuItem.url.startswith("/"): - menuItem.url="%s%s" % (self.baseUrl,menuItem.url) - return menuList - - def frontends(self) -> str: - ''' - render the frontends view - - Returns: - str: the html for the admin view - ''' - menuList=self.adminMenuList("Frontends") - html = render_template("tableview.html", title="Frontends", menuList=menuList,dictList=self.server.frontendConfigs) - return html - - def wikis(self) -> str: - ''' - render the wikis table - - Returns: - str: the html code for the table of wikis - ''' - wikiUsers = WikiUser.getWikiUsers() - dictList = [] - for wikiUser in wikiUsers.values(): - url="%s%s/" % (wikiUser.url,wikiUser.scriptPath) - wikiBackup=WikiBackup(wikiUser) - dictList.append({ - 'wikiId': Link(url,wikiUser.wikiId), - 'url': Link(wikiUser.url,wikiUser.url), - 'scriptPath':wikiUser.scriptPath, - 'version':wikiUser.version, - 'backup': "✅" if wikiBackup.exists() else "❌", - 'git': Icon("github",32) if wikiBackup.hasGit() else "" - }) - menuList=self.adminMenuList("Wikis") - html = render_template("tableview.html", menuList=menuList,title="Wikis", dictList=dictList) - return html - - def logo(self, siteName:str) -> str: - ''' - render the Logo for the given siteName - - Args: - siteName(str): the name of the site e.g. wiki.bitplan.com - Returns: - the rendered Logo for the given Site - ''' - wikiFamily = WikiFamily() - if not siteName in wikiFamily.family: - return self.error("Logo Error","invalid siteName %s" % siteName) - wiki=wikiFamily.family[siteName] - logoFile=wiki.getLogo() - if logoFile is None: - return "no logo for %s" %siteName - else: - return send_file(logoFile) - - def family(self) -> str: - ''' - show a html representation of the family of wikis on this server (if any) - - Returns: - str: a html table of all wikis in the family - ''' - dictList = [] - wikiFamily = WikiFamily() - for siteName in wikiFamily.family: - localWiki = wikiFamily.family[siteName] - logoAccess="%s/family/%s/logo" % (self.baseUrl,siteName) - apacheAvailable=self.server.checkApacheConfiguration(localWiki.siteId,'available') - apacheEnabled=self.server.checkApacheConfiguration(localWiki.siteId,'enabled') - dbName=localWiki.database - dburl=self.server.sqlGetDatabaseUrl(localWiki.database, localWiki.dbUser, localWiki.dbPassword,hostname='localhost') - dbState=self.server.sqlDatabaseExist(dburl) - dbStateSymbol=self.server.stateSymbol(dbState) - backupState=self.server.sqlBackupStateAsHtml(dbName) - hereState=self.server.stateSymbol(localWiki.ip==self.server.ip) - statusSymbol="❌" - if localWiki.statusCode==200: - statusSymbol="✅" - elif localWiki.statusCode==404: - statusSymbol="⚠️" - siteDict={ - 'site': "%s %s" % (Link(localWiki.url,localWiki.siteName),statusSymbol), - 'logo': Image(logoAccess,height=70), - } - if not current_user.is_anonymous: - adminDict={ - 'database': "%s %s" % (localWiki.database,dbStateSymbol), - 'SQL backup': backupState, - 'ip': "%s%s" % (hereState,localWiki.ip), - 'apache': "%s/%s" % (apacheAvailable,apacheEnabled) - } - siteDict={**siteDict,**adminDict} - dictList.append(siteDict) - menuList=self.adminMenuList("Family") - html = render_template("welcome.html", server=self.server,menuList=menuList,title="Wiki Family", dictList=dictList) - return html - - def wrap(self, siteName, path): - ''' - wrap the given path for the given site - Args: - siteName(str): the name of the site to wrap - path(path): the path to wrap - ''' - if not siteName in self.enabledSites: - error = "access to site '%s' is not enabled you might want to add it via the --sites command line option" % siteName - content = None - template = "index.html" - title = "Error" - return render_template(template, title=title, content=content, error=error) - else: - frontend = self.server.getFrontend(siteName) - return frontend.render(path) - -# -# route of this Web application -# - -# construct the web application -wcw=WikiCMSWeb() - -# get the app to define routings for -app=wcw.app + def show_wikis(self): + """ + """ + self.lod_grid=ListOfDictsGrid(lod=self.wiki_grid.lod) + self.lod_grid.ag_grid._props['html_columns']= [0, 1, 2] + pass + + def configure_menu(self): + """ + configure specific menu entries + """ + username=app.storage.user.get('username', '?') + ui.label(username) - -@app.route('/family//logo') -def wikiLogo(siteName:str): - ''' - render the Logo for the given siteName - - Args: - siteName(str): the name of the site e.g. wiki.bitplan.com - Returns: - the rendered Logo for the given Site - ''' - return wcw.logo(siteName) - -@app.route('/') -def wrap(path): - ''' - wrap the url request for the given path - - Args: - path(str): the path to wrap - the path should start with // followed by the actual path in the wiki - ''' - site,path=AppWrap.splitPath(path) - return wcw.wrap(site,path) - -if __name__ == '__main__': - parser=wcw.getParser(description="Wiki Content Management webservice") - parser.add_argument('--sites',nargs='+',required=False,help="the sites to enable") - args=parser.parse_args() - wcw.optionalDebug(args) - wcw.enableSites(args.sites) - wcw.run(args) \ No newline at end of file + + def configure_run(self): + self.enableSites(self.args.sites) + self.args.storage_secret=self.server.storage_secret \ No newline at end of file diff --git a/frontend/wikigrid.py b/frontend/wikigrid.py index 68e2c99..85f9d7d 100644 --- a/frontend/wikigrid.py +++ b/frontend/wikigrid.py @@ -3,56 +3,18 @@ @author: wf ''' -import asyncio from wikibot3rd.wikiuser import WikiUser from wikibot3rd.wikiclient import WikiClient from wikibot3rd.smw import SMWClient -from jpwidgets.bt5widgets import Link, Switch -from jpwidgets.widgets import LodGrid from frontend.html_table import HtmlTables from lodstorage.lod import LOD +from frontend.family import WikiFamily, WikiBackup import os import glob import time from pathlib import Path +from ngwidgets.widgets import Link -class Display: - ''' - generic Display - ''' - noneValue="-" - - def setDefaultColDef(self,agGrid): - """ - set the default column definitions - """ - defaultColDef=agGrid.options.defaultColDef - defaultColDef.resizable=True - defaultColDef.sortable=True - defaultColDef.filter=True - # https://www.ag-grid.com/javascript-data-grid/grid-size/ - defaultColDef.wrapText=True - defaultColDef.autoHeight=True - defaultColDef.headerClass="font-bold" - - def addFitSizeButton(self,a): - self.onSizeColumnsToFitButton=Switch( - a=a, - labelText="fit", - checked=False - #iconName='format-columns', - #classes="btn btn-primary btn-sm col-1" - ) - self.onSizeColumnsToFitButton.on("input",self.onSizeColumnsToFit) - - async def onSizeColumnsToFit(self,_msg:dict): - try: - await asyncio.sleep(0.2) - if self.agGrid: - await self.agGrid.run_api('sizeColumnsToFit()', self.app.wp) - except Exception as ex: - self.app.handleException(ex) - class WikiCheck: """ @@ -81,45 +43,49 @@ def asRadioButton(self,jp,a): jp.Span(classes='ml-1', a=label, text=self.name) return radio_btn -class WikiGrid(Display): +class WikiGrid(): """ grid of Wikis """ - def __init__(self,a,app): + def __init__(self): """ constructor Args: targets(dict): a list of targets - a: the parent element - app: the parent app """ - self.app=app - self.jp=app.jp self.setupWikiUsers() - self.addCheckButtons(a=a) - self.agGrid=LodGrid(a=a) - self.agGrid.theme="ag-theme-material" + #self.addCheckButtons(a=a) self.lod=[] self.lodindex={} for index,wikiUser in enumerate(self.sortedWikiUsers): + wikiBackup=WikiBackup(wikiUser) + url=f"{wikiUser.url}{wikiUser.scriptPath}" + link=Link.create(url=url,text=wikiUser.wikiId,target="_blank") self.lod.append({ "#": index+1, - "wiki": Link.newTab(url=wikiUser.getWikiUrl(), text=wikiUser.wikiId), + "wiki": link, "version": wikiUser.version, "since": "", "until": "", "pages": "", - "backup": "", + "backup": "✅" if wikiBackup.exists() else "❌", + "git": "✅" if wikiBackup.hasGit() else "❌", "age": "" }) - self.lodindex[wikiUser.wikiId]=index+1 - self.setDefaultColDef(self.agGrid) - self.agGrid.load_lod(self.lod) - #self.agGrid.options.columnDefs[0].checkboxSelection = True - self.agGrid.html_columns=[1] + self.lodindex[wikiUser.wikiId]=index+1 + + #dictList.append({ + # 'wikiId': Link(url,wikiUser.wikiId), + # 'url': Link(wikiUser.url,wikiUser.url), + # 'scriptPath':wikiUser.scriptPath, + # 'version':wikiUser.version, + # 'backup': , + # 'git': Icon("github",32) if wikiBackup.hasGit() else "" + #}) + def setupWikiUsers(self): # wiki users @@ -245,12 +211,4 @@ async def checkBackup4WikiUser(self,wikiUser): await self.updateRow(row, "backup", msg) except BaseException as ex: self.app.handleException(ex) - - async def onPageReady(self,_msg): - """ - react on page Ready - """ - try: - pass - except BaseException as ex: - self.app.handleException(ex) \ No newline at end of file + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 8b60cf9..67590df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,12 +16,12 @@ maintainers = [ readme = "README.md" license = {text = "Apache-2.0"} dependencies = [ - # https://github.com/justpy-org/justpy - 'justpy>=0.11.3', + # https://github.com/WolfgangFahl/nicegui_widgets + "ngwidgets>=0.2.3", # https://pypi.org/project/pylodstorage/ - 'pyLodStorage>=0.4.7', - # https://pypi.org/project/pyJustpyWidgets/ - 'pyJustpyWidgets>=0.1.11', + "pyLodStorage>=0.4.7", + # https://pypi.org/project/SQLAlchemy/ + "SQLAlchemy>=2.0.22", # https://pypi.org/project/py-3rdparty-mediawiki/ 'py-3rdparty-mediawiki>=0.9.1', # Beautiful Soup HTML parser @@ -31,8 +31,6 @@ dependencies = [ 'lxml', #https://pypi.org/project/pydevd/ 'pydevd', - # pyFlaskBootstrap4 - 'pyFlaskBootstrap4>=0.6.1' ] requires-python = ">=3.8" diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 2a77dd3..0000000 --- a/templates/base.html +++ /dev/null @@ -1,36 +0,0 @@ -{% from 'bootstrap/nav.html' import render_nav_item %} -{% from 'bootstrap/utils.html' import render_messages %} -{% import 'macros.html' as macros %} - - - - {% block head %} - - - - {% block styles %} - - {{ bootstrap.load_css() }} - {% endblock %} - - {% if title is not none %}{{ title }}{%endif %} - {% endblock %} - - -{% if menuList %}{{ macros.menu(menuList) }}{%endif %} -
- {{ render_messages(container=False, dismissible=True) }} - {% block content %}{% endblock %} -
- -
- {% block footer %} - © 2020– Wolfgang Fahl Powered by pyWikiCMS - {% endblock %} -
- - {% block scripts %} - {{ bootstrap.load_js() }} - {% endblock %} - - diff --git a/templates/bootstrap.html b/templates/bootstrap.html deleted file mode 100644 index aa6b752..0000000 --- a/templates/bootstrap.html +++ /dev/null @@ -1,6 +0,0 @@ -{% import 'macros.html' as macros %} -{{ macros.header(title) }} -{% if menuList %}{{ macros.menu(menuList) }}{%endif %} -{% if content is not none %}{{ content|safe }}{%endif %} -{% if error is not none %}Error: {{ error }}{%endif %} -{{ macros.footer() }} \ No newline at end of file diff --git a/templates/design.html b/templates/design.html deleted file mode 100644 index 1b470f9..0000000 --- a/templates/design.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% block styles %} - {{ super() }} -{% endblock %} -{% block content %} - {% if content is not none %}{{ content|safe }}{%endif %} - {% if error is not none %}Error: {{ error }}{%endif %} -{% endblock %} -{% block scripts %} -{{ super() }} -{% endblock %} \ No newline at end of file diff --git a/templates/generator.html b/templates/generator.html deleted file mode 100644 index 31ce88e..0000000 --- a/templates/generator.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends 'base.html' %} -{% import 'macros.html' as macros %} -{% block content %} -
- {{ formTable4DictList(dictList) }} -
-{% endblock %} -{% block scripts %} - {{ super() }} - {{ loadDataTable() }} - {{ activateDataTable() }} -{% endblock %} - -{# table for dict list #} -{% macro formTable4DictList(dictList) -%} - - - {% if dictList %} - - - - {% for key in dictList[0] %} - - {% endfor %} - - - - - {% for dict_item in dictList %} - - - {% for key, value in dict_item.items() %} - - {% endfor %} - - {% endfor %} - - {% endif %} -
# {{ key }}
{{loop.index}} - {% if key == "Topic" %} - {{ value.label }}{{ value|safe }} - {% else %} - {{ value|safe }} - {% endif %} -
-{%- endmacro %} - -{% macro loadDataTable() -%} - - -{%- endmacro %} -{% macro activateDataTable() -%} - -{%- endmacro %} -{% macro addFormTags(siteName, formNames) %} -{% for name in formNames %} -
-{% endfor %} -{% endmacro %} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 1415b36..0000000 --- a/templates/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - {% if content is not none %}{{ content|safe }}{%endif %} - {% if error is not none %}Error: {{ error }}{%endif %} - - - \ No newline at end of file diff --git a/templates/login.html b/templates/login.html deleted file mode 100644 index 559d8cc..0000000 --- a/templates/login.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends 'base.html' %} -{% from 'bootstrap/form.html' import render_form, render_field, render_form_row %} - -{% block content %} -

Login

-
- {{ form.csrf_token }} - {{ render_form_row([form.username, form.password], col_class_default='col-md-2') }} - {{ render_form_row([form.rememberMe]) }} - {{ render_form_row([form.submit]) }} -
-{% endblock %} \ No newline at end of file diff --git a/templates/macros.html b/templates/macros.html deleted file mode 100644 index 429afe9..0000000 --- a/templates/macros.html +++ /dev/null @@ -1,94 +0,0 @@ -{# - Useful Jinja Template Macros - - e.g. table4DictList - https://stackoverflow.com/a/42848018/1497139 -#} -{# show html header #} -{% macro header(title) -%} - - - - {% block head %} - - - - {% block styles %} - - {{ bootstrap.load_css() }} - {% endblock %} - - {% if title is not none %}{{ title }}{%endif %} - {% endblock %} - - -{%- endmacro %} -{# show html footer #} -{% macro footer() -%} - {% block scripts %} - - {{ bootstrap.load_js() }} - {% endblock %} - - -{%- endmacro %} -{# menu see https://getbootstrap.com/docs/4.0/components/navbar/ #} -{% macro menu(menuList) -%} - -{%- endmacro %} -{# table for dict list #} -{% macro table4DictList(dictList) -%} - - - {% if dictList %} - - - - {% for key in dictList[0] %} - - {% endfor %} - - - - - {% for dict_item in dictList %} - - - {% for value in dict_item.values() %} - - {% endfor %} - - {% endfor %} - - {% endif %} -
# {{ key }}
{{loop.index}} {{ value|safe }}
-{%- endmacro %} -{% macro loadDataTable() -%} - - -{%- endmacro %} -{% macro activateDataTable() -%} - -{%- endmacro %} \ No newline at end of file diff --git a/templates/server.html b/templates/server.html deleted file mode 100644 index 111068f..0000000 --- a/templates/server.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - {% if server.logo is not none %}{%endif %} - -
{{server.platform}} logo{{server.name}} logoWelcome to {{ server.name }} ({{ server.ip }}) {{ server.purpose }} -
-
diff --git a/templates/welcome.html b/templates/welcome.html deleted file mode 100644 index ab12474..0000000 --- a/templates/welcome.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends 'base.html' %} -{% import 'macros.html' as macros %} -{% block content %} -{% if server is not none %}{{ server.render() | safe }}{%endif %} -{% if title is not none %}

{{ title }}

{%endif %} -{{ macros.table4DictList(dictList) }} -{% endblock %} -{% block scripts %} - {{ super() }} - {{ macros.loadDataTable() }} - {{ macros.activateDataTable() }} -{% endblock %} \ No newline at end of file diff --git a/tests/test_family.py b/tests/test_family.py index edf0edd..68e0200 100644 --- a/tests/test_family.py +++ b/tests/test_family.py @@ -3,9 +3,9 @@ @author: wf ''' -import unittest from frontend.family import LocalWiki, WikiFamily from tests.basetest import Basetest + class TestFamily(Basetest): ''' test wiki family code @@ -45,9 +45,4 @@ def testGetSetting(self): lWiki.settingLines=['''$wgLogo = "/images/wgt/thumb/3/35/Heureka-wgt.png/132px-Heureka-wgt.png";'''] logo=lWiki.getSetting("wgLogo") self.assertTrue(logo.startswith("/images/wgt")) - pass - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file + pass \ No newline at end of file diff --git a/tests/test_frontend.py b/tests/test_frontend.py index d752c4f..2b2ff3f 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -6,7 +6,6 @@ import unittest from frontend.wikicms import Frontend from tests.test_webserver import TestWebServer -import getpass from tests.basetest import Basetest class TestFrontend(Basetest): diff --git a/tests/test_parser.py b/tests/test_parser.py index 3ebd593..39fb00c 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -59,8 +59,4 @@ def testLxml(self): ''' #parser=etree.HTMLParser() tree=etree.parse(StringIO(self.html)) - self.assertTrue(tree is not None) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file + self.assertTrue(tree is not None) \ No newline at end of file diff --git a/tests/test_server.py b/tests/test_server.py index f63497e..642f162 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -24,14 +24,9 @@ def testServer(self): server.platform='linux' logo=server.getPlatformLogo() if self.debug: - print("platform logo is %s " % logo) + print(f"platform logo is {logo}") print (server.uname) - print ("%s(%s)" % (server.hostname,server.ip)) + print (f"{server.hostname}({server.ip})") self.assertTrue("Tux" in logo) pass - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file diff --git a/tests/test_sql.py b/tests/test_sql.py deleted file mode 100644 index 515ed6f..0000000 --- a/tests/test_sql.py +++ /dev/null @@ -1,48 +0,0 @@ -''' -Created on 2021-01-10 - -@author: wf -''' -import unittest -from flask import Flask -from flask_sqlalchemy import SQLAlchemy -from frontend.server import Server -from tests.basetest import Basetest - -class TestSQL(Basetest): - ''' - test SQL access - ''' - - def setUp(self): - Basetest.setUp(self) - pass - - - def testSQL(self): - ''' - try out SQL access with SQL Alchemy - ''' - server=Server() - server.load() - app = Flask(__name__) - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - - if "credentials" in server.__dict__: - server.hostname="capri.bitplan.com" - cred=server.credentials[0] - user=cred["user"] - password=cred["password"] - dbname=cred["dbname"] - dburl=server.sqlGetDatabaseUrl(dbname,user,password) - - dbExists=server.sqlDatabaseExist(dburl) - self.assertTrue(dbExists) - app.config["SQLALCHEMY_DATABASE_URI"] = dburl - sqldb = SQLAlchemy(app) - pass - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file diff --git a/tests/test_webserver.py b/tests/test_webserver.py index 24a2f95..a562668 100644 --- a/tests/test_webserver.py +++ b/tests/test_webserver.py @@ -6,7 +6,6 @@ import unittest import warnings import os -from fb4.app import AppWrap from frontend.server import Server from tests.test_wikicms import TestWikiCMS import tempfile @@ -178,7 +177,3 @@ def test(): html,error=frontend.renderTemplate("test.html",msg="Hello world!") self.assertIsNone(error) self.assertTrue("Hello world!" in html) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() diff --git a/tests/test_wikicms.py b/tests/test_wikicms.py index 18043b3..affb479 100644 --- a/tests/test_wikicms.py +++ b/tests/test_wikicms.py @@ -71,9 +71,4 @@ def testWikiCMS(self): if self.debug: print(text) self.assertTrue("OpenResearch" in text) - pass - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file + pass \ No newline at end of file