You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2018/11/21 23:29:34 UTC
[airavata-django-portal] 01/02: Documentation on adding Django app
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit ab419f54c328dfc036e2fd63ed548bda1d18b148
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Wed Nov 21 18:22:44 2018 -0500
Documentation on adding Django app
---
.gitignore | 2 +
docs/dev/new_django_app.md | 353 +++++++++++++++++++++++++++++++++++++++++++++
docs/index.md | 8 +
mkdocs.yml | 12 ++
requirements-dev.txt | 1 +
5 files changed, 376 insertions(+)
diff --git a/.gitignore b/.gitignore
index 1729ed7..734cdd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ jsconfig.json
airavata_django_portal.egg-info
# AIRAVATA-2951 package-lock.json files cause the build to fail when deploying to server
package-lock.json
+# mkdocs
+site/
diff --git a/docs/dev/new_django_app.md b/docs/dev/new_django_app.md
new file mode 100644
index 0000000..56ba67e
--- /dev/null
+++ b/docs/dev/new_django_app.md
@@ -0,0 +1,353 @@
+# Adding a Django App
+
+The functionality of the Airavata Django Portal is broken up into separate
+Django apps. The apps live in the `django_airavata/apps` directory. When
+adding new functionality to the Django portal it may make sense to add it as
+a new separate Django app instead of adding it to an existing app. The
+following steps document how to do this.
+
+## Create the new Django App
+
+For this example, assume the name of the app is **myapp**. The following also
+assumes you have sourced your virtual environment.
+
+```
+cd airavata-django-portal
+mkdir django_airavata/apps/myapp
+python manage.py startapp myapp django_airavata/apps/myapp
+```
+
+## Integrating with the Django Portal
+
+### AppConfig settings
+
+Edit the AppConfig so that it extends the AiravataAppConfig and fill in the
+required details:
+
+```python
+from django_airavata.app_config import AiravataAppConfig
+
+
+class MyAppConfig(AiravataAppConfig):
+ name = 'django_airavata.apps.myapp'
+ label = 'django_airavata_myapp'
+ verbose_name = 'My App'
+ url_app_name = label
+ app_order = 10
+ url_home = 'django_airavata_myapp:home'
+ fa_icon_class = 'fa-bolt'
+ app_description = """
+ My app for doing stuff in the Airavata Django Portal.
+ """
+```
+
+Some of these are self explanatory, but here are some details on each of
+these properties:
+
+- _name_ - this is the python package of the app
+- _label_ - this needs to be unique across all installed Django apps. I
+ just make this match the _app_name_ in `urls.py`.
+- _verbose_name_ - display name of app
+- _url_app_name_ - this needs to match the _app_name_ in `urls.py`
+- _app_order_ - order of app in the menu listing. Range is 0 - 100. See the
+ other Django apps for their values to figure out how to order this app
+ relative to them.
+- _url_home_ - namespaced url of the "home" page of this app. This will be
+ the url used when a user selects this app in a navigational menu.
+- _fa_icon_class_ - a FontAwesome icon class. See [the list of available icons
+ for v. 4.7](https://fontawesome.com/v4.7.0/icons/).
+- _app_description_ - description of this app
+
+### Add AppConfig to INSTALLED_APPS
+
+Edit INSTALLED_APPS in settings.py:
+
+```python
+INSTALLED_APPS = [
+ # ...
+ 'django_airavata.apps.myapp.MyAppConfig'
+]
+```
+
+### Add the apps urls to the site's urls.py
+
+Edit `django_airavata/urls.py` and add the app's urls config:
+
+```python
+urlpatterns = [
+ url(r'^djadmin/', admin.site.urls),
+ url(r'^admin/', include('django_airavata.apps.admin.urls')),
+ url(r'^auth/', include('django_airavata.apps.auth.urls')),
+ url(r'^workspace/', include('django_airavata.apps.workspace.urls')),
+ url(r'^api/', include('django_airavata.apps.api.urls')),
+ url(r'^groups/', include('django_airavata.apps.groups.urls')),
+ # ... Add the app urls here
+ url(r'^myapp/', include('django_airavata.apps.myapp.urls')),
+ # ...
+ url(r'^home$', views.home, name='home'),
+ url(r'^cms/', include(wagtailadmin_urls)),
+ url(r'^documents/', include(wagtaildocs_urls)),
+ url(r'', include(wagtail_urls)),
+] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+```
+
+It's important that the app urls are added before the `wagtail_urls` since
+wagtail controls those urls.
+
+
+## App urls.py and base template
+
+Let's add a starter home page and urls.py config for this app. Create a `urls.py` file in `myapp/`:
+
+```python
+from django.conf.urls import url
+
+from . import views
+
+app_name = 'django_airavata_myapp'
+urlpatterns = [
+ url(r'^home$', views.home, name='home'),
+]
+```
+
+Add a view function called `home` in views.py:
+
+```python
+from django.shortcuts import render
+
+
+def home(request):
+ return render(request, 'django_airavata_myapp/home.html')
+```
+
+Create a templates directory called in `myapp` called `templates/django_airavata_myapp/`.
+
+Then create a base template in that directory called `base.html`:
+
+```html
+
+{% extends 'base.html' %}
+
+{% load static %}
+
+{% block css %}
+{% comment %}Load any styles sheets that are common across all pages in your app{% endcomment %}
+<link rel=stylesheet type=text/css href="{% static 'django_airavata_myapp/dist/common.css' %}">
+{% endblock %}
+
+{% block nav-items %}
+
+ {% comment %}Define the left side navigation for drilling into sub pages of your app{% endcomment %}
+ <a href="{% url 'django_airavata_myapp:function1' %}"
+ class="c-nav__item {% if request.active_nav_item == 'function1' %}is-active{% endif %}"
+ data-toggle=tooltip data-placement=right title="Function #1">
+ <i class="fa fa-bolt"></i> <span class=sr-only>Function #1</span>
+ </a>
+ <a href="{% url 'django_airavata_myapp:function2' %}"
+ class="c-nav__item {% if request.active_nav_item == 'function2' %}is-active{% endif %}"
+ data-toggle=tooltip data-placement=right title="Function #2">
+ <i class="fa fa-bolt"></i> <span class=sr-only>Function #2</span>
+ </a>
+ <a href="{% url 'django_airavata_myapp:function3' %}"
+ class="c-nav__item {% if request.active_nav_item == 'function3' %}is-active{% endif %}"
+ data-toggle=tooltip data-placement=right title="Function #3">
+ <i class="fa fa-bolt"></i> <span class=sr-only>Function #3</span>
+ </a>
+
+{% endblock %}
+
+{% block scripts %}
+{% comment %}Load any javascript common to all pages in your app{% endcomment %}
+<script src="{% static "django_airavata_workspace/dist/common.js" %}"></script>
+{% endblock %}
+```
+
+Now, create a `home.html` template:
+
+```html
+
+{% extends './base.html' %}
+
+{% load static %}
+
+{% block css %}
+{{ block.super }}
+{% comment %}Load any CSS specific to this page here, if necessary{% endcomment %}
+{% endblock %}
+
+{% block content %}
+
+<h1>Hello World!</h1>
+
+{% endblock content %}
+
+{% block scripts %}
+{{ block.super }}
+{% comment %}Load any JS specific to this page here, if necessary{% endcomment %}
+{% endblock %}
+```
+
+Now if you log into the Django portal you should see "My App" in the menu at
+the top and clicking on it should display the home page of this app.
+
+
+## JS build config - Vue.js
+
+Now we'll add JavaScript build config to the app using Vue.js, npm and webpack.
+
+Add a package.json file to the app's directory (i.e., django_airavata/apps/myapp):
+
+```json
+{
+ "name": "django-airavata-myapp-views",
+ "description": "A Vue.js project",
+ "version": "1.0.0",
+ "author": "Marcus Christie <ma...@apache.org>",
+ "private": true,
+ "scripts": {
+ "dev": "cross-env NODE_ENV=development webpack --progress --hide-modules",
+ "watch": "cross-env NODE_ENV=development webpack --watch",
+ "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
+ },
+ "dependencies": {
+ "bootstrap": "^4.0.0-beta.2",
+ "bootstrap-vue": "^1.4.1",
+ "django-airavata-api": "file:../api",
+ "django-airavata-common-ui": "file:../../static/common",
+ "vue": "^2.5.17"
+ },
+ "devDependencies": {
+ "babel-core": "^6.0.0",
+ "babel-loader": "^7.1.2",
+ "babel-preset-env": "^1.5.1",
+ "clean-webpack-plugin": "^0.1.17",
+ "cross-env": "^3.0.0",
+ "css-loader": "^0.25.0",
+ "extract-text-webpack-plugin": "^3.0.2",
+ "file-loader": "^0.9.0",
+ "style-loader": "^0.19.0",
+ "vue-loader": "^12.1.0",
+ "vue-template-compiler": "^2.3.3",
+ "webpack": "^3.1.0",
+ "webpack-dev-server": "^2.4.5"
+ }
+}
+```
+
+Add a `.babelrc` to this directory too:
+
+```json
+{
+ "presets": [
+ ["env", { "modules": false }]
+ ]
+}
+```
+
+Now add a `webpack.config.js` file too:
+
+```javascript
+var path = require('path')
+var webpack = require('webpack')
+const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const CleanWebpackPlugin = require('clean-webpack-plugin');
+
+module.exports = {
+ entry: {
+ 'home': './static/django_airavata_myapp/js/entry-home',
+ },
+ output: {
+ path: path.resolve(__dirname, './static/django_airavata_myapp/dist/'),
+ publicPath: '/static/django_airavata_myapp/dist/',
+ filename: '[name].js'
+ },
+ module: {
+ rules: [
+ {
+ test: /\.vue$/,
+ loader: 'vue-loader',
+ options: {
+ loaders: {
+ },
+ extractCSS: true
+ // other vue-loader options go here
+ }
+ },
+ {
+ test: /\.js$/,
+ loader: 'babel-loader',
+ exclude: /node_modules/
+ },
+ {
+ test: /\.css$/,
+ use: ExtractTextPlugin.extract({
+ fallback: "style-loader",
+ use: "css-loader"
+ })
+ },
+ {
+ test: /\.(png|jpg|gif|svg)$/,
+ loader: 'file-loader',
+ options: {
+ name: '[name].[ext]?[hash]'
+ }
+ }
+ ]
+ },
+ resolve: {
+ alias: {
+ 'vue$': 'vue/dist/vue.esm.js'
+ }
+ },
+ devServer: {
+ historyApiFallback: true,
+ noInfo: true
+ },
+ performance: {
+ hints: false
+ },
+ devtool: '#eval-source-map',
+ plugins: [
+ new ExtractTextPlugin("[name].css"),
+ new CleanWebpackPlugin(['./static/interactwel_gui/dist']),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'common',
+ }),
+
+ ]
+}
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports.devtool = '#source-map'
+ // http://vue-loader.vuejs.org/en/workflow/production.html
+ module.exports.plugins = (module.exports.plugins || []).concat([
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: '"production"'
+ }
+ }),
+ new webpack.optimize.UglifyJsPlugin({
+ sourceMap: true,
+ compress: {
+ warnings: false
+ }
+ }),
+ new webpack.LoaderOptionsPlugin({
+ minimize: true
+ })
+ ])
+}
+```
+
+You'll customize *entry* by modifying and/or adding additional entry points
+and you'll need to modify *output.path* and *output.publicPath* to correspond
+to your folder structure.
+
+Now create a static folder for holding javascript code. For this example we
+would create `static/django_airavata_myapp/js`. In this folder you can put
+the entry points, for example `entry-home.js`.
+
+For each entry point you'll create a template, extending your app's
+`base.html` and including that entry points generated css and js file.
+
+For a complete example, see the *workspace* app.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..a23d6cf
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,8 @@
+
+# Apache Airavata Django Portal
+
+The Airavata Django Portal is a web interface to the [Apache
+Airavata](http://airavata.apache.org/) API implemented using the Django web
+framework. The intention is that the Airavata Django Portal can be used as is
+for a full featured web based science gateway but it can also be customized
+through various plugins to add more domain specific functionality as needed.
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..7eca799
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,12 @@
+site_name: Airavata Django Portal
+
+repo_url: https://github.com/apache/airavata-django-portal/
+
+nav:
+ - Home: index.md
+ - User Guide:
+ - Using the integrated CMS: cms.md
+ - Developer Guide:
+ - Adding a Django App: dev/new_django_app.md
+
+theme: readthedocs
diff --git a/requirements-dev.txt b/requirements-dev.txt
index e1e5f8e..b90575d 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,6 @@
-r requirements.txt
flake8==3.5.0
flake8-isort==2.5
+mkdocs==1.0.4
-e ".[dev]"