Compare commits
3
Commits
4219680e7a
...
66b4fffb7b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66b4fffb7b | ||
|
|
8aae29f3e8 | ||
|
|
15f2e4a6e4 |
@@ -12,3 +12,5 @@ dist
|
|||||||
test/*
|
test/*
|
||||||
|
|
||||||
*.build
|
*.build
|
||||||
|
|
||||||
|
C:/* # Windows paths on linux
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['anon_gui.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=[],
|
|
||||||
hiddenimports=[],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='anon_gui',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
@echo off
|
|
||||||
rem This script was created by Nuitka to execute 'anon_gui.exe' with Python DLL being found.
|
|
||||||
set PATH=C:\PROGRA~1\PYTHON~1;%PATH%
|
|
||||||
set PYTHONHOME=C:\Program Files\Python311
|
|
||||||
"%~dp0anon_gui.exe" %*
|
|
||||||
Binary file not shown.
@@ -1,3 +1,6 @@
|
|||||||
|
# This is a legacy app written in wxPython
|
||||||
|
# Whilst it works (or did prior to any API changes)
|
||||||
|
# it was not particularly well designed
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
import typer
|
|
||||||
import requests
|
|
||||||
|
|
||||||
app = typer.Typer()
|
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
# client.py
|
|
||||||
LOGIN_URL = "https://www.penracourses.org.uk/accounts/login"
|
|
||||||
# ADD_URL = 'http://localhost/add'
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def login(user: str, password: str):
|
|
||||||
rqst = requests.session()
|
|
||||||
rsp = rqst.get(LOGIN_URL)
|
|
||||||
|
|
||||||
token = BeautifulSoup(rsp.content, "html.parser").find("input").attrs["value"]#, attr={"name": "csrfmiddlewaretoken"}).attrs("value")
|
|
||||||
|
|
||||||
#token = rsp.cookies["csrftoken"]
|
|
||||||
#header = {"X-CSRFToken": token}
|
|
||||||
#cookies = {"csrftoken": token}
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"username": user,
|
|
||||||
"password": password,
|
|
||||||
"csrfmiddlewaretoken": token,
|
|
||||||
"next": "/",
|
|
||||||
}
|
|
||||||
|
|
||||||
print(data)
|
|
||||||
rqst.headers["Referer"] = LOGIN_URL
|
|
||||||
|
|
||||||
rsp = rqst.post(
|
|
||||||
LOGIN_URL,
|
|
||||||
data=data,
|
|
||||||
#headers=header,
|
|
||||||
#cookies=cookies
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(rsp.content, "html.parser")
|
|
||||||
print(token)
|
|
||||||
print(rqst.headers)
|
|
||||||
print(rqst.cookies)
|
|
||||||
|
|
||||||
login_success = False
|
|
||||||
if soup.find("button") and soup.find("button").find(string="Login"):
|
|
||||||
print("login fail")
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print("login success")
|
|
||||||
login_success = True
|
|
||||||
#print(rqst.headers)
|
|
||||||
#print(rsp.content)
|
|
||||||
|
|
||||||
#new = rqst.get(
|
|
||||||
# "https://www.penracourses.org.uk/api/atlas/get_cases_user"
|
|
||||||
#)
|
|
||||||
|
|
||||||
#print(new)
|
|
||||||
#print(new.content)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app()
|
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
import asyncio
|
||||||
import pynng
|
import pynng
|
||||||
#asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
||||||
|
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from local_file_picker import local_file_picker
|
|
||||||
|
|
||||||
from nicegui import ui, app, html, run, Client, native
|
from nicegui import ui, app, html, run, Client, native
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import zmq
|
|
||||||
import zmq.asyncio
|
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -26,10 +23,9 @@ import time
|
|||||||
|
|
||||||
from multiprocessing import Manager, Queue, freeze_support
|
from multiprocessing import Manager, Queue, freeze_support
|
||||||
|
|
||||||
import watchfiles
|
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
from local_file_picker import local_file_picker
|
||||||
from local_folder_picker import local_folder_picker
|
from local_folder_picker import local_folder_picker
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -37,35 +33,15 @@ from typing import Optional
|
|||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
#import typer
|
import typer
|
||||||
|
|
||||||
#typer_app = typer.Typer()
|
SOCKET_PATH = "tcp://localhost:9976"
|
||||||
|
|
||||||
#logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
|
WORK_DIR = Path("C:/uploader")
|
||||||
|
|
||||||
#async def pick_file() -> None:
|
EXPORT_PATH = WORK_DIR / Path("export/")
|
||||||
# result = await local_file_picker('~', multiple=True)
|
PROCESS_PATH = WORK_DIR / Path("to_process/")
|
||||||
# ui.notify(f'You chose {result}')
|
ANON_PATH = WORK_DIR / Path("anon/")
|
||||||
# ui.html("<h2>Uploader tool</h2>")
|
|
||||||
#
|
|
||||||
# ui.chat_message(f'{result}!',
|
|
||||||
# name='Robot',
|
|
||||||
# stamp='now',
|
|
||||||
# avatar='https://robohash.org/ui')
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#@ui.page('/')
|
|
||||||
#def index():
|
|
||||||
# ui.html("<h2>Uploader tool</h2>")
|
|
||||||
# ui.button('Choose file', on_click=pick_file, icon='folder')
|
|
||||||
|
|
||||||
NATIVE = False
|
|
||||||
|
|
||||||
SOCKET_PATH = 'tcp://localhost:9976'
|
|
||||||
|
|
||||||
EXPORT_PATH = Path("C:/uploader/export")
|
|
||||||
PROCESS_PATH = Path("C:/uploader/to_process")
|
|
||||||
ANON_PATH = Path("C:/uploader/anon")
|
|
||||||
|
|
||||||
EXPORT_PATH.mkdir(exist_ok=True, parents=True)
|
EXPORT_PATH.mkdir(exist_ok=True, parents=True)
|
||||||
PROCESS_PATH.mkdir(exist_ok=True, parents=True)
|
PROCESS_PATH.mkdir(exist_ok=True, parents=True)
|
||||||
@@ -81,6 +57,7 @@ LOGGED_IN_USER = "None"
|
|||||||
|
|
||||||
|
|
||||||
REFRESH_TRIGGERED = True
|
REFRESH_TRIGGERED = True
|
||||||
|
SHUTDOWN = False
|
||||||
|
|
||||||
DELETE_FILES_ON_UPLOAD = True
|
DELETE_FILES_ON_UPLOAD = True
|
||||||
|
|
||||||
@@ -96,8 +73,6 @@ if DEBUG:
|
|||||||
rqst = None
|
rqst = None
|
||||||
|
|
||||||
|
|
||||||
use_zmq = False
|
|
||||||
use_nng = True
|
|
||||||
loaded_files: dict = {}
|
loaded_files: dict = {}
|
||||||
loaded_series = defaultdict(list)
|
loaded_series = defaultdict(list)
|
||||||
loaded_series_data = {}
|
loaded_series_data = {}
|
||||||
@@ -105,71 +80,22 @@ uploaded_files: dict = {}
|
|||||||
duplicate_series = set()
|
duplicate_series = set()
|
||||||
|
|
||||||
|
|
||||||
|
async def read_nng_messages():
|
||||||
if use_zmq:
|
|
||||||
context = zmq.asyncio.Context()
|
|
||||||
socket = context.socket(zmq.PULL)
|
|
||||||
#socket.bind('tcp://localhost:5575')
|
|
||||||
try:
|
|
||||||
socket.bind(SOCKET_PATH)
|
|
||||||
except zmq.error.ZMQError as e:
|
|
||||||
logger.debug("send reload")
|
|
||||||
socket = context.socket(zmq.PUSH)
|
|
||||||
socket.connect(SOCKET_PATH)
|
|
||||||
socket.send_string("loaded")
|
|
||||||
sys.exit(0)
|
|
||||||
#app.shutdown()
|
|
||||||
|
|
||||||
poller = zmq.asyncio.Poller()
|
|
||||||
poller.register(socket, zmq.POLLIN)
|
|
||||||
|
|
||||||
async def read_loop() -> None:
|
|
||||||
while not app.is_stopped:
|
|
||||||
events = await poller.poll()
|
|
||||||
if socket in dict(events):
|
|
||||||
data = await socket.recv()
|
|
||||||
msg = data.decode()
|
|
||||||
|
|
||||||
logger.debug(f'Received msg {msg}')
|
|
||||||
|
|
||||||
match msg:
|
|
||||||
case "loaded":
|
|
||||||
for client in Client.instances.values():
|
|
||||||
if not client.has_socket_connection:
|
|
||||||
continue
|
|
||||||
with client:
|
|
||||||
ui.notify("Export done", color='positive')
|
|
||||||
|
|
||||||
global REFRESH_TRIGGERED
|
|
||||||
REFRESH_TRIGGERED = True
|
|
||||||
logger.debug("loaded")
|
|
||||||
|
|
||||||
#with updates:
|
|
||||||
# ui.item(f"{datetime.now().strftime('%H:%M:%S')} - {msg}")
|
|
||||||
#logger.debug("loaded")
|
|
||||||
#load_files_start()
|
|
||||||
|
|
||||||
|
|
||||||
case _:
|
|
||||||
logger.error("invalid message")
|
|
||||||
|
|
||||||
if use_nng:
|
|
||||||
async def read_nng_messages():
|
|
||||||
print("start nng")
|
print("start nng")
|
||||||
try:
|
try:
|
||||||
with pynng.Pair0(listen=SOCKET_PATH) as sub:
|
with pynng.Pair0(listen=SOCKET_PATH) as sub:
|
||||||
#sub.subscribe('')
|
# sub.subscribe('')
|
||||||
while not app.is_stopped:
|
while not app.is_stopped:
|
||||||
msg = await sub.arecv_msg()
|
msg = await sub.arecv_msg()
|
||||||
data = msg.bytes.decode()
|
data = msg.bytes.decode()
|
||||||
logger.debug(f'Received msg {data}')
|
logger.debug(f"Received msg {data}")
|
||||||
match data:
|
match data:
|
||||||
case "loaded":
|
case "loaded":
|
||||||
for client in Client.instances.values():
|
for client in Client.instances.values():
|
||||||
if not client.has_socket_connection:
|
if not client.has_socket_connection:
|
||||||
continue
|
continue
|
||||||
with client:
|
with client:
|
||||||
ui.notify("Export done", color='positive')
|
ui.notify("Export done", color="positive")
|
||||||
global REFRESH_TRIGGERED
|
global REFRESH_TRIGGERED
|
||||||
REFRESH_TRIGGERED = True
|
REFRESH_TRIGGERED = True
|
||||||
logger.debug("loaded")
|
logger.debug("loaded")
|
||||||
@@ -179,14 +105,19 @@ if use_nng:
|
|||||||
logger.debug("Address in use")
|
logger.debug("Address in use")
|
||||||
with pynng.Pair0(dial=SOCKET_PATH) as sub:
|
with pynng.Pair0(dial=SOCKET_PATH) as sub:
|
||||||
await sub.asend(b"loaded")
|
await sub.asend(b"loaded")
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
#asyncio.create_task(read_nng_messages())
|
# Trigger shutdown from in the async loop
|
||||||
|
# This shouldn't be necessary
|
||||||
|
global SHUTDOWN
|
||||||
|
SHUTDOWN = True
|
||||||
|
return
|
||||||
|
|
||||||
|
# asyncio.create_task(read_nng_messages())
|
||||||
|
|
||||||
|
|
||||||
anonymizer = Anonymizer(ANON_PATH)
|
anonymizer = Anonymizer(ANON_PATH)
|
||||||
|
|
||||||
|
|
||||||
async def upload_files_start(progress, upload_queue):
|
async def upload_files_start(progress, upload_queue):
|
||||||
global uploaded_files, duplicate_series
|
global uploaded_files, duplicate_series
|
||||||
progress.visible = True
|
progress.visible = True
|
||||||
@@ -195,67 +126,91 @@ async def upload_files_start(progress, upload_queue):
|
|||||||
progress.visible = False
|
progress.visible = False
|
||||||
|
|
||||||
if results:
|
if results:
|
||||||
new_uploaded_files, upload_file_list, duplicate_file_list, failed, duplicate_series = results
|
(
|
||||||
|
new_uploaded_files,
|
||||||
|
upload_file_list,
|
||||||
|
duplicate_file_list,
|
||||||
|
failed,
|
||||||
|
duplicate_series,
|
||||||
|
) = results
|
||||||
else:
|
else:
|
||||||
ui.notify("No files to upload", color="negative")
|
ui.notify("No files to upload", color="negative")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
uploaded_files.update(new_uploaded_files)
|
uploaded_files.update(new_uploaded_files)
|
||||||
|
|
||||||
ui.notify(f"Uploaded {len(upload_file_list)} files", color='positive')
|
ui.notify(f"Uploaded {len(upload_file_list)} files", color="positive")
|
||||||
ui.notify(f"Duplicate {len(duplicate_file_list)} files", color='warning')
|
|
||||||
ui.notify(f"Failed {len(failed)} files", color='negative')
|
if duplicate_file_list:
|
||||||
|
ui.notify(f"Duplicate {len(duplicate_file_list)} files", color="warning")
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
ui.notify(f"Failed {len(failed)} files", color="negative")
|
||||||
|
|
||||||
if DELETE_FILES_ON_UPLOAD:
|
if DELETE_FILES_ON_UPLOAD:
|
||||||
clear_anonymized_files()
|
clear_anonymized_files()
|
||||||
|
|
||||||
|
|
||||||
loaded_series_ui.refresh()
|
loaded_series_ui.refresh()
|
||||||
loaded_files_ui.refresh()
|
loaded_files_ui.refresh()
|
||||||
uploaded_files_ui.refresh()
|
uploaded_files_ui.refresh()
|
||||||
|
|
||||||
|
|
||||||
async def load_files_start(progress_bar, queue, custom_path=None):
|
async def load_files_start(progress_bar, queue, custom_path=None):
|
||||||
logger.debug("load files start")
|
logger.debug("load files start")
|
||||||
global loaded_files, loaded_series, loaded_series_data
|
global loaded_files, loaded_series, loaded_series_data
|
||||||
progress_bar.visible = True
|
progress_bar.visible = True
|
||||||
|
|
||||||
new_loaded_files, new_loaded_series, new_loaded_series_data = await run.cpu_bound(load_files, queue, custom_path)
|
new_loaded_files, new_loaded_series, new_loaded_series_data = await run.cpu_bound(
|
||||||
|
load_files, queue, custom_path
|
||||||
|
)
|
||||||
|
|
||||||
|
for client in Client.instances.values():
|
||||||
|
if not client.has_socket_connection:
|
||||||
|
continue
|
||||||
|
with client:
|
||||||
|
ui.notify(f"Files loaded: {len(new_loaded_files)}, [Series: {len(new_loaded_series)}] ", color="positive")
|
||||||
|
|
||||||
loaded_files.update(new_loaded_files)
|
loaded_files.update(new_loaded_files)
|
||||||
loaded_series.update(new_loaded_series)
|
loaded_series.update(new_loaded_series)
|
||||||
loaded_series_data.update(new_loaded_series_data)
|
loaded_series_data.update(new_loaded_series_data)
|
||||||
|
|
||||||
progress_bar.visible = False
|
progress_bar.visible = False
|
||||||
#load_series_view(loaded_series, loaded_series_data)
|
# load_series_view(loaded_series, loaded_series_data)
|
||||||
loaded_series_ui.refresh()
|
loaded_series_ui.refresh()
|
||||||
loaded_files_ui.refresh()
|
loaded_files_ui.refresh()
|
||||||
|
|
||||||
|
|
||||||
async def reload_anonymized_start(progress_bar, queue):
|
async def reload_anonymized_start(progress_bar, queue):
|
||||||
global loaded_files, loaded_series, loaded_series_data
|
global loaded_files, loaded_series, loaded_series_data
|
||||||
progress_bar.visible = True
|
progress_bar.visible = True
|
||||||
loaded_files, loaded_series, loaded_series_data = await run.cpu_bound(reload_anonymized, queue)
|
loaded_files, loaded_series, loaded_series_data = await run.cpu_bound(
|
||||||
|
reload_anonymized, queue
|
||||||
|
)
|
||||||
progress_bar.visible = False
|
progress_bar.visible = False
|
||||||
#load_series_view(loaded_series, loaded_series_data)
|
|
||||||
|
ui.notify(f"Files reloaded: {len(loaded_files)}, [Series: {len(loaded_series)}] ", color="positive")
|
||||||
|
# load_series_view(loaded_series, loaded_series_data)
|
||||||
loaded_series_ui.refresh()
|
loaded_series_ui.refresh()
|
||||||
loaded_files_ui.refresh()
|
loaded_files_ui.refresh()
|
||||||
|
|
||||||
|
|
||||||
@ui.refreshable
|
@ui.refreshable
|
||||||
def user_info_ui():
|
def user_info_ui():
|
||||||
global LOGGED_IN_USER
|
global LOGGED_IN_USER
|
||||||
user_label = ui.label(f"User: {LOGGED_IN_USER}")
|
user_label = ui.label(f"User: {LOGGED_IN_USER}")
|
||||||
#user_label.bind_visibility_from(globals(), "LOGIN_SUCCESS")
|
# user_label.bind_visibility_from(globals(), "LOGIN_SUCCESS")
|
||||||
|
|
||||||
|
|
||||||
@ui.refreshable
|
@ui.refreshable
|
||||||
def loaded_series_ui() -> None:
|
def loaded_series_ui() -> None:
|
||||||
global loaded_series, loaded_series_data
|
global loaded_series, loaded_series_data
|
||||||
|
|
||||||
series_title = ui.label('Series to upload').classes("text-h3")
|
series_title = ui.label("Series to upload").classes("text-h3")
|
||||||
series_title.visible = False
|
series_title.visible = False
|
||||||
|
|
||||||
series = ui.row()
|
series = ui.row()
|
||||||
|
|
||||||
#def load_series_view(loaded_series, loaded_series_data):
|
# def load_series_view(loaded_series, loaded_series_data):
|
||||||
logger.debug("load series view")
|
logger.debug("load series view")
|
||||||
logger.debug(loaded_series)
|
logger.debug(loaded_series)
|
||||||
for key in loaded_series:
|
for key in loaded_series:
|
||||||
@@ -269,9 +224,10 @@ def loaded_series_ui() -> None:
|
|||||||
if loaded_series:
|
if loaded_series:
|
||||||
series_title.visible = True
|
series_title.visible = True
|
||||||
|
|
||||||
|
|
||||||
@ui.refreshable
|
@ui.refreshable
|
||||||
def loaded_files_ui() -> None:
|
def loaded_files_ui() -> None:
|
||||||
with ui.expansion('Loaded files', icon='drive_folder_upload').classes('w-full'):
|
with ui.expansion("Loaded files", icon="drive_folder_upload").classes("w-full"):
|
||||||
ui.label(f"Items: {len(loaded_files)}")
|
ui.label(f"Items: {len(loaded_files)}")
|
||||||
with ui.list():
|
with ui.list():
|
||||||
for file, data in loaded_files.items():
|
for file, data in loaded_files.items():
|
||||||
@@ -280,15 +236,21 @@ def loaded_files_ui() -> None:
|
|||||||
|
|
||||||
@ui.refreshable
|
@ui.refreshable
|
||||||
def uploaded_files_ui() -> None:
|
def uploaded_files_ui() -> None:
|
||||||
with ui.expansion('Uploaded files', icon='file_upload').classes('w-full'):
|
with ui.expansion("Uploaded files", icon="file_upload").classes("w-full"):
|
||||||
ui.label(f"Items: {len(uploaded_files)}")
|
ui.label(f"Items: {len(uploaded_files)}")
|
||||||
with ui.list():
|
with ui.list():
|
||||||
for series in duplicate_series:
|
for series in duplicate_series:
|
||||||
ui.item(f"Duplicate series: {series}", on_click=lambda: ui.navigate.to(f'{BASE_SITE_URL}{series}', new_tab=True)).classes("text-red-500")
|
ui.item(
|
||||||
|
f"Duplicate series: {series}",
|
||||||
|
on_click=lambda: ui.navigate.to(
|
||||||
|
f"{BASE_SITE_URL}{series}", new_tab=True
|
||||||
|
),
|
||||||
|
).classes("text-red-500")
|
||||||
with ui.list():
|
with ui.list():
|
||||||
for file, data in uploaded_files.items():
|
for file, data in uploaded_files.items():
|
||||||
ui.item(f"{file} - {data}")
|
ui.item(f"{file} - {data}")
|
||||||
|
|
||||||
|
|
||||||
def load_files(q: Queue, src_path: Path | None = None):
|
def load_files(q: Queue, src_path: Path | None = None):
|
||||||
loaded_files: dict = {}
|
loaded_files: dict = {}
|
||||||
loaded_series = defaultdict(list)
|
loaded_series = defaultdict(list)
|
||||||
@@ -309,47 +271,58 @@ def load_files(q: Queue, src_path: Path | None = None):
|
|||||||
to_process_len = len(to_process)
|
to_process_len = len(to_process)
|
||||||
for n, file in enumerate(to_process):
|
for n, file in enumerate(to_process):
|
||||||
dataset, output_file = anonymizer.anonymize_file(file, remove_original=True)
|
dataset, output_file = anonymizer.anonymize_file(file, remove_original=True)
|
||||||
#with processed_files:
|
# with processed_files:
|
||||||
# ui.item(f"{datetime.now().strftime('%H:%M:%S')} - {file.name} -> {output_file.name}")
|
# ui.item(f"{datetime.now().strftime('%H:%M:%S')} - {file.name} -> {output_file.name}")
|
||||||
|
|
||||||
loaded_files[output_file] = (dataset.StudyDescription, dataset.SeriesDescription, str(dataset.SeriesInstanceUID))
|
loaded_files[output_file] = (
|
||||||
|
dataset.StudyDescription,
|
||||||
|
dataset.SeriesDescription,
|
||||||
|
str(dataset.SeriesInstanceUID),
|
||||||
|
)
|
||||||
|
|
||||||
loaded_series[dataset.SeriesInstanceUID].append(output_file)
|
loaded_series[dataset.SeriesInstanceUID].append(output_file)
|
||||||
loaded_series_data[dataset.SeriesInstanceUID] = (dataset.StudyDescription, dataset.SeriesDescription)
|
loaded_series_data[dataset.SeriesInstanceUID] = (
|
||||||
|
dataset.StudyDescription,
|
||||||
|
dataset.SeriesDescription,
|
||||||
|
)
|
||||||
|
|
||||||
q.put_nowait((n + 1) / to_process_len)
|
q.put_nowait((n + 1) / to_process_len)
|
||||||
|
|
||||||
#time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
|
|
||||||
return loaded_files, loaded_series, loaded_series_data
|
return loaded_files, loaded_series, loaded_series_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reload_anonymized(q: Queue):
|
def reload_anonymized(q: Queue):
|
||||||
loaded_files = {}
|
loaded_files = {}
|
||||||
loaded_series = defaultdict(list)
|
loaded_series = defaultdict(list)
|
||||||
loaded_series_data = {}
|
loaded_series_data = {}
|
||||||
|
|
||||||
#processed_files.clear()
|
# processed_files.clear()
|
||||||
|
|
||||||
|
|
||||||
to_process = list(ANON_PATH.glob("**/*.dcm"))
|
to_process = list(ANON_PATH.glob("**/*.dcm"))
|
||||||
to_process_len = len(to_process)
|
to_process_len = len(to_process)
|
||||||
for n, file in enumerate(to_process):
|
for n, file in enumerate(to_process):
|
||||||
with pydicom.dcmread(file) as dataset:
|
with pydicom.dcmread(file) as dataset:
|
||||||
loaded_files[file] = (dataset.StudyDescription, dataset.SeriesDescription, str(dataset.SeriesInstanceUID))
|
loaded_files[file] = (
|
||||||
|
dataset.StudyDescription,
|
||||||
|
dataset.SeriesDescription,
|
||||||
|
str(dataset.SeriesInstanceUID),
|
||||||
|
)
|
||||||
|
|
||||||
loaded_series[dataset.SeriesInstanceUID].append(file)
|
loaded_series[dataset.SeriesInstanceUID].append(file)
|
||||||
loaded_series_data[dataset.SeriesInstanceUID] = (dataset.StudyDescription, dataset.SeriesDescription)
|
loaded_series_data[dataset.SeriesInstanceUID] = (
|
||||||
|
dataset.StudyDescription,
|
||||||
|
dataset.SeriesDescription,
|
||||||
|
)
|
||||||
|
|
||||||
#with processed_files:
|
# with processed_files:
|
||||||
# ui.item(f"{datetime.now().strftime('%H:%M:%S')} - {file.name} -> Reloaded")
|
# ui.item(f"{datetime.now().strftime('%H:%M:%S')} - {file.name} -> Reloaded")
|
||||||
|
|
||||||
|
|
||||||
q.put_nowait(f"{(n + 1) / to_process_len * 100:.0f}%")
|
q.put_nowait(f"{(n + 1) / to_process_len * 100:.0f}%")
|
||||||
logger.debug(file)
|
logger.debug(file)
|
||||||
|
|
||||||
#time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
|
|
||||||
logger.debug("end reload")
|
logger.debug("end reload")
|
||||||
logger.debug(loaded_files)
|
logger.debug(loaded_files)
|
||||||
@@ -358,6 +331,7 @@ def reload_anonymized(q: Queue):
|
|||||||
|
|
||||||
return loaded_files, loaded_series, loaded_series_data
|
return loaded_files, loaded_series, loaded_series_data
|
||||||
|
|
||||||
|
|
||||||
def clear_anonymized_files():
|
def clear_anonymized_files():
|
||||||
for file in ANON_PATH.iterdir():
|
for file in ANON_PATH.iterdir():
|
||||||
if file.is_file():
|
if file.is_file():
|
||||||
@@ -367,8 +341,9 @@ def clear_anonymized_files():
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def upload_files(q, rqst):
|
def upload_files(q, rqst):
|
||||||
#global rqst#, uploaded_files
|
# global rqst#, uploaded_files
|
||||||
|
|
||||||
uploaded_files = {}
|
uploaded_files = {}
|
||||||
|
|
||||||
@@ -385,7 +360,6 @@ def upload_files(q, rqst):
|
|||||||
files_to_upload[i : i + n] for i in range(0, len(files_to_upload), n)
|
files_to_upload[i : i + n] for i in range(0, len(files_to_upload), n)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
upload_file_list = []
|
upload_file_list = []
|
||||||
duplicate_file_list = []
|
duplicate_file_list = []
|
||||||
failed = []
|
failed = []
|
||||||
@@ -445,26 +419,28 @@ def upload_files(q, rqst):
|
|||||||
for f in failed:
|
for f in failed:
|
||||||
uploaded_files[f] = "failed"
|
uploaded_files[f] = "failed"
|
||||||
|
|
||||||
|
return (
|
||||||
return uploaded_files, upload_file_list, duplicate_file_list, failed, duplicate_series
|
uploaded_files,
|
||||||
|
upload_file_list,
|
||||||
|
duplicate_file_list,
|
||||||
|
failed,
|
||||||
|
duplicate_series,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ui.page("/login")
|
||||||
|
|
||||||
@ui.page('/login')
|
|
||||||
def login() -> Optional[RedirectResponse]:
|
def login() -> Optional[RedirectResponse]:
|
||||||
dark = ui.dark_mode()
|
dark = ui.dark_mode()
|
||||||
dark.enable()
|
dark.enable()
|
||||||
def try_login() -> None: # local function to avoid passing username and password as arguments
|
|
||||||
logger.debug(username.value)
|
def try_login() -> (
|
||||||
#if passwords.get(username.value) == password.value:
|
None
|
||||||
# app.storage.user.update({'username': username.value, 'authenticated': True})
|
): # local function to avoid passing username and password as arguments
|
||||||
# ui.navigate.to(app.storage.user.get('referrer_path', '/')) # go back to where the user wanted to go
|
# Because I haven't gotten around to implementing a proper login system yet
|
||||||
#else:
|
# we just log in via requests and store teh session in the global rqst variable
|
||||||
# ui.notify('Wrong username or password', color='negative')
|
|
||||||
user = username.value
|
user = username.value
|
||||||
pw = password.value
|
pw = password.value
|
||||||
|
|
||||||
@@ -473,7 +449,7 @@ def login() -> Optional[RedirectResponse]:
|
|||||||
try:
|
try:
|
||||||
rsp = rqst.get(LOGIN_URL)
|
rsp = rqst.get(LOGIN_URL)
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
ui.notify('Connection error!', color='negative')
|
ui.notify("Connection error!", color="negative")
|
||||||
return
|
return
|
||||||
|
|
||||||
token = (
|
token = (
|
||||||
@@ -512,94 +488,97 @@ def login() -> Optional[RedirectResponse]:
|
|||||||
LOGIN_SUCCESS = True
|
LOGIN_SUCCESS = True
|
||||||
LOGGED_IN_USER = user
|
LOGGED_IN_USER = user
|
||||||
user_info_ui.refresh()
|
user_info_ui.refresh()
|
||||||
#user_label.clear()
|
# user_label.clear()
|
||||||
#user_label.text = f"User: {user}"
|
# user_label.text = f"User: {user}"
|
||||||
# print(rqst.headers)
|
# print(rqst.headers)
|
||||||
|
|
||||||
rqst.headers["X-CSRFToken"] = token
|
rqst.headers["X-CSRFToken"] = token
|
||||||
|
|
||||||
print(rqst.headers)
|
|
||||||
print(rqst.cookies)
|
|
||||||
new = rqst.get(f"{BASE_SITE_URL}/accounts/api/atlas/get_cases_user")
|
|
||||||
|
|
||||||
|
|
||||||
if LOGIN_SUCCESS:
|
if LOGIN_SUCCESS:
|
||||||
ui.notify('Logged in!', color='positive')
|
ui.notify("Logged in!", color="positive")
|
||||||
ui.navigate.to("/")
|
ui.navigate.to("/")
|
||||||
#login_button.visible = False
|
|
||||||
pass
|
|
||||||
#self.parent.upload_button.Enable()
|
|
||||||
#self.parent.login_button.Hide()
|
|
||||||
#self.parent.panel.Layout()
|
|
||||||
#self.Destroy()
|
|
||||||
|
|
||||||
# self.parent.login_button.SetBackgroundColour(wx.BLUE)
|
|
||||||
# self.parent.login_button.SetForegroundColour(wx.WHITE)
|
|
||||||
else:
|
else:
|
||||||
ui.notify('Wrong username or password', color='negative')
|
ui.notify("Wrong username or password", color="negative")
|
||||||
pass
|
|
||||||
#self.login_fail()
|
|
||||||
|
|
||||||
#if app.storage.user.get('authenticated', False):
|
# if app.storage.user.get('authenticated', False):
|
||||||
# return RedirectResponse('/')
|
# return RedirectResponse('/')
|
||||||
with ui.card().classes('absolute-center'):
|
with ui.card().classes("absolute-center"):
|
||||||
username = ui.input('Username').on('keydown.enter', try_login)
|
username = ui.input("Username").on("keydown.enter", try_login)
|
||||||
password = ui.input('Password', password=True, password_toggle_button=True).on('keydown.enter', try_login)
|
password = ui.input("Password", password=True, password_toggle_button=True).on(
|
||||||
ui.button('Log in', on_click=try_login)
|
"keydown.enter", try_login
|
||||||
|
)
|
||||||
|
with ui.row():
|
||||||
|
ui.button("Log in", on_click=try_login)
|
||||||
|
ui.button("Cancel", on_click=lambda: ui.navigate.to("/"), color="red")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ui.page('/')
|
|
||||||
def main_page():
|
|
||||||
|
|
||||||
|
@ui.page("/")
|
||||||
|
def main_page():
|
||||||
|
async def watch_for_shutdown():
|
||||||
|
global SHUTDOWN
|
||||||
|
while not app.is_stopped:
|
||||||
|
if SHUTDOWN:
|
||||||
|
logger.debug("Trigger app shutdown")
|
||||||
|
app.shutdown()
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
asyncio.create_task(watch_for_shutdown())
|
||||||
|
|
||||||
dark = ui.dark_mode()
|
dark = ui.dark_mode()
|
||||||
dark.enable()
|
dark.enable()
|
||||||
ui.page_title('Uploader tool')
|
ui.page_title("Uploader tool")
|
||||||
ui.label('Uploader tool').classes("text-h2")
|
ui.label("Uploader tool").classes("text-h2")
|
||||||
|
|
||||||
user_info_ui()
|
user_info_ui()
|
||||||
|
|
||||||
login_button = ui.button("LOGIN", on_click=lambda: ui.navigate.to("/login"))
|
login_button = ui.button("LOGIN", on_click=lambda: ui.navigate.to("/login"))
|
||||||
login_button.bind_visibility_from(globals(), "LOGIN_SUCCESS", backward=lambda v: not v)
|
login_button.bind_visibility_from(
|
||||||
|
globals(), "LOGIN_SUCCESS", backward=lambda v: not v
|
||||||
|
)
|
||||||
|
|
||||||
updates = ui.list()
|
updates = ui.list()
|
||||||
|
|
||||||
loaded_series_ui()
|
loaded_series_ui()
|
||||||
|
|
||||||
|
# typer_app()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# typer_app()
|
|
||||||
# Create a queue to communicate with the heavy computation process
|
# Create a queue to communicate with the heavy computation process
|
||||||
queue = Manager().Queue()
|
queue = Manager().Queue()
|
||||||
# Update the progress bar on the main process
|
# Update the progress bar on the main process
|
||||||
ui.timer(0.1, callback=lambda: progressbar.set_value(queue.get() if not queue.empty() else progressbar.value))
|
ui.timer(
|
||||||
|
0.1,
|
||||||
|
callback=lambda: progressbar.set_value(
|
||||||
|
queue.get() if not queue.empty() else progressbar.value
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# Create a queue to communicate with the heavy computation process
|
# Create a queue to communicate with the heavy computation process
|
||||||
upload_queue = Manager().Queue()
|
upload_queue = Manager().Queue()
|
||||||
# Update the progress bar on the main process
|
# Update the progress bar on the main process
|
||||||
ui.timer(0.1, callback=lambda: upload_progressbar.set_value(upload_queue.get() if not upload_queue.empty() else upload_progressbar.value))
|
ui.timer(
|
||||||
|
0.1,
|
||||||
|
callback=lambda: upload_progressbar.set_value(
|
||||||
|
upload_queue.get() if not upload_queue.empty() else upload_progressbar.value
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
upload_progress = ui.row().classes("w-full place-content-center bg-blue-900")
|
upload_progress = ui.row().classes("w-full place-content-center bg-blue-900")
|
||||||
|
|
||||||
with upload_progress:
|
with upload_progress:
|
||||||
ui.label("Uploading files")
|
ui.label("Uploading files")
|
||||||
upload_progressbar = ui.linear_progress(value=0).props('instant-feedback')
|
upload_progressbar = ui.linear_progress(value=0).props("instant-feedback")
|
||||||
upload_progress.visible = False
|
upload_progress.visible = False
|
||||||
|
|
||||||
upload_button = ui.button("Upload", on_click=partial(upload_files_start, upload_progress, upload_queue))
|
upload_button = ui.button(
|
||||||
|
"Upload", on_click=partial(upload_files_start, upload_progress, upload_queue)
|
||||||
|
)
|
||||||
upload_button.bind_visibility_from(globals(), "LOGIN_SUCCESS")
|
upload_button.bind_visibility_from(globals(), "LOGIN_SUCCESS")
|
||||||
|
|
||||||
anon_progress = ui.row().classes("w-full place-content-center bg-blue-900")
|
anon_progress = ui.row().classes("w-full place-content-center bg-blue-900")
|
||||||
|
|
||||||
with anon_progress:
|
with anon_progress:
|
||||||
ui.label("Anonymizing files")
|
ui.label("Anonymizing files")
|
||||||
progressbar = ui.linear_progress(value=0).props('instant-feedback')
|
progressbar = ui.linear_progress(value=0).props("instant-feedback")
|
||||||
anon_progress.visible = False
|
anon_progress.visible = False
|
||||||
|
|
||||||
async def load_files_from_folder() -> None:
|
async def load_files_from_folder() -> None:
|
||||||
@@ -608,20 +587,25 @@ def main_page():
|
|||||||
if folder is not None:
|
if folder is not None:
|
||||||
ui.notify(f"Selected folder: {folder}")
|
ui.notify(f"Selected folder: {folder}")
|
||||||
await load_files_start(anon_progress, queue, folder)
|
await load_files_start(anon_progress, queue, folder)
|
||||||
#return dir
|
# return dir
|
||||||
else:
|
else:
|
||||||
ui.notify("No folder selected", color='negative')
|
ui.notify("No folder selected", color="negative")
|
||||||
|
|
||||||
with ui.expansion('Extra', icon='build').classes('w-full'):
|
with ui.expansion("Extra", icon="build").classes("w-full"):
|
||||||
ui.button("Load files", on_click=partial(load_files_start, anon_progress, queue))
|
ui.button(
|
||||||
|
"Load files", on_click=partial(load_files_start, anon_progress, queue)
|
||||||
|
)
|
||||||
ui.button("Load files from folder", on_click=load_files_from_folder)
|
ui.button("Load files from folder", on_click=load_files_from_folder)
|
||||||
ui.button("Reload anon", on_click=partial(reload_anonymized_start, anon_progress, queue))
|
ui.button(
|
||||||
|
"Reload anon",
|
||||||
|
on_click=partial(reload_anonymized_start, anon_progress, queue),
|
||||||
|
)
|
||||||
|
|
||||||
with ui.expansion('File status', icon='view_list').classes('w-full'):
|
with ui.expansion("File status", icon="view_list").classes("w-full"):
|
||||||
loaded_files_ui()
|
loaded_files_ui()
|
||||||
uploaded_files_ui()
|
uploaded_files_ui()
|
||||||
|
|
||||||
#with ui.expansion('Processed files!', icon='work').classes('w-full'):
|
# with ui.expansion('Processed files!', icon='work').classes('w-full'):
|
||||||
# processed_files = ui.list()
|
# processed_files = ui.list()
|
||||||
|
|
||||||
with ui.expansion("Settings", icon="settings").classes("w-full"):
|
with ui.expansion("Settings", icon="settings").classes("w-full"):
|
||||||
@@ -629,13 +613,13 @@ def main_page():
|
|||||||
ui.label(f"Export path: {EXPORT_PATH}")
|
ui.label(f"Export path: {EXPORT_PATH}")
|
||||||
ui.label(f"Process path: {PROCESS_PATH}")
|
ui.label(f"Process path: {PROCESS_PATH}")
|
||||||
ui.label(f"Anon path: {ANON_PATH}")
|
ui.label(f"Anon path: {ANON_PATH}")
|
||||||
ui.button("Clear anon path", on_click=clear_anonymized_files).tooltip("Delete all files in ANON_PATH")
|
ui.button("Clear anon path", on_click=clear_anonymized_files).tooltip(
|
||||||
|
"Delete all files in ANON_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
ui.button("Shutdown", on_click=app.shutdown)
|
def shutdown_app():
|
||||||
|
app.shutdown()
|
||||||
|
ui.run_javascript("window.close()")
|
||||||
|
|
||||||
ui.notify("Welcome to the uploader tool", color='positive')
|
|
||||||
|
|
||||||
async def watch_for_refresh():
|
async def watch_for_refresh():
|
||||||
global REFRESH_TRIGGERED
|
global REFRESH_TRIGGERED
|
||||||
@@ -647,15 +631,58 @@ def main_page():
|
|||||||
|
|
||||||
asyncio.create_task(watch_for_refresh())
|
asyncio.create_task(watch_for_refresh())
|
||||||
|
|
||||||
|
with ui.dialog() as about_dialog:
|
||||||
|
with ui.card().classes("absolute-center"):
|
||||||
|
ui.label("Uploader tool").classes("text-h2")
|
||||||
|
ui.label("A tool to help upload files to penracourses.org.uk")
|
||||||
|
ui.image("icon/icon1.png")
|
||||||
|
ui.label("Version: 0.1")
|
||||||
|
ui.label("Author: Ross Kruger")
|
||||||
|
|
||||||
|
ui.button("Close", on_click=about_dialog.close, icon="close", color="secondary")
|
||||||
|
|
||||||
|
with ui.footer():
|
||||||
|
with ui.row():
|
||||||
|
ui.button("Shutdown", on_click=shutdown_app).props("outline color=white")
|
||||||
|
ui.button("About", on_click=about_dialog.open).props("outline color=white")
|
||||||
|
|
||||||
|
|
||||||
|
def launch_app(
|
||||||
|
work_dir: Path = typer.Option(WORK_DIR, help="Working directory"),
|
||||||
|
nng: bool = typer.Option(True, help="Use nng"),
|
||||||
|
native_mode: bool = typer.Option(False, help="Use native mode"),
|
||||||
|
):
|
||||||
|
global WORK_DIR, EXPORT_PATH, PROCESS_PATH, ANON_PATH
|
||||||
|
|
||||||
|
WORK_DIR = work_dir
|
||||||
|
EXPORT_PATH = WORK_DIR / Path("export/")
|
||||||
|
PROCESS_PATH = WORK_DIR / Path("to_process/")
|
||||||
|
ANON_PATH = WORK_DIR / Path("anon/")
|
||||||
|
|
||||||
|
logger.debug(f"Work dir: {WORK_DIR}")
|
||||||
|
|
||||||
|
if nng:
|
||||||
|
try:
|
||||||
|
with pynng.Pair0(listen=SOCKET_PATH) as sub:
|
||||||
|
pass
|
||||||
|
except pynng.exceptions.AddressInUse:
|
||||||
|
logger.debug("Address in use")
|
||||||
|
with pynng.Pair0(dial=SOCKET_PATH) as sub:
|
||||||
|
sub.send(b"loaded")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
app.on_startup(read_nng_messages)
|
||||||
|
|
||||||
|
app.on_exception(logger.debug)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ui.run(reload=False, show=True, port=native.find_open_port(), native=native_mode, favicon="icon/icon1.ico")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ in {"__main__", "__mp_main__"}:
|
if __name__ in {"__main__", "__mp_main__"}:
|
||||||
freeze_support()
|
freeze_support()
|
||||||
|
|
||||||
if use_zmq:
|
typer.run(launch_app)
|
||||||
app.on_startup(read_loop)
|
|
||||||
if use_nng:
|
|
||||||
app.on_startup(read_nng_messages)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ui.run(storage_secret="123456", reload=False, show=True, port=native.find_open_port(), native=NATIVE)
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import pynng
|
|
||||||
|
|
||||||
with pynng.Pair0(listen='tcp://127.0.0.1:54321') as s1:
|
|
||||||
|
|
||||||
with pynng.Pair0(listen='tcp://127.0.0.1:54321') as s2:
|
|
||||||
pass
|
|
||||||
-193
@@ -1,193 +0,0 @@
|
|||||||
import webview
|
|
||||||
import time
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from datauri import DataURI
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pydicom
|
|
||||||
import dicognito.anonymizer
|
|
||||||
|
|
||||||
import typer
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from watchdog.observers import Observer
|
|
||||||
|
|
||||||
# from watchdog.events import LoggingEventHandler
|
|
||||||
from watchdog.events import PatternMatchingEventHandler
|
|
||||||
|
|
||||||
|
|
||||||
anonymizer = dicognito.anonymizer.Anonymizer()
|
|
||||||
|
|
||||||
app = typer.Typer()
|
|
||||||
|
|
||||||
|
|
||||||
def anonymize_file(filepath):
|
|
||||||
try:
|
|
||||||
with pydicom.dcmread(filepath) as dataset:
|
|
||||||
anonymizer.anonymize(dataset)
|
|
||||||
p = Path(filepath)
|
|
||||||
new_filename = Path(p.parent, f"{p.stem}_anon{p.suffix}")
|
|
||||||
dataset.save_as(new_filename)
|
|
||||||
return new_filename
|
|
||||||
except:
|
|
||||||
# Cannot anonymise so just return the same path
|
|
||||||
return filepath
|
|
||||||
|
|
||||||
|
|
||||||
class PythonJavascriptApi:
|
|
||||||
def __init__(self):
|
|
||||||
self.cancel_heavy_stuff_flag = False
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
response = {"message": "Hello from Python {0}".format(sys.version)}
|
|
||||||
return response
|
|
||||||
|
|
||||||
def sayHelloTo(self, name):
|
|
||||||
response = {"message": "Hello {0}!".format(name)}
|
|
||||||
return response
|
|
||||||
|
|
||||||
def error(self):
|
|
||||||
raise Exception("This is a Python exception")
|
|
||||||
|
|
||||||
|
|
||||||
class Watcher:
|
|
||||||
def __init__(self, window, anonymise: bool) -> None:
|
|
||||||
self.window = window
|
|
||||||
self.anonymise = anonymise
|
|
||||||
|
|
||||||
def on_created(self, event):
|
|
||||||
print(f"hey, {event.src_path} has been created!")
|
|
||||||
path = event.src_path
|
|
||||||
if self.anonymise:
|
|
||||||
path = anonymize_file(path)
|
|
||||||
|
|
||||||
try:
|
|
||||||
file_uri = DataURI.from_file(path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("File deleted...")
|
|
||||||
return
|
|
||||||
|
|
||||||
file_name = Path(path).name
|
|
||||||
self.injectFile(file_uri, file_name)
|
|
||||||
|
|
||||||
def on_deleted(self, event):
|
|
||||||
print(f"what the f**k! Someone deleted {event.src_path}!")
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
print(f"hey buddy, {event.src_path} has been modified")
|
|
||||||
|
|
||||||
def on_moved(self, event):
|
|
||||||
print(f"ok ok ok, someone moved {event.src_path} to {event.dest_path}")
|
|
||||||
|
|
||||||
def injectFile(self, file_uri: DataURI, file_name: str):
|
|
||||||
current_url = webview.windows[0].get_current_url()
|
|
||||||
if current_url == "https://www.penracourses.org.uk/rapids/create/":
|
|
||||||
print(f"inject {file_name}")
|
|
||||||
self.window.evaluate_js(
|
|
||||||
f"""
|
|
||||||
function urltoFile(url, filename, mimeType){{
|
|
||||||
return (fetch(url)
|
|
||||||
.then(function(res){{ return res.arrayBuffer(); }})
|
|
||||||
.then(function(buf){{return new File([buf], filename,{{type:mimeType}});}})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(() => {{
|
|
||||||
//Usage example:
|
|
||||||
urltoFile('{file_uri}', '{file_name}', '{file_uri.mimetype}')
|
|
||||||
.then(function(file){{
|
|
||||||
|
|
||||||
console.log(file);
|
|
||||||
|
|
||||||
//inputs = $("#rapid-form input[type=file][id^=id_images-]");
|
|
||||||
inputs = extendInputs(1);
|
|
||||||
console.log(inputs);
|
|
||||||
el = inputs.get(0);
|
|
||||||
console.log(el);
|
|
||||||
|
|
||||||
let dT = new DataTransfer();
|
|
||||||
dT.clearData();
|
|
||||||
dT.items.add(file);
|
|
||||||
|
|
||||||
el.files = dT.files;
|
|
||||||
|
|
||||||
// Manually fire the change event (qt doesn't seem to...)
|
|
||||||
$(el).change();
|
|
||||||
|
|
||||||
|
|
||||||
}});
|
|
||||||
|
|
||||||
}});
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def anatomy(filepath: str):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def rapid(watch_path: Path = "/home/ross/watch", anonymise: bool = True):
|
|
||||||
|
|
||||||
api = PythonJavascriptApi()
|
|
||||||
window = webview.create_window(
|
|
||||||
"Upload Rapid", "https://www.penracourses.org.uk/rapids/create/", js_api=api
|
|
||||||
)
|
|
||||||
|
|
||||||
# def on_loaded():
|
|
||||||
# print('DOM is ready')
|
|
||||||
# current_url = webview.windows[0].get_current_url()
|
|
||||||
|
|
||||||
# if current_url == "https://www.penracourses.org.uk/rapids/create/":
|
|
||||||
# #window.evaluate_js("alert('tent')")
|
|
||||||
|
|
||||||
# for file_uri, file_name in uri_name_list:
|
|
||||||
# #time.sleep(2)
|
|
||||||
|
|
||||||
# ## unsubscribe event listener
|
|
||||||
# #webview.windows[0].loaded -= on_loaded
|
|
||||||
# #webview.windows[0].load_url('https://google.com')
|
|
||||||
|
|
||||||
# window.loaded += on_loaded
|
|
||||||
# window = upload_rapid(["/home/ross/Downloads/DICOM CT/DICOM CXR/STD1/SER1/CXR.dcm"])
|
|
||||||
|
|
||||||
patterns = ["*"]
|
|
||||||
ignore_patterns = ["*anon*"]
|
|
||||||
ignore_directories = False
|
|
||||||
case_sensitive = True
|
|
||||||
my_event_handler = PatternMatchingEventHandler(
|
|
||||||
patterns, ignore_patterns, ignore_directories, case_sensitive
|
|
||||||
)
|
|
||||||
|
|
||||||
watcher = Watcher(window, anonymise)
|
|
||||||
my_event_handler.on_created = watcher.on_created
|
|
||||||
my_event_handler.on_deleted = watcher.on_deleted
|
|
||||||
my_event_handler.on_modified = watcher.on_modified
|
|
||||||
my_event_handler.on_moved = watcher.on_moved
|
|
||||||
my_observer = Observer()
|
|
||||||
|
|
||||||
Path(watch_path).mkdir(parents=True, exist_ok=True)
|
|
||||||
my_observer.schedule(my_event_handler, watch_path, recursive=True)
|
|
||||||
|
|
||||||
my_observer.start()
|
|
||||||
# try:
|
|
||||||
# while True:
|
|
||||||
# time.sleep(1)
|
|
||||||
# except KeyboardInterrupt:
|
|
||||||
# my_observer.stop()
|
|
||||||
# my_observer.join()
|
|
||||||
webview.start(None, window, debug=True, gui="qt")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# f = "/media/ross/f6200b79-c343-4882-a245-28fc06d43006/Downloads/temp/breasts.jpg"
|
|
||||||
f = "/home/ross/Downloads/DICOM CT/DICOM CXR/STD1/SER1/CXR.dcm"
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app()
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import asyncio
|
|
||||||
import random
|
|
||||||
|
|
||||||
import zmq
|
|
||||||
import zmq.asyncio
|
|
||||||
|
|
||||||
context = zmq.asyncio.Context()
|
|
||||||
socket = context.socket(zmq.PUSH)
|
|
||||||
socket.connect('tcp://localhost:5575')
|
|
||||||
|
|
||||||
|
|
||||||
async def send_loop():
|
|
||||||
#print(f'Sending number {number}')
|
|
||||||
print("Send hello")
|
|
||||||
await socket.send_string("loaded")
|
|
||||||
#await asyncio.sleep(0.1)
|
|
||||||
|
|
||||||
print("end")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(send_loop())
|
|
||||||
Reference in New Issue
Block a user