Skip to content

Commit

Permalink
Merge pull request #92 from mathben/generate_manual_from_google_doc_#79
Browse files Browse the repository at this point in the history
Generate manual and lore from google doc #79
  • Loading branch information
mathben authored Apr 8, 2018
2 parents 70fce83 + a5a2e2c commit 985e05b
Show file tree
Hide file tree
Showing 20 changed files with 1,401 additions and 27 deletions.
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

0 comments on commit 985e05b

Please sign in to comment.