diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7a456be..e294b1b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -41,7 +41,7 @@ jobs: - name: Build & export uses: docker/build-push-action@v3 with: - file: docker/Dockerfile + file: Dockerfile push: false tags: ${{ steps.meta.outputs.tags }} outputs: type=docker,dest=/tmp/wikmd.tar @@ -96,6 +96,10 @@ jobs: - name: Assert wikmd status run: curl -I localhost:5000 2>&1 | awk '/HTTP\// {print $2}' | grep -w "200\|301" + # Check that wikmd is rendering + - name: Check wikmd rendering status + run: curl -s localhost:5000 | grep -w "What is it?" + publish: # Publish if official repo and push to 'main' or new tag if: | @@ -116,6 +120,6 @@ jobs: - name: Publish uses: docker/build-push-action@v3 with: - file: docker/Dockerfile + file: Dockerfile push: true tags: ${{ needs.build.outputs.wikmd_tags }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1db1fe9..6ada4ba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,15 +21,16 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install .[dev] - name: Test with pytest run: | - python -m pytest -v + python -m pytest - name: Start wikmd - run: python wiki.py & - + run: | + cd src + python -m wikmd.wiki & + - name: screenshots-ci-action uses: flameddd/screenshots-ci-action@master with: diff --git a/.gitignore b/.gitignore index a793919..ce8f20e 100644 --- a/.gitignore +++ b/.gitignore @@ -96,15 +96,12 @@ dmypy.json # Cython debug symbols cython_debug/ +# Generated plugin files +src/wikmd/plugins/draw/drawings/* +!src/wikmd/plugins/draw/drawings/.gitkeep + + # Personal wiki pages wiki/* -wiki/src/* - -!wiki/Features.md -!wiki/How to use the wiki.md -!wiki/Markdown cheatsheet.md -!wiki/Using the version control system.md -!wiki/homepage.md -!wiki/src temp/* diff --git a/docker/README.md b/DOCKER.md similarity index 91% rename from docker/README.md rename to DOCKER.md index 49f276d..e4c1732 100644 --- a/docker/README.md +++ b/DOCKER.md @@ -2,10 +2,6 @@ [wikmd](https://github.com/Linbreux/wikmd) is a file based wiki that uses markdown. -This repo provides Docker files that are loosely based on those of the [linuxserver](https://www.linuxserver.io/) community. - -Docker files are available for arm, arm64 and amd64. - ## Usage Here are some example snippets to help you get started creating a container. @@ -13,7 +9,7 @@ Here are some example snippets to help you get started creating a container. Build the image, ```bash -docker build -t linbreux/wikmd:latest -f docker/Dockerfile . +docker build -t linbreux/wikmd:latest -f Dockerfile . ``` ### docker-compose (recommended, [click here for more info](https://docs.linuxserver.io/general/docker-compose)) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f1832e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +FROM python:3.9-alpine3.17 as python-base + +# Prevents Python from writing pyc files. +ENV PYTHONDONTWRITEBYTECODE=1 + +# Keeps Python from buffering stdout and stderr to avoid situations where +# the application crashes without emitting any logs due to buffering. +ENV PYTHONUNBUFFERED=1 + +# We will be installing venv +ENV VIRTUAL_ENV="/venv" + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/go/dockerfile-user-best-practices/ +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser + + +# Add project path to python path, this to ensure we can reach it from anywhere +WORKDIR /code +ENV PYTHONPATH="/code:$PYTHONPATH" + +# prepare the virtual env +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +RUN python -m venv $VIRTUAL_ENV + +# BUILDER +FROM python-base as python-builder + +# Install our dependencies +RUN apk update +RUN apk add git +RUN apk add pandoc +RUN apk add build-base linux-headers + +# Python dependencies +WORKDIR /code + +COPY pyproject.toml /code + +# Copy the py project and use a package to convert our pyproject.toml file into a requirements file +# We can not install the pyproject with pip as that would install the project and we only +# wants to install the project dependencies. +RUN python -m pip install toml-to-requirements==0.2.0 +RUN toml-to-req --toml-file pyproject.toml + +RUN python -m pip install --no-cache-dir --upgrade -r ./requirements.txt + +# Copy our source content over +COPY ./src/wikmd /code/wikmd + +# Change the directory to the root. +WORKDIR / + +# Expose the port that the application listens on. +EXPOSE 5000 + +# Run the application. +CMD ["python", "-m", "wikmd.wiki"] diff --git a/README.md b/README.md index fc58c08..69a947f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It’s a file-based wiki that aims to simplicity. Instead of storing the data in To view the documents in the browser, the document is converted to html. -![preview](static/images/readme-img.png) +![preview](src/wikmd/static/images/readme-img.png) ## Features @@ -31,6 +31,10 @@ To view the documents in the browser, the document is converted to html. Detailed installation instruction can be found [here](https://linbreux.github.io/wikmd/installation.html). +## Development + +Instructions on the easiest way to develop on the project can be found [here](https://linbreux.github.io/wikmd/development.html). + ## Plugins & Knowledge graph (beta) More info can be found in the [docs](https://linbreux.github.io/wikmd/knowledge%20graph.html). diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 0f57069..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM ghcr.io/linuxserver/baseimage-ubuntu:jammy - -COPY . /app/wikmd - -RUN \ - echo "**** install wikmd dependencies ****" && \ - apt-get update -y && \ - apt-get install -y python3-pip python3-dev pandoc git && \ - # echo "**** install wikmd ****" && \ - # WIKMD_RELEASE=$(curl -sX GET https://api.github.com/repos/Linbreux/wikmd/releases/latest \ - # | awk '/tag_name/{print $4;exit}' FS='[""]' | sed 's|^v||') && \ - # mkdir -p /app/wikmd && \ - # curl -o \ - # /tmp/wikmd.tar.gz -L \ - # https://github.com/Linbreux/wikmd/archive/master.tar.gz && \ - # #"https://github.com/Linbreux/wikmd/archive/refs/tags/v${WIKMD_RELEASE}.tar.gz" && \ - # tar xf /tmp/wikmd.tar.gz -C \ - # /app/wikmd --strip-components=1 && \ - # cp -R . /app/wikmd && \ - echo "**** install pip requirements ****" && \ - cd /app/wikmd && \ - pip3 install -r requirements.txt && \ - echo "**** cleanup ****" && \ - apt-get -y purge \ - python3-pip && \ - apt-get -y autoremove && \ - rm -rf \ - /tmp/* \ - /var/lib/apt/lists/* \ - /var/tmp/* \ - /root/.cache - -COPY docker/root/ / - -ENV LANG=C.UTF-8 -ENV HOME=/wiki - -# ports and volumes -EXPOSE 5000 diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 deleted file mode 100644 index bb7658e..0000000 --- a/docker/Dockerfile.aarch64 +++ /dev/null @@ -1,38 +0,0 @@ -FROM ghcr.io/linuxserver/baseimage-ubuntu:arm64v8-jammy - -COPY . /app/wikmd - -RUN \ - echo "**** install wikmd dependencies ****" && \ - apt-get update -y && \ - apt-get install -y python3-pip python3-dev pandoc git libxml2-dev libxslt1-dev && \ - # echo "**** install wikmd ****" && \ - # WIKMD_RELEASE=$(curl -sX GET https://api.github.com/repos/Linbreux/wikmd/releases/latest \ - # | awk '/tag_name/{print $4;exit}' FS='[""]' | sed 's|^v||') && \ - # mkdir -p /app/wikmd && \ - # curl -o \ - # /tmp/wikmd.tar.gz -L \ - # https://github.com/Linbreux/wikmd/archive/master.tar.gz && \ - # #"https://github.com/Linbreux/wikmd/archive/refs/tags/v${WIKMD_RELEASE}.tar.gz" && \ - # tar xf /tmp/wikmd.tar.gz -C \ - # /app/wikmd --strip-components=1 && \ - echo "**** install pip requirements ****" && \ - cd /app/wikmd && \ - pip3 install -r requirements.txt && \ - echo "**** cleanup ****" && \ - apt-get -y purge \ - python3-pip && \ - apt-get -y autoremove && \ - rm -rf \ - /tmp/* \ - /var/lib/apt/lists/* \ - /var/tmp/* \ - /root/.cache - -COPY docker/root/ / - -ENV LANG=C.UTF-8 -ENV HOME=/wiki - -# ports and volumes -EXPOSE 5000 diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf deleted file mode 100644 index b2ab826..0000000 --- a/docker/Dockerfile.armhf +++ /dev/null @@ -1,38 +0,0 @@ -FROM ghcr.io/linuxserver/baseimage-ubuntu:arm32v7-jammy - -COPY . /app/wikmd - -RUN \ - echo "**** install wikmd dependencies ****" && \ - apt-get update -y && \ - apt-get install -y python3-pip python3-dev pandoc git libxml2-dev libxslt1-dev && \ - # echo "**** install wikmd ****" && \ - # WIKMD_RELEASE=$(curl -sX GET https://api.github.com/repos/Linbreux/wikmd/releases/latest \ - # | awk '/tag_name/{print $4;exit}' FS='[""]' | sed 's|^v||') && \ - # mkdir -p /app/wikmd && \ - # curl -o \ - # /tmp/wikmd.tar.gz -L \ - # https://github.com/Linbreux/wikmd/archive/master.tar.gz && \ - # #"https://github.com/Linbreux/wikmd/archive/refs/tags/v${WIKMD_RELEASE}.tar.gz" && \ - # tar xf /tmp/wikmd.tar.gz -C \ - # /app/wikmd --strip-components=1 && \ - echo "**** install pip requirements ****" && \ - cd /app/wikmd && \ - pip3 install -r requirements.txt && \ - echo "**** cleanup ****" && \ - apt-get -y purge \ - python3-pip && \ - apt-get -y autoremove && \ - rm -rf \ - /tmp/* \ - /var/lib/apt/lists/* \ - /var/tmp/* \ - /root/.cache - -COPY docker/root/ / - -ENV LANG=C.UTF-8 -ENV HOME=/wiki - -# ports and volumes -EXPOSE 5000 diff --git a/docker/root/etc/cont-init.d/30-config b/docker/root/etc/cont-init.d/30-config deleted file mode 100644 index 91710bb..0000000 --- a/docker/root/etc/cont-init.d/30-config +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/with-contenv bash - -# Create log file -if [ ! -f "/var/log/wikmd.log" ] -then - touch /var/log/wikmd.log - chown abc:abc /var/log/wikmd.log - chmod 666 /var/log/wikmd.log -fi - -# if /wiki isn't mounted create it -if [ ! -d "/wiki" ] -then - # create directories - mkdir -p /wiki - chown -R abc:abc /wiki - chown -R abc:abc /wiki/* -fi - -# If /wiki exists and is empty, populate it with the examples -if [ -d "/wiki" ] && [ ! "$(ls -A /wiki)" ] -then - # copy examples - cp /app/wikmd/wiki/*.md /wiki/. - - # permissions - chown -R abc:abc /wiki/* - chown -R abc:abc /wiki -fi - -chown -R abc:abc /app/wikmd/plugins/* -chown -R abc:abc /app/wikmd/plugins -chown -R abc:abc /app/wikmd/static/* -chown -R abc:abc /app/wikmd/static diff --git a/docker/root/etc/services.d/wikmd/run b/docker/root/etc/services.d/wikmd/run deleted file mode 100644 index adead31..0000000 --- a/docker/root/etc/services.d/wikmd/run +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/with-contenv bash -export WIKI_DIRECTORY='/wiki' -export WIKMD_LOGGING_FILE='/var/log/wikmd.log' - -exec s6-setuidgid abc python3 /app/wikmd/wiki.py diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..e899a01 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,36 @@ +--- +layout: default +title: Development +parent: Installation +nav_order: 12 +--- + +# Regular installation +! It's tested on windows and linux based systems. +! Runs on flask server + +Clone the repository +``` +git clone https://github.com/Linbreux/wikmd.git +``` + +cd in wikmd +``` +cd wikmd +``` + +Create a virtual env and activate it (optional, but highly recommended) +``` +virtualenv venv +source venv/bin/activate +``` + +Install it in [development mode aka editable install](https://setuptools.pypa.io/en/latest/userguide/development_mode.html) +``` +python -m pip install .[dev] --editable +``` + +Run the wiki +``` +python -m wikmd.wiki +``` diff --git a/docs/installation/docker.md b/docs/installation/docker.md index 7cffd43..d55c9ca 100644 --- a/docs/installation/docker.md +++ b/docs/installation/docker.md @@ -5,14 +5,6 @@ parent: Installation nav_order: 2 --- -# wikmd Docker image - -[wikmd](https://github.com/Linbreux/wikmd) is a file based wiki that uses Markdown. - -This repo provides Docker files that are loosely based on those of the [linuxserver](https://www.linuxserver.io/) community. - -Docker files are available for various architectures, including arm (`Dockerfile.armhf`), arm64 (`Dockerfile.aarch64`), and amd64 (`Dockerfile`). - ## Usage Here are some example snippets to help you get started creating a container. @@ -27,7 +19,7 @@ Or, build the image after cloning the source code itself: ```bash git clone https://github.com/linbreux/wikmd.git && cd wikmd -docker build -t linbreux/wikmd:latest -f docker/Dockerfile . +docker build -t linbreux/wikmd:latest -f Dockerfile . ``` ### docker-compose (recommended, [click here for more info](https://docs.linuxserver.io/general/docker-compose)) @@ -69,16 +61,16 @@ docker run -d \ Container images are configured using parameters passed at runtime (such as those above). These parameters are separated by a colon and indicate `:` respectively. For example, `-p 5000:5000` would expose port `5000` from inside the container to be accessible from the host's IP on port `5000` outside the container. -| Parameter | Function | -| :----: | --- | -| `-p 5000` | Port for wikmd webinterface. | -| `-e PUID=1000` | for UserID - see below for explanation | -| `-e PGID=1000` | for GroupID - see below for explanation | -| `-e TZ=Europe/Paris` | Specify a timezone to use EG Europe/Paris | -| `-e HOMEPAGE=homepage.md` | Specify the file to use as a homepage | -| `-e HOMEPAGE_TITLE=title` | Specify the homepage's title | -| `-e WIKMD_LOGGING=1` | Enable/disable file logging | -| `-v /wiki` | Path to the file-based wiki. | +| Parameter | Function | +|:--------------------------|-------------------------------------------| +| `-p 5000` | Port for wikmd webinterface. | +| `-e PUID=1000` | for UserID - see below for explanation | +| `-e PGID=1000` | for GroupID - see below for explanation | +| `-e TZ=Europe/Paris` | Specify a timezone to use EG Europe/Paris | +| `-e HOMEPAGE=homepage.md` | Specify the file to use as a homepage | +| `-e HOMEPAGE_TITLE=title` | Specify the homepage's title | +| `-e WIKMD_LOGGING=1` | Enable/disable file logging | +| `-v /wiki` | Path to the file-based wiki. | ## User / Group Identifiers diff --git a/docs/installation/regular_install.md b/docs/installation/regular_install.md index 2d57f5f..b7e438d 100644 --- a/docs/installation/regular_install.md +++ b/docs/installation/regular_install.md @@ -18,23 +18,22 @@ cd in wikmd cd wikmd ``` -Create a virtual env and activate it(optional) +Create a virtual env and activate it (optional, but highly recommended) ``` virtualenv venv source venv/bin/activate ``` -Install requirements -``` -pip install -r requirements.txt -``` -Run the wiki + +Install ``` -export FLASK_APP=wiki.py -flask run --host=0.0.0.0 +python -m pip install . ``` -or + +Run the wiki, remember that all paths within wikmd are defined as relative. +That means that your current working directory will be the base of the project. +As such your current directory will hold the `wiki` directory that contains all md files. ``` -python wiki.py +python -m wikmd.wiki ``` Now visit localhost:5000 and you will see the wiki. With the 0.0.0.0. option it will show up everywhere on the network. @@ -47,7 +46,8 @@ Maybe you need to install pandoc on your system before this works. ``` sudo apt-get update && sudo apt-get install pandoc ``` -You may experience an issue when running `pip install -r requirements.txt` where you receive the following error: + +You may experience an issue when running `python -m pip install -r requirements.txt` where you receive the following error: ``` psutil/_psutil_common.c:9:10: fatal error: Python.h: No such file or directory 9 | #include @@ -70,7 +70,7 @@ sudo dnf install python3-devel ``` For other distros, you can search up `[distro] install python 3 dev`. -You may experience an error when running `pip install -r requirements.txt` where it asks you to install `gcc python3-dev`. Example: +You may experience an error when running `python pip install .` where it asks you to install `gcc python3-dev`. Example: ``` unable to execute 'x86_64-linux-gnu-gcc': No such file or directory C compiler or Python headers are not installed on this system. Try to run: @@ -97,8 +97,7 @@ After=network.target [Service] User= WorkingDirectory= -Environment=FLASK_APP=wiki.py -ExecStart=/env/bin/python3 wiki.py +ExecStart=/env/bin/python3 -m wikmd.wiki Restart=always [Install] @@ -125,7 +124,7 @@ To fix, run the following commands: sudo su cd ~ umask 022 -pip install -r /requirements.txt +pip install -r . ``` This will install the python packages system-wide, allowing the wiki service to access it. @@ -137,7 +136,7 @@ Run `systemctl restart wiki.service` and it should be working. You should install [pandoc](https://pandoc.org/installing.html) on your windows system. Now you should be able to start the server. ``` -python wiki.py +python -m wikmd.wiki ``` If the content of the markdown files are not visible you should add the `pandoc-xnos` location to your path variable. Info about [Environment variables](https://www.computerhope.com/issues/ch000549.htm). ``` diff --git a/plugins/draw/drawings/.gitignore b/plugins/draw/drawings/.gitignore deleted file mode 100644 index f59ec20..0000000 --- a/plugins/draw/drawings/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5b59b3c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,81 @@ +[project] +requires-python = ">= 3.8" +name = "wikmd" +description = "A file-based wiki that aims for simplicity." +authors = [ + { name = "linbreux" } +] + +version = "1.9.0" + +dependencies=[ + "Flask==3.0.2", + "GitPython==3.1.42", + "Markdown==3.5.2", + "PyYAML==6.0.1", + "Werkzeug==3.0.1", + "Whoosh==2.7.4", + "beautifulsoup4==4.12.3", + "pandoc-eqnos==2.5.0", + "pandoc-fignos==2.4.0", + "pandoc-secnos==2.2.2", + "pandoc-tablenos==2.3.0", + "pandoc-xnos==2.5.0", + "pandocfilters==1.5.1", + "pypandoc==1.13", + "requests==2.31.0", + "lxml==5.1.0", + "watchdog==2.1.9", + "cachelib==0.12.0", +] + +[project.optional-dependencies] +dev = [ + "pytest", +] + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +wikmd = [ + "wikmd-config.yaml", + "plugins/draw/default_draw", + "static/**/*", + "templates/**/*", + "wiki_template/**/*", +] + +[tool.pytest.ini_options] +pythonpath = [ + "src" +] + +[tool.ruff] +src = ["", "tests"] +select = ["ALL"] + +[tool.ruff.lint.per-file-ignores] +"tests/*" = [ + # S101: Check for assert + "S101", + + # ANN001: Missing type annotation for public function + "ANN001", + + # ANN201: Missing return type annotation for public function + "ANN201", + + # D100: Missing docstring in public module + "D100", + + # D103: Missing docstring in public function + "D103", + + # PLR2004: Magic numbers + "PLR2004", +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7b25c49..0000000 --- a/requirements.txt +++ /dev/null @@ -1,26 +0,0 @@ -Flask==3.0.2 -GitPython==3.1.42 -itsdangerous==2.1.2 -Jinja2==3.1.3 -Markdown==3.5.2 -MarkupSafe==2.1.5 -PyYAML==6.0.1 -Werkzeug==3.0.1 -Whoosh==2.7.4 -beautifulsoup4==4.12.3 -click==8.1.7 -gitdb==4.0.11 -idna==3.6 -pandoc-eqnos==2.5.0 -pandoc-fignos==2.4.0 -pandoc-secnos==2.2.2 -pandoc-tablenos==2.3.0 -pandoc-xnos==2.5.0 -pandocfilters==1.5.1 -pypandoc==1.13 -requests==2.31.0 -smmap==5.0.1 -lxml==5.1.0 -watchdog==2.1.9 -cachelib==0.12.0 -psutil==5.9.8 diff --git a/__init__.py b/src/wikmd/__init__.py similarity index 100% rename from __init__.py rename to src/wikmd/__init__.py diff --git a/cache.py b/src/wikmd/cache.py similarity index 100% rename from cache.py rename to src/wikmd/cache.py diff --git a/config.py b/src/wikmd/config.py similarity index 99% rename from config.py rename to src/wikmd/config.py index 83b4363..0d50e53 100644 --- a/config.py +++ b/src/wikmd/config.py @@ -1,4 +1,5 @@ import os + import yaml WIKMD_CONFIG_FILE = "wikmd-config.yaml" diff --git a/git_manager.py b/src/wikmd/git_manager.py similarity index 96% rename from git_manager.py rename to src/wikmd/git_manager.py index 2590e27..d832757 100644 --- a/git_manager.py +++ b/src/wikmd/git_manager.py @@ -1,13 +1,11 @@ -import os import datetime - +import os from typing import Optional -from flask import Flask -from git import Repo, InvalidGitRepositoryError, GitCommandError, NoSuchPathError - -from config import WikmdConfig -from utils import move_all_files +from flask import Flask +from git import GitCommandError, InvalidGitRepositoryError, NoSuchPathError, Repo +from wikmd.config import WikmdConfig +from wikmd.utils import move_all_files TEMP_DIR = "temp" @@ -37,12 +35,14 @@ def __init__(self, flask_app: Flask): self.wiki_directory = cfg.wiki_directory self.sync_with_remote = cfg.sync_with_remote - if not os.path.exists(self.wiki_directory): - os.mkdir(self.wiki_directory) self.remote_url = cfg.remote_url - self.repo: Optional[Repo] = None + + def initialize(self): + if not os.path.exists(self.wiki_directory): + self.flask_app.logger.warning("wiki directory doesn't exist") + return self.__git_repo_init() def __git_repo_init(self): diff --git a/image_manager.py b/src/wikmd/image_manager.py similarity index 98% rename from image_manager.py rename to src/wikmd/image_manager.py index 10aa385..2e55d64 100644 --- a/image_manager.py +++ b/src/wikmd/image_manager.py @@ -5,7 +5,7 @@ from base64 import b32encode from hashlib import sha1 -from werkzeug.utils import secure_filename, safe_join +from werkzeug.utils import safe_join, secure_filename class ImageManager: @@ -64,7 +64,7 @@ def cleanup_images(self): """Deletes images not used by any page""" saved_images = set(os.listdir(self.images_path)) # Don't delete .gitignore - saved_images.discard(".gitignore") + saved_images.discard(".gitkeep") # Matches [*](/img/*) it does not matter if images_route is "/img" or "img" image_link_pattern = fr"\[(.*?)\]\(({os.path.join('/', self.cfg.images_route)}.+?)\)" diff --git a/knowledge_graph.py b/src/wikmd/knowledge_graph.py similarity index 98% rename from knowledge_graph.py rename to src/wikmd/knowledge_graph.py index af24e1d..4b6d9ce 100644 --- a/knowledge_graph.py +++ b/src/wikmd/knowledge_graph.py @@ -2,7 +2,7 @@ import re from urllib.parse import unquote -from config import WikmdConfig +from wikmd.config import WikmdConfig cfg = WikmdConfig() diff --git a/plugins/__init__.py b/src/wikmd/plugins/__init__.py similarity index 100% rename from plugins/__init__.py rename to src/wikmd/plugins/__init__.py diff --git a/plugins/alerts/__init__.py b/src/wikmd/plugins/alerts/__init__.py similarity index 100% rename from plugins/alerts/__init__.py rename to src/wikmd/plugins/alerts/__init__.py diff --git a/plugins/alerts/alerts.py b/src/wikmd/plugins/alerts/alerts.py similarity index 97% rename from plugins/alerts/alerts.py rename to src/wikmd/plugins/alerts/alerts.py index b978503..0cbfff4 100644 --- a/plugins/alerts/alerts.py +++ b/src/wikmd/plugins/alerts/alerts.py @@ -1,9 +1,9 @@ -import uuid import os import re -import shutil + from flask import Flask -from config import WikmdConfig +from wikmd.config import WikmdConfig + class Plugin: def __init__(self, flask_app: Flask, config: WikmdConfig, web_dep ): diff --git a/plugins/draw/__init__.py b/src/wikmd/plugins/draw/__init__.py similarity index 100% rename from plugins/draw/__init__.py rename to src/wikmd/plugins/draw/__init__.py diff --git a/plugins/draw/default_draw b/src/wikmd/plugins/draw/default_draw similarity index 100% rename from plugins/draw/default_draw rename to src/wikmd/plugins/draw/default_draw diff --git a/plugins/draw/draw.py b/src/wikmd/plugins/draw/draw.py similarity index 91% rename from plugins/draw/draw.py rename to src/wikmd/plugins/draw/draw.py index 49b8372..b2e6f1d 100644 --- a/plugins/draw/draw.py +++ b/src/wikmd/plugins/draw/draw.py @@ -1,9 +1,11 @@ -import uuid import os import re import shutil +import uuid + from flask import Flask -from config import WikmdConfig +from wikmd.config import WikmdConfig + class Plugin: def import_head(self): @@ -45,7 +47,7 @@ def communicate_plugin(self, request): self.flask_app.logger.info(f"Plug/{self.name} - changing drawing {id}") # look for folder - location = os.path.join(os.path.dirname(__file__),"drawings", id) + location = os.path.join(os.path.dirname(__file__), "drawings", id) if os.path.exists(location): file = open(location, "w") file.write(image) @@ -58,7 +60,7 @@ def look_for_existing_drawid(self, drawid: str) -> str: look for a drawId in the wiki/draw folder and return the file as a string """ try: - file = open(os.path.join(self.this_location,"drawings",drawid),"r") + file = open(os.path.join(self.this_location, "drawings", drawid), "r") return file.read() except Exception: print("Did not find the file") @@ -68,7 +70,7 @@ def create_draw_file(self, filename: str) -> None: """ Copy the default drawing to a new one with this filename """ - path_to_file = os.path.join(self.this_location,"drawings",filename) + path_to_file = os.path.join(self.this_location, "drawings", filename) shutil.copyfile(os.path.join(self.this_location, "default_draw"), path_to_file) s = open(path_to_file,"r") result = re.sub("id=\"\"","id=\"" + filename + "\"",s.read()) diff --git a/plugins/embed-pages/__init__.py b/src/wikmd/plugins/draw/drawings/.gitkeep similarity index 100% rename from plugins/embed-pages/__init__.py rename to src/wikmd/plugins/draw/drawings/.gitkeep diff --git a/plugins/mermaid/__init__.py b/src/wikmd/plugins/embed-pages/__init__.py similarity index 100% rename from plugins/mermaid/__init__.py rename to src/wikmd/plugins/embed-pages/__init__.py diff --git a/plugins/embed-pages/embed-pages.py b/src/wikmd/plugins/embed-pages/embed-pages.py similarity index 96% rename from plugins/embed-pages/embed-pages.py rename to src/wikmd/plugins/embed-pages/embed-pages.py index b4e2ae0..3af63a5 100644 --- a/plugins/embed-pages/embed-pages.py +++ b/src/wikmd/plugins/embed-pages/embed-pages.py @@ -1,9 +1,9 @@ -import uuid import os import re -import shutil + from flask import Flask -from config import WikmdConfig +from wikmd.config import WikmdConfig + class Plugin: def __init__(self, flask_app: Flask, config: WikmdConfig, web_dep): diff --git a/plugins/load_plugins.py b/src/wikmd/plugins/load_plugins.py similarity index 80% rename from plugins/load_plugins.py rename to src/wikmd/plugins/load_plugins.py index a8376d7..80ac3bd 100644 --- a/plugins/load_plugins.py +++ b/src/wikmd/plugins/load_plugins.py @@ -1,6 +1,7 @@ import importlib + from flask import Flask -from config import WikmdConfig +from wikmd.config import WikmdConfig class PluginLoader(): @@ -14,7 +15,7 @@ def __init__(self, flask_app: Flask, config: WikmdConfig, web_deps= None, plugin if plugins != []: # create a list of plugins self._plugins = [ - importlib.import_module(f"plugins.{plugin}.{plugin}",".").Plugin(flask_app, config, web_deps) for plugin in plugins + importlib.import_module(f"wikmd.plugins.{plugin}.{plugin}",".").Plugin(flask_app, config, web_deps) for plugin in plugins ] else: self._plugins = [] diff --git a/static/js/.empty b/src/wikmd/plugins/mermaid/__init__.py similarity index 100% rename from static/js/.empty rename to src/wikmd/plugins/mermaid/__init__.py diff --git a/plugins/mermaid/mermaid.py b/src/wikmd/plugins/mermaid/mermaid.py similarity index 90% rename from plugins/mermaid/mermaid.py rename to src/wikmd/plugins/mermaid/mermaid.py index cf82130..f92dd91 100644 --- a/plugins/mermaid/mermaid.py +++ b/src/wikmd/plugins/mermaid/mermaid.py @@ -1,10 +1,6 @@ -import uuid import os -import re -import shutil from flask import Flask -from config import WikmdConfig -from web_dependencies import WEB_DEPENDENCIES +from wikmd.config import WikmdConfig injected_script = """ diff --git a/templates/list_files.html b/src/wikmd/templates/list_files.html similarity index 100% rename from templates/list_files.html rename to src/wikmd/templates/list_files.html diff --git a/templates/login.html b/src/wikmd/templates/login.html similarity index 95% rename from templates/login.html rename to src/wikmd/templates/login.html index 670edf2..f572d81 100644 --- a/templates/login.html +++ b/src/wikmd/templates/login.html @@ -1,4 +1,4 @@ -{% extends 'base.html'%} +{% extends 'base.html' %} {%block content%} diff --git a/templates/new.html b/src/wikmd/templates/new.html similarity index 94% rename from templates/new.html rename to src/wikmd/templates/new.html index d457bf8..7c60179 100644 --- a/templates/new.html +++ b/src/wikmd/templates/new.html @@ -4,12 +4,12 @@ {%block head%} - + {% if system.darktheme == True %} - - + + {% endif %} {%endblock%} diff --git a/templates/search.html b/src/wikmd/templates/search.html similarity index 100% rename from templates/search.html rename to src/wikmd/templates/search.html diff --git a/utils.py b/src/wikmd/utils.py similarity index 59% rename from utils.py rename to src/wikmd/utils.py index c63ed4b..283fae0 100644 --- a/utils.py +++ b/src/wikmd/utils.py @@ -1,5 +1,38 @@ import os +import unicodedata +_windows_device_files = { + "CON", + "PRN", + "AUX", + "NUL", + *(f"COM{i}" for i in range(10)), + *(f"LPT{i}" for i in range(10)), +} + + +def secure_filename(filename: str) -> str: + """Convert your filename to be safe for the os. + + Function from werkzeug. Changed to allow space in the file name. + """ + filename = unicodedata.normalize("NFKD", filename) + filename = filename.encode("ascii", "ignore").decode("ascii") + for sep in os.sep, os.path.altsep: + if sep: + filename = filename.replace(sep, "_") + filename = filename.strip("._") + # on nt a couple of special files are present in each folder. We + # have to ensure that the target file is not such a filename. In + # this case we prepend an underline + if ( + os.name == "nt" + and filename + and filename.split(".")[0].upper() in _windows_device_files + ): + filename = f"_{filename}" + + return filename def pathify(path1, path2): """ diff --git a/web_dependencies.py b/src/wikmd/web_dependencies.py similarity index 100% rename from web_dependencies.py rename to src/wikmd/web_dependencies.py diff --git a/wiki.py b/src/wikmd/wiki.py similarity index 90% rename from wiki.py rename to src/wikmd/wiki.py index b5435c3..8e347c8 100644 --- a/wiki.py +++ b/src/wikmd/wiki.py @@ -1,28 +1,37 @@ +import logging import os -from os.path import exists +import secrets import time -import logging import uuid -from lxml.html.clean import clean_html -import pypandoc -import knowledge_graph -import secrets - -from flask import Flask, render_template, request, redirect, url_for, make_response, send_file, \ - send_from_directory, flash - -from werkzeug.utils import safe_join, secure_filename -from threading import Thread from hashlib import sha256 -from cache import Cache -from image_manager import ImageManager -from config import WikmdConfig -from git_manager import WikiRepoManager -from search import Search, Watchdog -from web_dependencies import get_web_deps -from plugins.load_plugins import PluginLoader +from os.path import exists +from threading import Thread +import shutil +from pathlib import Path -from utils import pathify +import pypandoc +from flask import ( + Flask, + flash, + make_response, + redirect, + render_template, + request, + send_file, + send_from_directory, + url_for, +) +from lxml.html.clean import clean_html +from werkzeug.utils import safe_join +from wikmd import knowledge_graph +from wikmd.cache import Cache +from wikmd.config import WikmdConfig +from wikmd.git_manager import WikiRepoManager +from wikmd.image_manager import ImageManager +from wikmd.plugins.load_plugins import PluginLoader +from wikmd.search import Search, Watchdog +from wikmd.utils import pathify, secure_filename +from wikmd.web_dependencies import get_web_deps SESSIONS = [] @@ -34,7 +43,11 @@ HOMEPAGE_PATH = pathify(cfg.wiki_directory, cfg.homepage) HIDDEN_PATHS = tuple([UPLOAD_FOLDER_PATH, GIT_FOLDER_PATH, HOMEPAGE_PATH] + HIDDEN_FOLDER_PATH_LIST) -app = Flask(__name__) +_project_folder = Path(__file__).parent +app = Flask(__name__, + template_folder=_project_folder / "templates", + static_folder=_project_folder / "static") + app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER_PATH app.config['SECRET_KEY'] = cfg.secret_key @@ -389,11 +402,11 @@ def edit(page): with open(filename, 'r', encoding="utf-8", errors='ignore') as f: content = f.read() return render_template("new.html", content=content, title=page, upload_path=cfg.images_route, - image_allowed_mime=cfg.image_allowed_mime, system=SYSTEM_SETTINGS) + image_allowed_mime=cfg.image_allowed_mime, system=SYSTEM_SETTINGS) else: logger.error(f"{filename} does not exists. Creating a new one.") return render_template("new.html", content="", title=page, upload_path=cfg.images_route, - image_allowed_mime=cfg.image_allowed_mime, system=SYSTEM_SETTINGS) + image_allowed_mime=cfg.image_allowed_mime, system=SYSTEM_SETTINGS) @app.route(os.path.join("/", cfg.images_route), methods=['POST', 'DELETE']) @@ -461,11 +474,11 @@ def nav_id_to_page(id): return redirect("/") -@app.route(os.path.join("/", cfg.images_route, "")) +@app.route(f"/{cfg.images_route}/") def display_image(image_name): image_path = safe_join(UPLOAD_FOLDER_PATH, image_name) try: - response = send_file(image_path) + response = send_file(Path(image_path).resolve()) except Exception: app.logger.error(f"Could not find image: {image_path}") return "" @@ -515,17 +528,34 @@ def setup_search(): search.index_all(cfg.wiki_directory, items) -def run_wiki(): - """ - Function that runs the wiki as a Flask app. - """ +def setup_wiki_template() -> bool: + """Copy wiki_template files into the wiki directory if it's empty.""" + root = Path(__file__).parent + + if not os.path.exists(cfg.wiki_directory): + app.logger.info("Wiki directory doesn't exists, copy template") + shutil.copytree(root / "wiki_template", cfg.wiki_directory) + return True + if len(os.listdir(cfg.wiki_directory)) == 0: + app.logger.info("Wiki directory is empty, copy template") + shutil.copytree(root / "wiki_template", cfg.wiki_directory, dirs_exist_ok=True) + return True + return False + + +def run_wiki() -> None: + """Run the wiki as a Flask app.""" + app.logger.info("Starting Wikmd with wiki directory %s", Path(cfg.wiki_directory).resolve()) if int(cfg.wikmd_logging) == 1: logging.basicConfig(filename=cfg.wikmd_logging_file, level=logging.INFO) + setup_wiki_template() + if not os.path.exists(UPLOAD_FOLDER_PATH): app.logger.info(f"Creating upload folder >>> {UPLOAD_FOLDER_PATH}") os.mkdir(UPLOAD_FOLDER_PATH) + wrm.initialize() im.cleanup_images() setup_search() app.logger.info("Spawning search indexer watchdog") @@ -533,6 +563,7 @@ def run_wiki(): watchdog.start() app.run(host=cfg.wikmd_host, port=cfg.wikmd_port, debug=True, use_reloader=False) + for plugin in plugins: if "request_html" in dir(plugin): plugin.request_html(get_html) diff --git a/wiki/Features.md b/src/wikmd/wiki_template/Features.md similarity index 94% rename from wiki/Features.md rename to src/wikmd/wiki_template/Features.md index fde4b21..47c389c 100644 --- a/wiki/Features.md +++ b/src/wikmd/wiki_template/Features.md @@ -1,109 +1,109 @@ ---- -title: Features -author: Linbreux ---- - - -# Footnotes -``` -Here is a footnote reference,[^1] and another. - -[^1]: Here is the footnote. -``` -Here is a footnote reference,[^1] and another. - -[^1]: Here is the footnote. - -``` -Here is an inline note.^[Inlines notes are easier to write, since -you don't have to pick an identifier and move down to type the -note.] -``` - -Here is an inline note.^[Inlines notes are easier to write, since -you don't have to pick an identifier and move down to type the -note.] - -``` -(@good) This is a good example. - -As (@good) illustrates, ... - -``` - -(@good) This is a good example. - -As (@good) illustrates, ... - -# Split lists - -``` -1. one -2. two -3. three - - - -1. uno -2. dos -3. tres -``` -1. one -2. two -3. three - - - - -1. uno -2. dos -3. tres - - -## Change image size -``` -![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} -``` -![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} - -## References - -For references we use [pandoc-xnos](https://github.com/tomduck/pandoc-xnos) - -### Images - -``` -![This is a landscape](https://i.ibb.co/Dzp0SfC/download.jpg){#fig:id width="50%"} - -As show in @fig:id theire is a nice landscape -``` -![This is a landscape](https://i.ibb.co/Dzp0SfC/download.jpg){#fig:landscape width="50%"} - - -As show in @fig:landscape theire is a nice landscape - - -## Math -``` -$y = mx + b$ {#eq:id} - -This is visible in @eq:id -``` -$y = mx + b$ {#eq:id} - -This is visible in @eq:id - -## Etc - -This is also possible for tables and sections. Same princip but with - -``` -{#tbl:id} (for tables) -{#sec:2} (for sections) -``` - -# Pandoc - -All default pandoc features are supported with the extend of mathjax and pandoc-xnos. -![caption](/img/3a2ce07d2109eb82f779f71748be8990.webp) +--- +title: Features +author: Linbreux +--- + + +# Footnotes +``` +Here is a footnote reference,[^1] and another. + +[^1]: Here is the footnote. +``` +Here is a footnote reference,[^1] and another. + +[^1]: Here is the footnote. + +``` +Here is an inline note.^[Inlines notes are easier to write, since +you don't have to pick an identifier and move down to type the +note.] +``` + +Here is an inline note.^[Inlines notes are easier to write, since +you don't have to pick an identifier and move down to type the +note.] + +``` +(@good) This is a good example. + +As (@good) illustrates, ... + +``` + +(@good) This is a good example. + +As (@good) illustrates, ... + +# Split lists + +``` +1. one +2. two +3. three + + + +1. uno +2. dos +3. tres +``` +1. one +2. two +3. three + + + + +1. uno +2. dos +3. tres + + +## Change image size +``` +![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} +``` +![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} + +## References + +For references we use [pandoc-xnos](https://github.com/tomduck/pandoc-xnos) + +### Images + +``` +![This is a landscape](https://i.ibb.co/Dzp0SfC/download.jpg){#fig:id width="50%"} + +As show in @fig:id theire is a nice landscape +``` +![This is a landscape](https://i.ibb.co/Dzp0SfC/download.jpg){#fig:landscape width="50%"} + + +As show in @fig:landscape theire is a nice landscape + + +## Math +``` +$y = mx + b$ {#eq:id} + +This is visible in @eq:id +``` +$y = mx + b$ {#eq:id} + +This is visible in @eq:id + +## Etc + +This is also possible for tables and sections. Same princip but with + +``` +{#tbl:id} (for tables) +{#sec:2} (for sections) +``` + +# Pandoc + +All default pandoc features are supported with the extend of mathjax and pandoc-xnos. +![caption](/img/3a2ce07d2109eb82f779f71748be8990.webp) ![caption](/img/pixil-frame-07165101.png) \ No newline at end of file diff --git a/wiki/How to use the wiki.md b/src/wikmd/wiki_template/How to use the wiki.md similarity index 96% rename from wiki/How to use the wiki.md rename to src/wikmd/wiki_template/How to use the wiki.md index 3d531ee..f911c4e 100644 --- a/wiki/How to use the wiki.md +++ b/src/wikmd/wiki_template/How to use the wiki.md @@ -1,94 +1,94 @@ -## Homepage - -The homepage is default the `homepage.md` file, this can't be changed. If this file doesn't exist create it in de wiki folder. - -## Plugins - -The plugins are used to extend the functionality of the wiki. Most of them are accessible through the use of `tags`. -For now there are only a few supported. - -- `[[draw]]` Allows you to add an **interactive drawio drawing** to the wiki. -- `[[info]]`, `[[warning]]`, `[[danger]]`, `[[success]]` Adds a nice **alert message**. -- `[[ page: some-page ]]` Allows to show an other page in the current one. - -[[success]] You are ready to go! - -## Latex - -It's possible to use latex syntax inside your markdown because the markdown is first converted to latex and after that to html. This means you have a lot more flexibility. - -### Change image size -``` -![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} -``` -![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} - -### Image references -``` -![\label{test}](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} - -Inside picture \ref{landscape picture} you can see a nice mountain. - -``` -![picture \label{landscape picture}](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} - -Clickable reference in picture \ref{landscape picture}. - -### Math -``` -\begin{align} -y(x) &= \int_0^\infty x^{2n} e^{-a x^2}\,dx\\ -&= \frac{2n-1}{2a} \int_0^\infty x^{2(n-1)} e^{-a x^2}\,dx\\ -&= \frac{(2n-1)!!}{2^{n+1}} \sqrt{\frac{\pi}{a^{2n+1}}}\\ -&= \frac{(2n)!}{n! 2^{2n+1}} \sqrt{\frac{\pi}{a^{2n+1}}} -\end{align} -``` -\begin{align} -y(x) &= \int_0^\infty x^{2n} e^{-a x^2}\,dx\\ -&= \frac{2n-1}{2a} \int_0^\infty x^{2(n-1)} e^{-a x^2}\,dx\\ -&= \frac{(2n-1)!!}{2^{n+1}} \sqrt{\frac{\pi}{a^{2n+1}}}\\ -&= \frac{(2n)!}{n! 2^{2n+1}} \sqrt{\frac{\pi}{a^{2n+1}}} -\end{align} - -``` -You can also use $inline$ math to show $a=2$ and $b=8$ -``` -You can also use $inline$ math to show $a=2$ and $b=8$ - -And many other latex functions. - -## Converting the files - -Open the wiki folder of your instance. - -|- static -|- templates -|- **wiki** $\leftarrow$ This folder -|- wiki.py - -In this folder all the markdownfiles are listed. Editing the files will be visible in the web-version. - -|- homepage.md -|- How to use the wiki.md -|- Markdown cheatsheet.md - -The advantage is that u can use the commandline to process some data. For example using pandoc: -``` -$ pandoc -f markdown -t latex homepage.md How\ to\ use\ the\ wiki.md -o file.pdf --pdf-engine=xelatex -``` -This creates a nice pdf version of your article. Its possible you have to create a yml header on top of your document to set the margins etc better -``` ---- -title: titlepage -author: your name -date: 05-11-2020 -geometry: margin=2.5cm -header-includes: | - \usepackage{caption} - \usepackage{subcaption} -lof: true ---- -``` -For more information you have to read the pandoc documentation. - -[Using the version control system](/Using the version control system) +## Homepage + +The homepage is default the `homepage.md` file, this can't be changed. If this file doesn't exist create it in de wiki folder. + +## Plugins + +The plugins are used to extend the functionality of the wiki. Most of them are accessible through the use of `tags`. +For now there are only a few supported. + +- `[[draw]]` Allows you to add an **interactive drawio drawing** to the wiki. +- `[[info]]`, `[[warning]]`, `[[danger]]`, `[[success]]` Adds a nice **alert message**. +- `[[ page: some-page ]]` Allows to show an other page in the current one. + +[[success]] You are ready to go! + +## Latex + +It's possible to use latex syntax inside your markdown because the markdown is first converted to latex and after that to html. This means you have a lot more flexibility. + +### Change image size +``` +![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} +``` +![](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} + +### Image references +``` +![\label{test}](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} + +Inside picture \ref{landscape picture} you can see a nice mountain. + +``` +![picture \label{landscape picture}](https://i.ibb.co/Dzp0SfC/download.jpg){width="50%"} + +Clickable reference in picture \ref{landscape picture}. + +### Math +``` +\begin{align} +y(x) &= \int_0^\infty x^{2n} e^{-a x^2}\,dx\\ +&= \frac{2n-1}{2a} \int_0^\infty x^{2(n-1)} e^{-a x^2}\,dx\\ +&= \frac{(2n-1)!!}{2^{n+1}} \sqrt{\frac{\pi}{a^{2n+1}}}\\ +&= \frac{(2n)!}{n! 2^{2n+1}} \sqrt{\frac{\pi}{a^{2n+1}}} +\end{align} +``` +\begin{align} +y(x) &= \int_0^\infty x^{2n} e^{-a x^2}\,dx\\ +&= \frac{2n-1}{2a} \int_0^\infty x^{2(n-1)} e^{-a x^2}\,dx\\ +&= \frac{(2n-1)!!}{2^{n+1}} \sqrt{\frac{\pi}{a^{2n+1}}}\\ +&= \frac{(2n)!}{n! 2^{2n+1}} \sqrt{\frac{\pi}{a^{2n+1}}} +\end{align} + +``` +You can also use $inline$ math to show $a=2$ and $b=8$ +``` +You can also use $inline$ math to show $a=2$ and $b=8$ + +And many other latex functions. + +## Converting the files + +Open the wiki folder of your instance. + +|- static +|- templates +|- **wiki** $\leftarrow$ This folder +|- wiki.py + +In this folder all the markdownfiles are listed. Editing the files will be visible in the web-version. + +|- homepage.md +|- How to use the wiki.md +|- Markdown cheatsheet.md + +The advantage is that u can use the commandline to process some data. For example using pandoc: +``` +$ pandoc -f markdown -t latex homepage.md How\ to\ use\ the\ wiki.md -o file.pdf --pdf-engine=xelatex +``` +This creates a nice pdf version of your article. Its possible you have to create a yml header on top of your document to set the margins etc better +``` +--- +title: titlepage +author: your name +date: 05-11-2020 +geometry: margin=2.5cm +header-includes: | + \usepackage{caption} + \usepackage{subcaption} +lof: true +--- +``` +For more information you have to read the pandoc documentation. + +[Using the version control system](/Using the version control system) diff --git a/wiki/Markdown cheatsheet.md b/src/wikmd/wiki_template/Markdown cheatsheet.md similarity index 95% rename from wiki/Markdown cheatsheet.md rename to src/wikmd/wiki_template/Markdown cheatsheet.md index 1f576b6..a62d290 100644 --- a/wiki/Markdown cheatsheet.md +++ b/src/wikmd/wiki_template/Markdown cheatsheet.md @@ -1,243 +1,243 @@ -# Heading 1 # - - Markup : # Heading 1 # - - -OR- - - Markup : ============= (below H1 text) - -## Heading 2 ## - - Markup : ## Heading 2 ## - - -OR- - - Markup: --------------- (below H2 text) - -### Heading 3 ### - - Markup : ### Heading 3 ### - -#### Heading 4 #### - - Markup : #### Heading 4 #### - - -Common text - - Markup : Common text - -_Emphasized text_ - - Markup : _Emphasized text_ or *Emphasized text* - -~~Strikethrough text~~ - - Markup : ~~Strikethrough text~~ - -__Strong text__ - - Markup : __Strong text__ or **Strong text** - -___Strong emphasized text___ - - Markup : ___Strong emphasized text___ or ***Strong emphasized text*** - -[Named Link](http://www.google.fr/ "Named link title") and http://www.google.fr/ or - - Markup : [Named Link](http://www.google.fr/ "Named link title") and http://www.google.fr/ or - -[heading-1](#heading-1 "Goto heading-1") - - Markup: [heading-1](#heading-1 "Goto heading-1") - -Table, like this one : - -First Header | Second Header -------------- | ------------- -Content Cell | Content Cell -Content Cell | Content Cell - -``` -First Header | Second Header -------------- | ------------- -Content Cell | Content Cell -Content Cell | Content Cell -``` - -Adding a pipe `|` in a cell : - -First Header | Second Header -------------- | ------------- -Content Cell | Content Cell -Content Cell | \| - -``` -First Header | Second Header -------------- | ------------- -Content Cell | Content Cell -Content Cell | \| -``` - -Left, right and center aligned table - -Left aligned Header | Right aligned Header | Center aligned Header -| :--- | ---: | :---: -Content Cell | Content Cell | Content Cell -Content Cell | Content Cell | Content Cell - -``` -Left aligned Header | Right aligned Header | Center aligned Header -| :--- | ---: | :---: -Content Cell | Content Cell | Content Cell -Content Cell | Content Cell | Content Cell -``` - -`code()` - - Markup : `code()` - -```javascript - var specificLanguage_code = - { - "data": { - "lookedUpPlatform": 1, - "query": "Kasabian+Test+Transmission", - "lookedUpItem": { - "name": "Test Transmission", - "artist": "Kasabian", - "album": "Kasabian", - "picture": null, - "link": "http://open.spotify.com/track/5jhJur5n4fasblLSCOcrTp" - } - } - } -``` - - Markup : ```javascript - ``` - -* Bullet list - * Nested bullet - * Sub-nested bullet etc -* Bullet list item 2 - -~~~ - Markup : * Bullet list - * Nested bullet - * Sub-nested bullet etc - * Bullet list item 2 - --OR- - - Markup : - Bullet list - - Nested bullet - - Sub-nested bullet etc - - Bullet list item 2 -~~~ - -1. A numbered list - 1. A nested numbered list - 2. Which is numbered -2. Which is numbered - -~~~ - Markup : 1. A numbered list - 1. A nested numbered list - 2. Which is numbered - 2. Which is numbered -~~~ - -- [ ] An uncompleted task -- [x] A completed task - -~~~ - Markup : - [ ] An uncompleted task - - [x] A completed task -~~~ - -- [ ] An uncompleted task - - [ ] A subtask - -~~~ - Markup : - [ ] An uncompleted task - - [ ] A subtask -~~~ - -> Blockquote ->> Nested blockquote - - Markup : > Blockquote - >> Nested Blockquote - -_Horizontal line :_ -- - - - - - Markup : - - - - - -_Image with alt :_ - -![picture alt](http://via.placeholder.com/200x150 "Title is optional") - - Markup : ![picture alt](http://via.placeholder.com/200x150 "Title is optional") - -Foldable text: - -
- Title 1 -

Content 1 Content 1 Content 1 Content 1 Content 1

-
-
- Title 2 -

Content 2 Content 2 Content 2 Content 2 Content 2

-
- - Markup :
- Title 1 -

Content 1 Content 1 Content 1 Content 1 Content 1

-
- -```html -

HTML

-

Some HTML code here

-``` - -Link to a specific part of the page: - -[Go To TOP](#TOP) - - Markup : [text goes here](#section_name) - section_title - -Hotkey: - -⌘F - -⇧⌘F - - Markup : ⌘F - -Hotkey list: - -| Key | Symbol | -| --- | --- | -| Option | ⌥ | -| Control | ⌃ | -| Command | ⌘ | -| Shift | ⇧ | -| Caps Lock | ⇪ | -| Tab | ⇥ | -| Esc | ⎋ | -| Power | ⌽ | -| Return | ↩ | -| Delete | ⌫ | -| Up | ↑ | -| Down | ↓ | -| Left | ← | -| Right | → | - -Emoji: - -:exclamation: Use emoji icons to enhance text. :+1: Look up emoji codes at [emoji-cheat-sheet.com](http://emoji-cheat-sheet.com/) - +# Heading 1 # + + Markup : # Heading 1 # + + -OR- + + Markup : ============= (below H1 text) + +## Heading 2 ## + + Markup : ## Heading 2 ## + + -OR- + + Markup: --------------- (below H2 text) + +### Heading 3 ### + + Markup : ### Heading 3 ### + +#### Heading 4 #### + + Markup : #### Heading 4 #### + + +Common text + + Markup : Common text + +_Emphasized text_ + + Markup : _Emphasized text_ or *Emphasized text* + +~~Strikethrough text~~ + + Markup : ~~Strikethrough text~~ + +__Strong text__ + + Markup : __Strong text__ or **Strong text** + +___Strong emphasized text___ + + Markup : ___Strong emphasized text___ or ***Strong emphasized text*** + +[Named Link](http://www.google.fr/ "Named link title") and http://www.google.fr/ or + + Markup : [Named Link](http://www.google.fr/ "Named link title") and http://www.google.fr/ or + +[heading-1](#heading-1 "Goto heading-1") + + Markup: [heading-1](#heading-1 "Goto heading-1") + +Table, like this one : + +First Header | Second Header +------------- | ------------- +Content Cell | Content Cell +Content Cell | Content Cell + +``` +First Header | Second Header +------------- | ------------- +Content Cell | Content Cell +Content Cell | Content Cell +``` + +Adding a pipe `|` in a cell : + +First Header | Second Header +------------- | ------------- +Content Cell | Content Cell +Content Cell | \| + +``` +First Header | Second Header +------------- | ------------- +Content Cell | Content Cell +Content Cell | \| +``` + +Left, right and center aligned table + +Left aligned Header | Right aligned Header | Center aligned Header +| :--- | ---: | :---: +Content Cell | Content Cell | Content Cell +Content Cell | Content Cell | Content Cell + +``` +Left aligned Header | Right aligned Header | Center aligned Header +| :--- | ---: | :---: +Content Cell | Content Cell | Content Cell +Content Cell | Content Cell | Content Cell +``` + +`code()` + + Markup : `code()` + +```javascript + var specificLanguage_code = + { + "data": { + "lookedUpPlatform": 1, + "query": "Kasabian+Test+Transmission", + "lookedUpItem": { + "name": "Test Transmission", + "artist": "Kasabian", + "album": "Kasabian", + "picture": null, + "link": "http://open.spotify.com/track/5jhJur5n4fasblLSCOcrTp" + } + } + } +``` + + Markup : ```javascript + ``` + +* Bullet list + * Nested bullet + * Sub-nested bullet etc +* Bullet list item 2 + +~~~ + Markup : * Bullet list + * Nested bullet + * Sub-nested bullet etc + * Bullet list item 2 + +-OR- + + Markup : - Bullet list + - Nested bullet + - Sub-nested bullet etc + - Bullet list item 2 +~~~ + +1. A numbered list + 1. A nested numbered list + 2. Which is numbered +2. Which is numbered + +~~~ + Markup : 1. A numbered list + 1. A nested numbered list + 2. Which is numbered + 2. Which is numbered +~~~ + +- [ ] An uncompleted task +- [x] A completed task + +~~~ + Markup : - [ ] An uncompleted task + - [x] A completed task +~~~ + +- [ ] An uncompleted task + - [ ] A subtask + +~~~ + Markup : - [ ] An uncompleted task + - [ ] A subtask +~~~ + +> Blockquote +>> Nested blockquote + + Markup : > Blockquote + >> Nested Blockquote + +_Horizontal line :_ +- - - - + + Markup : - - - - + +_Image with alt :_ + +![picture alt](http://via.placeholder.com/200x150 "Title is optional") + + Markup : ![picture alt](http://via.placeholder.com/200x150 "Title is optional") + +Foldable text: + +
+ Title 1 +

Content 1 Content 1 Content 1 Content 1 Content 1

+
+
+ Title 2 +

Content 2 Content 2 Content 2 Content 2 Content 2

+
+ + Markup :
+ Title 1 +

Content 1 Content 1 Content 1 Content 1 Content 1

+
+ +```html +

HTML

+

Some HTML code here

+``` + +Link to a specific part of the page: + +[Go To TOP](#TOP) + + Markup : [text goes here](#section_name) + section_title + +Hotkey: + +⌘F + +⇧⌘F + + Markup : ⌘F + +Hotkey list: + +| Key | Symbol | +| --- | --- | +| Option | ⌥ | +| Control | ⌃ | +| Command | ⌘ | +| Shift | ⇧ | +| Caps Lock | ⇪ | +| Tab | ⇥ | +| Esc | ⎋ | +| Power | ⌽ | +| Return | ↩ | +| Delete | ⌫ | +| Up | ↑ | +| Down | ↓ | +| Left | ← | +| Right | → | + +Emoji: + +:exclamation: Use emoji icons to enhance text. :+1: Look up emoji codes at [emoji-cheat-sheet.com](http://emoji-cheat-sheet.com/) + Markup : Code appears between colons :EMOJICODE: \ No newline at end of file diff --git a/wiki/Using the version control system.md b/src/wikmd/wiki_template/Using the version control system.md similarity index 96% rename from wiki/Using the version control system.md rename to src/wikmd/wiki_template/Using the version control system.md index b1f56f0..cf9bd12 100644 --- a/wiki/Using the version control system.md +++ b/src/wikmd/wiki_template/Using the version control system.md @@ -1,30 +1,30 @@ -## Git - -We use git as a version control system. Everytime you save a file it will commit it to git. You could also use the cli to add and commit files, make sure you are in the "wiki" folder, if you are still in the "wikmd" folder you are using the wrong git folder. - -``` -git add . (or the specific file) -git commit -m "your message" (default date of today) -``` - -or you could just go to the homepage of the wiki, this will do all these automatic. - -## How to go to previous file? - -cd inside 'wikmd/wiki' - -Find the version you would like to revert to. - -``` -git log -p file.md -``` - -This will give you a long commit string. Copy the first part of it. (for example b4b580411b) - -Modify the file - -``` -git checkout b4b580411b -- file.md -``` - -Now reload the homepage or use [git](#git) +## Git + +We use git as a version control system. Everytime you save a file it will commit it to git. You could also use the cli to add and commit files, make sure you are in the "wiki" folder, if you are still in the "wikmd" folder you are using the wrong git folder. + +``` +git add . (or the specific file) +git commit -m "your message" (default date of today) +``` + +or you could just go to the homepage of the wiki, this will do all these automatic. + +## How to go to previous file? + +cd inside 'wikmd/wiki' + +Find the version you would like to revert to. + +``` +git log -p file.md +``` + +This will give you a long commit string. Copy the first part of it. (for example b4b580411b) + +Modify the file + +``` +git checkout b4b580411b -- file.md +``` + +Now reload the homepage or use [git](#git) diff --git a/wiki/homepage.md b/src/wikmd/wiki_template/homepage.md similarity index 97% rename from wiki/homepage.md rename to src/wikmd/wiki_template/homepage.md index 27c882c..4cdea1e 100644 --- a/wiki/homepage.md +++ b/src/wikmd/wiki_template/homepage.md @@ -1,42 +1,42 @@ -## What is it? -It’s a file-based wiki that aims to simplicity. The documents are completely written in Markdown which is an easy markup language that you can learn in 60 sec. - -## Why markdown? -If you compare markdown to a WYSIWYG editor it may look less easy to use but the opposite is true. When writing markdown you don’t get to see the result directly which is the only downside. -There are more pros: - -- Easy to process to other file formats -- Scalable, it reformats for the perfect display width - -## How does it work? -Instead of storing the data in a database I chose to have a file-based system. The advantage of this system is that every file is directly readable inside a terminal etc. Also when you have direct access to the system you can export the files to anything you like. - -To view the documents in the browser, the document is converted to html. - -## Plugins (beta) - -The plugins are used to extend the functionality of the wiki. Most of them are accessible through the use of `tags`. -For now there are only a few supported. - -- `[[draw]]` Allows you to add an **interactive drawio drawing** to the wiki. -- `[[info]]`, `[[warning]]`, `[[danger]]`, `[[success]]` Adds a nice **alert message**. -- `[[ page: some-page ]]` Allows to show an other page in the current one. - -### Image support -![](https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Markdown-mark.svg/208px-Markdown-mark.svg.png) - -### Latex support - -$$x_{1,2} = \frac{-b ± \sqrt{b^2 - 4 a c}}{2a}$$ - -## How to use the wiki -You can learn more on how to use the wiki [here](How to use the wiki) - -## Features -Read all the features [here](Features) - -## Handy links -- [Google](http://google.be) -- [Duckduckgo](http://duckduckgo.org) - - +## What is it? +It’s a file-based wiki that aims to simplicity. The documents are completely written in Markdown which is an easy markup language that you can learn in 60 sec. + +## Why markdown? +If you compare markdown to a WYSIWYG editor it may look less easy to use but the opposite is true. When writing markdown you don’t get to see the result directly which is the only downside. +There are more pros: + +- Easy to process to other file formats +- Scalable, it reformats for the perfect display width + +## How does it work? +Instead of storing the data in a database I chose to have a file-based system. The advantage of this system is that every file is directly readable inside a terminal etc. Also when you have direct access to the system you can export the files to anything you like. + +To view the documents in the browser, the document is converted to html. + +## Plugins (beta) + +The plugins are used to extend the functionality of the wiki. Most of them are accessible through the use of `tags`. +For now there are only a few supported. + +- `[[draw]]` Allows you to add an **interactive drawio drawing** to the wiki. +- `[[info]]`, `[[warning]]`, `[[danger]]`, `[[success]]` Adds a nice **alert message**. +- `[[ page: some-page ]]` Allows to show an other page in the current one. + +### Image support +![](https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Markdown-mark.svg/208px-Markdown-mark.svg.png) + +### Latex support + +$$x_{1,2} = \frac{-b ± \sqrt{b^2 - 4 a c}}{2a}$$ + +## How to use the wiki +You can learn more on how to use the wiki [here](How to use the wiki) + +## Features +Read all the features [here](Features) + +## Handy links +- [Google](http://google.be) +- [Duckduckgo](http://duckduckgo.org) + + diff --git a/wikmd-config.yaml b/src/wikmd/wikmd-config.yaml similarity index 100% rename from wikmd-config.yaml rename to src/wikmd/wikmd-config.yaml diff --git a/tests/test_basics.py b/tests/test_basics.py index 0f4b371..fccb24e 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1,147 +1,149 @@ -import wiki -import pytest -import pypandoc import os +from pathlib import Path +import shutil + +import pytest +from wikmd import wiki +from wikmd.wiki import app, cfg -from wiki import app +cfg.wiki_directory = (Path(__file__).parent.parent / "src" / "wikmd" / "wiki_template").as_posix() -@pytest.fixture +@pytest.fixture() def client(): return app.test_client -# test if homepage responses + +@pytest.fixture(scope="module") +def test_file_content(): + return b"this is the header", b"extra content" + + +@pytest.fixture() +def project_file(test_file_content): + testing_folder = Path(cfg.wiki_directory) / "testing_folder" + testing_folder.mkdir() + + test_file = testing_folder / "test.md" + with test_file.open("wb") as fp: + fp.writelines(test_file_content) + + yield test_file + + shutil.rmtree(str(testing_folder)) + + +@pytest.fixture() +def wiki_file(project_file): + return f"/{project_file.parent.name}/{project_file.stem}" + + def test_homepage(): + """Homepage renders.""" rv = app.test_client().get("/") - - # see if hompage responses + assert rv.status_code == 200 # Check if homepage loads - assert b'What is it?' in rv.data + assert b"What is it?" in rv.data + -# check if list returns all files (tests only 2 defaults) def test_list(): + """List functionality returns one of the standard files.""" rv = app.test_client().get("/list/") assert rv.status_code == 200 - #assert b'homepage.md' in rv.data - assert b'Features.md' in rv.data - -# creates a file and check if the content of the file is visible in the wiki -def test_create_file_in_folder(): - # create dir if it does not exist - if not os.path.exists("wiki/testing_folder_0123"): - os.makedirs("wiki/testing_folder_0123") + assert b"Features.md" in rv.data - # write content in the test.md file - f = open("wiki/testing_folder_0123/test.md","w+") - f.write("# this is the header\n extra content") - f.close() - rv = app.test_client().get("/testing_folder_0123/test") +def test_create_file_in_folder(wiki_file, test_file_content): + """Make sure the created file is accessible.""" + rv = app.test_client().get(wiki_file) assert rv.status_code == 200 - assert b'this is the header' in rv.data - assert b'extra content' in rv.data - - # remove created folders - os.remove("wiki/testing_folder_0123/test.md") - os.removedirs("wiki/testing_folder_0123/") - -# checks if the search response with searchterm = Features + assert test_file_content[0] in rv.data + assert test_file_content[1] in rv.data + + def test_search(): - wiki.setup_search() - rv = app.test_client().get("/?q=Features") - assert rv.status_code == 200 - assert b'Found' in rv.data - assert b'result(s)' in rv.data - assert b'Features' in rv.data - -# create a new file using the wiki and check if it is visible in the wiki + """Search functionality returns result.""" + wiki.setup_search() + rv = app.test_client().get("/?q=Features") + assert rv.status_code == 200 + assert b"Found" in rv.data + assert b"result(s)" in rv.data + assert b"Features" in rv.data + + def test_new_file(): + """App can create files.""" rv = app.test_client().get("/add_new") assert rv.status_code == 200 - assert b'content' in rv.data + assert b"content" in rv.data # create new file - rv = app.test_client().post("/add_new", data=dict( - PN="testing01234filenotexisting", - CT="#testing file\n this is a test" - )) + app.test_client().post("/add_new", data={ + "PN": "testing01234filenotexisting", + "CT": "#testing file\n this is a test", + }) # look at file rv = app.test_client().get("/testing01234filenotexisting") - assert b'testing file' in rv.data - assert b'this is a test' in rv.data + assert b"testing file" in rv.data + assert b"this is a test" in rv.data + + f = Path(cfg.wiki_directory) / "testing01234filenotexisting.md" + f.unlink() - os.remove("wiki/testing01234filenotexisting.md") # create a new file in a folder using the wiki and check if it is visible in the wiki def test_new_file_folder(): + """App can create folders.""" rv = app.test_client().get("/add_new") assert rv.status_code == 200 - assert b'content' in rv.data + assert b"content" in rv.data + + # create new file in a folder + app.test_client().post("/add_new", data={ + "PN": "testingfolder01234/testing01234filenotexisting", + "CT": "#testing file\n this is a test", + }) - # create new file - rv = app.test_client().post("/add_new", data=dict( - PN="testingfolder01234/testing01234filenotexisting", - CT="#testing file\n this is a test" - )) - # look at file rv = app.test_client().get("/testingfolder01234/testing01234filenotexisting") - assert b'testing file' in rv.data - assert b'this is a test' in rv.data + assert b"testing file" in rv.data + assert b"this is a test" in rv.data + + f = Path(cfg.wiki_directory) / "testingfolder01234" + shutil.rmtree(f) - os.remove("wiki/testingfolder01234/testing01234filenotexisting.md") - os.removedirs("wiki/testingfolder01234") # edits file using the wiki and check if it is visible in the wiki -def test_edit_file(): - f = open("wiki/testing01234filenotexisting.md","w+") - f.write("# this is the header\n extra content") - f.close() +def test_get_file_after_file_edit(project_file, wiki_file): + with project_file.open("w+") as fp: + fp.write("our new content") - rv = app.test_client().get("/edit/testing01234filenotexisting") + rv = app.test_client().get(wiki_file) assert rv.status_code == 200 - assert b'this is the header' in rv.data - - # create new file - rv = app.test_client().post("/edit/testing01234filenotexisting", data=dict( - PN="testing01234filenotexisting", - CT="#testing file\n this is a test" - )) - - # look at file - rv = app.test_client().get("/testing01234filenotexisting") - assert b'testing file' in rv.data - assert b'this is a test' in rv.data + assert b"our new content" in rv.data - os.remove("wiki/testing01234filenotexisting.md") -# edits file in folder using the wiki and check if it is visible in the wiki -def test_edit_file_folder(): - if not os.path.exists("wiki/testingfolder01234"): - os.makedirs("wiki/testingfolder01234") +def test_get_file_after_api_edit(wiki_file): + # Edit the file through API + app.test_client().post(f"/edit{wiki_file}", data={ + "PN": wiki_file[1:], + "CT": "#testing file\n this is a test", + }) - f = open("wiki/testingfolder01234/testing01234filenotexisting.md","w+") - f.write("# this is the header\n extra content") - f.close() + rv = app.test_client().get(wiki_file) + assert b"testing file" in rv.data + assert b"this is a test" in rv.data - rv = app.test_client().get("/edit/testingfolder01234/testing01234filenotexisting") - assert rv.status_code == 200 - assert b'this is the header' in rv.data - # create new file - rv = app.test_client().post("/edit/testingfolder01234/testing01234filenotexisting", data=dict( - PN="testingfolder01234/testing01234filenotexisting", - CT="#testing file\n this is a test" - )) - - # look at file - rv = app.test_client().get("/testingfolder01234/testing01234filenotexisting") - assert b'testing file' in rv.data - assert b'this is a test' in rv.data +# edits file in folder using the wiki and check if it is visible in the wiki +def test_get_edit_page_content(project_file, wiki_file): + with project_file.open("w+") as fp: + fp.write("# this is the header\n extra content") - os.remove("wiki/testingfolder01234/testing01234filenotexisting.md") - os.removedirs("wiki/testingfolder01234") + rv = app.test_client().get(f"/edit{wiki_file}") + assert rv.status_code == 200 + assert b"this is the header" in rv.data diff --git a/tests/test_plugins.py b/tests/test_plugins.py index acd8606..8e60616 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,38 +1,40 @@ -import wiki import pytest -import pypandoc -import os +from wikmd import wiki +from wikmd.wiki import app -from wiki import app -@pytest.fixture +@pytest.fixture() def client(): return app.test_client + def test_plugin_loading(): assert wiki.plugins + def test_process_md(): before = "#test this is test\n text should still be available after plugin" md = before for plugin in wiki.plugins: - if ("process_md" in dir(plugin)): + if "process_md" in dir(plugin): md = plugin.process_md(md) assert md == before + def test_draw_md(): before = "#test this is test\n[[draw]] \n next line" md = before for plugin in wiki.plugins: - if ("process_md" in dir(plugin)): + if "process_md" in dir(plugin): md = plugin.process_md(md) assert md != before assert md != "" - + + def test_process_html(): before = "

this is a test

" html = before for plugin in wiki.plugins: - if ("process_html" in dir(plugin)): + if "process_html" in dir(plugin): html = plugin.process_html(html) assert html == before \ No newline at end of file diff --git a/tests/test_search.py b/tests/test_search.py index ba974f6..7155634 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,87 +1,95 @@ import os import tempfile import time -from unittest import mock +from pathlib import Path +import pytest -from search import Search, Watchdog -from wiki import app +from wikmd.search import Search, Watchdog + + +@pytest.fixture(scope="module") +def search_dir(): + return tempfile.mkdtemp() + + +@pytest.fixture() +def search_file(): + file_name = "test_index.md" + title = "test index" + content = "index\nsearch\ntest" + return file_name, title, content + + +@pytest.fixture() +def search_engine(search_dir): + return Search(search_dir, create=True) + + +@pytest.fixture() +def search_engine_with_content(search_engine, search_dir, search_file): + file_name, title, content = search_file + + search_engine.index(search_dir, file_name, title, content) + return search_engine def test_textify(): tmp = tempfile.mkdtemp() s = Search(tmp, create=True) - md = "\n".join(("# h1", "## h2", "test")) + md = "# h1\n## h2\ntest" assert s.textify(md) == "h1\nh2\ntest" -def test_index_and_search(): - tmp = tempfile.mkdtemp() - s = Search(tmp, create=True) - fname, title = "test_index.md", "test index" - content = "\n".join(("index", "search" "test")) - - s.index(tmp, fname, title, content) - res, total, pages, _ = s.search("index", 1) +def test_search(search_engine_with_content, search_dir, search_file): + res, total, pages, _ = search_engine_with_content.search("index", 1) assert total == 1 assert pages == 1 - assert res[0].path == tmp - assert res[0].filename == fname + assert res[0].path == search_dir + assert res[0].filename == search_file[0] - _, _, _, sug = s.search("ndex", 1) + _, _, _, sug = search_engine_with_content.search("ndex", 1) assert "index" in sug -def test_pagination(): - tmp = tempfile.mkdtemp() - s = Search(tmp, create=True) - +def test_pagination(search_engine, search_dir): for i in range(25): fname, title = f"test_index_{i}.md", f"test index {i}" - content = "\n".join(("index", "search" "test")) - s.index(tmp, fname, title, content) + content = "index\nsearch\ntest" + search_engine.index(search_dir, fname, title, content) - res, total, pages, _ = s.search("index", 1) + res, total, pages, _ = search_engine.search("index", 1) assert total == 25 assert pages == 3 -def test_index_and_delete(): - tmp = tempfile.mkdtemp() - s = Search(tmp, create=True) - fname, title = "test_index.md", "test index" - content = "\n".join(("index", "search" "test")) - - s.index(tmp, fname, title, content) - res, total, pages, _ = s.search("index", 1) - assert total == 1 - assert pages == 1 - assert res[0].filename == fname - - s.delete(tmp, fname) - res, total, pages, _ = s.search("index", 1) +def test_index_and_delete(search_engine_with_content, search_dir, search_file ): + search_engine_with_content.delete(search_dir, search_file[0]) + res, total, pages, _ = search_engine_with_content.search("index", 1) assert total == 0 assert pages == 0 assert len(res) == 0 -def test_index_all(): - tmps, tmpd = tempfile.mkdtemp(), tempfile.mkdtemp() - s = Search(tmps, create=True) +def test_index_all(search_engine, search_dir): nf = [] - content = "\n".join(("index", "search" "test")) + content = "index\nsearch\ntest" for n in ("a", "b"): - fname = f"{n}.md" - with open(os.path.join(tmpd, fname), "w") as f: + file_name = f"{n}.md" + p = Path(search_dir) / file_name + with p.open("w") as f: f.write(content) - nf.append((fname, n, ".")) + nf.append((file_name, n, ".")) - os.mkdir(os.path.join(tmpd, "z")) - with open(os.path.join(tmpd, "z", "y.md"), "w") as f: + # Add a file to a sub folder + p_dir = Path(search_dir) + (p_dir / "z").mkdir() + file_name = p_dir / "z" / "y.md" + with file_name.open("w") as f: f.write(content) nf.append(("y.md", "y", "z")) - s.index_all(tmpd, nf) - res, total, pages, _ = s.search("index", 1) + search_engine.index_all(search_dir, nf) + res, total, pages, _ = search_engine.search("index", 1) assert total == 3 assert pages == 1 assert len(res) == 3