Skip to content

Commit

Permalink
Merge pull request #20 from eyra/extraction-status-message
Browse files Browse the repository at this point in the history
#18 Print state on screen during extraction
  • Loading branch information
mellelieuwes authored Jun 27, 2024
2 parents 2ad2609 + 6d14f13 commit 2ff2bfd
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

## \#2 unreleased

* Added: Support for progress prompt
* Added: German translations
* Added: Support for assets available in Python

Expand Down
2 changes: 1 addition & 1 deletion src/framework/command_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class CommandRouter implements CommandHandler {
this.bridge.send(command)

if (isCommandSystemExit(command)) {
console.log("[CommandRouter] Application exit")
console.log('[CommandRouter] Application exit')
} else {
resolve({ __type__: 'Response', command, payload: { __type__: 'PayloadVoid', value: undefined } })
}
Expand Down
23 changes: 23 additions & 0 deletions src/framework/processing/py/port/api/props.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,29 @@ def toDict(self):
return dict


@dataclass
class PropsUIPromptProgress:
"""Prompt the user information during the extraction
Attributes:
description: text with an explanation
message: can be used to show extraction progress
"""

description: Translatable
message: str
percentage: Optional[int] = None

def toDict(self):
dict = {}
dict["__type__"] = "PropsUIPromptProgress"
dict["description"] = self.description.toDict()
dict["message"] = self.message
dict["percentage"] = self.percentage

return dict


class RadioItem(TypedDict):
"""Radio button
Expand Down
56 changes: 42 additions & 14 deletions src/framework/processing/py/port/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pandas as pd
import zipfile
import json
import time


def process(sessionId):
Expand All @@ -21,11 +22,23 @@ def process(sessionId):
promptFile = prompt_file("application/zip, text/plain")
fileResult = yield render_donation_page(promptFile)
if fileResult.__type__ == 'PayloadString':
# Extracting the zipfile
meta_data.append(("debug", f"{key}: extracting file"))
extractionResult = doSomethingWithTheFile(fileResult.value)
if extractionResult != 'invalid':
extraction_result = []
zipfile_ref = get_zipfile(fileResult.value)
print(zipfile_ref, fileResult.value)
files = get_files(zipfile_ref)
fileCount = len(files)
for index, filename in enumerate(files):
percentage = ((index+1)/fileCount)*100
promptMessage = prompt_extraction_message(f"Extracting file: {filename}", percentage)
yield render_donation_page(promptMessage)
file_extraction_result = extract_file(zipfile_ref, filename)
extraction_result.append(file_extraction_result)

if len(extraction_result) >= 0:
meta_data.append(("debug", f"{key}: extraction successful, go to consent form"))
data = extractionResult
data = extraction_result
break
else:
meta_data.append(("debug", f"{key}: prompt confirmation to retry file selection"))
Expand Down Expand Up @@ -88,24 +101,39 @@ def prompt_file(extensions):

return props.PropsUIPromptFileInput(description, extensions)

def prompt_extraction_message(message, percentage):
description = props.Translatable({
"en": "One moment please. Information is now being extracted from the selected file.",
"de": "Einen Moment bitte. Es werden nun Informationen aus der ausgewählten Datei extrahiert.",
"nl": "Een moment geduld. Informatie wordt op dit moment uit het geselecteerde bestaand gehaald."
})

def doSomethingWithTheFile(filename):
return extract_zip_contents(filename)
return props.PropsUIPromptProgress(description, message, percentage)


def extract_zip_contents(filename):
names = []
def get_zipfile(filename):
try:
file = zipfile.ZipFile(filename)
data = []
for name in file.namelist():
names.append(name)
info = file.getinfo(name)
data.append((name, info.compress_size, info.file_size))
return data
return zipfile.ZipFile(filename)
except zipfile.error:
return "invalid"


def get_files(zipfile_ref):
try:
return zipfile_ref.namelist()
except zipfile.error:
return []


def extract_file(zipfile_ref, filename):
try:
# make it slow for demo reasons only
time.sleep(1)
info = zipfile_ref.getinfo(filename)
return (filename, info.compress_size, info.file_size)
except zipfile.error:
return "invalid"


def prompt_consent(data, meta_data):

Expand Down
12 changes: 6 additions & 6 deletions src/framework/processing/worker_engine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommandHandler, ProcessingEngine } from '../types/modules'
import {CommandSystemEvent, isCommand, Response } from '../types/commands'
import { CommandSystemEvent, isCommand, Response } from '../types/commands'

export default class WorkerProcessingEngine implements ProcessingEngine {
sessionId: String
Expand All @@ -23,8 +23,8 @@ export default class WorkerProcessingEngine implements ProcessingEngine {
}
}

sendSystemEvent(name: string): void {
const command: CommandSystemEvent = { __type__: 'CommandSystemEvent', name}
sendSystemEvent (name: string): void {
const command: CommandSystemEvent = { __type__: 'CommandSystemEvent', name }
this.commandHandler.onCommand(command).then(
() => {},
() => {}
Expand Down Expand Up @@ -58,9 +58,9 @@ export default class WorkerProcessingEngine implements ProcessingEngine {
const waitForInitialization: Promise<void> = this.waitForInitialization()

waitForInitialization.then(
() => {
this.sendSystemEvent("initialized")
this.firstRunCycle()
() => {
this.sendSystemEvent('initialized')
this.firstRunCycle()
},
() => {}
)
Expand Down
2 changes: 1 addition & 1 deletion src/framework/types/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export type CommandSystem =
CommandSystemExit

export function isCommandSystem (arg: any): arg is CommandSystem {
return isCommandSystemDonate(arg) || isCommandSystemEvent(arg) || isCommandSystemExit(arg)
return isCommandSystemDonate(arg) || isCommandSystemEvent(arg) || isCommandSystemExit(arg)
}

export interface CommandSystemEvent {
Expand Down
14 changes: 7 additions & 7 deletions src/framework/types/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type PropsUI =
PropsUICheckBox |
PropsUIRadioItem |
PropsUISpinner |
PropsUIProgress |
PropsUIProgressBar |
PropsUIHeader |
PropsUITable |
PropsUISearchBar |
Expand Down Expand Up @@ -310,17 +310,17 @@ export function isPropsUISpinner (arg: any): arg is PropsUISpinner {
return isInstanceOf<PropsUISpinner>(arg, 'PropsUISpinner', ['color', 'spinning'])
}

// PROGRESS
// PROGRESS BAR

export interface PropsUIProgress {
__type__: 'PropsUIProgress'
export interface PropsUIProgressBar {
__type__: 'PropsUIProgressBar'
percentage: number
}
export function isPropsUIProgress (arg: any): arg is PropsUIProgress {
return isInstanceOf<PropsUIProgress>(arg, 'PropsUIProgress', ['percentage'])
export function isPropsUIProgress (arg: any): arg is PropsUIProgressBar {
return isInstanceOf<PropsUIProgressBar>(arg, 'PropsUIProgressBar', ['percentage'])
}

// Header
// HEADER

export interface PropsUIHeader {
__type__: 'PropsUIHeader'
Expand Down
4 changes: 2 additions & 2 deletions src/framework/types/pages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isInstanceOf } from '../helpers'
import { PropsUIHeader } from './elements'
import { PropsUIPromptFileInput, PropsUIPromptConfirm, PropsUIPromptConsentForm, PropsUIPromptRadioInput } from './prompts'
import { PropsUIPromptFileInput, PropsUIPromptProgress, PropsUIPromptConfirm, PropsUIPromptConsentForm, PropsUIPromptRadioInput } from './prompts'

export type PropsUIPage =
PropsUIPageSplashScreen |
Expand All @@ -22,7 +22,7 @@ export interface PropsUIPageDonation {
__type__: 'PropsUIPageDonation'
platform: string
header: PropsUIHeader
body: PropsUIPromptFileInput | PropsUIPromptConfirm | PropsUIPromptConsentForm | PropsUIPromptRadioInput
body: PropsUIPromptFileInput | PropsUIPromptProgress | PropsUIPromptConfirm | PropsUIPromptConsentForm | PropsUIPromptRadioInput
}
export function isPropsUIPageDonation (arg: any): arg is PropsUIPageDonation {
return isInstanceOf<PropsUIPageDonation>(arg, 'PropsUIPageDonation', ['platform', 'header', 'body'])
Expand Down
11 changes: 11 additions & 0 deletions src/framework/types/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PropsUIRadioItem, Text } from './elements'

export type PropsUIPrompt =
PropsUIPromptFileInput |
PropsUIPromptProgress |
PropsUIPromptRadioInput |
PropsUIPromptConsentForm |
PropsUIPromptConfirm
Expand Down Expand Up @@ -32,6 +33,16 @@ export function isPropsUIPromptFileInput (arg: any): arg is PropsUIPromptFileInp
return isInstanceOf<PropsUIPromptFileInput>(arg, 'PropsUIPromptFileInput', ['description', 'extensions'])
}

export interface PropsUIPromptProgress {
__type__: 'PropsUIPromptProgress'
description: Text
message: string
percentage?: number
}
export function isPropsUIPromptProgress (arg: any): arg is PropsUIPromptProgress {
return isInstanceOf<PropsUIPromptProgress>(arg, 'PropsUIPromptProgress', ['description', 'message'])
}

export interface PropsUIPromptRadioInput {
__type__: 'PropsUIPromptRadioInput'
title: Text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Weak } from '../../../../helpers'
import { PropsUIProgress } from '../../../../types/elements'
import { PropsUIProgressBar } from '../../../../types/elements'

type Props = Weak<PropsUIProgress>
type Props = Weak<PropsUIProgressBar>

export const Progress = ({ percentage }: Props): JSX.Element => {
export const ProgressBar = ({ percentage }: Props): JSX.Element => {
return (
<div id='progress' className='relative w-full overflow-hidden rounded-full'>
<div className='flex flex-row items-center gap-4'>
Expand Down
6 changes: 5 additions & 1 deletion src/framework/visualisation/react/ui/pages/donation_page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import TextBundle from '../../../../text_bundle'
import { Translator } from '../../../../translator'
import { Translatable } from '../../../../types/elements'
import { PropsUIPageDonation } from '../../../../types/pages'
import { isPropsUIPromptConfirm, isPropsUIPromptConsentForm, isPropsUIPromptFileInput, isPropsUIPromptRadioInput } from '../../../../types/prompts'
import { isPropsUIPromptConfirm, isPropsUIPromptConsentForm, isPropsUIPromptFileInput, isPropsUIPromptProgress, isPropsUIPromptRadioInput } from '../../../../types/prompts'
import { ReactFactoryContext } from '../../factory'
import { Title1 } from '../elements/text'
import { Confirm } from '../prompts/confirm'
import { ConsentForm } from '../prompts/consent_form'
import { FileInput } from '../prompts/file_input'
import { Progress } from '../prompts/progress'
import { RadioInput } from '../prompts/radio_input'
import { Page } from './templates/page'

Expand All @@ -25,6 +26,9 @@ export const DonationPage = (props: Props): JSX.Element => {
if (isPropsUIPromptFileInput(body)) {
return <FileInput {...body} {...context} />
}
if (isPropsUIPromptProgress(body)) {
return <Progress {...body} {...context} />
}
if (isPropsUIPromptConfirm(body)) {
return <Confirm {...body} {...context} />
}
Expand Down
8 changes: 5 additions & 3 deletions src/framework/visualisation/react/ui/prompts/consent_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const ConsentForm = (props: Props): JSX.Element => {
const [waiting, setWaiting] = React.useState<boolean>(false)

const { locale, resolve } = props
const cancelButton = Translator.translate(cancelButtonLabel, props.locale)
const cancelButton = Translator.translate(cancelButtonLabel, props.locale)

function rowCell (dataFrame: any, column: string, row: number): PropsUITableCell {
const text = String(dataFrame[column][`${row}`])
Expand Down Expand Up @@ -157,8 +157,10 @@ export const ConsentForm = (props: Props): JSX.Element => {
<div>
<BodyLarge margin='' text={Translator.translate(props.donateQuestion ?? donateQuestionLabel, locale)} />
<div className='flex flex-row gap-4 mt-4 mb-4'>
<PrimaryButton label={Translator.translate(props.donateButton ?? donateButtonLabel, locale)}
onClick={handleDonate} color='bg-success text-white' spinning={waiting} />
<PrimaryButton
label={Translator.translate(props.donateButton ?? donateButtonLabel, locale)}
onClick={handleDonate} color='bg-success text-white' spinning={waiting}
/>
<LabelButton label={cancelButton} onClick={handleCancel} color='text-grey1' />
</div>
</div>
Expand Down
61 changes: 61 additions & 0 deletions src/framework/visualisation/react/ui/prompts/progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Weak } from '../../../../helpers'
import { Translator } from '../../../../translator'
import { ReactFactoryContext } from '../../factory'
import { PropsUIPromptProgress } from '../../../../types/prompts'
import { ProgressBar } from '../elements/progress_bar'

type Props = Weak<PropsUIPromptProgress> & ReactFactoryContext

export const Progress = (props: Props): JSX.Element => {
const { resolve, percentage } = props
const { description, message } = prepareCopy(props)

function progressBar (): JSX.Element {
if (percentage !== undefined) {
return (
<>
<div className='mt-2' />
<ProgressBar percentage={percentage} />
</>
)
} else {
return <></>
}
}

function autoResolve (): void {
resolve?.({ __type__: 'PayloadTrue', value: true })
}

// No user action possible, resolve directly to give control back to script
autoResolve()

return (
<>
<div id='select-panel'>
<div className='flex-wrap text-bodylarge font-body text-grey1 text-left'>
{description}
</div>
<div className='mt-8' />
<div className='p-6 border-grey4 border-2 rounded'>
<div className='flex-wrap text-bodylarge font-body text-grey2 text-left'>
{message}
</div>
{progressBar()}
</div>
</div>
</>
)
}

interface Copy {
description: string
message: string
}

function prepareCopy ({ description, message, locale }: Props): Copy {
return {
description: Translator.translate(description, locale),
message: message
}
}
10 changes: 5 additions & 5 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ if (process.env.REACT_APP_BUILD !== 'standalone' && process.env.NODE_ENV === 'pr
}

const observer = new ResizeObserver(() => {
const height = window.document.body.scrollHeight;
const action = "resize"
window.parent.postMessage({action, height}, "*")
});
const height = window.document.body.scrollHeight
const action = 'resize'
window.parent.postMessage({ action, height }, '*')
})

observer.observe(window.document.body);
observer.observe(window.document.body)
2 changes: 1 addition & 1 deletion src/live_bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ export default class LiveBridge implements Bridge {

private log (level: 'info' | 'error', ...message: any[]): void {
const logger = level === 'info' ? console.log : console.error
logger("[LiveBridge]", ...message)
logger('[LiveBridge]', ...message)
}
}

0 comments on commit 2ff2bfd

Please sign in to comment.