Skip to content

Commit

Permalink
Add spinner
Browse files Browse the repository at this point in the history
  • Loading branch information
kfur committed Jun 16, 2021
1 parent 8fbbfb0 commit 4546b43
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 16 deletions.
63 changes: 47 additions & 16 deletions fineocr/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
import pickle
from os import path
from .finescanner import DocExportType, DocLangType, FineScannerTask, FineUser

from .spinner import LineSpinner
import argparse


def format(format_str):
for dtype in DocExportType:
if format_str.lower() == dtype.value.lower():
return dtype
raise ValueError


def lang(lang_str):
lsstr = lang_str.split('+')
result = []
Expand Down Expand Up @@ -66,19 +68,24 @@ def __call__(self, parser, namespace, values, option_string=None):
print(dtype.value)
parser.exit()


parser = argparse.ArgumentParser()
parser.add_argument('-lf', action=_ListFormatsAction,
help='list available formats')
parser.add_argument('-ll', action=_ListLangsAction, help='list available languages')
parser.add_argument('-t', metavar='FORMAT', required=True, type=format,
parser.add_argument('-ll', action=_ListLangsAction,
help='list available languages')
parser.add_argument('-t', metavar='format', required=True, type=format,
help='target format(example: -t epub)')
parser.add_argument('-l', metavar='langs', required=True, type=lang, help='source file languages,'
' allowed up to 3 '
'simultaneous(example: -l English+French)')
parser.add_argument('-l', metavar='languages', required=True, type=lang, help='source file languages,'
' allowed up to 3 '
'simultaneous(example: -l English+French)')
parser.add_argument('-o', metavar='output', required=False,
help='output destination file name')
parser.add_argument('source_file', help='source file path')
# parser.
args = parser.parse_args()


async def start() -> None:
pass
fine_scanner_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=1800,
Expand All @@ -87,6 +94,7 @@ async def start() -> None:
fstat = os.stat(args.source_file)
fname = os.path.basename(args.source_file)
config_path = path.expanduser("~") + path.sep + '.fineocr'
# load creadits or create one
try:
fine_user_uuid = pickle.load(open(config_path, mode='rb'))
fine_user = FineUser(fine_user_uuid, fine_scanner_session)
Expand All @@ -95,29 +103,52 @@ async def start() -> None:
pickle.dump(fine_user.uuid, open(config_path, mode='wb'))
token = await fine_user.get_token()
ftask = FineScannerTask(token, fine_scanner_session)

# uploading file to server
line_spinner = LineSpinner(message='Uploading to server... ')
async with aiofiles.open(args.source_file, mode='rb') as file:
await ftask.upload_file(file, fname, fstat.st_size)
upload_task = asyncio.get_event_loop().create_task(
ftask.upload_file(file, fname, fstat.st_size))
while upload_task.done() is not True:
await asyncio.sleep(0.5)
line_spinner.next()

# run convering and wait for competance
line_spinner.message = 'Converting... '
await ftask.run_task(args.t, args.l)

while True:
status = await ftask.task_status()
if status['Status'] == 'Processing' or status['Status'] == 'Awaiting':
await asyncio.sleep(15)
# wait 15 seconds like official apps do
c = 30
while c:
await asyncio.sleep(0.5)
line_spinner.next()
c -= 1
else:
break
result = status['ResultStatus']
if result == 'Success':
async with aiofiles.open(f"{os.getcwd()}/{status['ResultFilename']}", 'wb') as rfile:
# saving file
line_spinner.writeln('Downloading file')
async with aiofiles.open(args.o if args.o else f"{os.getcwd()}/{status['ResultFilename']}", 'wb') as rfile:
async for data in await ftask.get_result():
await rfile.write(data)
line_spinner.writeln('Done')
else:
raise Exception(status)
line_spinner.writeln(f"Failed OCR: {result}")

line_spinner.finish()
await fine_scanner_session.close()

try:
asyncio.get_event_loop().run_until_complete(start())
except Exception as e:
logging.exception(e)
sys.exit(1)

def main():
try:
asyncio.get_event_loop().run_until_complete(start())
except Exception as e:
logging.exception(e)
sys.exit(1)


if __name__ == "__main__":
main()
146 changes: 146 additions & 0 deletions fineocr/spinner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Copyright (c) 2012 Georgios Verigakis <verigak@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


from sys import stderr
from collections import deque
try:
from time import monotonic
except ImportError:
from time import time as monotonic


HIDE_CURSOR = '\x1b[?25l'
SHOW_CURSOR = '\x1b[?25h'


class Infinite(object):
file = stderr
sma_window = 10 # Simple Moving Average window
check_tty = True
hide_cursor = True

def __init__(self, message='', **kwargs):
self.index = 0
self.start_ts = monotonic()
self.avg = 0
self._avg_update_ts = self.start_ts
self._ts = self.start_ts
self._xput = deque(maxlen=self.sma_window)
for key, val in kwargs.items():
setattr(self, key, val)

self._max_width = 0
self._hidden_cursor = False
self.message = message

if self.file and self.is_tty():
if self.hide_cursor:
print(HIDE_CURSOR, end='', file=self.file)
self._hidden_cursor = True
self.writeln('')

def __del__(self):
if self._hidden_cursor:
print(SHOW_CURSOR, end='', file=self.file)

def __getitem__(self, key):
if key.startswith('_'):
return None
return getattr(self, key, None)

@property
def elapsed(self):
return int(monotonic() - self.start_ts)

@property
def elapsed_td(self):
return timedelta(seconds=self.elapsed)

def update_avg(self, n, dt):
if n > 0:
xput_len = len(self._xput)
self._xput.append(dt / n)
now = monotonic()
# update when we're still filling _xput, then after every second
if (xput_len < self.sma_window or
now - self._avg_update_ts > 1):
self.avg = sum(self._xput) / len(self._xput)
self._avg_update_ts = now

def update(self):
pass

def start(self):
pass

def writeln(self, line):
if self.file and self.is_tty():
width = len(line)
if width < self._max_width:
# Add padding to cover previous contents
line += ' ' * (self._max_width - width)
else:
self._max_width = width
print('\r' + line, end='', file=self.file)
self.file.flush()

def finish(self):
if self.file and self.is_tty():
print(file=self.file)
if self._hidden_cursor:
print(SHOW_CURSOR, end='', file=self.file)
self._hidden_cursor = False

def is_tty(self):
try:
return self.file.isatty() if self.check_tty else True
except AttributeError:
msg = "%s has no attribute 'isatty'. Try setting check_tty=False." % self
raise AttributeError(msg)

def next(self, n=1):
now = monotonic()
dt = now - self._ts
self.update_avg(n, dt)
self._ts = now
self.index = self.index + n
self.update()

def iter(self, it):
self.iter_value = None
with self:
for x in it:
self.iter_value = x
yield x
self.next()
del self.iter_value

def __enter__(self):
self.start()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.finish()


class LineSpinner(Infinite):
phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻']
hide_cursor = False

def update(self):
i = self.index % len(self.phases)
message = self.message % self
line = ''.join([message, self.phases[i]])
self.writeln(line)

0 comments on commit 4546b43

Please sign in to comment.