Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto updater #50

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ dist
aceinna.ico
.pytest_cache
loggers/
/backup
/backup
/versions
16 changes: 1 addition & 15 deletions build.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,7 @@ sys.setrecursionlimit(5000)
block_cipher = None
root_path = os.path.join(os.getcwd(),'src')

a = Analysis([os.path.join(root_path,'aceinna','executor.py'),
# os.path.join(os.getcwd(), 'src', 'bootstrap/base.py'),
# os.path.join(os.getcwd(), 'src', 'bootstrap/cli.py'),
# os.path.join(os.getcwd(), 'src', 'bootstrap/loader.py'),
# os.path.join(os.getcwd(), 'src', 'bootstrap/web.py'),
# os.path.join(os.getcwd(), 'src','devices', 'base/uart_base.py'),
# os.path.join(os.getcwd(), 'src','devices', 'configs/openimu_predefine.py'),
# os.path.join(os.getcwd(), 'src','devices', 'configs/openrtk_predefine.py'),
# os.path.join(os.getcwd(), 'src','devices', 'openimu/uart_provider.py'),
# os.path.join(os.getcwd(), 'src','devices', 'openrtk/uart_provider.py'),
# os.path.join(os.getcwd(), 'src','devices', 'device_manager.py'),
# os.path.join(os.getcwd(), 'src','framework', 'communicator.py'),
# os.path.join(os.getcwd(), 'src','framework', 'context.py'),
# os.path.join(os.getcwd(), 'src','framework', 'file_storage.py'),
# os.path.join(os.getcwd(), 'src','framework', 'utils/helper.py'),
a = Analysis([os.path.join(root_path,'aceinna','executor.py')
],
pathex=[root_path],
binaries=[],
Expand Down
1 change: 1 addition & 0 deletions requirements-2.x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ singledispatch==3.4.0.3
six==1.13.0
tornado==5.1.1
urllib3==1.25.7
windows-curses==2.2.0
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ requests-oauthlib==1.3.0
six==1.13.0
tornado==6.0.3
urllib3==1.25.7
windows-curses==2.2.0
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"pathlib",
"psutil",
"azure-storage-blob==2.1.0",
"tornado"
"tornado",
'windows-curses; platform_system=="Windows"'
]

setup(
Expand Down
2 changes: 1 addition & 1 deletion src/aceinna/devices/base/provider_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ def _do_download_firmware(self, file):

def download_firmware(self, file):
'''
Downlaod firmware from Azure storage
Download firmware from Azure storage
'''
can_download = False
firmware_content = None
Expand Down
16 changes: 12 additions & 4 deletions src/aceinna/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
import signal
import time
from aceinna.bootstrap import Loader
from aceinna.framework.decorator import (
receive_args, handle_application_exception)
from aceinna.framework.decorator import (receive_args,
handle_application_exception)
from aceinna.update import AutoUpdater

IS_WINDOWS = sys.platform.__contains__(
'win32') or sys.platform.__contains__('win64')
IS_WINDOWS = sys.platform.__contains__('win32') or sys.platform.__contains__(
'win64')
IS_LATER_PY_38 = sys.version_info > (3, 8)


Expand All @@ -26,6 +27,7 @@ def from_command_line(**kwargs):
'''
Work as command line, with WebSocket and UART
'''
check_update()
application = Loader.create('cli', vars(kwargs['options']))
application.listen()

Expand All @@ -36,6 +38,7 @@ def start_app(**kwargs):
'''
Work as a executor, with WebSocket and UART
'''
check_update()
application = None
mode = 'web'
if kwargs['options'].use_cli:
Expand All @@ -45,6 +48,11 @@ def start_app(**kwargs):
application.listen()


def check_update():
autoUpdater = AutoUpdater()
autoUpdater.check()


if __name__ == '__main__':
signal.signal(signal.SIGINT, kill_app)
# compatible code for windows python 3.8
Expand Down
133 changes: 133 additions & 0 deletions src/aceinna/framework/terminal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# inspire from https://github.com/wong2/pick/blob/master/pick/__init__.py
import curses
import sys

KEYS_ENTER = (curses.KEY_ENTER, ord('\n'), ord('\r'))
KEYS_UP = (curses.KEY_UP, ord('k'))
KEYS_DOWN = (curses.KEY_DOWN, ord('j'))
KEYS_SELECT = (curses.KEY_RIGHT, ord(' '))

# class Fore(object):
# red = code_to_chars(31)
# green = code_to_chars(32)
# yellow = code_to_chars(33)
# blue = code_to_chars(34)


class Choice(object):
def __init__(self, title=None, options=[], indicator='>', default_index=0):
self.options = options
self.title = title
self.indicator = indicator

if default_index >= len(options):
raise ValueError(
'default_index should be less than the length of options')

self.index = default_index

def move_up(self):
self.index -= 1
if self.index < 0:
self.index = len(self.options) - 1

def move_down(self):
self.index += 1
if self.index >= len(self.options):
self.index = 0

def get_selected(self):
"""return the current selected option as a tuple: (option, index)
or as a list of tuples (in case multiselect==True)
"""
return self.index, self.options[self.index]

def get_title_lines(self):
if self.title:
return self.title.split('\n') + ['']
return []

def get_option_lines(self):
lines = []
for index, option in enumerate(self.options):
# pass the option through the options map of one was passed in
if index == self.index:
prefix = self.indicator
format = curses.color_pair(1)
line = ('{0} {1}'.format(prefix, option), format)
else:
prefix = len(self.indicator) * ' '
line = '{0} {1}'.format(prefix, option)

lines.append(line)

return lines

def get_lines(self):
title_lines = self.get_title_lines()
option_lines = self.get_option_lines()
lines = title_lines + option_lines
current_line = self.index + len(title_lines) + 1
return lines, current_line

def draw(self):
"""draw the curses ui on the screen, handle scroll if needed"""
self.screen.clear()

x, y = 1, 1 # start point
max_y, max_x = self.screen.getmaxyx()
max_rows = max_y - y # the max rows we can draw

lines, current_line = self.get_lines()

# calculate how many lines we should scroll, relative to the top
scroll_top = getattr(self, 'scroll_top', 0)
if current_line <= scroll_top:
scroll_top = 0
elif current_line - scroll_top > max_rows:
scroll_top = current_line - max_rows
self.scroll_top = scroll_top

lines_to_draw = lines[scroll_top:scroll_top + max_rows]

for line in lines_to_draw:
if type(line) is tuple:
self.screen.addnstr(y, x, line[0], max_x - 2, line[1])
else:
self.screen.addnstr(y, x, line, max_x - 2)
y += 1

self.screen.refresh()

def run_loop(self):
while True:
self.draw()
c = self.screen.getch()
if c in KEYS_UP:
self.move_up()
elif c in KEYS_DOWN:
self.move_down()
elif c in KEYS_ENTER:
return self.get_selected()

def config_curses(self):
try:
# use the default colors of the terminal
curses.use_default_colors()
# hide the cursor
curses.curs_set(0)
# add some color for multi_select
# @todo make colors configurable
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
except:
# Curses failed to initialize color support, eg. when TERM=vt100
curses.initscr()

def _start(self, screen):
self.screen = screen
self.config_curses()
return self.run_loop()

def get_choice(self):
return curses.wrapper(self._start)
return self.run_loop()
51 changes: 48 additions & 3 deletions src/aceinna/framework/utils/resource.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
import sys
import pkgutil
import platform
from ... import PACKAGE_NAME


def is_in_bundle():
return hasattr(sys, 'frozen') and getattr(sys, 'frozen') and hasattr(sys, '_MEIPASS')
return hasattr(sys, 'frozen') and getattr(sys, 'frozen') and hasattr(
sys, '_MEIPASS')


def is_dev_mode():
Expand All @@ -15,13 +17,20 @@ def is_dev_mode():
def get_executor_path():
if is_in_bundle():
path = os.path.abspath(os.path.dirname(sys.executable))

# check if local is installed the package, only for windows
_, is_installed = get_installed_info()
if is_installed:
path = os.path.join(os.getenv("LOCALAPPDATA"),
'AceinnaDevicesDriver')
else:
if is_dev_mode(): # if start from main.py
path = os.getcwd()
else:
path = os.path.join(os.path.expanduser('~'), PACKAGE_NAME)
if not os.path.isdir(path):
os.makedirs(path)

if not os.path.exists(path):
os.makedirs(path)
return path


Expand All @@ -33,3 +42,39 @@ def get_content_from_bundle(package, path):
content = pkgutil.get_data(module_name, os.path.join(package, path))

return content


def get_installed_info():
if not 'Windows' in platform.system():
return None, False

import winreg

location = None
has_value = False
string = r'Software\Aceinna_Devices_Driver'
try:
handle = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, string, 0,
winreg.KEY_WOW64_32KEY + winreg.KEY_READ)
location, _ = winreg.QueryValueEx(handle, "Install_Dir")
has_value = True
except:
location = None
has_value = False

return location, has_value

def calculate_package_type():
default_package_type='pip'
if is_in_bundle():
_, is_installed = get_installed_info()
if is_installed:
return 'installed-exe'
else:
return 'exe'

if is_dev_mode():
return 'local'

return default_package_type

3 changes: 2 additions & 1 deletion src/aceinna/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .args import WebserverArgs
from .internal_combine_app_parse_rule import InternalCombineAppParseRule
from .internal_combine_app_parse_rule import InternalCombineAppParseRule
from .auto_updater import (VersionInfo, LocalInstallerInfo)
9 changes: 9 additions & 0 deletions src/aceinna/models/auto_updater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class VersionInfo(object):
name = ''
version_no = ''
url = ''


class LocalInstallerInfo(object):
path = ''
version_no = ''
1 change: 1 addition & 0 deletions src/aceinna/update/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .auto_updater import AutoUpdater
Loading