Merge pull request #4 from avatao-content/webide_rework

Webide rework
This commit is contained in:
Bokros Bálint 2018-02-08 17:52:45 +01:00 committed by GitHub
commit a933248e90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 56 deletions

View File

@ -51,7 +51,8 @@ ENV TFW_APP_DIR="/srv/app"
ENV TFW_FRONTEND_DIR="/srv/frontend"
ENV TFW_LOGIN_APP_DIR="/tmp/source_code_server"
ENV TFW_TERMINADO_DIR="/tmp/terminado_server"
ENV TFW_TERMINADO_WD="/home/${AVATAO_USER}"
ENV TFW_WEBIDE_WD="/home/${AVATAO_USER}/workdir"
ENV TFW_TERMINADO_WD=${TFW_WEBIDE_WD}
ENV TFW_LIB_DIR="/usr/local/lib/"
ENV TFW_SUPERVISORD_CONF="/etc/supervisor/supervisord.conf"
ENV TFW_SUPERVISORD_COMPONENTS="/etc/supervisor/conf"
@ -75,6 +76,12 @@ COPY src/event_handlers ${TFW_EVENT_HANDLERS_DIR}
RUN mv /data/dist ${TFW_FRONTEND_DIR}
COPY src/event_handlers/source_code_server/server.py ${TFW_LOGIN_APP_DIR}/
COPY src/event_handlers/source_code_server/users.db ${TFW_LOGIN_APP_DIR}/
COPY src/event_handlers/source_code_server/login_component.py ${TFW_WEBIDE_WD}/
RUN chown -R ${AVATAO_USER} ${TFW_WEBIDE_WD}
USER ${AVATAO_USER}
WORKDIR /home/${AVATAO_USER}

View File

@ -7,6 +7,7 @@ SUPERVISOR_HTTP_PORT = os.getenv('TFW_SUPERVISOR_PORT', 9001)
LOGIN_APP_PORT = os.getenv('TFW_LOGIN_APP_PORT', 6666)
TERMINADO_PORT = os.getenv('TFW_TERMINADO_PORT', 9999)
TERMINADO_WD = os.getenv('TFW_TERMINADO_WD')
WEBIDE_WD = os.getenv('TFW_WEBIDE_WD')
SUPERVISOR_HTTP_URI = 'http://localhost:{}'.format(SUPERVISOR_HTTP_PORT)

View File

@ -1,3 +1,3 @@
import logging
logging.basicConfig(level=logging.CRITICAL)
logging.basicConfig(level=logging.DEBUG)

View File

@ -1,4 +1,6 @@
import xmlrpc.client, zmq
from contextlib import suppress
from xmlrpc.client import Fault as SupervisorFault
from .config.envvars import SUPERVISOR_HTTP_URI
@ -14,6 +16,17 @@ def create_source_code_response_data(filename, content, language):
class SupervisorMixin:
supervisor = xmlrpc.client.ServerProxy(SUPERVISOR_HTTP_URI).supervisor
def stop_process(self):
with suppress(SupervisorFault):
self.supervisor.stopProcess(self.process_name)
def start_process(self):
self.supervisor.startProcess(self.process_name)
def restart_process(self):
self.stop_process()
self.start_process()
class ZMQConnectorBase:
def __init__(self, zmq_context=None):

View File

@ -2,8 +2,10 @@ from source_code_event_handler import SourceCodeEventHandler
from terminado_event_handler import TerminadoEventHandler
from tornado.ioloop import IOLoop
from tfw.config.envvars import WEBIDE_WD
if __name__ == '__main__':
anchor_webide = SourceCodeEventHandler('anchor_webide', 'login_component.py', 'login')
anchor_webide = SourceCodeEventHandler('anchor_webide', WEBIDE_WD, 'login')
anchor_terminado = TerminadoEventHandler('anchor_terminado', 'terminado')
IOLoop.instance().start()

View File

@ -1,69 +1,99 @@
from shutil import copy, rmtree, copytree
from os.path import splitext
from contextlib import suppress
from xmlrpc.client import Fault as SupervisorFault
from os.path import splitext, isfile, join, relpath
from glob import glob
from tfw.util import SupervisorMixin
from tfw.config import LOGIN_APP_DIR
from tfw.event_handler_base import EventHandlerBase
from tfw.config.logs import logging
log = logging.getLogger(__name__)
class FileManager:
def __init__(self, working_directory, selected_file=None):
self.exclude = ['__pycache__']
self._workdir = working_directory
self.filename = selected_file or self._relpath(self.files[0])
self.language = map_file_extension_to_language(self.filename)
def select_file(self, filename):
if not filename in self.files:
raise EnvironmentError('No such file in workdir!')
self.filename = filename
self.language = map_file_extension_to_language(self.filename)
@property
def files(self):
return [self._relpath(file) for file in glob(join(self._workdir, '**/*'), recursive=True)
if isfile(file) and
not any(word in file for word in self.exclude)]
@property
def file_contents(self):
with open(self._filepath(self.filename), 'r', errors='surrogateescape') as ifile:
return ifile.read()
@file_contents.setter
def file_contents(self, value):
with open(self._filepath(self.filename), 'w', errors='surrogateescape') as ofile:
ofile.write(value)
def _filepath(self, filename):
return join(self._workdir, filename)
def _relpath(self, filename):
return relpath(self._filepath(filename), start=self._workdir)
class SourceCodeEventHandler(EventHandlerBase, SupervisorMixin):
def __init__(self, anchor, filename, process_name=None):
def __init__(self, anchor, directory, process_name, selected_file=None):
super().__init__(anchor)
self.working_directory = LOGIN_APP_DIR
self.filename = filename
self.language = map_file_extension_to_language(filename)
self.process_name = process_name or splitext(filename)[0]
self.filemanager = FileManager(directory, selected_file=selected_file)
self.process_name = process_name
self.commands = {
'read': self.read_file,
'write': self.write_file
'read': self.read,
'write': self.write,
'select': self.select
}
self.file = self.create_initial_state()
def read(self, data):
try: data['content'] = self.filemanager.file_contents
except PermissionError: data['content'] = 'You have no permission to open that file :('
data['filename'] = self.filemanager.filename
data['language'] = self.filemanager.language
data['files'] = self.filemanager.files
return data
def write(self, data):
try: self.filemanager.file_contents = data['content']
except Exception: log.exception('Error reading file!')
data['files'] = self.filemanager.files
self.restart_process()
return data
def select(self, data):
try: self.filemanager.select_file(data['filename'])
except EnvironmentError: log.exception('Failed to select file "{}"'.format(data['filename']))
return data
def handle_event(self, anchor, data_json):
data = data_json['data']
self.commands[data['command']](data_json)
data_json['data'] = self.commands[data['command']](data)
return data_json
def handle_reset(self, data_json):
self.create_initial_state()
def read_file(self, data_json):
with open(self.file, 'r') as ifile:
content = ifile.read()
data_json['data'] = {
'filename': self.filename,
'content': content,
'language': self.language
}
def write_file(self, data_json):
data = data_json['data']
with open(self.file, 'w') as ofile:
ofile.write(data['content'])
self.supervisor.stopProcess(self.process_name)
self.supervisor.startProcess(self.process_name)
def create_initial_state(self):
with suppress(SupervisorFault):
self.supervisor.stopProcess(self.process_name)
rmtree(self.working_directory, ignore_errors=True)
copytree('source_code_server/', self.working_directory)
file = copy(self.filename, self.working_directory)
self.supervisor.startProcess(self.process_name)
return file
def map_file_extension_to_language(filename):
language_map = {
# TODO: extend, maybe auto-generate???
'.py': 'python',
'.js': 'javascript'
'.js': 'javascript',
'.html': 'html',
'.css': 'css',
'.java': 'java',
'.cpp': 'c_cpp',
'.hpp': 'c_cpp',
'.c': 'c_cpp',
'.h': 'c_cpp',
'.cs': 'csharp'
}
_, extension = splitext(filename)
return language_map[extension]
return language_map.get(extension, 'text')

View File

@ -1,9 +1,10 @@
import json
import json, sys
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application
from tfw.config import LOGIN_APP_PORT
from tfw.config import LOGIN_APP_PORT, WEBIDE_WD
sys.path.append(WEBIDE_WD)
from login_component import authorize_login

View File

@ -17,7 +17,7 @@ class TerminadoEventHandler(EventHandlerBase, SupervisorMixin):
def setup_terminado_server(self):
rmtree(self.working_directory, ignore_errors=True)
copytree('terminado_server/', self.working_directory)
self.supervisor.startProcess(self.process_name)
self.start_process()
def handle_event(self, anchor, data_json):
log.debug('TerminadoEventHandler received event for anchor {}'.format(anchor))

@ -1 +1 @@
Subproject commit 7e4bc12616026c10829c5b50984f6053e31d3b82
Subproject commit a6ee39156b49b003a23cce27e9be25a5e6494a1f