diff options
author | Milosz Wasilewski <milosz.wasilewski@linaro.org> | 2014-01-27 09:51:08 +0000 |
---|---|---|
committer | Milosz Wasilewski <milosz.wasilewski@linaro.org> | 2014-01-27 09:51:08 +0000 |
commit | 541ab774dc97821529122904fe3192577e684ff9 (patch) | |
tree | 6400417362dcfae50cafe05fd48b258002153658 | |
parent | 0cc0164f5734060689102016f626be5919209e58 (diff) | |
parent | 19dffd8aa718f5127ba38bb5c6217b30358d539b (diff) |
pull master from repo
Change-Id: If264f868db46bfa16be7c75a345c96c116504da2
63 files changed, 699 insertions, 253 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..073ceb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.pyc +local_settings.py +*.db +*.swp @@ -4,6 +4,8 @@ Step-by-step installation instructions for production environment apt-get install python-pip apt-get install git apt-get install apache2 libapache2-mod-wsgi +apt-get install python-tz +apt-get install expect 2. install virtualenvwrapper and create virtualenv pip install virtualenvwrapper diff --git a/ansible/README b/ansible/README new file mode 100644 index 0000000..ccb0130 --- /dev/null +++ b/ansible/README @@ -0,0 +1,10 @@ +To run the configuration, it is necessary to fill in the secrets.yml file +with the correct user names and passwords for the service used. + +The secrets.yml file contains variables for: + - Crowd user name and password + - Jira user name and password + + Those values are necessary for the local_settings.py file to run the + application. +
\ No newline at end of file diff --git a/ansible/group_vars/all b/ansible/group_vars/all new file mode 100644 index 0000000..8a32cd0 --- /dev/null +++ b/ansible/group_vars/all @@ -0,0 +1,7 @@ +# Common variables. +install_base: /srv +roadmap_repo: http://git.linaro.org/git/infrastructure/roadmap.git +apache_user: www-data +crowd_url: https://login.linaro.org:8443/crowd/rest +jira_server: https://cards.linaro.org +jira_sfid: 10301 diff --git a/ansible/hosts b/ansible/hosts new file mode 100644 index 0000000..dd5fb49 --- /dev/null +++ b/ansible/hosts @@ -0,0 +1,3 @@ +[all] +staging.status.linaro.org ansible_ssh_user=ubuntu role=staging install_dir=staging.status.linaro.org +status.linaro.org ansible_ssh_user=ubuntu role=production install_dir=status.linaro.org diff --git a/ansible/roles/common/handlers/main.yml b/ansible/roles/common/handlers/main.yml new file mode 100644 index 0000000..7ec48c2 --- /dev/null +++ b/ansible/roles/common/handlers/main.yml @@ -0,0 +1,8 @@ +- name: restart-apache + service: name=apache2 state=restarted + +- name: stop-apache + service: name=apache2 state=stopped + +- name: reload-apache + service: name=apache2 state=reloaded diff --git a/ansible/roles/common/tasks/apache.yml b/ansible/roles/common/tasks/apache.yml new file mode 100644 index 0000000..8f05d84 --- /dev/null +++ b/ansible/roles/common/tasks/apache.yml @@ -0,0 +1,18 @@ +# Enables necessary Apache modules and disables websites. +- name: enable-modules + command: a2enmod {{ item }} + with_items: + - wsgi + - headers + - expires + notify: restart-apache + +- name: disable-sites + command: a2dissite {{ item }} + with_items: + - default + notify: restart-apache + +# Make sure the web server is running. +- name: apache2-started + service: name=apache2 state=started enabled=yes diff --git a/ansible/roles/common/tasks/install_deps.yml b/ansible/roles/common/tasks/install_deps.yml new file mode 100644 index 0000000..775f4bf --- /dev/null +++ b/ansible/roles/common/tasks/install_deps.yml @@ -0,0 +1,15 @@ +# Install all dependencies required by roadmap. +- name: install-os-deps + apt: name={{ item }} + with_items: + - apache2 + - libapache2-mod-wsgi + - git + - python-pip + - python-tz + +# PIP installation if necessary. +- name: install-pip-deps + pip: name={{ item }} + with_items: + - virtualenvwrapper diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml new file mode 100644 index 0000000..1aa5b62 --- /dev/null +++ b/ansible/roles/common/tasks/main.yml @@ -0,0 +1,3 @@ +# Common tasks for all instances (production and staging). +- include: install_deps.yml +- include: apache.yml diff --git a/ansible/roles/status/tasks/apache_conf.yml b/ansible/roles/status/tasks/apache_conf.yml new file mode 100644 index 0000000..1c9e797 --- /dev/null +++ b/ansible/roles/status/tasks/apache_conf.yml @@ -0,0 +1,11 @@ +- name: apache-website-conf + template: src=apache_website.conf dest=/etc/apache2/sites-available/{{ install_dir }} owner=root group=root mode=0644 + tags: + - apache-conf + notify: reload-apache + +- name: apache-website-enable + command: a2ensite {{ install_dir }} + notify: reload-apache + tags: + - apache-enable diff --git a/ansible/roles/status/tasks/clone_code.yml b/ansible/roles/status/tasks/clone_code.yml new file mode 100644 index 0000000..c64cdc8 --- /dev/null +++ b/ansible/roles/status/tasks/clone_code.yml @@ -0,0 +1,6 @@ +# Clone roadmap/status code and fix files and dirs permission. +- name: clone-roadmap + git: name={{ roadmap_repo }} dest={{ install_base }}/{{ install_dir }} + +- name: fix-repo-owner + file: path={{ install_base}}/{{ install_dir }} recurse=yes owner={{ apache_user }} group={{ apache_user }} diff --git a/ansible/roles/status/tasks/cronjobs.yml b/ansible/roles/status/tasks/cronjobs.yml new file mode 100644 index 0000000..e650270 --- /dev/null +++ b/ansible/roles/status/tasks/cronjobs.yml @@ -0,0 +1,10 @@ +# Install necessary cronjobs. +- name: copy-update-script + template: src=roadmap_update_cron.sh dest={{ install_base }}/{{ install_dir }}/bin/roadmap_update_cron.sh owner={{ apache_user }} group={{ apache_user }} mode=0770 + tags: + - cronjob + +- name: install-update-cronjob + cron: name='Update cards' cron_file={{ install_dir }} state=present user={{ apache_user }} job={{ install_base }}/{{ install_dir }}/bin/roadmap_update_cron.sh minute=0 hour=0 + tags: + - cronjob diff --git a/ansible/roles/status/tasks/install_roadmap.yml b/ansible/roles/status/tasks/install_roadmap.yml new file mode 100644 index 0000000..d998683 --- /dev/null +++ b/ansible/roles/status/tasks/install_roadmap.yml @@ -0,0 +1,35 @@ +# Install roadmap via virtualenv. +- name: roadmap-log-directory + file: path=/var/log/roadmap state=directory owner={{ apache_user }} group={{apache_user }} + +- name: create-virtualenv + command: virtualenv --system-site-packages {{ install_base }}/virtualenv/{{ install_dir }} + +- name: install-requirements + pip: virtualenv={{ install_base }}/virtualenv/{{ install_dir }} requirements={{ install_base }}/{{ install_dir }}/requirements.txt + +# Roadmap installation steps. +- name: roadmap-django-syncdb + django_manage: command=syncdb virtualenv={{ install_base }}/virtualenv/{{ install_dir }} app_path={{install_base }}/{{ install_dir }} + +- name: roadmap-django-migrate + django_manage: command=migrate virtualenv={{ install_base }}/virtualenv/{{ install_dir }} app_path={{install_base }}/{{ install_dir }} + +- name: roadmap-static-files + file: state=directory src={{ install_base }}/{{ install_dir }}/roadmap/static dest=/var/www/{{ install_dir }}/static/ + +- name: roadmap-django-collectstatic + django_manage: command=collectstatic virtualenv={{ install_base }}/virtualenv/{{ install_dir }} app_path={{install_base }}/{{ install_dir }} + +# Make sure everything can be accessed by the Apache user. +- name: fix-virtualenv-ownership + file: path={{ install_base }}/virtualenv recurse=yes owner={{ apache_user }} group={{ apache_user }} + +- name: fix-roadmap-install-ownership + file: path={{ install_base }}/{{ install_dir }} owner={{ apache_user }} group={{ apache_user }} recurse=yes + +- name: fix-roadmap-static-ownership + file: path=/var/www/{{ install_dir }} recurse=yes owner={{ apache_user }} group={{ apache_user }} + +- name: fix-roadmap-log-ownership + file: path=/var/log/roadmap recurse=yes owner={{ apache_user }} group={{ apache_user }} diff --git a/ansible/roles/status/tasks/local_settings.yml b/ansible/roles/status/tasks/local_settings.yml new file mode 100644 index 0000000..9790836 --- /dev/null +++ b/ansible/roles/status/tasks/local_settings.yml @@ -0,0 +1,6 @@ +# Create the local_settings file and fix its ownership. +- name: local-settings + template: src=local_settings.py dest={{ install_base }}/{{ install_dir }}/linaroroadmap + +- name: fix-local-settings-ownership + file: path={{ install_base }}/{{ install_dir }}/linaroroadmap/local_settings.py owner={{ apache_user }} group={{ apache_user }} diff --git a/ansible/roles/status/tasks/main.yml b/ansible/roles/status/tasks/main.yml new file mode 100644 index 0000000..26bdebe --- /dev/null +++ b/ansible/roles/status/tasks/main.yml @@ -0,0 +1,6 @@ +- include: clone_code.yml +- include: local_settings.yml +- include: install_roadmap.yml +- include: wsgi.yml +- include: apache_conf.yml +- include: cronjobs.yml diff --git a/ansible/roles/status/tasks/wsgi.yml b/ansible/roles/status/tasks/wsgi.yml new file mode 100644 index 0000000..dd52c0d --- /dev/null +++ b/ansible/roles/status/tasks/wsgi.yml @@ -0,0 +1,6 @@ +# Install the correct WSGI script. +- name: install-wsgi + template: src=roadmap.wsgi dest={{ install_base }}/{{ install_dir }}/{{ install_dir }}.wsgi owner={{ apache_user }} group={{ apache_user }} mode=0744 + tags: + - wsgi + notify: restart-apache diff --git a/ansible/roles/status/templates/apache_production.conf b/ansible/roles/status/templates/apache_production.conf new file mode 100644 index 0000000..bc3aa7e --- /dev/null +++ b/ansible/roles/status/templates/apache_production.conf @@ -0,0 +1,56 @@ +<VirtualHost *:80> + ServerName {{ install_dir }} + ServerAdmin webmaster@linaro.org + + Redirect permanent / https://{{ install_dir }} +</VirtualHost> + +<VirtualHost *:443> + ServerName {{ install_dir }} + ServerAdmin webmaster@linaro.org + + CustomLog ${APACHE_LOG_DIR}/{{ install_dir }}-access.log combined + ErrorLog ${APACHE_LOG_DIR}/{{ install_dir }}-error.log + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/{{ install_dir }}.crt + SSLCertificateKeyFile /etc/ssl/certs/{{ install_dir }}.key + SSLCACertificateFile /etc/ssl/certs/gd_bundle.crt + + SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown + #DocumentRoot /var/www/{{ install_dir }} + WSGIScriptAlias / {{ install_base }}/{{ install_dir }}/{{ install_dir }}.wsgi + + ExpiresActive On + ExpiresDefault "access plus 300 seconds" + + ExpiresByType text/css "access plus 1 month" + ExpiresByType text/javascript "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType image/jpg "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/x-icon "access plus 1 month" + + Header append Cache-Control "public, no-transform" + + <FilesMatch "\.(html|htm)$"> + Header add Cache-Control "must-revalidate" + </FilesMatch> + + <FilesMatch "\.(js|css)$"> + Header add Cache-Control "max-age=604800" + </FilesMatch> + + Alias /static/ /var/www/{{ install_dir }}/static/ + <Location "/static/"> + Options -Indexes + SetOutputFilter DEFLATE + + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4\.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html + + SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary + Header append Vary User-Agent env=!dont-vary + </Location> +</VirtualHost> diff --git a/ansible/roles/status/templates/apache_staging.conf b/ansible/roles/status/templates/apache_staging.conf new file mode 100644 index 0000000..68e1995 --- /dev/null +++ b/ansible/roles/status/templates/apache_staging.conf @@ -0,0 +1,47 @@ +<VirtualHost *:80> + ServerName {{ install_dir }} + ServerAdmin webmaster@linaro.org + + CustomLog ${APACHE_LOG_DIR}/{{ install_dir }}-access.log combined + ErrorLog ${APACHE_LOG_DIR}/{{ install_dir }}-error.log + + SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown + #DocumentRoot /var/www/{{ install_dir }} + + WSGIDaemonProcess {{ install_dir }} maximum-requests=10000 + WSGIProcessGroup {{ install_dir }} + WSGIScriptAlias / {{ install_base }}/{{ install_dir }}/{{ install_dir }}.wsgi + + ExpiresActive On + ExpiresDefault "access plus 300 seconds" + + ExpiresByType text/css "access plus 1 month" + ExpiresByType text/javascript "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType image/jpg "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/x-icon "access plus 1 month" + + Header append Cache-Control "public, no-transform" + + <FilesMatch "\.(html|htm)$"> + Header add Cache-Control "must-revalidate" + </FilesMatch> + + <FilesMatch "\.(js|css)$"> + Header add Cache-Control "max-age=604800" + </FilesMatch> + + Alias /static/ /var/www/{{ install_dir }}/static/ + <Location "/static/"> + Options -Indexes + SetOutputFilter DEFLATE + + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4\.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html + + SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary + Header append Vary User-Agent env=!dont-vary + </Location> +</VirtualHost> diff --git a/ansible/roles/status/templates/apache_website.conf b/ansible/roles/status/templates/apache_website.conf new file mode 100644 index 0000000..c6e763e --- /dev/null +++ b/ansible/roles/status/templates/apache_website.conf @@ -0,0 +1,8 @@ +WSGIRestrictEmbedded On +WSGILazyInitialization On + +{% if role == 'staging' %} +{% extends "apache_staging.conf" %} +{% else %} +{% extends "apache_production.conf" %} +{% endif %} diff --git a/ansible/roles/status/templates/roadmap.wsgi b/ansible/roles/status/templates/roadmap.wsgi new file mode 100644 index 0000000..dde7fd5 --- /dev/null +++ b/ansible/roles/status/templates/roadmap.wsgi @@ -0,0 +1,21 @@ +import os +import sys +import site + +# Add the site-packages of the chosen virtualenv to work with +site.addsitedir('{{ install_base }}/virtualenv/{{ install_dir }}/' + 'local/lib/python2.7/site-packages') + +# Add the app's directory to the PYTHONPATH +sys.path.append('{{ install_base }}/{{ install_dir }}/') +sys.path.append('{{ install_base }}/{{ install_dir }}/linaroroadmap/') + +os.environ['DJANGO_SETTINGS_MODULE'] = 'linaroroadmap.settings' + +# Activate your virtual env +activate_env = os.path.expanduser( + "{{ install_base }}/virtualenv/{{ install_dir }}/bin/activate_this.py") +execfile(activate_env, dict(__file__=activate_env)) + +import django.core.handlers.wsgi +application = django.core.handlers.wsgi.WSGIHandler() diff --git a/ansible/roles/status/templates/roadmap_update_cron.sh b/ansible/roles/status/templates/roadmap_update_cron.sh new file mode 100644 index 0000000..b4fdf7d --- /dev/null +++ b/ansible/roles/status/templates/roadmap_update_cron.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright (C) 2013, 2014 Linaro +# +# This file is part of roadmap. +# +# roadmap is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# roadmap is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with roadmap. If not, see <http://www.gnu.org/licenses/>. + +export WORKON_HOME={{install_base }}/virtualenv +source /usr/local/bin/virtualenvwrapper.sh + +workon {{ install_dir }} + +cd /srv/{{ install_dir }} + +{% if role == 'staging' %} +./manage.py roadmap_import --debug +./manage.py burndown_snapshot --debug +{% else %} +./manage.py roadmap_import +./manage.py burndown_snapshot +{% endif %}
\ No newline at end of file diff --git a/ansible/secrets.yml b/ansible/secrets.yml new file mode 100644 index 0000000..31f12ca --- /dev/null +++ b/ansible/secrets.yml @@ -0,0 +1,5 @@ +# Secrets the user need to insert. +crowd_app_name: +crowd_app_password: +jira_username: +jira_password: diff --git a/ansible/site.yml b/ansible/site.yml new file mode 100644 index 0000000..98e5f36 --- /dev/null +++ b/ansible/site.yml @@ -0,0 +1,11 @@ +# Install everything. + +- hosts: all + gather_facts: no + sudo: yes + roles: + - common + - status + vars_files: + - secrets.yml +
\ No newline at end of file diff --git a/bin/roadmap_expect.sh b/bin/roadmap_expect.sh new file mode 100755 index 0000000..d2a3599 --- /dev/null +++ b/bin/roadmap_expect.sh @@ -0,0 +1,8 @@ +#!/usr/bin/expect +set timeout 11000 +log_file -noappend roadmap_sync.log; +spawn ./roadmap_update.sh +expect "JIRA password (roadmap-sync):" +send "" +expect eof +exit diff --git a/bin/roadmap_update.sh b/bin/roadmap_update.sh new file mode 100755 index 0000000..06bef13 --- /dev/null +++ b/bin/roadmap_update.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copyright (C) 2013, 2014 Linaro +# +# This file is part of roadmap. +# +# roadmap is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# roadmap is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with roadmap. If not, see <http://www.gnu.org/licenses/>. + +export WORKON_HOME=/srv/virtualenv +source /usr/local/bin/virtualenvwrapper.sh + +workon roadmap + +cd /srv/production_roadmap + +./manage.py roadmap_import --debug +./manage.py burndown_snapshot --debug diff --git a/bin/wsgi_roadmap.wsgi b/bin/wsgi_roadmap.wsgi new file mode 100644 index 0000000..cbe346b --- /dev/null +++ b/bin/wsgi_roadmap.wsgi @@ -0,0 +1,20 @@ +import os +import sys +import site + +# Add the site-packages of the chosen virtualenv to work with +site.addsitedir('/srv/virtualenv/roadmap/local/lib/python2.7/site-packages') + +# Add the app's directory to the PYTHONPATH +sys.path.append('/srv/roadmap.linaro.org/') +sys.path.append('/srv/roadmap.linaro.org/linaroroadmap/') + +os.environ['DJANGO_SETTINGS_MODULE'] = 'linaroroadmap.settings' + +# Activate your virtual env +activate_env = os.path.expanduser( + "/srv/virtualenv/roadmap/bin/activate_this.py") +execfile(activate_env, dict(__file__=activate_env)) + +import django.core.handlers.wsgi +application = django.core.handlers.wsgi.WSGIHandler() diff --git a/linaroroadmap/local_settings.py.dev b/linaroroadmap/local_settings.py.dev new file mode 100644 index 0000000..8b6d4ab --- /dev/null +++ b/linaroroadmap/local_settings.py.dev @@ -0,0 +1,29 @@ +import random +import string + +CROWD = { + 'url': '', + 'app_name': '', + 'password': '', + 'superuser': False, +} + +JIRA_SERVER = None +JIRA_USERNAME = None +JIRA_PASSWORD = None + +JIRA_PROJECT = '' +JIRA_STATUSES = [] +SFID = None + +AUTHENTICATION_BACKENDS = ( + 'crowd.backends.CrowdBackend', +) + +char_selection = string.ascii_letters + string.digits +char_selection += '!@#$%^&*(-_=+)' + +SECRET_KEY = '{0}'.format(''.join(random.sample(char_selection, 50))) + +STATIC_ROOT = '/var/www/roadmap.linaro.org/static/' +DEBUG = False diff --git a/linaroroadmap/settings.py b/linaroroadmap/settings.py index d18dd88..1d07e04 100644 --- a/linaroroadmap/settings.py +++ b/linaroroadmap/settings.py @@ -3,7 +3,7 @@ DEBUG = True ADMINS = ( - # ('Your Name', 'your_email@example.com'), + ('linaro-infrastructure', 'linaro-infrastructure-errors@linaro.org') ) AUTH_CROWD_ALWAYS_UPDATE_USER = True @@ -19,29 +19,44 @@ AUTH_CROWD_SERVER_TRUSTED_ROOT_CERTS_FILE = None TRUSTED_ADDRESS = [] -JIRA_PROJECT = "" +CROWD = { + 'url': '', + 'app_name': '', + 'password': '', + 'superuser': False, +} + +# The URL of the Jira server to connect to. +JIRA_SERVER = None +# The user name to use to connect to JIRA_SERVER. +JIRA_USERNAME = None +# The password for JIRA_SERVER. +JIRA_PASSWORD = None + +JIRA_PROJECT = '' JIRA_STATUSES = [] -SFID = None # custom field that defines relation between blueprint and engineering card in greenhopper +# Custom field that defines relation between blueprint and engineering card +# in greenhopper +SFID = None AUTHENTICATION_BACKENDS = ( #'django.contrib.auth.backends.ModelBackend', 'crowdrest.backend.CrowdRestBackend', ) -MANAGERS = ADMINS - DATABASES = { 'default': { - 'ENGINE': '', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '', # Or path to database file if using sqlite3. - # The following settings are not used with sqlite3: + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': '/srv/production_roadmap/linaroroadmap.db', 'USER': '', 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. - 'PORT': '', # Set to empty string for default. + 'HOST': '', + 'PORT': '', } } +MANAGERS = ADMINS + # Hosts/domain names that are valid for this site; required if DEBUG is False # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts ALLOWED_HOSTS = ['*'] @@ -50,29 +65,20 @@ ALLOWED_HOSTS = ['*'] # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # In a Windows environment this must be set to your system time zone. -TIME_ZONE = 'America/Chicago' +TIME_ZONE = 'Europe/London' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'en-us' -#USE_I18N = True -#ugettext = lambda s: s #LANGUAGES = ( # ('en-us', u'English'), #) SITE_ID = 1 -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. USE_I18N = True - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale. USE_L10N = True - -# If you set this to False, Django will not use timezone-aware datetimes. USE_TZ = True # Absolute filesystem path to the directory that will hold user-uploaded files. @@ -110,7 +116,7 @@ STATICFILES_FINDERS = ( ) # Make this unique, and don't share it with anybody. -SECRET_KEY = 'd0jdu-gqrsj*8n@v!%x+s+!t-77y^$pz*s=ewebv0%c06wnyz-' +SECRET_KEY = '' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( @@ -183,11 +189,24 @@ LOGGING = { '()': 'django.utils.log.RequireDebugFalse' } }, + 'formatters': { + 'simple': { + 'format': '[%(asctime)s] %(levelname)-8s %(message)s', + } + }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' + }, + 'roadmap_to_file': { + 'level': 'DEBUG', + 'class': 'logging.handlers.TimedRotatingFileHandler', + 'filename': '/var/log/roadmap/roadmap.helpers.log', + 'backupCount': 5, + 'when': 'midnight', + 'formatter': 'simple' } }, 'loggers': { @@ -196,12 +215,31 @@ LOGGING = { 'level': 'ERROR', 'propagate': True, }, + 'roadmap.helpers': { + 'level': 'INFO', + 'handlers': ['roadmap_to_file'], + 'propagate': False, + } } } try: from local_settings import * -except: - print "local_settings not found" +except ImportError: + import random + import string + + # Create local_settings with random SECRET_KEY. + char_selection = string.ascii_letters + string.digits + char_selection_with_punctuation = char_selection + '!@#$%^&*(-_=+)' + + # SECRET_KEY contains anything but whitespace + secret_key = ''.join(random.sample(char_selection_with_punctuation, 50)) + local_settings_content = "SECRET_KEY = '{0}'\n".format(secret_key) + + with open(os.path.join(PROJECT_ROOT, "local_settings.py"), "w") as f: + f.write(local_settings_content) + + from local_settings import * TEMPLATE_DEBUG = DEBUG diff --git a/requirements.txt b/requirements.txt index a9ca68e..c840714 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ simplejson==2.3.2 parsedatetime==1.1.2 South==0.8.2 django-reversion==1.7.1 --e git://git.linaro.org/people/pfalcon/django-crowd-rest-backend-linaro.git@8e599abd2aa1af210992f97f8dea5727e18a1928#egg=django_crowd_rest_backend-dev +-e git://git.linaro.org/lava/django-crowd-rest-backend.git@c741f1738786c58253b400ffc27a16967702f84c#egg=django_crowd_rest_backend-dev diff --git a/roadmap/helpers.py b/roadmap/helpers.py index 0de4600..5bdf13c 100644 --- a/roadmap/helpers.py +++ b/roadmap/helpers.py @@ -15,12 +15,13 @@ # You should have received a copy of the GNU Affero General Public License # along with roadmap. If not, see <http://www.gnu.org/licenses/>. +import logging + from django.conf import settings -from django.db.models import ManyToManyField, Count +from django.db.models import ManyToManyField from django.utils import timezone from datetime import datetime from operator import attrgetter -from inspect import currentframe, getframeinfo from roadmap.models import ( Burndown, BurndownBar, @@ -43,9 +44,11 @@ JIRA_VERIFY = 'jira_verify' JIRA_DEPENDSON = 'dependson' JIRA_IMPLEMENTS = 'implements' JIRA_KEY = 'key' -VERBOSE = 'verbose' +DEBUG = 'debug' SINGLE_UPDATE = 'single_update' +log = logging.getLogger('roadmap.helpers') + class Issue(object): @@ -59,9 +62,12 @@ class Issue(object): def create_or_update(issue, options, jira=None): + if isinstance(issue, dict): issue = Issue(issue) + log.debug("Creating or updating issue {0}".format(issue.key)) + project = None if hasattr(issue.fields, 'project') and issue.fields.project: project, created = Project.objects.get_or_create( @@ -88,22 +94,9 @@ def create_or_update(issue, options, jira=None): cp, created = Component.objects.get_or_create( name=comp.name, project=project) components.append(cp.pk) - if options[VERBOSE]: - try: - print "%s %s" % (issue.key, cp.name) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + log.debug("{0}: {1}".format(issue.key, cp.name)) else: - if options[VERBOSE]: - try: - print "****** NO COMPONENT *******" - print issue.key - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + log.info("Issue {0} has no component".format(issue.key)) fixVersion = None fixVersions = [] @@ -123,33 +116,19 @@ def create_or_update(issue, options, jira=None): project=project ) fixVersions.append(fv) + fixVersion = min(fixVersions, key=attrgetter('fix_date')) - if options[VERBOSE]: - try: - print "fixVersion: %s" % (fixVersion) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + log.debug("Fix version is: {0}".format(fixVersion)) + status = None if hasattr(issue.fields, 'status') and issue.fields.status: # find status in database or create one status, created = Status.objects.get_or_create( name=issue.fields.status.name, project=project) - if options[VERBOSE]: - try: - print "status: %s" % (status) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" - - key = issue.key - url = options[JIRA_SERVER] + "/browse/" + issue.key - summary = issue.fields.summary + log.debug("Status is: {0}".format(status)) + cardStart = None end = None - if fixVersion: if fixVersion.name == "ONGOING": if (hasattr(issue.fields, 'customfield_10300') @@ -166,16 +145,9 @@ def create_or_update(issue, options, jira=None): if cardStart: break else: - # fixVersion is not ONGOING cardStart = fixVersion.fix_date + log.debug("Start: {0} - End: {1}".format(cardStart, end)) - if options[VERBOSE]: - try: - print "start: {0}, end: {1}".format(cardStart, end) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" implementedby_list = [] depends_list = [] if hasattr(issue.fields, 'issuelinks'): @@ -197,10 +169,10 @@ def create_or_update(issue, options, jira=None): try: implementedby_list.append( Card.objects.get(key=link.inwardIssue.key)) - except: - if options[VERBOSE]: - # should not happen - print "Card not found" + except Exception as ex: + log.error("Card not found") + log.exception(ex) + if link.type.name == "Depends" and options[JIRA_DEPENDSON]: if hasattr(link, "inwardIssue"): if jira: @@ -218,56 +190,37 @@ def create_or_update(issue, options, jira=None): try: depends_list.append( Card.objects.get(key=link.inwardIssue.key)) - except: - if options[VERBOSE]: - # should not happen - print "Card not found" + except Exception as ex: + log.error("Card not found") + log.exception(ex) + blueprints = [] - if hasattr(issue.fields, 'issuetype') and issue.fields.issuetype.name == "Engineering card": - if options[VERBOSE]: - try: - print "Engineering card: %s" % issue.key - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + if (hasattr(issue.fields, 'issuetype') and + issue.fields.issuetype.name == "Engineering card"): + log.debug("Engineering card {0}".format(issue.key)) + bp_search_string = "cf[%s] = %s" % (settings.SFID, issue.key) bp_issues = jira.search_issues(bp_search_string) start = 0 while len(bp_issues) > 0: for index, blueprint in enumerate(bp_issues, start=1): - if options[VERBOSE]: - try: - print "Processing BP %s/%s (%s/%s) %s" % ( - index + start, - bp_issues.total, - index, - len(bp_issues), - blueprint.key) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + log.debug("Processing Blueprint {0}/{1} ({2}/{3}) {4}".format( + index + start, bp_issues.total, index, len(bp_issues), + blueprint.key)) + blueprints.append( create_or_update( - jira.issue(blueprint.key, expand="changelog"), - options, - jira).pk) - start = start + len(bp_issues) + jira.issue(blueprint.key, expand="changelog"), + options, jira).pk) + + start += len(bp_issues) bp_issues = jira.search_issues(bp_search_string, startAt=start) # compare implemented by and blueprints to eliminate duplicates - implementedby_list = set(implementedby_list) | set(blueprints) - - if options[VERBOSE]: - try: - print "Dependencies" - print depends_list - print "Implements" - print implementedby_list - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + # implementedby_list = set(implementedby_list) | set(blueprints) + implementedby_list = list(set(implementedby_list).union(set(blueprints))) + + log.debug("Dependencies: {0}".format(depends_list)) + log.debug("Implements: {0}".format(implementedby_list)) label_list = [] if hasattr(issue.fields, 'labels'): @@ -280,6 +233,9 @@ def create_or_update(issue, options, jira=None): lbl.save() label_list.append(lbl.pk) + url = settings.JIRA_SERVER + "/browse/" + issue.key + summary = issue.fields.summary + defaults = { 'url': url, 'summary': summary, @@ -293,15 +249,10 @@ def create_or_update(issue, options, jira=None): 'card_type': card_type } - if options[VERBOSE]: - try: - print defaults - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + log.debug("Default values: {0}".format(defaults)) - card, created = Card.objects.get_or_create(key=key, defaults=defaults) + card, created = Card.objects.get_or_create( + key=issue.key, defaults=defaults) defaults.update({'components': components}) if options[JIRA_IMPLEMENTS]: @@ -316,23 +267,14 @@ def create_or_update(issue, options, jira=None): if isinstance(card._meta.get_field_by_name(attr)[0], ManyToManyField): if set([x.pk for x in attribute.all()]) != set(value): - if options[VERBOSE]: - try: - print "{0} old: {1}, new: {2}".format( - attr, [x.pk for x in attribute.all()], value) - except: - print "Error!" + log.debug("{0} old: {1}, new: {2}".format( + attr, [x.pk for x in attribute.all()], value)) setattr(card, attr, value) else: if attribute != value: - if options[VERBOSE]: - try: - print "{0} old: {1}, new: {2}".format( - attr, getattr(card, attr), value) - except: - print "Error!" + log.debug("{0} old: {1}, new: {2}".format( + attr, getattr(card, attr), value)) setattr(card, attr, value) - card.save() else: card.components = components if options[JIRA_IMPLEMENTS]: @@ -340,31 +282,27 @@ def create_or_update(issue, options, jira=None): if options[JIRA_DEPENDSON]: card.dependson = depends_list card.labels = label_list - card.save() - if options[VERBOSE]: - try: - print "Saving %s %s" % (key, summary) - except: - frameinfo = getframeinfo(currentframe()) - print frameinfo.filename, frameinfo.lineno - print "Error!" + + log.info("Saving card {0}".format(issue.key)) + card.save() + log.debug("Saved {0} - {1}".format(issue.key, summary)) + return card -def get_card_blueprints(card, verbose=1): +def get_card_blueprints(card): blueprint_type = CardType.objects.get(name="Blueprint") blueprints = [] for c in card.implementedby.all(): if c.card_type == blueprint_type: blueprints.append(c) - if verbose > 1: - print "\t\t%s - %s" % (c.key, c.status.name) + log.debug("{0} - {1}".format(c.key, c.status.name)) else: blueprints.extend(get_card_blueprints(c)) return blueprints -def get_component_blueprints(component, snapshot_date, verbose=1): +def get_component_blueprints(component, snapshot_date): blueprint_type = CardType.objects.get(name="Blueprint") blueprints = [] from_milestone = Milestone.objects.filter( @@ -376,50 +314,48 @@ def get_component_blueprints(component, snapshot_date, verbose=1): is_major=True).order_by("date")[0] end_date = to_milestone.date for card in component.card_set.filter( - fix_version__fix_date__gt=start_date, - fix_version__fix_date__lt=end_date): - if verbose > 1: - print "\t%s" % card.key + fix_version__fix_date__gt=start_date, + fix_version__fix_date__lt=end_date): + + log.debug("{0}".format(card.key)) if card.card_type == blueprint_type: - plueprints.append(card) - if verbose > 1: - print "\t\t%s" % card.key + blueprints.append(card) else: - blueprints.extend(get_card_blueprints(card, verbose)) + blueprints.extend(get_card_blueprints(card)) return blueprints -def collect_component_burndown(project, verbose=1): +def collect_component_burndown(project): components = Component.objects.filter(project=project) - status_names = settings.JIRA_STATUSES + for component in components: - if verbose > 1: - print component.name + log.debug("Processing component {0}".format(component)) snapshot_date = timezone.make_aware( datetime.now(), timezone.get_default_timezone()) - blueprints = get_component_blueprints(component, snapshot_date, verbose) + + blueprints = get_component_blueprints(component, snapshot_date) burndown, created = Burndown.objects.get_or_create(component=component) snapshot = BurndownSnapshot(burndown=burndown, date=snapshot_date) snapshot.save() - if verbose > 1: - print "************* Snapshot **************" - for name in status_names: - if verbose > 1: - print "\t%s" % name + + for name in settings.JIRA_STATUSES: + log.debug("Analyzing status {0}".format(name)) bbar = BurndownBar(snapshot=snapshot, name=name, value=0) + for bp in blueprints: if bp.status.name == name: - if verbose > 1: - print "\t\t%s" % bp.key - bbar.value = bbar.value + 1 + log.debug("Blueprint with status {0}: {1}".format( + name, bp.key)) + bbar.value += 1 bbar.save() -def collect_burndown(project, card_type, verbose=1): +def collect_burndown(project, card_type): snapshot_date = timezone.make_aware( datetime.now(), timezone.get_default_timezone()) + blueprint_type = CardType.objects.get(name="Blueprint") from_milestone = Milestone.objects.filter( date__lte=snapshot_date, @@ -430,9 +366,10 @@ def collect_burndown(project, card_type, verbose=1): is_major=True).order_by("date")[0] end_date = to_milestone.date cards = Card.objects.filter( - project=project, - fix_version__fix_date__gt=start_date, + project=project, + fix_version__fix_date__gt=start_date, fix_version__fix_date__lt=end_date) + blueprints = [] for card in cards: if card.card_type == blueprint_type: @@ -443,17 +380,14 @@ def collect_burndown(project, card_type, verbose=1): burndown, created = Burndown.objects.get_or_create(project=project) snapshot = BurndownSnapshot(burndown=burndown, date=snapshot_date) snapshot.save() - status_names = settings.JIRA_STATUSES - if verbose > 1: - print "************* Snapshot **************" - for name in status_names: - if verbose > 1: - print "\t%s" % name + + for name in settings.JIRA_STATUSES: + log.debug("Analyzing status {0}".format(name)) + bbar = BurndownBar(snapshot=snapshot, name=name, value=0) for bp in blueprints: if bp.status.name == name: - if verbose > 1: - print "\t\t%s" % bp.key - bbar.value = bbar.value + 1 + log.debug("Blueprint with status {0}: {1}".format( + name, bp.key)) + bbar.value += 1 bbar.save() - diff --git a/roadmap/management/commands/burndown_snapshot.py b/roadmap/management/commands/burndown_snapshot.py index 90e8d8c..df13ea4 100644 --- a/roadmap/management/commands/burndown_snapshot.py +++ b/roadmap/management/commands/burndown_snapshot.py @@ -15,37 +15,57 @@ # You should have received a copy of the GNU Affero General Public License # along with roadmap. If not, see <http://www.gnu.org/licenses/>. -from django.core.management.base import BaseCommand, CommandError +import logging + +from django.core.management.base import BaseCommand from django.conf import settings from optparse import make_option -from roadmap.helpers import collect_burndown, collect_component_burndown +from roadmap.helpers import ( + collect_burndown, + collect_component_burndown, + DEBUG, +) from roadmap.models import Project, CardType BURNDOWN_PROJECT = 'project' BURNDOWN_CARDTYPE = 'cardtype' +log = logging.getLogger("roadmap.helpers") + class Command(BaseCommand): option_list = BaseCommand.option_list + ( - make_option('--project', + make_option( + '--project', dest=BURNDOWN_PROJECT, action='store', type='string', help='Project name to create the burndown for', default=settings.JIRA_PROJECT ), - make_option('--card-type', + make_option( + '--card-type', dest=BURNDOWN_CARDTYPE, action='store', type='string', help='Jira issue type to aggregate the values for', default='Blueprint' ), + make_option( + '--debug', + dest=DEBUG, + action='store_true', + default=False, + help='Enables debug logging. Default: false.' + ), ) help = 'Collects the aggregate data to create the burndown bars' def handle(self, *args, **options): + if options[DEBUG]: + log.setLevel(logging.DEBUG) + project = Project.objects.get(name=options[BURNDOWN_PROJECT]) card_type = CardType.objects.get(name=options[BURNDOWN_CARDTYPE]) - collect_burndown(project, card_type, int(options['verbosity'])) - collect_component_burndown(project, int(options['verbosity'])) + collect_burndown(project, card_type) + collect_component_burndown(project) diff --git a/roadmap/management/commands/roadmap_import.py b/roadmap/management/commands/roadmap_import.py index bad3ef5..3c08b0e 100644 --- a/roadmap/management/commands/roadmap_import.py +++ b/roadmap/management/commands/roadmap_import.py @@ -1,4 +1,4 @@ -# Copyright (C) 2013 Linaro +# Copyright (C) 2013, 2014 Linaro # # This file is part of roadmap. # @@ -15,121 +15,124 @@ # You should have received a copy of the GNU Affero General Public License # along with roadmap. If not, see <http://www.gnu.org/licenses/>. -from django.core.management.base import BaseCommand, CommandError +import logging + +from django.core.management.base import ( + BaseCommand, + CommandError +) from django.conf import settings -from getpass import getpass from jira.client import JIRA from optparse import make_option from roadmap.helpers import ( create_or_update, - JIRA_SERVER, - JIRA_USER, JIRA_VERIFY, JIRA_DEPENDSON, JIRA_IMPLEMENTS, JIRA_KEY, SINGLE_UPDATE, - VERBOSE + DEBUG ) +log = logging.getLogger('roadmap.helpers') + class Command(BaseCommand): option_list = BaseCommand.option_list + ( - make_option('--server', - dest=JIRA_SERVER, - action='store', - type='string', - help='Jira server URL' - ), - make_option('--username', - dest=JIRA_USER, - action='store', - type='string', - help='Jira username' - ), - make_option('--key', + make_option( + '--key', dest=JIRA_KEY, action='store', type='string', - help='Jira issue key. Only this issue is synced' + help="Jira issue key. Only this issue is synced" ), - make_option('--no-implements', + make_option( + '--no-implements', dest=JIRA_IMPLEMENTS, action='store_false', default=True, - help='If set the "implements" relations are \ - not retrieved. Default: false' + help=("If set the 'implements' relations are not retrieved. " + "Default: false") ), - make_option('--depends', + make_option( + '--depends', dest=JIRA_DEPENDSON, action='store_true', default=False, - help='If set the "depends" relations are not \ - retrieved. Default: false. Be careful when \ - not using --no-implements might run into infinite loop.' + help=("If set the 'depends' relations are not retrieved. Default: " + "false. Be careful when not using --no-implements might " + "run into infinite loop.") ), - make_option('--no-verify', + make_option( + '--no-verify', dest=JIRA_VERIFY, action='store_false', default=True, - help='If set allows to connect to Jira server \ - secured with self-signed certificate' + help=("If set allows to connect to Jira server secured with " + "self-signed certificate") ), - make_option('--verbose', - dest=VERBOSE, + make_option( + '--debug', + dest=DEBUG, action='store_true', default=False, - help='Enables verbose logging. Default: false.' + help='Enables debug logging. Default: false.' ), - ) help = 'Imports current snapshot from Jira server' def handle(self, *args, **options): - if options[JIRA_SERVER] == None: - raise CommandError("Option `--server=...` must be specified.") - if options[JIRA_USER] == None: - raise CommandError("Option `--username=...` must be specified.") - jira_password = getpass("JIRA password (%s):" % options[JIRA_USER]) + if options[DEBUG]: + log.setLevel(logging.DEBUG) + + if settings.JIRA_SERVER is None: + raise CommandError("Missing Jira server parameter.") + if settings.JIRA_USERNAME is None: + raise CommandError("Missing Jira username parameter.") + if settings.JIRA_PASSWORD is None: + raise CommandError("Missing Jira password parameter.") + jira_options = { - 'server': options[JIRA_SERVER], + 'server': settings.JIRA_SERVER, 'verify': options[JIRA_VERIFY]} jira = JIRA( options=jira_options, - basic_auth=(options[JIRA_USER], jira_password)) + basic_auth=(settings.JIRA_USERNAME, settings.JIRA_PASSWORD) + ) + start = 0 - if options[JIRA_KEY] == None: + + if options[JIRA_KEY] is None: issues = jira.search_issues( - 'project="%s"' % settings.JIRA_PROJECT, + 'project="{0}"'.format(settings.JIRA_PROJECT), startAt=start, expand="changelog") else: issues = jira.search_issues( - 'key="%s"' % options[JIRA_KEY], + 'key="{0}"'.format(options[JIRA_KEY]), startAt=start, expand="changelog") options.update({SINGLE_UPDATE: False}) - print "Total: ", issues.total + log.info("Total issues: {0}".format(issues.total)) + while len(issues) > 0: - #allissues.extend(issues) for index, issue in enumerate(issues, start=1): - print "Processing %s/%s (%s/%s) %s" % ( - index + start, - issues.total, - index, - len(issues), - issue.key) + log.info("Processing issue {0}/{1} ({2}/{3}) {4}".format( + index+start, issues.total, index, len(issues), issue.key)) + create_or_update(issue, options, jira) - start = start + len(issues) - if options[JIRA_KEY] == None: + start += len(issues) + + log.info("Searching issues starting at {0}".format(start)) + if options[JIRA_KEY] is None: issues = jira.search_issues( - 'project="%s"' % settings.JIRA_PROJECT, + 'project="{0}"'.format(settings.JIRA_PROJECT), startAt=start, expand="changelog") else: issues = jira.search_issues( - 'key="%s"' % options[JIRA_KEY], + 'key="{0}"'.format(options[JIRA_KEY]), startAt=start, expand="changelog") diff --git a/roadmap/models.py b/roadmap/models.py index 15c2b3b..03411f4 100644 --- a/roadmap/models.py +++ b/roadmap/models.py @@ -42,7 +42,10 @@ class Burndown(models.Model): component = models.ForeignKey('Component', null=True, blank=True) def __unicode__(self): - return self.project.name + ret_val = self.pk + if self.component: + ret_val = self.component.name + return ret_val class BurndownSnapshot(models.Model): diff --git a/roadmap/static/css/img/16/delete.png b/roadmap/static/css/img/16/delete.png Binary files differnew file mode 100644 index 0000000..d54d0e0 --- /dev/null +++ b/roadmap/static/css/img/16/delete.png diff --git a/roadmap/static/css/img/16/moveleft.png b/roadmap/static/css/img/16/moveleft.png Binary files differnew file mode 100644 index 0000000..44c0d82 --- /dev/null +++ b/roadmap/static/css/img/16/moveleft.png diff --git a/roadmap/static/css/img/16/moveright.png b/roadmap/static/css/img/16/moveright.png Binary files differnew file mode 100644 index 0000000..9d39bde --- /dev/null +++ b/roadmap/static/css/img/16/moveright.png diff --git a/roadmap/static/css/img/16/new.png b/roadmap/static/css/img/16/new.png Binary files differnew file mode 100644 index 0000000..7eeba13 --- /dev/null +++ b/roadmap/static/css/img/16/new.png diff --git a/roadmap/static/css/img/16/zoomin.png b/roadmap/static/css/img/16/zoomin.png Binary files differnew file mode 100644 index 0000000..b68dfad --- /dev/null +++ b/roadmap/static/css/img/16/zoomin.png diff --git a/roadmap/static/css/img/16/zoomout.png b/roadmap/static/css/img/16/zoomout.png Binary files differnew file mode 100644 index 0000000..64e3727 --- /dev/null +++ b/roadmap/static/css/img/16/zoomout.png diff --git a/roadmap/static/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png b/roadmap/static/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png Binary files differindex 5b5dab2..5b5dab2 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png +++ b/roadmap/static/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png diff --git a/roadmap/static/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png b/roadmap/static/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png Binary files differindex 47acaad..47acaad 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png +++ b/roadmap/static/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png diff --git a/roadmap/static/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png b/roadmap/static/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png Binary files differindex 9fb564f..9fb564f 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png +++ b/roadmap/static/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png diff --git a/roadmap/static/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png b/roadmap/static/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png Binary files differindex 0149515..0149515 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png +++ b/roadmap/static/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png diff --git a/roadmap/static/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png b/roadmap/static/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png Binary files differindex 4443fdc..4443fdc 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png +++ b/roadmap/static/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png diff --git a/roadmap/static/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png b/roadmap/static/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png Binary files differindex 81ecc36..81ecc36 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +++ b/roadmap/static/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png diff --git a/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png b/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png Binary files differindex 4f3faf8..4f3faf8 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +++ b/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png diff --git a/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png b/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png Binary files differindex 38c3833..38c3833 100755..100644 --- a/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +++ b/roadmap/static/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png diff --git a/roadmap/static/css/redmond/images/ui-icons_217bc0_256x240.png b/roadmap/static/css/redmond/images/ui-icons_217bc0_256x240.png Binary files differindex 9c88458..9c88458 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_217bc0_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_217bc0_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_2e83ff_256x240.png b/roadmap/static/css/redmond/images/ui-icons_2e83ff_256x240.png Binary files differindex b425c44..b425c44 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_2e83ff_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_2e83ff_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_469bdd_256x240.png b/roadmap/static/css/redmond/images/ui-icons_469bdd_256x240.png Binary files differindex 5e7915f..5e7915f 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_469bdd_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_469bdd_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_6da8d5_256x240.png b/roadmap/static/css/redmond/images/ui-icons_6da8d5_256x240.png Binary files differindex 60e20ca..60e20ca 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_6da8d5_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_6da8d5_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_cd0a0a_256x240.png b/roadmap/static/css/redmond/images/ui-icons_cd0a0a_256x240.png Binary files differindex 2db88b7..2db88b7 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_cd0a0a_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_cd0a0a_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_d8e7f3_256x240.png b/roadmap/static/css/redmond/images/ui-icons_d8e7f3_256x240.png Binary files differindex 2c8aac4..2c8aac4 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_d8e7f3_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_d8e7f3_256x240.png diff --git a/roadmap/static/css/redmond/images/ui-icons_f9bd01_256x240.png b/roadmap/static/css/redmond/images/ui-icons_f9bd01_256x240.png Binary files differindex e81603f..e81603f 100755..100644 --- a/roadmap/static/css/redmond/images/ui-icons_f9bd01_256x240.png +++ b/roadmap/static/css/redmond/images/ui-icons_f9bd01_256x240.png diff --git a/roadmap/static/css/redmond/jquery-ui-1.7.1.custom.css b/roadmap/static/css/redmond/jquery-ui-1.7.1.custom.css index 3e15a2e..3e15a2e 100755..100644 --- a/roadmap/static/css/redmond/jquery-ui-1.7.1.custom.css +++ b/roadmap/static/css/redmond/jquery-ui-1.7.1.custom.css diff --git a/roadmap/static/css/ui.slider.extras.css b/roadmap/static/css/ui.slider.extras.css index 862388b..862388b 100755..100644 --- a/roadmap/static/css/ui.slider.extras.css +++ b/roadmap/static/css/ui.slider.extras.css diff --git a/roadmap/static/js/selectToUISlider.jQuery.js b/roadmap/static/js/selectToUISlider.jQuery.js index 18704d7..18704d7 100755..100644 --- a/roadmap/static/js/selectToUISlider.jQuery.js +++ b/roadmap/static/js/selectToUISlider.jQuery.js diff --git a/roadmap/templates/roadmap/component.html b/roadmap/templates/roadmap/component.html index 9ec8039..4797f54 100644 --- a/roadmap/templates/roadmap/component.html +++ b/roadmap/templates/roadmap/component.html @@ -16,7 +16,6 @@ <script type="text/javascript" src="{{ STATIC_URL }}js/highcharts.js"></script> <script type="text/javascript" src="{{ STATIC_URL }}js/modules/exporting.js"></script> <script type="text/javascript" src="{{ STATIC_URL }}js/ui/jquery-ui.js"></script> -<script type="text/javascript" src="{{ STATIC_URL }}js/ajax.js"></script> <!-- status chart --> <script type="text/javascript"> $(function () { diff --git a/roadmap/templates/roadmap/roadmap.html b/roadmap/templates/roadmap/roadmap.html index 4a16563..0b1abba 100644 --- a/roadmap/templates/roadmap/roadmap.html +++ b/roadmap/templates/roadmap/roadmap.html @@ -14,7 +14,7 @@ <div class="concept" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Drafting</div><div class="timeline-event timeline-event-dot drafting" style="position: absolute;"></div></div> <div class="approved" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Approved</div><div class="timeline-event timeline-event-dot approved" style="position: absolute;"></div></div> <div class="scheduled" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Scheduled</div><div class="timeline-event timeline-event-dot scheduled" style="position: absolute;"></div></div> - <div class="development" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Development</div><div class="timeline-event timeline-event-dot development" style="position: absolute;"></div></div> + <div class="development" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Development</div><div class="timeline-event timeline-event-dot engineering" style="position: absolute;"></div></div> <div class="released" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Closed</div><div class="timeline-event timeline-event-dot closed" style="position: absolute;"></div></div> </div> diff --git a/roadmap/templates/roadmap/roadmap_index.html b/roadmap/templates/roadmap/roadmap_index.html new file mode 100644 index 0000000..c3cda71 --- /dev/null +++ b/roadmap/templates/roadmap/roadmap_index.html @@ -0,0 +1,21 @@ +{% extends 'roadmap/base.html' %} + +{% block title %}Linaro Roadmap{% endblock %} + +{% block headertitle %}Linaro Roadmap{% endblock %} + +{% block main_content %} + + <div style="margin: 10px;"> + <table class="roadmaptable" width="100%"> + {% for component in components %} + <tr class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}"> + <td><a href="/component/{{ component.pk }}/roadmap">{{ component.name }}</a></td> + </tr> + {% endfor %} + </table> + </div> + +{% include "roadmap/help.html" %} + +{% endblock %} diff --git a/roadmap/templates/roadmap/timeline.html b/roadmap/templates/roadmap/timeline.html index 09226a7..0e1d714 100644 --- a/roadmap/templates/roadmap/timeline.html +++ b/roadmap/templates/roadmap/timeline.html @@ -14,7 +14,7 @@ <div class="concept" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Drafting</div><div class="timeline-event timeline-event-dot drafting" style="position: absolute;"></div></div> <div class="approved" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Approved</div><div class="timeline-event timeline-event-dot approved" style="position: absolute;"></div></div> <div class="scheduled" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Scheduled</div><div class="timeline-event timeline-event-dot scheduled" style="position: absolute;"></div></div> - <div class="development" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Development</div><div class="timeline-event timeline-event-dot development" style="position: absolute;"></div></div> + <div class="development" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Development</div><div class="timeline-event timeline-event-dot engineering" style="position: absolute;"></div></div> <div class="released" style="position: relative; float:left; margin-left: 20px;"><div class="timeline-event-content">Closed</div><div class="timeline-event timeline-event-dot closed" style="position: absolute;"></div></div> </div> diff --git a/roadmap/urls.py b/roadmap/urls.py index 16a23f4..c4ca46e 100644 --- a/roadmap/urls.py +++ b/roadmap/urls.py @@ -26,12 +26,14 @@ from roadmap.views import ( status, search, update_card, - index + index, + roadmap_index, ) urlpatterns = patterns( '', url(r'^$', index, name="index"), + url(r'^roadmap/$', roadmap_index), url(r'^roadmap/(?P<roadmap_id>\d+)$', roadmap), url(r'^component/(?P<roadmap_id>\d+)$', component), url(r'^status/(?P<status_id>\d+)$', status), diff --git a/roadmap/views.py b/roadmap/views.py index df27888..a32bb10 100644 --- a/roadmap/views.py +++ b/roadmap/views.py @@ -18,7 +18,6 @@ import json import sys import traceback -import reversion from datetime import ( datetime, @@ -29,13 +28,12 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.db.models import Max, Min from django.http import HttpResponse, HttpResponseServerError -from django.shortcuts import redirect from django.template import RequestContext, loader from django.views.decorators.csrf import csrf_exempt -from roadmap.forms import RoadmapForm, LabelForm +from roadmap.forms import LabelForm from roadmap.helpers import ( - VERBOSE, + DEBUG, SINGLE_UPDATE, JIRA_SERVER, JIRA_IMPLEMENTS, @@ -47,7 +45,6 @@ from roadmap.models import ( Card, Component, Status, - RoadmapRelease, Project, Label, Milestone, @@ -380,7 +377,7 @@ def update_card(request): card_json = json.loads(request.body) jira_server_parts = card_json['issue']['self'].split("/") options = { - VERBOSE: settings.DEBUG, + DEBUG: settings.DEBUG, JIRA_SERVER: jira_server_parts[0] + "//" + jira_server_parts[2], JIRA_IMPLEMENTS: True, JIRA_DEPENDSON: True, @@ -394,3 +391,18 @@ def update_card(request): return HttpResponseServerError() return HttpResponse() + + +@login_required +def roadmap_index(request): + template = loader.get_template('roadmap/roadmap_index.html') + + project = Project.objects.get(name=settings.JIRA_PROJECT) + components = Component.objects.filter(project=project).order_by("name") + + context = RequestContext(request, { + 'project': project, + 'components': components, + }) + + return HttpResponse(template.render(context)) |