├── .gitignore ├── .yarnrc ├── README.md ├── app ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── data.json ├── manage.py ├── media └── src │ ├── app.js │ └── app.scss ├── package.json ├── requirements.txt ├── tasks ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180322_0444.py │ ├── 0003_task_custom_class.py │ ├── 0004_auto_20180323_0104.py │ ├── 0005_auto_20180404_2234.py │ ├── 0006_auto_20180404_2235.py │ └── __init__.py ├── models.py ├── templates │ └── index.html ├── tests.py └── views.py ├── webpack.mix.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/* 2 | *.pyc 3 | *sqlite3 4 | node_modules 5 | media/dist 6 | mix-manifest.json 7 | *.log -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | small test with django-treebeard and frappe-gantt 2 | 3 | ![front end](https://i.imgur.com/VTFvWRq.png) 4 | 5 | ![admin](https://i.imgur.com/CF4QnXU.png) 6 | 7 | ## Install 8 | pip install -r requirements.txt 9 | yarn install 10 | yarn run dev 11 | python manage.py runserver 12 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodman/django-frappe-gantt/0167ac33be58a5a73939cbd1f06d09e823f4295a/app/__init__.py -------------------------------------------------------------------------------- /app/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for app project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.11. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '%3kyp9!7vfc4n$jga^d+#fz#0!^p87-44at)2o!@^(&u0w_=wh' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'bsync', 40 | 'django.contrib.staticfiles', 41 | 'treebeard', 42 | 'tasks', 43 | 'django_model_to_dict', 44 | 45 | ] 46 | 47 | MIDDLEWARE_CLASSES = [ 48 | 'django.middleware.security.SecurityMiddleware', 49 | 'django.contrib.sessions.middleware.SessionMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'app.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': [], 63 | 'APP_DIRS': True, 64 | 'OPTIONS': { 65 | 'context_processors': [ 66 | 'django.template.context_processors.debug', 67 | 'django.template.context_processors.request', 68 | 'django.template.context_processors.media', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'app.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'en-us' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | STATIC_ROOT = os.path.join(BASE_DIR,'static') 128 | MEDIA_URL ='/media/' 129 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 130 | 131 | -------------------------------------------------------------------------------- /app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib import admin 3 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 4 | from django.conf import settings 5 | from django.conf.urls.static import static 6 | from tasks.views import TaskView 7 | 8 | urlpatterns = [ 9 | url(r'^admin/', admin.site.urls), 10 | url(r'^$', TaskView.as_view()), 11 | ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + staticfiles_urlpatterns() 12 | -------------------------------------------------------------------------------- /app/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for app project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | [{"model": "tasks.task", "pk": 5, "fields": {"path": "0002", "depth": 1, "numchild": 1, "name": "Introducci\u00f3n", "start": "2018-04-01", "end": "2018-05-16", "progress": 0}}, {"model": "tasks.task", "pk": 6, "fields": {"path": "00020001", "depth": 2, "numchild": 1, "name": "Frameworks de desarrollo actuales.", "start": "2018-04-01", "end": "2018-04-13", "progress": 0}}, {"model": "tasks.task", "pk": 7, "fields": {"path": "0002000100010001", "depth": 4, "numchild": 0, "name": "Arquitecturas de software como servicio", "start": "2018-05-01", "end": "2018-05-11", "progress": 0}}, {"model": "tasks.task", "pk": 8, "fields": {"path": "000200010001", "depth": 3, "numchild": 1, "name": "Revisi\u00f3n de arquitecturas.", "start": "2018-04-16", "end": "2018-04-30", "progress": 0}}, {"model": "tasks.task", "pk": 9, "fields": {"path": "0003", "depth": 1, "numchild": 1, "name": "Frameworks de desarrollo:", "start": "2018-05-17", "end": "2018-06-18", "progress": 0}}, {"model": "tasks.task", "pk": 10, "fields": {"path": "00030001", "depth": 2, "numchild": 1, "name": "Componentes", "start": "2018-05-17", "end": "2018-05-31", "progress": 0}}, {"model": "tasks.task", "pk": 11, "fields": {"path": "000300010001", "depth": 3, "numchild": 0, "name": "Modelo Vista Controlador", "start": "2018-06-01", "end": "2018-06-18", "progress": 0}}, {"model": "tasks.task", "pk": 12, "fields": {"path": "0004", "depth": 1, "numchild": 0, "name": "M\u00e9todo software como producto a software como servicio.", "start": "2018-06-19", "end": "2018-07-13", "progress": 0}}, {"model": "tasks.task", "pk": 13, "fields": {"path": "0005", "depth": 1, "numchild": 0, "name": "Caso 1 Social Management Workflow System (SMWS)", "start": "2018-07-13", "end": "2018-08-10", "progress": 0}}, {"model": "tasks.task", "pk": 14, "fields": {"path": "0006", "depth": 1, "numchild": 0, "name": "Caso 2 Auditmy.link", "start": "2018-08-10", "end": "2018-08-17", "progress": 0}}, {"model": "tasks.task", "pk": 15, "fields": {"path": "0007", "depth": 1, "numchild": 0, "name": "Conclusiones", "start": "2018-08-17", "end": "2018-08-31", "progress": 0}}] -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /media/src/app.js: -------------------------------------------------------------------------------- 1 | var Gantt_ = require("frappe-gantt").default; 2 | var gantt; 3 | 4 | 5 | toggle_class = function($this){ 6 | // toggle class 7 | var elemtns = document.querySelectorAll(".change") 8 | for(i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 |
19 | 20 | 31 |
32 | 33 | 38 |
39 |
40 | {% if tasks_count %} 41 |
42 | 43 | 44 |
45 |
    46 |
  • 47 | Day 48 |
  • 49 |
  • 50 | Week 51 |
  • 52 |
  • 53 | Month 54 |
  • 55 |
56 | 64 |
65 |
66 |
67 | {% endif %} 68 |
69 |
70 |
71 | 72 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /tasks/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.test import TestCase 5 | 6 | # Create your tests here. 7 | -------------------------------------------------------------------------------- /tasks/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | from django.shortcuts import render 4 | from django.views.generic import TemplateView 5 | from tasks.models import Task, Project 6 | import json 7 | 8 | class TaskView(TemplateView): 9 | template_name ="index.html" 10 | 11 | def process(self, t): 12 | task = t.to_dict() 13 | for i in ('path', 'depth', 'path', 'numchild','project'): 14 | if i in task: 15 | del task[i] 16 | task['id'] = str(task['id']) 17 | childrens = [c.id for c in t.get_children()] 18 | task["dependencies"] = ",".join([str(i) for i in childrens]) 19 | return task 20 | 21 | def get_context_data(self, **kwargs): 22 | ctx = super(TaskView, self).get_context_data(**kwargs) 23 | id = self.request.GET.get("id",None) 24 | 25 | if not id: 26 | tasks = Task.objects.none() 27 | else: 28 | tasks = Task.objects.filter(project__id=id) 29 | 30 | ctx["id"] = id 31 | ctx["projects"] = Project.objects.all() 32 | ctx["tasks_count"] = tasks.count() 33 | ctx["tasks"] = json.dumps([ self.process(t) for t in tasks]) 34 | 35 | return ctx 36 | 37 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel application. By default, we are compiling the Sass 10 | | file for your application, as well as bundling up your JS files. 11 | | 12 | */ 13 | 14 | mix.setPublicPath('media/'); 15 | mix.js(path.join('media', 'src', 'app.js'), path.join('dist')) 16 | .sass(path.join('media','src','app.scss'), path.join('dist')); 17 | 18 | // Full API 19 | // mix.js(src, output); 20 | // mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation. 21 | // mix.preact(src, output); <-- Identical to mix.js(), but registers Preact compilation. 22 | // mix.coffee(src, output); <-- Identical to mix.js(), but registers CoffeeScript compilation. 23 | // mix.ts(src, output); <-- TypeScript support. Requires tsconfig.json to exist in the same folder as webpack.mix.js 24 | // mix.extract(vendorLibs); 25 | // mix.sass(src, output); 26 | // mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack. 27 | // mix.fastSass('src', output); <-- Alias for mix.standaloneSass(). 28 | // mix.less(src, output); 29 | // mix.stylus(src, output); 30 | // mix.postCss(src, output, [require('postcss-some-plugin')()]); 31 | mix.browserSync({ 32 | 'proxy':'localhost:8000', 33 | 'logFileChanges':true, 34 | 'notify':true, 35 | 'files':[ 36 | path.join("media","dist","*"), 37 | path.join("*","templates", "**","*.html"), 38 | ], 39 | }); 40 | // mix.combine(files, destination); 41 | // mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation. 42 | // mix.copy(from, to); 43 | // mix.copyDirectory(fromDir, toDir); 44 | // mix.minify(file); 45 | // mix.sourceMaps(); // Enable sourcemaps 46 | // mix.version(); // Enable versioning. 47 | // mix.disableNotifications(); 48 | // mix.setPublicPath('path/to/public'); 49 | // mix.setResourceRoot('prefix/for/resource/locators'); 50 | // mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin. 51 | // mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly. 52 | // mix.babelConfig({}); <-- Merge extra Babel configuration (plugins, etc.) with Mix's default. 53 | // mix.then(function () {}) <-- Will be triggered each time Webpack finishes building. 54 | // mix.extend(name, handler) <-- Extend Mix's API with your own components. 55 | // mix.options({ 56 | // extractVueStyles: false, // Extract .vue component styling to file, rather than inline. 57 | // globalVueStyles: file, // Variables file to be imported in every component. 58 | // processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched. 59 | // purifyCss: false, // Remove unused CSS selectors. 60 | // uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin 61 | // postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md 62 | // }); 63 | --------------------------------------------------------------------------------