GEC-backend

This commit is contained in:
Wojciech Jarmosz 2022-06-01 10:19:32 +02:00
parent 57a136204e
commit b586f3c7ed
41 changed files with 20944 additions and 0 deletions

145
.gitignore vendored Normal file
View File

@ -0,0 +1,145 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv/
env/
venv/
ENV/
env.bak/
venv.bak/
.idea/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
#VS Code
.vscode
db.sqlite3

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# System automatycznej korekty tekstu - Transfix
## Instalacja serwera
Uruchomienie skryptu install.sh
```chmod u+x install.sh && ./install.sh
```
Utworzenie w katalogu ./backend pliku konfiguracyjnego .env
```
SECRET_KEY=secret_key
DEBUG=1
ALLOWED_HOSTS=localhost 127.0.0.1
TRANSLATION_WEBSOCKET=ws://150.254.78.132:443/translate
SCRIPT_PATH=/home/zary/Desktop/grammatical-error-correction-system
```
Uruchomienie serwera websocket na środowisku karty graficznej na porcie 443
```sudo ./tools/marian/build/marian-server --port 443 -m model/ens/model.npz -v model/ens/vocab.ende.yml model/ens/vocab.ende.yml```

16
backend/backend/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for backend project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
application = get_asgi_application()

153
backend/backend/settings.py Normal file
View File

@ -0,0 +1,153 @@
"""
Django settings for backend project.
Generated by 'django-admin startproject' using Django 2.2.10.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
from gettext import translation
import os
from pathlib import Path
import environ
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env()
if os.path.exists(env_path := os.path.join(BASE_DIR, "dev.env")):
env.read_env(env_path)
if os.path.exists(env_path := os.path.join(BASE_DIR, ".env")):
env.read_env(env_path)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool("DEBUG")
ALLOWED_HOSTS = env("ALLOWED_HOSTS").split()
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# packages
"rest_framework",
"corsheaders",
# apps
'translation'
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
# CORS Middleware
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "backend.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "backend.wsgi.application"
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = "pl-PL"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_ROOT = BASE_DIR / "static"
STATIC_URL = "/static/"
STATICFILES_DIRS = (BASE_DIR / "staticfiles",)
MEDIA_ROOT = BASE_DIR / "media"
MEDIA_URL = "/media/"
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# CORS
CORS_ALLOW_ALL_ORIGINS = True
TRANSLATION_WEBSOCKET = env("TRANSLATION_WEBSOCKET")
SCRIPT_PATH = env("SCRIPT_PATH")

22
backend/backend/urls.py Normal file
View File

@ -0,0 +1,22 @@
"""backend URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("translation.urls"))
]

16
backend/backend/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for backend project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
application = get_wsgi_application()

22
backend/manage.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class TranslationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'translation'

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,12 @@
#!/bin/sh
# path to moses decoder: https://github.com/moses-smt/mosesdecoder
mosesdecoder=/home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/moses-scripts
# path to subword segmentation scripts: https://github.com/rsennrich/subword-nmt
subword_nmt=/home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/subword-nmt
cat $1 \
| sed 's/\@\@ //g' \
| /home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/moses-scripts/scripts/recaser/detruecase.perl 2>/dev/null \
| /home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/moses-scripts/scripts/tokenizer/detokenizer.perl -l pl 2>/dev/null

View File

@ -0,0 +1,13 @@
#!/bin/sh
# path to moses decoder: https://github.com/moses-smt/mosesdecoder
mosesdecoder=/home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/moses-scripts
# path to subword segmentation scripts: https://github.com/rsennrich/subword-nmt
subword_nmt=/home/zary/Desktop/grammatical-error-correction-system/backend/translation/tools/subword-nmt
cat $1 \
| $mosesdecoder/scripts/tokenizer/normalize-punctuation.perl -l pl \
| $mosesdecoder/scripts/tokenizer/tokenizer.perl -a -l pl \
| $mosesdecoder/scripts/recaser/truecase.perl -model /home/zary/Desktop/grammatical-error-correction-system/backend/translation/model/tc.er \
| $subword_nmt/apply_bpe.py -c /home/zary/Desktop/grammatical-error-correction-system/backend/translation/model/erco.bpe 2> /dev/null

View File

@ -0,0 +1,54 @@
"""
Provides XML rendering support.
"""
from io import StringIO
from django.utils.encoding import force_str
from django.utils.xmlutils import SimplerXMLGenerator
from rest_framework.renderers import BaseRenderer
class MyXMLRenderer(BaseRenderer):
"""
Renderer which serializes to XML.
"""
media_type = "application/xml"
format = "xml"
charset = "utf-8"
item_tag_name = "error"
root_tag_name = "matches"
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders `data` into serialized XML.
"""
if data is None:
return ""
stream = StringIO()
xml = SimplerXMLGenerator(stream, self.charset)
xml.startDocument()
self._to_xml(xml, data)
xml.endDocument()
return stream.getvalue()
def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
xml.startElement(self.root_tag_name, {})
for item in data:
self._to_xml(xml, item)
xml.endElement(self.root_tag_name)
elif isinstance(data, dict):
xml.startElement(self.item_tag_name, data)
xml.endElement(self.item_tag_name)
elif data is None:
# Don't output any value
pass
else:
xml.characters(force_str(data))

View File

@ -0,0 +1,7 @@
from rest_framework import serializers
LANGUAGE_CHOICES = [('pl', 'Polish')]
class TranslationSerializer(serializers.Serializer):
text = serializers.CharField(max_length=20000, min_length = 1)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES)

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1 @@
Subproject commit 958dd5a6b026197de988c0264d45112215bc37bd

@ -0,0 +1 @@
Subproject commit 2598310dbedabebb582336d06dd91a5f60f33daa

View File

@ -0,0 +1,11 @@
from django.urls import include, path
from rest_framework.urlpatterns import format_suffix_patterns
from .views import TranslationAPIView, xml_translate
urlpatterns = [
path("translate", TranslationAPIView.as_view()),
path("translate-xml", xml_translate),
]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['xml'])

View File

@ -0,0 +1,87 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from websocket import create_connection
from django.conf import settings
import subprocess
from .serializers import TranslationSerializer
from rest_framework import status
from pathlib import Path
import regex as re
BASE_DIR = Path(__file__).resolve().parent
from rest_framework.decorators import api_view, renderer_classes
from .renderers import MyXMLRenderer
def diff_text(original, corrected):
lines = corrected.splitlines(1)
original_lines = original.splitlines(1)
output = []
for idx, line in enumerate(lines):
groups_found = re.findall('\[-([^\[]*?)\+}', line)
new_line = re.sub('\[-([^\[]*?)\+}', '', line)
for group in groups_found:
removed = re.findall('^(.*?)\-]', group)
added = re.findall('{\+(.*?)$', group)
output.append({'id': 'grammar-error', 'type': 'grammar', 'correction': added[0], 'context': original_lines[idx], 'msg': f"Zamiana '{removed[0]}' na '{added[0]}'"})
removed = re.findall('\[\-(.*?)\-]', new_line)
added = re.findall('{\+(.*?)\+}', new_line)
if removed is list:
for remove in removed:
output.append({'id': 'grammar-error', 'type': 'grammar', 'correction': "", 'context': original_lines[idx], 'msg': f"Usunięcie '{remove}"})
if added is list:
for add in added:
output.append({'id': 'grammar-error', 'type': 'grammar', 'correction': "", 'context': original_lines[idx], 'msg': f"Dodanie '{add}"})
return output
@api_view(['POST'])
@renderer_classes([MyXMLRenderer])
def xml_translate(request):
serializer = TranslationSerializer(data=request.data)
if serializer.is_valid():
# Encode to BPE.
proc = subprocess.Popen(f'echo "{request.data["text"]}" | sh {BASE_DIR}/preprocess_text.sh', stdout=subprocess.PIPE, shell=True)
output, err = proc.communicate()
ws = create_connection(settings.TRANSLATION_WEBSOCKET)
text = output.decode('utf-8')
ws.send(text)
result = ws.recv()
# Decode from BPE.
sec_proc = subprocess.Popen(f'echo "{result.rstrip()}" | sh {BASE_DIR}/postprocess_text.sh', stdout=subprocess.PIPE, shell=True)
sec_output, err = sec_proc.communicate()
# Decode from BPE.
third_proc = subprocess.Popen(f'git diff $(echo "{request.data["text"]}" | git hash-object -w --stdin) $(echo "{sec_output.decode("utf-8").rstrip()}" | git hash-object -w --stdin) --word-diff | tail -n +6', stdout=subprocess.PIPE, shell=True)
third_output, err = third_proc.communicate()
marked_errors = third_output.decode('utf-8').rstrip()
return Response(diff_text(request.data["text"], marked_errors), status=status.HTTP_200_OK)
else:
return Response("Bad request", status=status.HTTP_400_BAD_REQUEST)
class TranslationAPIView(APIView):
def post(self, request):
serializer = TranslationSerializer(data=request.data)
if serializer.is_valid():
# Encode to BPE.
proc = subprocess.Popen(f'echo "{request.data["text"]}" | sh {BASE_DIR}/preprocess_text.sh', stdout=subprocess.PIPE, shell=True)
output, err = proc.communicate()
ws = create_connection(settings.TRANSLATION_WEBSOCKET)
text = output.decode('utf-8')
ws.send(text)
result = ws.recv()
# Decode from BPE.
sec_proc = subprocess.Popen(f'echo "{result.rstrip()}" | sh {BASE_DIR}/postprocess_text.sh', stdout=subprocess.PIPE, shell=True)
sec_output, err = sec_proc.communicate()
# Decode from BPE.
third_proc = subprocess.Popen(f'git diff $(echo "{request.data["text"]}" | git hash-object -w --stdin) $(echo "{sec_output.decode("utf-8").rstrip()}" | git hash-object -w --stdin) --word-diff | tail -n +6', stdout=subprocess.PIPE, shell=True)
third_output, err = third_proc.communicate()
marked_errors = third_output.decode('utf-8').rstrip()
return Response({'corrected_text': sec_output.decode('utf-8').rstrip(), "errors": marked_errors}, status=status.HTTP_200_OK)
else:
return Response("Bad request", status=status.HTTP_400_BAD_REQUEST)

23
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
frontend/README.md Normal file
View File

@ -0,0 +1,24 @@
# frontend
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
frontend/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

20059
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

48
frontend/package.json Normal file
View File

@ -0,0 +1,48 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.6.14",
"vuetify": "^2.6.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"sass": "~1.32.0",
"sass-loader": "^10.0.0",
"vue-cli-plugin-vuetify": "~2.5.0",
"vue-template-compiler": "^2.6.14",
"vuetify-loader": "^1.7.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

30
frontend/src/App.vue Normal file
View File

@ -0,0 +1,30 @@
<template>
<v-app>
<v-main>
<Navbar />
<Workspace />
</v-main>
<Footer />
</v-app>
</template>
<script>
import Navbar from "@/components/Navbar.vue";
import Footer from "@/components/Footer.vue";
import Workspace from "@/components/Workspace.vue";
export default {
name: 'App',
components: {
Navbar,
Footer,
Workspace,
},
data: () => ({
}),
};
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1,14 @@
<template>
<v-footer padless>
<v-col class="text-center py-1" cols="12"
>&copy; 2021 -
{{ new Date().toLocaleDateString("pl", { year: "numeric" }) }} Transfix
</v-col>
</v-footer>
</template>
<script>
export default {
name: 'app-footer'
};
</script>

View File

@ -0,0 +1,18 @@
<template>
<v-app-bar app color="primary" dark>
<div class="d-flex align-center">
<h1>Transfix v0.1</h1>
</div>
</v-app-bar>
</template>
<script>
export default {
name: "navbar-app",
components: {},
data() {
return {};
},
};
</script>

View File

@ -0,0 +1,56 @@
<template>
<div>
<v-row class="pa-3 workspace-height">
<v-col class="col-12 col-md-6 workspace-height">
<h2 class="mb-2">Tekst do korekty</h2>
<v-textarea
class="workspace-height"
v-model="input_text"
counter
maxlength="15000"
full-width
clearable
filled
auto-grow
></v-textarea
></v-col>
<v-col class="col-12 col-md-6">
<h2 class="mb-2">Poprawiony tekst</h2>
<v-textarea
class="workspace-height"
v-model="result_text"
full-width
filled
auto-grow
:loading="loading"
loader-height="2"
></v-textarea
></v-col>
</v-row>
</div>
</template>
<script>
export default {
name: "workspace-app",
components: {},
data() {
return {
input_text: "",
result_text: "",
loading: false,
};
},
};
</script>
<style>
.workspace-height {
height: 100% !important;
}
.v-input__slot {
height: 100% !important;
}
</style>

10
frontend/src/main.js Normal file
View File

@ -0,0 +1,10 @@
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
new Vue({
vuetify,
render: h => h(App)
}).$mount('#app')

View File

@ -0,0 +1,17 @@
import Vue from "vue";
import Vuetify from "vuetify/lib/framework";
Vue.use(Vuetify);
export default new Vuetify({
theme: {
themes: {
light: {
primary: "#3f51b5",
secondary: "#b0bec5",
accent: "#8c9eff",
error: "#b71c1c",
},
},
},
});

6
frontend/vue.config.js Normal file
View File

@ -0,0 +1,6 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: [
'vuetify'
]
})