diff --git a/news_check/client/public/favicon.png b/news_check/client/public/favicon.png deleted file mode 100644 index 7e6f5eb..0000000 Binary files a/news_check/client/public/favicon.png and /dev/null differ diff --git a/news_check/client/public/global.css b/news_check/client/public/global.css deleted file mode 100644 index bb28a94..0000000 --- a/news_check/client/public/global.css +++ /dev/null @@ -1,63 +0,0 @@ -html, body { - position: relative; - width: 100%; - height: 100%; -} - -body { - color: #333; - margin: 0; - padding: 8px; - box-sizing: border-box; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; -} - -a { - color: rgb(0,100,200); - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -a:visited { - color: rgb(0,80,160); -} - -label { - display: block; -} - -input, button, select, textarea { - font-family: inherit; - font-size: inherit; - -webkit-padding: 0.4em 0; - padding: 0.4em; - margin: 0 0 0.5em 0; - box-sizing: border-box; - border: 1px solid #ccc; - border-radius: 2px; -} - -input:disabled { - color: #ccc; -} - -button { - color: #333; - background-color: #f4f4f4; - outline: none; -} - -button:disabled { - color: #999; -} - -button:not(:disabled):active { - background-color: #ddd; -} - -button:focus { - border-color: #666; -} diff --git a/news_check/client/src/App.svelte b/news_check/client/src/App.svelte index 33da745..7e9ff01 100644 --- a/news_check/client/src/App.svelte +++ b/news_check/client/src/App.svelte @@ -2,10 +2,15 @@ import PDFView from './PDFView.svelte'; import ArticleStatus from './ArticleStatus.svelte'; import ArticleOperations from './ArticleOperations.svelte'; + + import Toast from './Toast.svelte'; + let current_id = 0; - const updateInterface = (async () => { + let interfaceState = updateInterface() + + async function updateInterface () { let url = ''; if (current_id == 0) { url = '/api/article/first'; @@ -19,12 +24,14 @@ const article_response = await fetch(article_url); const article_data = await article_response.json(); return article_data; - })() - - + } + + function triggerUpdate () { + interfaceState = updateInterface(); + } -{#await updateInterface} +{#await interfaceState} ... {:then article_data}
@@ -33,7 +40,9 @@
- +
{/await} + + \ No newline at end of file diff --git a/news_check/client/src/ArticleOperations.svelte b/news_check/client/src/ArticleOperations.svelte index e1db46e..de55266 100644 --- a/news_check/client/src/ArticleOperations.svelte +++ b/news_check/client/src/ArticleOperations.svelte @@ -1,55 +1,85 @@ @@ -58,21 +88,22 @@

Your options: (click on action or use keyboard)

- + - {#each actions as action} - + {#each actions as action} - + - {/each}
Action Keyboard shortcut
{ action.kbd } + { action.kbd } + {#if action.comment}({ action.comment }){/if} +
@@ -80,14 +111,9 @@
+ + + + -{#if toast_visible} -
-
-
- { toast_state.text }. -
-
-
-{/if} \ No newline at end of file diff --git a/news_check/client/src/ArticleStatus.svelte b/news_check/client/src/ArticleStatus.svelte index 3ffb9db..6c426dc 100644 --- a/news_check/client/src/ArticleStatus.svelte +++ b/news_check/client/src/ArticleStatus.svelte @@ -2,13 +2,21 @@ export let article_data; const status_items = [ {name: 'Title', value: article_data.title}, + {name: 'Url', value: article_data.article_url}, + {name: 'Source', value: article_data.source_name}, {name: 'Filename', value: article_data.file_name}, + {name: 'Location', value: article_data.save_path}, {name: 'Language', value: article_data.language}, {name: 'Authors', value: article_data.authors}, {name: "Related", value: article_data.related}, ] +

Article overview:

@@ -23,16 +31,18 @@ {#each status_items as item} { item.name } - {#if item.value != ""} + {#if item.name == "Url"} + { item.value } + {:else} { item.value } + {/if} {:else} - { item.value } + not set {/if} {/each}
-
\ No newline at end of file diff --git a/news_check/client/src/Toast.svelte b/news_check/client/src/Toast.svelte new file mode 100644 index 0000000..0c13236 --- /dev/null +++ b/news_check/client/src/Toast.svelte @@ -0,0 +1,34 @@ + + +
+ {#each $toasts as toast} +
+
{ toast.text }.
+
+ {/each} +
diff --git a/news_check/client/src/main.js b/news_check/client/src/main.js index d6cacbb..d80e9a3 100644 --- a/news_check/client/src/main.js +++ b/news_check/client/src/main.js @@ -2,9 +2,6 @@ import App from './App.svelte'; const app = new App({ target: document.body, - props: { - name: 'world' - } }); export default app; \ No newline at end of file diff --git a/news_check/server/app.py b/news_check/server/app.py index 418d854..9d99796 100644 --- a/news_check/server/app.py +++ b/news_check/server/app.py @@ -1,5 +1,7 @@ from flask import Flask, send_from_directory, request +import os import configuration + models = configuration.models db = configuration.db app = Flask(__name__) @@ -30,9 +32,9 @@ def get_article_by_id(id): return article.to_dict() @app.route("/api/article/first") -def get_article_first(): +def get_article_first(min_id=0): with db: - article = models.ArticleDownload.select(models.ArticleDownload.id).where(models.ArticleDownload.verified == 0).order_by(models.ArticleDownload.id).first() + article = models.ArticleDownload.select(models.ArticleDownload.id).where((models.ArticleDownload.verified == 0) & (models.ArticleDownload.id > min_id)).order_by(models.ArticleDownload.id).first() return {"id" : article.id} @app.route("/api/article//next") @@ -41,27 +43,44 @@ def get_article_next(id): if models.ArticleDownload.get_by_id(id + 1).verified == 0: return {"id" : id + 1} else: - return get_article_first() + return get_article_first(min_id=id) # if the current article was skipped, but the +1 is already verified, get_first will return the same article again. so specify min id. @app.route("/api/article//set", methods=['POST']) def set_article(id): - action = request.json['action'] + try: + action = request.json.get('action', None) + except Exception as e: + print(f"Exception in set_article {e}") + action = None with db: article = models.ArticleDownload.get_by_id(id) - if action == "a": - article.verified = 1 - elif action == "b": - article.verified = -1 - elif action == "r": - article.set_related() - article.save() - return "ok" + if action: + if action == "a": + article.verified = 1 + elif action == "b": + article.verified = -1 + else: # implicitly action == "r": + print(request.files) + file = request.files.get("file", None) + if file is None: # upload tends to crash + return "No file uploaded", 400 + artname, _ = os.path.splitext(article.file_name) + fname = f"{artname} -- related_{article.related.count() + 1}.{file.filename.split('.')[-1]}" + fpath = os.path.join(article.save_path, fname) + print(fpath) + file.save(fpath) + article.set_related([fname]) + return {"file_path": fpath} + + article.save() + return "ok" if __name__ == "__main__": - app.run(host="0.0.0.0", port="80") + debug = os.getenv("DEBUG", "false") == "true" + app.run(host="0.0.0.0", port="80", debug=debug) diff --git a/news_check/server/configuration.py b/news_check/server/configuration.py index c6cd798..f3214da 100644 --- a/news_check/server/configuration.py +++ b/news_check/server/configuration.py @@ -1,5 +1,6 @@ from peewee import PostgresqlDatabase import configparser +import time main_config = configparser.ConfigParser() main_config.read("/app/containerdata/config/news_fetch.config.ini") @@ -8,6 +9,7 @@ db_config = configparser.ConfigParser() db_config.read("/app/containerdata/config/db.config.ini") cred = db_config["DATABASE"] +time.sleep(10) # wait for the vpn to connect (can't use a healthcheck because there is no depends_on) db = PostgresqlDatabase( cred["db_name"], user=cred["user_name"], password=cred["password"], host="vpn", port=5432 ) diff --git a/news_fetch/configuration.py b/news_fetch/configuration.py index 01eb52e..49ba39e 100644 --- a/news_fetch/configuration.py +++ b/news_fetch/configuration.py @@ -2,8 +2,8 @@ import os import configparser import logging import time -import shutil -from datetime import datetime +# import shutil +# from datetime import datetime from peewee import SqliteDatabase, PostgresqlDatabase from rich.logging import RichHandler diff --git a/news_fetch/runner.py b/news_fetch/runner.py index d3baf18..7e17cb4 100644 --- a/news_fetch/runner.py +++ b/news_fetch/runner.py @@ -1,4 +1,5 @@ """Main coordination of other util classes. Handles inbound and outbound calls""" +from time import sleep import configuration models = configuration.models from threading import Thread @@ -110,7 +111,8 @@ class Dispatcher(Thread): logger.error("Dispatcher.incoming_request called with no arguments") return - if is_new or (article.file_name == "" and article.verified == 0): + if is_new or (article.file_name == "" and article.verified == 0) \ + or (not is_new and len(self.workers_in) == 1): # this is for upload # check for models that were created but were abandonned. This means they have missing information, most importantly no associated file # this overwrites previously set information, but that should not be too important ArticleWatcher( @@ -121,7 +123,6 @@ class Dispatcher(Thread): else: # manually trigger notification immediatly logger.info(f"Found existing article {article}. Now sending") - self.article_complete_notifier(article) @@ -142,6 +143,8 @@ if __name__ == "__main__": class PrintWorker: def send(self, article): print(f"Uploaded article {article}") + def keep_alive(self): # keeps script running, because there is nothing else in the main thread + while True: sleep(1) articles = models.ArticleDownload.select().where(models.ArticleDownload.archive_url == "" or models.ArticleDownload.archive_url == "TODO:UPLOAD").execute() logger.info(f"Launching upload to archive for {len(articles)} articles.") @@ -149,6 +152,9 @@ if __name__ == "__main__": dispatcher.workers_in = [{"UploadWorker": UploadWorker()}] dispatcher.workers_out = [{"PrintWorker": PrintWorker()}] dispatcher.start() + for a in articles: + dispatcher.incoming_request(article=a) + PrintWorker().keep_alive() else: # launch with full action try: