diff --git a/config.ini b/config.ini index 5698572..17e4cd3 100755 --- a/config.ini +++ b/config.ini @@ -3,7 +3,6 @@ [main] lang = fr db_sqlite_file = db.sqlite -newcomment_polling = 60 [site] name = "My blog" diff --git a/dbmigration/create_empty_db.py b/dbmigration/create_empty_db.py index 8bb64ac..a597869 100644 --- a/dbmigration/create_empty_db.py +++ b/dbmigration/create_empty_db.py @@ -6,9 +6,6 @@ import sqlite3 connection = sqlite3.connect("db.sqlite") cursor = connection.cursor() -# What script performs: -# - first, remove site table: crash here if table doesn't exist (compatibility test without effort) -# - remove site_id colum from comment table script = """ CREATE TABLE comment ( id INTEGER NOT NULL PRIMARY KEY, diff --git a/dbmigration/migrate_from_2.1_to_3.0.py b/dbmigration/migrate_from_2.1_to_3.0.py deleted file mode 100644 index e421d6e..0000000 --- a/dbmigration/migrate_from_2.1_to_3.0.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -import sqlite3 -import datetime -from ulid import ULID - -# add column ulid -connection = sqlite3.connect("db.sqlite") -cursor = connection.cursor() -script = """ -PRAGMA foreign_keys = OFF; -BEGIN TRANSACTION; -ALTER TABLE comment ADD ulid INTEGER; -COMMIT; -PRAGMA foreign_keys = ON; -""" -cursor.executescript(script) -connection.close() - -# fill in ulid column -connection = sqlite3.connect("db.sqlite") -cursor = connection.cursor() -updates = [] -for row in cursor.execute('SELECT * FROM comment'): - row_id = row[0] - string_created = row[2] - date_created = datetime.datetime.strptime(string_created, "%Y-%m-%d %H:%M:%S") - ulid = ULID.from_datetime(date_created) - update = "UPDATE comment SET ulid = " + str(int(ulid)) + " WHERE id = " + str(row_id) - print(update) - updates.append(update) - -for update in updates: - pass - connection.execute(update) -connection.commit() -connection.close() diff --git a/poetry.lock b/poetry.lock index 70cb658..4073351 100644 --- a/poetry.lock +++ b/poetry.lock @@ -54,6 +54,14 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +[[package]] +name = "background" +version = "0.2.1" +description = "It does what it says it does." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "backports.zoneinfo" version = "0.2.1" @@ -495,14 +503,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" [package.dependencies] six = ">=1.5" -[[package]] -name = "python-ulid" -version = "1.0.3" -description = "Universally Unique Lexicographically Sortable Identifier" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "pytz" version = "2021.3" @@ -696,7 +696,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "24b77862cfbece0c68447f4d026bed51431e3a655a92dde2697368e758bfac89" +content-hash = "1062100a70ba0ca6a9f5db6470d0a980784af1d9b96d0bb681e1b65bacc204cf" [metadata.files] appdirs = [ @@ -715,6 +715,10 @@ attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] +background = [ + {file = "background-0.2.1-py3-none-any.whl", hash = "sha256:c230e2813c773f93ecae54281ce6b1b425c895c24599cc203b7f137e4d7c4802"}, + {file = "background-0.2.1.tar.gz", hash = "sha256:4a5ed40b4a2a9f3340b1402862725d35016dc2490f95d89a2de47c3ddf215b91"}, +] "backports.zoneinfo" = [ {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, @@ -1008,10 +1012,6 @@ python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] -python-ulid = [ - {file = "python-ulid-1.0.3.tar.gz", hash = "sha256:5dd8b969312a40e2212cec9c1ad63f25d4b6eafd92ee3195883e0287b6e9d19e"}, - {file = "python_ulid-1.0.3-py3-none-any.whl", hash = "sha256:8704dc20f547f531fe3a41d4369842d737a0f275403b909d0872e7ea0fe8d6f2"}, -] pytz = [ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, diff --git a/pyproject.toml b/pyproject.toml index 718ebee..4dd72e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ requests = "^2.25.1" coverage = "^5.5" peewee = "^3.14.8" tox = "^3.24.5" -python-ulid = "^1.0.3" +background = "^0.2.1" [tool.poetry.dev-dependencies] rope = "^0.16.0" diff --git a/run.py b/run.py index 5a669f5..b295253 100644 --- a/run.py +++ b/run.py @@ -2,7 +2,6 @@ # -*- coding: UTF-8 -*- import argparse -import hashlib import logging import os import sys @@ -14,7 +13,6 @@ from stacosys.db import database from stacosys.interface import api from stacosys.interface import app from stacosys.interface import form -from stacosys.interface import scheduler from stacosys.interface.web import admin @@ -79,20 +77,14 @@ def stacosys_server(config_pathname): conf.get(ConfigParameter.SITE_ADMIN_EMAIL) ) - # configure scheduler - conf.put(ConfigParameter.SITE_TOKEN, hashlib.sha1(conf.get(ConfigParameter.SITE_NAME).encode('utf-8')).hexdigest()) - scheduler.configure( - conf.get_int(ConfigParameter.COMMENT_POLLING), - conf.get(ConfigParameter.SITE_NAME), - mailer, - ) - # inject config parameters into flask app.config.update(LANG=conf.get(ConfigParameter.LANG)) + app.config.update(SITE_NAME=conf.get(ConfigParameter.SITE_NAME)) app.config.update(SITE_URL=conf.get(ConfigParameter.SITE_URL)) app.config.update(SITE_REDIRECT=conf.get(ConfigParameter.SITE_REDIRECT)) app.config.update(WEB_USERNAME=conf.get(ConfigParameter.WEB_USERNAME)) app.config.update(WEB_PASSWORD=conf.get(ConfigParameter.WEB_PASSWORD)) + app.config.update(MAILER=mailer) logger.info(f"start interfaces {api} {form} {admin}") # start Flask diff --git a/stacosys/conf/config.py b/stacosys/conf/config.py index 9e923d4..4e77fb1 100644 --- a/stacosys/conf/config.py +++ b/stacosys/conf/config.py @@ -9,7 +9,6 @@ import profig class ConfigParameter(Enum): DB_SQLITE_FILE = "main.db_sqlite_file" LANG = "main.lang" - COMMENT_POLLING = "main.newcomment_polling" HTTP_HOST = "http.host" HTTP_PORT = "http.port" @@ -24,7 +23,6 @@ class ConfigParameter(Enum): SITE_NAME = "site.name" SITE_URL = "site.url" - SITE_TOKEN = "site.token" SITE_ADMIN_EMAIL = "site.admin_email" SITE_REDIRECT = "site.redirect" diff --git a/stacosys/core/cron.py b/stacosys/core/cron.py deleted file mode 100644 index 2dd87c0..0000000 --- a/stacosys/core/cron.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import logging - -from stacosys.db import dao - -logger = logging.getLogger(__name__) - - -def submit_new_comment(site_name, mailer): - for comment in dao.find_not_notified_comments(): - comment_list = ( - "author: %s" % comment.author_name, - "site: %s" % comment.author_site, - "date: %s" % comment.created, - "url: %s" % comment.url, - "", - "%s" % comment.content, - "", - ) - email_body = "\n".join(comment_list) - - # send email to notify admin - subject = "STACOSYS %s" % site_name - if mailer.send(subject, email_body): - logger.debug("new comment processed ") - - # save notification datetime - dao.notify_comment(comment) - else: - logger.warning("rescheduled. send mail failure " + subject) diff --git a/stacosys/interface/form.py b/stacosys/interface/form.py index 085c586..8bdabc0 100644 --- a/stacosys/interface/form.py +++ b/stacosys/interface/form.py @@ -1,8 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - import logging +import background from flask import abort, redirect, request from stacosys.db import dao @@ -13,7 +13,6 @@ logger = logging.getLogger(__name__) @app.route("/newcomment", methods=["POST"]) def new_form_comment(): - data = request.form logger.info("form data " + str(data)) @@ -40,7 +39,10 @@ def new_form_comment(): abort(400) # add a row to Comment table - dao.create_comment(url, author_name, author_site, author_gravatar, message) + comment = dao.create_comment(url, author_name, author_site, author_gravatar, message) + + # send notification e-mail asynchronously + submit_new_comment(comment) return redirect(app.config.get("SITE_REDIRECT"), code=302) @@ -51,3 +53,32 @@ def check_form_data(d): return not filtered +@background.task +def submit_new_comment(comment): + comment_list = ( + "Web admin interface: %s/web/admin" % app.config.get("SITE_URL"), + "", + "author: %s" % comment.author_name, + "site: %s" % comment.author_site, + "date: %s" % comment.created, + "url: %s" % comment.url, + "", + "%s" % comment.content, + "", + ) + email_body = "\n".join(comment_list) + + # send email to notify admin + subject = "STACOSYS " + app.config.get("SITE_NAME") + if app.config.get("MAILER").send(subject, email_body): + logger.debug("new comment processed") + + # save notification datetime + dao.notify_comment(comment) + else: + logger.warning("rescheduled. send mail failure " + subject) + + +@background.callback +def submit_new_comment_callback(future): + pass diff --git a/stacosys/interface/scheduler.py b/stacosys/interface/scheduler.py deleted file mode 100644 index 1de9a0e..0000000 --- a/stacosys/interface/scheduler.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from flask_apscheduler import APScheduler -from stacosys.interface import app - - -class JobConfig(object): - - JOBS: list = [] - - SCHEDULER_EXECUTORS = {"default": {"type": "threadpool", "max_workers": 1}} - - def __init__( - self, - new_comment_polling_seconds, - site_name, - mailer, - ): - self.JOBS = [ - { - "id": "submit_new_comment", - "func": "stacosys.core.cron:submit_new_comment", - "args": [site_name, mailer], - "trigger": "interval", - "seconds": new_comment_polling_seconds, - }, - ] - - -def configure( - comment_polling, - site_name, - mailer, -): - app.config.from_object( - JobConfig( - comment_polling, - site_name, - mailer, - ) - ) - scheduler = APScheduler() - scheduler.init_app(app) - scheduler.start()