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

Generate manual and lore from google doc #79 #92

Merged
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,36 @@ Or download it from here : https://github.com/AdminTL/gestion_personnage_TL/arch

Dependencies
------------
You need python3, python3-tornado and python3-sockjs-tornado
You need python3.5

Arch Linux
------
```{r, engine='bash', count_lines}
sudo pacman -S python python-pip
sudo pip install tornado sockjs-tornado tinydb bcrypt
sudo pip install tornado sockjs-tornado tinydb bcrypt PyOpenSSL oauth2client gspread
```

Mac OSX
-------
```{r, engine='bash', count_lines}
brew install python3
sudo pip3 install tornado sockjs-tornado tinydb bcrypt
sudo pip3 install tornado sockjs-tornado tinydb bcrypt PyOpenSSL oauth2client gspread
```

Ubuntu / Debian
---------------
```{r, engine='bash', count_lines}
sudo apt-get install python3 python3-pip
sudo pip3 install tornado sockjs-tornado tinydb bcrypt
sudo pip3 install tornado sockjs-tornado tinydb bcrypt PyOpenSSL oauth2client gspread
```

If you have problem with oauth2client, maybe you need to update pyasn.
```{r, engine='bash', count_lines}
sudo apt-get --reinstall install python-pyasn1 python-pyasn1-modules
```
or
```{r, engine='bash', count_lines}
sudo pip4 install --upgrade google-auth-oauthlib
```

Windows
Expand All @@ -47,7 +56,7 @@ Install python 3 from https://www.python.org/downloads/ using the installer
Install nodejs if not done already https://nodejs.org/en/download/
Start a cmd prompt with admin privileges by right-clicking->run as administrator (git-bash works well)
```
pip3 install tornado sockjs-tornado tinydb
pip3 install tornado sockjs-tornado tinydb PyOpenSSL oauth2client gspread
```

Bower
Expand Down
5 changes: 5 additions & 0 deletions database/example_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"google_spreadsheet": {
"file_url": null
}
}
21 changes: 21 additions & 0 deletions doc/developer/google_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Google API
==========

* The Google OAuth2 is used, [read file authentication.md](./authentication.md).
* The Google Spreadsheet is used to update the database documentation for manual and other document.

Google Spreadsheet
------------------

Good documentation: http://gspread.readthedocs.io/en/latest/oauth2.html

To resume :
1. Create signed credentials "Service account key" for Drive API in format JSON.
2. Move the file to "database/client_secret.json"

Manual generator
----------------

To enable the option to generate the manual from a spreadsheet:
1. Copy the file ./database/example_config.json to ./database/config.json
2. Fill information in key "google_spreadsheet".
12 changes: 10 additions & 2 deletions src/web/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import os
import web
from py_class.config import Config

WEB_ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
WEB_DEFAULT_STATIC_DIR = os.path.join(WEB_ROOT_DIR)
Expand All @@ -15,6 +16,8 @@
DB_MANUAL_PATH = os.path.join(WEB_ROOT_DIR, "..", "..", "database", "tl_manual.json")
DB_LORE_PATH = os.path.join(WEB_ROOT_DIR, "..", "..", "database", "tl_lore.json")
DB_AUTH_PATH = os.path.join(WEB_ROOT_DIR, "..", "..", "database", "auth.json")
GOOGLE_API_SECRET_PATH = os.path.join(WEB_ROOT_DIR, "..", "..", "database", "client_secret.json")
CONFIG_PATH = os.path.join(WEB_ROOT_DIR, "..", "..", "database", "config.json")


def main():
Expand Down Expand Up @@ -85,15 +88,20 @@ def parse_args():
_parser.db_manual_path = DB_MANUAL_PATH
_parser.db_lore_path = DB_LORE_PATH
_parser.db_auth_keys_path = DB_AUTH_PATH
_parser.db_google_API_path = GOOGLE_API_SECRET_PATH
_parser.db_config_path = CONFIG_PATH

# apply condition
# Apply condition
if not _parser.ssl and _parser.redirect_http_to_https:
# cannot redirect http to https if ssl is not enable
# Cannot redirect http to https if ssl is not enable
_parser.redirect_http_to_https = False

if _parser.disable_character:
_parser.disable_user_character = True

# Add general configuration in parser
_parser.config = Config(_parser)

return _parser


Expand Down
4 changes: 4 additions & 0 deletions src/web/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class BaseHandler(tornado.web.RequestHandler):
_db = None
_invalid_login = None
_redirect_http_to_https = None
_config = None
_doc_generator_gspread = None
_global_arg = {}

def initialize(self, **kwargs):
Expand All @@ -23,6 +25,8 @@ def initialize(self, **kwargs):
self._invalid_login = self.get_argument("invalid",
default="disable_login" if kwargs.get("disable_login") else None)
self._redirect_http_to_https = kwargs.get("redirect_http_to_https")
self._config = kwargs.get("config")
self._doc_generator_gspread = kwargs.get("doc_generator_gspread")

self._global_arg = {
"debug": self._debug,
Expand Down
182 changes: 182 additions & 0 deletions src/web/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,25 @@ def get(self):
raise tornado.web.Finish()


class AdminEditorHandler(base_handler.BaseHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def get(self):
if self._global_arg["disable_admin"]:
# Not Found
self.set_status(404)
self.send_error(404)
raise tornado.web.Finish()
if self.is_permission_admin():
self.render('admin/editor.html', **self._global_arg)
else:
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()


class ProfileHandler(base_handler.BaseHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
Expand Down Expand Up @@ -686,6 +705,169 @@ def get(self):
self.finish()


class EditorCmdInfoHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def get(self):
if not self.is_permission_admin():
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

current_user = self.get_current_user()

# Do get_instance first
doc_generator = self._doc_generator_gspread.get_instance()

# Fetch information
if doc_generator:
has_access_perm = doc_generator.check_has_permission()
has_user_writer_perm = doc_generator.has_user_write_permission(current_user.get("email"))
else:
has_user_writer_perm = False
has_access_perm = False

file_url = self._doc_generator_gspread.get_url()
email_google_service = self._doc_generator_gspread.get_email_service()
is_auth = self._doc_generator_gspread.is_auth()
can_generate = bool(doc_generator and
not self._doc_generator_gspread.has_error() and
not doc_generator.has_error() and
has_access_perm and is_auth
)

info = {
"file_url": file_url,
"is_auth": is_auth,
"user_has_writer_perm": has_user_writer_perm,
"has_access_perm": has_access_perm,
"email_google_service": email_google_service,
"can_generate": can_generate
}

if self._doc_generator_gspread.has_error():
error = self._doc_generator_gspread.get_error()
info["error"] = error

self.write(info)
self.finish()


class EditorCmdAddGeneratorShareHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def post(self):
if not self.is_permission_admin():
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

current_user = self.get_current_user()

doc_generator = self._doc_generator_gspread.get_instance()
if not doc_generator:
status = self._doc_generator_gspread.get_error()
self.write(status)
self.finish()
return

email = current_user.get("email")
has_writer_perm = doc_generator.has_user_write_permission(email)
if not has_writer_perm:
status = doc_generator.share_document(current_user.get("email"))

if status:
data = {"status": "Document shared."}
else:
data = {"error": "Cannot share the document."}
else:
data = {"status": "Document already shared to user %s." % email}

self.write(data)
self.finish()


class EditorCmdUpdateFileUrlHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def post(self):
if not self.is_permission_admin():
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

self.prepare_json()

file_url = self.get_argument("file_url")
if not file_url:
status = {"error": "The url is empty."}
self.write(status)
self.finish()
return

# Validate is not the same link
actual_file_url = self._doc_generator_gspread.get_url()
if actual_file_url == file_url:
status = {"error": "The url is already open."}
self.write(status)
self.finish()
return

# Update and save the new link
if self._doc_generator_gspread.connect():
self._doc_generator_gspread.update_url(url=file_url, save=True)

# Return data
if self._doc_generator_gspread.has_error():
data = self._doc_generator_gspread.get_error()
else:
data = {"status": "Document url is updated."}

self.write(data)
self.finish()


class EditorCmdGenerateAndSaveHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def post(self):
if not self.is_permission_admin():
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

# Generate the document. An error is returned if status is not True
doc_generator = self._doc_generator_gspread.get_instance()
if not doc_generator:
status = self._doc_generator_gspread.get_error()
self.write(status)
self.finish()
return
status = doc_generator.generate_doc()
if status:
document = doc_generator.get_generated_doc()
if "manual" in document:
doc_part = document.get("manual")
self._manual.update({"manual": doc_part}, save=True)
if "lore" in document:
doc_part = document.get("lore")
self._lore.update({"lore": doc_part}, save=True)
status = {"status": "Generated with success. Database updated."}
else:
status = doc_generator.get_error(force_error=True)

self.write(status)
self.finish()


class StatSeasonPass(jsonhandler.JsonHandler):
@tornado.web.asynchronous
def get(self):
Expand Down
5 changes: 3 additions & 2 deletions src/web/partials/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
{% else %}
<link href="{{ static_url('bower_components/bootstrap/dist/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/bootstrap/dist/css/bootstrap-theme.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/components-font-awesome/css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/components-font-awesome/css/fontawesome-all.min.css') }}" rel="stylesheet">
{% end %}
<!-- Custom style -->
<link href="{{ static_url('resources/css/_base.css') }}" rel="stylesheet">
Expand Down Expand Up @@ -81,7 +81,7 @@
<li ng-class="{ active: isActive('/admin') }"><a href="admin" style="color:red;"><span class="glyphicon glyphicon-king"></span> Admin</a></li>
{% end %}

{% if not disable_login and not hide_menu_login %}
{% if not disable_login and (not hide_menu_login or current_user) %}
{% if current_user %}
<li><a href="/profile/"><span class="glyphicon glyphicon-user"></span> {{current_user.get("username")}}</a></li>
<li><a href="/logout"><span class="glyphicon glyphicon-log-out"></span> Déconnexion</a></li>
Expand Down Expand Up @@ -181,6 +181,7 @@ <h1 class="detect_javascript_enable">
<script src="{{ static_url('resources/js/tl_module/manual_ctrl/manual_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/lore_ctrl/lore_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/profile_ctrl/profile_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/editor_ctrl/editor_ctrl.js') }}"></script>

<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode.js') }}"></script>
<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode_UTF8.js') }}"></script>
Expand Down
7 changes: 5 additions & 2 deletions src/web/partials/admin/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
{% else %}
<link href="{{ static_url('bower_components/bootstrap/dist/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/bootstrap/dist/css/bootstrap-theme.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/components-font-awesome/css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ static_url('bower_components/components-font-awesome/css/fontawesome-all.min.css') }}" rel="stylesheet">
{% end %}
<!-- Custom style -->
<link href="{{ static_url('resources/css/_base.css') }}" rel="stylesheet">
Expand Down Expand Up @@ -67,6 +67,7 @@
{% if not disable_character %}
<li ng-class="{ active: isActive('/admin/character') }"><a href="/admin/character"><span class="glyphicon glyphicon-knight"></span> Personnage</a></li>
{% end %}
<li ng-class="{ active: isActive('/admin/editor') }"><a href="/admin/editor"><span class="glyphicon glyphicon-edit"></span> Éditeur</a></li>

{% if not disable_custom_css %}
<li class="dropdown">
Expand All @@ -77,7 +78,7 @@
</li>
{% end %}

<li ng-class="{ active: isActive('/') }"><a href="/" style="color:red;"><span class="glyphicon glyphicon-remove-circle"></span> Quitter</a></li>
<li><a href="/" style="color:red;"><span class="glyphicon glyphicon-remove-circle"></span> Quitter</a></li>

{% if not disable_login %}
{% if current_user %}
Expand Down Expand Up @@ -178,6 +179,8 @@ <h1 class="detect_javascript_enable">
<script src="{{ static_url('resources/js/tl_module/page_ctrl/page_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/manual_ctrl/manual_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/lore_ctrl/lore_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/profile_ctrl/profile_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/editor_ctrl/editor_ctrl.js') }}"></script>

<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode.js') }}"></script>
<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode_UTF8.js') }}"></script>
Expand Down
Loading