Commit 8440c81eed24bee897467bbf2286530eaaac351c
Exists in
master
and in
28 other branches
Merge pull request #58 from colab/refactor-data-import
Refactor data import
Showing
49 changed files
with
1127 additions
and
603 deletions
Show diff stats
README.rst
@@ -64,7 +64,7 @@ To run Colab with development server you will have to: | @@ -64,7 +64,7 @@ To run Colab with development server you will have to: | ||
64 | 64 | ||
65 | .. code-block:: | 65 | .. code-block:: |
66 | 66 | ||
67 | - colab-init-config > /etc/colab/settings.yaml | 67 | + colab-admin initconfig > /etc/colab/settings.py |
68 | 68 | ||
69 | 2- Edit the configuration file. Make sure you set everything you need including **database** credentials. | 69 | 2- Edit the configuration file. Make sure you set everything you need including **database** credentials. |
70 | 70 | ||
@@ -92,4 +92,4 @@ How to run the tests | @@ -92,4 +92,4 @@ How to run the tests | ||
92 | Follow the steps below: | 92 | Follow the steps below: |
93 | 93 | ||
94 | * Go to vagrant/colab/ | 94 | * Go to vagrant/colab/ |
95 | -* run: ./runtests.sh | ||
96 | \ No newline at end of file | 95 | \ No newline at end of file |
96 | +* run: ./runtests.sh |
colab/management/__init__.py
@@ -1,20 +0,0 @@ | @@ -1,20 +0,0 @@ | ||
1 | - | ||
2 | -import os | ||
3 | - | ||
4 | -from django.core.management import ManagementUtility | ||
5 | - | ||
6 | -from .initconfig import initconfig | ||
7 | - | ||
8 | - | ||
9 | -def execute_from_command_line(argv=None): | ||
10 | - """ | ||
11 | - A simple method that runs a ManagementUtility. | ||
12 | - """ | ||
13 | - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "colab.settings") | ||
14 | - | ||
15 | - utility = ManagementUtility(argv) | ||
16 | - utility.execute() | ||
17 | - | ||
18 | - | ||
19 | -def run_colab_config(argv=None): | ||
20 | - initconfig() |
@@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
1 | +from __future__ import absolute_import, unicode_literals | ||
2 | + | ||
3 | +from celery.bin import celery | ||
4 | + | ||
5 | +from colab.celery import app | ||
6 | +from colab.queue.command import CeleryCommand | ||
7 | + | ||
8 | +base = celery.CeleryCommand(app=app) | ||
9 | + | ||
10 | + | ||
11 | +# this is a reimplementation of the djcelery 'celery' command | ||
12 | +# taken from Sentry | ||
13 | +class Command(CeleryCommand): | ||
14 | + """The celery command.""" | ||
15 | + help = 'celery commands, see celery help' | ||
16 | + options = (CeleryCommand.options | ||
17 | + + base.get_options() | ||
18 | + + base.preload_options) | ||
19 | + | ||
20 | + def run_from_argv(self, argv): | ||
21 | + argv = self.handle_default_options(argv) | ||
22 | + if self.requires_system_checks: | ||
23 | + self.validate() | ||
24 | + base.execute_from_commandline( | ||
25 | + ['{0[0]} {0[1]}'.format(argv)] + argv[2:], | ||
26 | + ) |
@@ -0,0 +1,124 @@ | @@ -0,0 +1,124 @@ | ||
1 | + | ||
2 | +from django.core.management.base import BaseCommand | ||
3 | +from django.utils.crypto import get_random_string | ||
4 | + | ||
5 | + | ||
6 | +CONFIG_TEMPLATE = """ | ||
7 | +## Set to false in production | ||
8 | +DEBUG = True | ||
9 | +TEMPLATE_DEBUG = True | ||
10 | + | ||
11 | +## System admins | ||
12 | +ADMINS = [['John Foo', 'john@example.com'], ['Mary Bar', 'mary@example.com']] | ||
13 | + | ||
14 | +MANAGERS = ADMINS | ||
15 | + | ||
16 | +COLAB_FROM_ADDRESS = '"Colab" <noreply@example.com>' | ||
17 | +SERVER_EMAIL = '"Colab" <noreply@example.com>' | ||
18 | + | ||
19 | +EMAIL_HOST = 'localhost' | ||
20 | +EMAIL_PORT = 25 | ||
21 | +EMAIL_SUBJECT_PREFIX = '[colab]' | ||
22 | + | ||
23 | +SECRET_KEY = '{secret_key}' | ||
24 | + | ||
25 | +ALLOWED_HOSTS = [ | ||
26 | + 'localhost', | ||
27 | +# 'example.com', | ||
28 | +# 'example.org', | ||
29 | +# 'example.net', | ||
30 | +] | ||
31 | + | ||
32 | +### Uncomment to enable social networks fields profile | ||
33 | +# SOCIAL_NETWORK_ENABLED = True | ||
34 | + | ||
35 | +## Database settings | ||
36 | +## | ||
37 | +## When DEBUG is True colab will create the DB on | ||
38 | +## the repository root. In case of production settings | ||
39 | +## (DEBUG False) the DB settings must be set. | ||
40 | +## | ||
41 | +# DATABASES = {{ | ||
42 | +# 'default': {{ | ||
43 | +# 'ENGINE': 'django.db.backends.sqlite3', | ||
44 | +# 'NAME': '/path/to/colab.sqlite3', | ||
45 | +# }} | ||
46 | +# }} | ||
47 | + | ||
48 | +## Disable indexing | ||
49 | +ROBOTS_NOINDEX = False | ||
50 | + | ||
51 | +LOGGING = {{ | ||
52 | + 'version': 1, | ||
53 | + | ||
54 | + 'handlers': {{ | ||
55 | + 'null': {{ | ||
56 | + 'level': 'DEBUG', | ||
57 | + 'class': 'logging.NullHandler', | ||
58 | + }}, | ||
59 | + }}, | ||
60 | + | ||
61 | + 'loggers': {{ | ||
62 | + 'colab.mailman': {{ | ||
63 | + 'handlers': ['null'], | ||
64 | + 'propagate': False, | ||
65 | + }}, | ||
66 | + 'haystack': {{ | ||
67 | + 'handlers': ['null'], | ||
68 | + 'propagate': False, | ||
69 | + }}, | ||
70 | + 'pysolr': {{ | ||
71 | + 'handlers': ['null'], | ||
72 | + 'propagate': False, | ||
73 | + }}, | ||
74 | + }}, | ||
75 | +}} | ||
76 | + | ||
77 | + | ||
78 | +## Gitlab plugin - Put this in plugins.d/gitlab.py to actiate ## | ||
79 | +# from django.utils.translation import ugettext_lazy as _ | ||
80 | +# from colab.plugins.utils.menu import colab_url_factory | ||
81 | +# | ||
82 | +# name = 'colab.plugins.gitlab' | ||
83 | +# verbose_name = 'Gitlab Plugin' | ||
84 | +# | ||
85 | +# upstream = 'localhost' | ||
86 | +# #middlewares = [] | ||
87 | +# | ||
88 | +# urls = {{ | ||
89 | +# 'include': 'colab.plugins.gitlab.urls', | ||
90 | +# 'namespace': 'gitlab', | ||
91 | +# 'prefix': 'gitlab', | ||
92 | +# }} | ||
93 | +# | ||
94 | +# menu_title = _('Code') | ||
95 | +# | ||
96 | +# url = colab_url_factory('gitlab') | ||
97 | +# | ||
98 | +# menu_urls = ( | ||
99 | +# url(display=_('Public Projects'), viewname='gitlab', | ||
100 | +# kwargs={{'path': '/public/projects'}}, auth=False), | ||
101 | +# url(display=_('Profile'), viewname='gitlab', | ||
102 | +# kwargs={{'path': '/profile'}}, auth=True), | ||
103 | +# url(display=_('New Project'), viewname='gitlab', | ||
104 | +# kwargs={{'path': '/projects/new'}}, auth=True), | ||
105 | +# url(display=_('Projects'), viewname='gitlab', | ||
106 | +# kwargs={{'path': '/dashboard/projects'}}, auth=True), | ||
107 | +# url(display=_('Groups'), viewname='gitlab', | ||
108 | +# kwargs={{'path': '/profile/groups'}}, auth=True), | ||
109 | +# url(display=_('Issues'), viewname='gitlab', | ||
110 | +# kwargs={{'path': '/dashboard/issues'}}, auth=True), | ||
111 | +# url(display=_('Merge Requests'), viewname='gitlab', | ||
112 | +# kwargs={{'path': '/merge_requests'}}, auth=True), | ||
113 | +# | ||
114 | +# ) | ||
115 | +""" | ||
116 | + | ||
117 | + | ||
118 | +class Command(BaseCommand): | ||
119 | + help = 'Returns an example config file for Colab' | ||
120 | + | ||
121 | + def handle(self, *args, **kwargs): | ||
122 | + chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' | ||
123 | + secret_key = get_random_string(50, chars) | ||
124 | + print(CONFIG_TEMPLATE.format(secret_key=secret_key)) |
colab/management/initconfig.py
@@ -1,120 +0,0 @@ | @@ -1,120 +0,0 @@ | ||
1 | - | ||
2 | -from django.utils.crypto import get_random_string | ||
3 | - | ||
4 | - | ||
5 | -CONFIG_TEMPLATE = """ | ||
6 | -## Set to false in production | ||
7 | -DEBUG = True | ||
8 | -TEMPLATE_DEBUG = True | ||
9 | - | ||
10 | -## System admins | ||
11 | -ADMINS = [['John Foo', 'john@example.com'], ['Mary Bar', 'mary@example.com']] | ||
12 | - | ||
13 | -MANAGERS = ADMINS | ||
14 | - | ||
15 | -COLAB_FROM_ADDRESS = '"Colab" <noreply@example.com>' | ||
16 | -SERVER_EMAIL = '"Colab" <noreply@example.com>' | ||
17 | - | ||
18 | -EMAIL_HOST = 'localhost' | ||
19 | -EMAIL_PORT = 25 | ||
20 | -EMAIL_SUBJECT_PREFIX = '[colab]' | ||
21 | - | ||
22 | -SECRET_KEY = '{secret_key}' | ||
23 | - | ||
24 | -ALLOWED_HOSTS = [ | ||
25 | - 'localhost', | ||
26 | -# 'example.com', | ||
27 | -# 'example.org', | ||
28 | -# 'example.net', | ||
29 | -] | ||
30 | - | ||
31 | -### Uncomment to enable social networks fields profile | ||
32 | -# SOCIAL_NETWORK_ENABLED = True | ||
33 | - | ||
34 | -## Database settings | ||
35 | -## | ||
36 | -## When DEBUG is True colab will create the DB on | ||
37 | -## the repository root. In case of production settings | ||
38 | -## (DEBUG False) the DB settings must be set. | ||
39 | -## | ||
40 | -# DATABASES = {{ | ||
41 | -# 'default': {{ | ||
42 | -# 'ENGINE': 'django.db.backends.sqlite3', | ||
43 | -# 'NAME': '/path/to/colab.sqlite3', | ||
44 | -# }} | ||
45 | -# }} | ||
46 | - | ||
47 | -## Disable indexing | ||
48 | -ROBOTS_NOINDEX = False | ||
49 | - | ||
50 | -LOGGING = {{ | ||
51 | - 'version': 1, | ||
52 | - | ||
53 | - 'handlers': {{ | ||
54 | - 'null': {{ | ||
55 | - 'level': 'DEBUG', | ||
56 | - 'class': 'logging.NullHandler', | ||
57 | - }}, | ||
58 | - }}, | ||
59 | - | ||
60 | - 'loggers': {{ | ||
61 | - 'colab.mailman': {{ | ||
62 | - 'handlers': ['null'], | ||
63 | - 'propagate': False, | ||
64 | - }}, | ||
65 | - 'haystack': {{ | ||
66 | - 'handlers': ['null'], | ||
67 | - 'propagate': False, | ||
68 | - }}, | ||
69 | - 'pysolr': {{ | ||
70 | - 'handlers': ['null'], | ||
71 | - 'propagate': False, | ||
72 | - }}, | ||
73 | - }}, | ||
74 | -}} | ||
75 | - | ||
76 | - | ||
77 | -## Gitlab plugin - Put this in plugins.d/gitlab.py to actiate ## | ||
78 | -# from django.utils.translation import ugettext_lazy as _ | ||
79 | -# from colab.plugins.utils.menu import colab_url_factory | ||
80 | -# | ||
81 | -# name = 'colab.plugins.gitlab' | ||
82 | -# verbose_name = 'Gitlab Proxy' | ||
83 | -# | ||
84 | -# upstream = 'localhost' | ||
85 | -# #middlewares = [] | ||
86 | -# | ||
87 | -# urls = {{ | ||
88 | -# 'include': 'colab.plugins.gitlab.urls', | ||
89 | -# 'namespace': 'gitlab', | ||
90 | -# 'prefix': 'gitlab', | ||
91 | -# }} | ||
92 | -# | ||
93 | -# menu_title = _('Code') | ||
94 | -# | ||
95 | -# url = colab_url_factory('gitlab') | ||
96 | -# | ||
97 | -# menu_urls = ( | ||
98 | -# url(display=_('Public Projects'), viewname='gitlab', | ||
99 | -# kwargs={{'path': '/public/projects'}}, auth=False), | ||
100 | -# url(display=_('Profile'), viewname='gitlab', | ||
101 | -# kwargs={{'path': '/profile'}}, auth=True), | ||
102 | -# url(display=_('New Project'), viewname='gitlab', | ||
103 | -# kwargs={{'path': '/projects/new'}}, auth=True), | ||
104 | -# url(display=_('Projects'), viewname='gitlab', | ||
105 | -# kwargs={{'path': '/dashboard/projects'}}, auth=True), | ||
106 | -# url(display=_('Groups'), viewname='gitlab', | ||
107 | -# kwargs={{'path': '/profile/groups'}}, auth=True), | ||
108 | -# url(display=_('Issues'), viewname='gitlab', | ||
109 | -# kwargs={{'path': '/dashboard/issues'}}, auth=True), | ||
110 | -# url(display=_('Merge Requests'), viewname='gitlab', | ||
111 | -# kwargs={{'path': '/merge_requests'}}, auth=True), | ||
112 | -# | ||
113 | -# ) | ||
114 | -""" | ||
115 | - | ||
116 | - | ||
117 | -def initconfig(): | ||
118 | - chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' | ||
119 | - secret_key = get_random_string(50, chars) | ||
120 | - print(CONFIG_TEMPLATE.format(secret_key=secret_key)) |
colab/plugins/apps.py
1 | 1 | ||
2 | from django.apps import AppConfig | 2 | from django.apps import AppConfig |
3 | 3 | ||
4 | +from .data import register_tasks | ||
4 | from .utils.signals import connect_signal, register_signal | 5 | from .utils.signals import connect_signal, register_signal |
5 | 6 | ||
6 | 7 | ||
@@ -10,3 +11,5 @@ class PluginAppConfig(AppConfig): | @@ -10,3 +11,5 @@ class PluginAppConfig(AppConfig): | ||
10 | def ready(self): | 11 | def ready(self): |
11 | register_signal() | 12 | register_signal() |
12 | connect_signal() | 13 | connect_signal() |
14 | + | ||
15 | + register_tasks() |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | + | ||
2 | +import abc | ||
3 | + | ||
4 | +from django.conf import settings | ||
5 | + | ||
6 | + | ||
7 | +class PluginDataImporter(object): | ||
8 | + | ||
9 | + def __init__(self): | ||
10 | + self.config = settings.COLAB_APPS.get(self.app_label, {}) | ||
11 | + | ||
12 | + @abc.abstractmethod | ||
13 | + def fetch_data(self): | ||
14 | + raise NotImplementedError | ||
15 | + fetch_data.is_abstract = True | ||
16 | + | ||
17 | + @abc.abstractmethod | ||
18 | + def app_label(self): | ||
19 | + raise NotImplementedError |
@@ -0,0 +1,61 @@ | @@ -0,0 +1,61 @@ | ||
1 | +#!/usr/bin/env python | ||
2 | + | ||
3 | +import importlib | ||
4 | +import logging | ||
5 | + | ||
6 | +import redis | ||
7 | + | ||
8 | +from django.conf import settings | ||
9 | + | ||
10 | +from colab.celery import app | ||
11 | + | ||
12 | +LOGGER = logging.getLogger('colab.plugins.data') | ||
13 | +TASKS = set() | ||
14 | + | ||
15 | + | ||
16 | +def lock(method, name): | ||
17 | + def wrapped_method(self, *args, **kwargs): | ||
18 | + lock_id = 'colab-data-importer-{}'.format(name) | ||
19 | + lock = redis.Redis().lock(lock_id) | ||
20 | + | ||
21 | + if lock.acquire(blocking=False): | ||
22 | + try: | ||
23 | + return method(*args, **kwargs) | ||
24 | + finally: | ||
25 | + lock.release() | ||
26 | + | ||
27 | + return wrapped_method | ||
28 | + | ||
29 | + | ||
30 | +def register_tasks(): | ||
31 | + | ||
32 | + global TASKS | ||
33 | + | ||
34 | + for app_name in settings.INSTALLED_APPS: | ||
35 | + | ||
36 | + module_name = '{}.data_importer'.format(app_name) | ||
37 | + try: | ||
38 | + module = importlib.import_module(module_name) | ||
39 | + except ImportError: | ||
40 | + continue | ||
41 | + | ||
42 | + for item_name in dir(module): | ||
43 | + item = getattr(module, item_name) | ||
44 | + | ||
45 | + if callable(getattr(item, 'fetch_data', None)): | ||
46 | + if getattr(item.fetch_data, 'is_abstract', False): | ||
47 | + continue | ||
48 | + instance = item() | ||
49 | + task_name = '{}.{}'.format(module.__name__, item_name) | ||
50 | + thread_safe_method = lock(instance.fetch_data, task_name) | ||
51 | + task = app.task(name=task_name, bind=True)(thread_safe_method) | ||
52 | + TASKS.add(task) | ||
53 | + LOGGER.debug('Registered task: %s', task_name) | ||
54 | + | ||
55 | + LOGGER.debug(TASKS) | ||
56 | + return TASKS | ||
57 | + | ||
58 | + | ||
59 | +def data_import(self): | ||
60 | + for task in TASKS: | ||
61 | + task.delay() |
colab/plugins/gitlab/__init__.py
colab/plugins/gitlab/apps.py
1 | 1 | ||
2 | -from ..utils.apps import ColabProxiedAppConfig | 2 | +from ..utils.apps import ColabPluginAppConfig |
3 | from colab.plugins.gitlab.tasks import handling_method | 3 | from colab.plugins.gitlab.tasks import handling_method |
4 | from colab.signals.signals import register_signal, connect_signal | 4 | from colab.signals.signals import register_signal, connect_signal |
5 | 5 | ||
6 | 6 | ||
7 | -class ProxyGitlabAppConfig(ColabProxiedAppConfig): | 7 | +class GitlabPluginAppConfig(ColabPluginAppConfig): |
8 | name = 'colab.plugins.gitlab' | 8 | name = 'colab.plugins.gitlab' |
9 | verbose_name = 'Gitlab Plugin' | 9 | verbose_name = 'Gitlab Plugin' |
10 | short_name = 'gitlab' | 10 | short_name = 'gitlab' |
colab/plugins/gitlab/data_api.py
@@ -1,205 +0,0 @@ | @@ -1,205 +0,0 @@ | ||
1 | -import json | ||
2 | -import urllib | ||
3 | -import urllib2 | ||
4 | -import logging | ||
5 | - | ||
6 | -from dateutil.parser import parse | ||
7 | - | ||
8 | -from django.conf import settings | ||
9 | -from django.db.models.fields import DateTimeField | ||
10 | - | ||
11 | -from colab.plugins.gitlab.models import (GitlabProject, GitlabMergeRequest, | ||
12 | - GitlabComment, GitlabIssue) | ||
13 | -from colab.plugins.utils.proxy_data_api import ProxyDataAPI | ||
14 | - | ||
15 | -LOGGER = logging.getLogger('colab.plugin.gitlab') | ||
16 | - | ||
17 | - | ||
18 | -class GitlabDataAPI(ProxyDataAPI): | ||
19 | - | ||
20 | - def get_request_url(self, path, **kwargs): | ||
21 | - proxy_config = settings.PROXIED_APPS.get(self.app_label, {}) | ||
22 | - | ||
23 | - upstream = proxy_config.get('upstream') | ||
24 | - kwargs['private_token'] = proxy_config.get('private_token') | ||
25 | - params = urllib.urlencode(kwargs) | ||
26 | - | ||
27 | - if upstream[-1] == '/': | ||
28 | - upstream = upstream[:-1] | ||
29 | - | ||
30 | - return u'{}{}?{}'.format(upstream, path, params) | ||
31 | - | ||
32 | - def get_json_data(self, api_url, page, pages=1000): | ||
33 | - url = self.get_request_url(api_url, per_page=pages, | ||
34 | - page=page) | ||
35 | - | ||
36 | - try: | ||
37 | - data = urllib2.urlopen(url, timeout=10) | ||
38 | - json_data = json.load(data) | ||
39 | - except urllib2.URLError: | ||
40 | - LOGGER.exception("Connection timeout: " + url) | ||
41 | - json_data = [] | ||
42 | - | ||
43 | - return json_data | ||
44 | - | ||
45 | - def fill_object_data(self, element, _object): | ||
46 | - for field in _object._meta.fields: | ||
47 | - try: | ||
48 | - if field.name == "user": | ||
49 | - _object.update_user( | ||
50 | - element["author"]["username"]) | ||
51 | - continue | ||
52 | - if field.name == "project": | ||
53 | - _object.project_id = element["project_id"] | ||
54 | - continue | ||
55 | - | ||
56 | - if isinstance(field, DateTimeField): | ||
57 | - value = parse(element[field.name]) | ||
58 | - else: | ||
59 | - value = element[field.name] | ||
60 | - | ||
61 | - setattr(_object, field.name, value) | ||
62 | - except KeyError: | ||
63 | - continue | ||
64 | - | ||
65 | - return _object | ||
66 | - | ||
67 | - def fetch_projects(self): | ||
68 | - page = 1 | ||
69 | - projects = [] | ||
70 | - | ||
71 | - while True: | ||
72 | - json_data = self.get_json_data('/api/v3/projects/all', page) | ||
73 | - page = page + 1 | ||
74 | - | ||
75 | - if not len(json_data): | ||
76 | - break | ||
77 | - | ||
78 | - for element in json_data: | ||
79 | - project = GitlabProject() | ||
80 | - self.fill_object_data(element, project) | ||
81 | - projects.append(project) | ||
82 | - | ||
83 | - return projects | ||
84 | - | ||
85 | - def fetch_merge_request(self, projects): | ||
86 | - all_merge_request = [] | ||
87 | - | ||
88 | - for project in projects: | ||
89 | - page = 1 | ||
90 | - while True: | ||
91 | - url = '/api/v3/projects/{}/merge_requests'.format(project.id) | ||
92 | - json_data_mr = self.get_json_data(url, page) | ||
93 | - page = page + 1 | ||
94 | - | ||
95 | - if len(json_data_mr) == 0: | ||
96 | - break | ||
97 | - | ||
98 | - for element in json_data_mr: | ||
99 | - single_merge_request = GitlabMergeRequest() | ||
100 | - self.fill_object_data(element, single_merge_request) | ||
101 | - all_merge_request.append(single_merge_request) | ||
102 | - | ||
103 | - return all_merge_request | ||
104 | - | ||
105 | - def fetch_issue(self, projects): | ||
106 | - all_issues = [] | ||
107 | - | ||
108 | - for project in projects: | ||
109 | - page = 1 | ||
110 | - while True: | ||
111 | - url = '/api/v3/projects/{}/issues'.format(project.id) | ||
112 | - json_data_issue = self.get_json_data(url, page) | ||
113 | - page = page + 1 | ||
114 | - | ||
115 | - if len(json_data_issue) == 0: | ||
116 | - break | ||
117 | - | ||
118 | - for element in json_data_issue: | ||
119 | - single_issue = GitlabIssue() | ||
120 | - self.fill_object_data(element, single_issue) | ||
121 | - all_issues.append(single_issue) | ||
122 | - | ||
123 | - return all_issues | ||
124 | - | ||
125 | - def fetch_comments(self): | ||
126 | - all_comments = [] | ||
127 | - all_comments.extend(self.fetch_comments_MR()) | ||
128 | - all_comments.extend(self.fetch_comments_issues()) | ||
129 | - | ||
130 | - return all_comments | ||
131 | - | ||
132 | - def fetch_comments_MR(self): | ||
133 | - all_comments = [] | ||
134 | - all_merge_requests = GitlabMergeRequest.objects.all() | ||
135 | - | ||
136 | - for merge_request in all_merge_requests: | ||
137 | - page = 1 | ||
138 | - while True: | ||
139 | - url = '/api/v3/projects/{}/merge_requests/{}/notes'.format( | ||
140 | - merge_request.project_id, merge_request.id) | ||
141 | - json_data_mr = self.get_json_data(url, page) | ||
142 | - page = page + 1 | ||
143 | - | ||
144 | - if len(json_data_mr) == 0: | ||
145 | - break | ||
146 | - | ||
147 | - for element in json_data_mr: | ||
148 | - single_comment = GitlabComment() | ||
149 | - self.fill_object_data(element, single_comment) | ||
150 | - single_comment.project = merge_request.project | ||
151 | - single_comment.issue_comment = False | ||
152 | - single_comment.parent_id = merge_request.id | ||
153 | - all_comments.append(single_comment) | ||
154 | - | ||
155 | - return all_comments | ||
156 | - | ||
157 | - def fetch_comments_issues(self): | ||
158 | - all_comments = [] | ||
159 | - all_issues = GitlabIssue.objects.all() | ||
160 | - | ||
161 | - for issue in all_issues: | ||
162 | - page = 1 | ||
163 | - while True: | ||
164 | - url = '/api/v3/projects/{}/issues/{}/notes'.format( | ||
165 | - issue.project_id, issue.id) | ||
166 | - json_data_mr = self.get_json_data(url, page) | ||
167 | - page = page + 1 | ||
168 | - | ||
169 | - if len(json_data_mr) == 0: | ||
170 | - break | ||
171 | - | ||
172 | - for element in json_data_mr: | ||
173 | - single_comment = GitlabComment() | ||
174 | - self.fill_object_data(element, single_comment) | ||
175 | - single_comment.project = issue.project | ||
176 | - single_comment.issue_comment = True | ||
177 | - single_comment.parent_id = issue.id | ||
178 | - all_comments.append(single_comment) | ||
179 | - | ||
180 | - return all_comments | ||
181 | - | ||
182 | - def fetch_data(self): | ||
183 | - LOGGER.info("Importing Projects") | ||
184 | - projects = self.fetch_projects() | ||
185 | - for datum in projects: | ||
186 | - datum.save() | ||
187 | - | ||
188 | - LOGGER.info("Importing Merge Requests") | ||
189 | - merge_request_list = self.fetch_merge_request(projects) | ||
190 | - for datum in merge_request_list: | ||
191 | - datum.save() | ||
192 | - | ||
193 | - LOGGER.info("Importing Issues") | ||
194 | - issue_list = self.fetch_issue(projects) | ||
195 | - for datum in issue_list: | ||
196 | - datum.save() | ||
197 | - | ||
198 | - LOGGER.info("Importing Comments") | ||
199 | - comments_list = self.fetch_comments() | ||
200 | - for datum in comments_list: | ||
201 | - datum.save() | ||
202 | - | ||
203 | - @property | ||
204 | - def app_label(self): | ||
205 | - return 'gitlab' |
@@ -0,0 +1,217 @@ | @@ -0,0 +1,217 @@ | ||
1 | +import json | ||
2 | +import urllib | ||
3 | +import urllib2 | ||
4 | +import logging | ||
5 | + | ||
6 | +from dateutil.parser import parse | ||
7 | + | ||
8 | +from django.db.models.fields import DateTimeField | ||
9 | +from colab.plugins.data import PluginDataImporter | ||
10 | + | ||
11 | +from .models import (GitlabProject, GitlabMergeRequest, | ||
12 | + GitlabComment, GitlabIssue) | ||
13 | + | ||
14 | + | ||
15 | +LOGGER = logging.getLogger('colab.plugin.gitlab') | ||
16 | + | ||
17 | + | ||
18 | +class GitlabDataImporter(PluginDataImporter): | ||
19 | + app_label = 'gitlab' | ||
20 | + | ||
21 | + def get_request_url(self, path, **kwargs): | ||
22 | + upstream = self.config.get('upstream') | ||
23 | + kwargs['private_token'] = self.config.get('private_token') | ||
24 | + params = urllib.urlencode(kwargs) | ||
25 | + | ||
26 | + if upstream[-1] == '/': | ||
27 | + upstream = upstream[:-1] | ||
28 | + | ||
29 | + return u'{}{}?{}'.format(upstream, path, params) | ||
30 | + | ||
31 | + def get_json_data(self, api_url, page, pages=1000): | ||
32 | + url = self.get_request_url(api_url, per_page=pages, | ||
33 | + page=page) | ||
34 | + | ||
35 | + try: | ||
36 | + data = urllib2.urlopen(url, timeout=10) | ||
37 | + json_data = json.load(data) | ||
38 | + except urllib2.URLError: | ||
39 | + LOGGER.exception("Connection timeout: " + url) | ||
40 | + json_data = [] | ||
41 | + | ||
42 | + return json_data | ||
43 | + | ||
44 | + def fill_object_data(self, element, _object): | ||
45 | + for field in _object._meta.fields: | ||
46 | + try: | ||
47 | + if field.name == "user": | ||
48 | + _object.update_user( | ||
49 | + element["author"]["username"]) | ||
50 | + continue | ||
51 | + if field.name == "project": | ||
52 | + _object.project_id = element["project_id"] | ||
53 | + continue | ||
54 | + | ||
55 | + if isinstance(field, DateTimeField): | ||
56 | + value = parse(element[field.name]) | ||
57 | + else: | ||
58 | + value = element[field.name] | ||
59 | + | ||
60 | + setattr(_object, field.name, value) | ||
61 | + except KeyError: | ||
62 | + continue | ||
63 | + | ||
64 | + return _object | ||
65 | + | ||
66 | + def fetch_projects(self): | ||
67 | + page = 1 | ||
68 | + projects = [] | ||
69 | + | ||
70 | + while True: | ||
71 | + json_data = self.get_json_data('/api/v3/projects/all', page) | ||
72 | + page = page + 1 | ||
73 | + | ||
74 | + if not len(json_data): | ||
75 | + break | ||
76 | + | ||
77 | + for element in json_data: | ||
78 | + project = GitlabProject() | ||
79 | + self.fill_object_data(element, project) | ||
80 | + projects.append(project) | ||
81 | + | ||
82 | + return projects | ||
83 | + | ||
84 | + def fetch_merge_request(self, projects): | ||
85 | + all_merge_request = [] | ||
86 | + | ||
87 | + for project in projects: | ||
88 | + page = 1 | ||
89 | + while True: | ||
90 | + url = '/api/v3/projects/{}/merge_requests'.format(project.id) | ||
91 | + json_data_mr = self.get_json_data(url, page) | ||
92 | + page = page + 1 | ||
93 | + | ||
94 | + if len(json_data_mr) == 0: | ||
95 | + break | ||
96 | + | ||
97 | + for element in json_data_mr: | ||
98 | + single_merge_request = GitlabMergeRequest() | ||
99 | + self.fill_object_data(element, single_merge_request) | ||
100 | + all_merge_request.append(single_merge_request) | ||
101 | + | ||
102 | + return all_merge_request | ||
103 | + | ||
104 | + def fetch_issue(self, projects): | ||
105 | + all_issues = [] | ||
106 | + | ||
107 | + for project in projects: | ||
108 | + page = 1 | ||
109 | + while True: | ||
110 | + url = '/api/v3/projects/{}/issues'.format(project.id) | ||
111 | + json_data_issue = self.get_json_data(url, page) | ||
112 | + page = page + 1 | ||
113 | + | ||
114 | + if len(json_data_issue) == 0: | ||
115 | + break | ||
116 | + | ||
117 | + for element in json_data_issue: | ||
118 | + single_issue = GitlabIssue() | ||
119 | + self.fill_object_data(element, single_issue) | ||
120 | + all_issues.append(single_issue) | ||
121 | + | ||
122 | + return all_issues | ||
123 | + | ||
124 | + def fetch_comments(self): | ||
125 | + all_comments = [] | ||
126 | + all_comments.extend(self.fetch_comments_MR()) | ||
127 | + all_comments.extend(self.fetch_comments_issues()) | ||
128 | + | ||
129 | + return all_comments | ||
130 | + | ||
131 | + def fetch_comments_MR(self): | ||
132 | + all_comments = [] | ||
133 | + all_merge_requests = GitlabMergeRequest.objects.all() | ||
134 | + | ||
135 | + for merge_request in all_merge_requests: | ||
136 | + page = 1 | ||
137 | + while True: | ||
138 | + url = '/api/v3/projects/{}/merge_requests/{}/notes'.format( | ||
139 | + merge_request.project_id, merge_request.id) | ||
140 | + json_data_mr = self.get_json_data(url, page) | ||
141 | + page = page + 1 | ||
142 | + | ||
143 | + if len(json_data_mr) == 0: | ||
144 | + break | ||
145 | + | ||
146 | + for element in json_data_mr: | ||
147 | + single_comment = GitlabComment() | ||
148 | + self.fill_object_data(element, single_comment) | ||
149 | + single_comment.project = merge_request.project | ||
150 | + single_comment.issue_comment = False | ||
151 | + single_comment.parent_id = merge_request.id | ||
152 | + all_comments.append(single_comment) | ||
153 | + | ||
154 | + return all_comments | ||
155 | + | ||
156 | + def fetch_comments_issues(self): | ||
157 | + all_comments = [] | ||
158 | + all_issues = GitlabIssue.objects.all() | ||
159 | + | ||
160 | + for issue in all_issues: | ||
161 | + page = 1 | ||
162 | + while True: | ||
163 | + url = '/api/v3/projects/{}/issues/{}/notes'.format( | ||
164 | + issue.project_id, issue.id) | ||
165 | + json_data_mr = self.get_json_data(url, page) | ||
166 | + page = page + 1 | ||
167 | + | ||
168 | + if len(json_data_mr) == 0: | ||
169 | + break | ||
170 | + | ||
171 | + for element in json_data_mr: | ||
172 | + single_comment = GitlabComment() | ||
173 | + self.fill_object_data(element, single_comment) | ||
174 | + single_comment.project = issue.project | ||
175 | + single_comment.issue_comment = True | ||
176 | + single_comment.parent_id = issue.id | ||
177 | + all_comments.append(single_comment) | ||
178 | + | ||
179 | + return all_comments | ||
180 | + | ||
181 | + | ||
182 | +class GitlabProjectImporter(GitlabDataImporter): | ||
183 | + | ||
184 | + def fetch_data(self): | ||
185 | + LOGGER.info("Importing Projects") | ||
186 | + projects = self.fetch_projects() | ||
187 | + for datum in projects: | ||
188 | + datum.save() | ||
189 | + | ||
190 | + | ||
191 | +class GitlabMergeRequestImporter(GitlabDataImporter): | ||
192 | + | ||
193 | + def fetch_data(self): | ||
194 | + LOGGER.info("Importing Merge Requests") | ||
195 | + projects = GitlabProject.objects.all() | ||
196 | + merge_request_list = self.fetch_merge_request(projects) | ||
197 | + for datum in merge_request_list: | ||
198 | + datum.save() | ||
199 | + | ||
200 | + | ||
201 | +class GitlabIssueImporter(GitlabDataImporter): | ||
202 | + | ||
203 | + def fetch_data(self): | ||
204 | + LOGGER.info("Importing Issues") | ||
205 | + projects = GitlabProject.objects.all() | ||
206 | + issue_list = self.fetch_issue(projects) | ||
207 | + for datum in issue_list: | ||
208 | + datum.save() | ||
209 | + | ||
210 | + | ||
211 | +class GitlabCommentImporter(GitlabDataImporter): | ||
212 | + | ||
213 | + def fetch_data(self): | ||
214 | + LOGGER.info("Importing Comments") | ||
215 | + comments_list = self.fetch_comments() | ||
216 | + for datum in comments_list: | ||
217 | + datum.save() |
colab/plugins/gitlab/views.py
colab/plugins/management/__init__.py
colab/plugins/management/commands/__init__.py
colab/plugins/management/commands/import_proxy_data.py
@@ -1,31 +0,0 @@ | @@ -1,31 +0,0 @@ | ||
1 | -#!/usr/bin/env python | ||
2 | - | ||
3 | -import importlib | ||
4 | -import inspect | ||
5 | - | ||
6 | -from django.core.management.base import BaseCommand | ||
7 | -from django.conf import settings | ||
8 | - | ||
9 | -from colab.plugins.utils.proxy_data_api import ProxyDataAPI | ||
10 | - | ||
11 | - | ||
12 | -class Command(BaseCommand): | ||
13 | - help = "Import proxy data into colab database" | ||
14 | - | ||
15 | - def handle(self, *args, **kwargs): | ||
16 | - print "Executing extraction command..." | ||
17 | - | ||
18 | - for module_name in settings.PROXIED_APPS.keys(): | ||
19 | - module_path = \ | ||
20 | - 'colab.plugins.{}.data_api'.format(module_name.split('.')[-1]) | ||
21 | - module = importlib.import_module(module_path) | ||
22 | - | ||
23 | - for module_item_name in dir(module): | ||
24 | - module_item = getattr(module, module_item_name) | ||
25 | - if not inspect.isclass(module_item): | ||
26 | - continue | ||
27 | - if issubclass(module_item, ProxyDataAPI): | ||
28 | - if module_item != ProxyDataAPI: | ||
29 | - api = module_item() | ||
30 | - api.fetch_data() | ||
31 | - break |
colab/plugins/mezuro/__init__.py
colab/plugins/mezuro/apps.py
1 | 1 | ||
2 | -from ..utils.apps import ColabProxiedAppConfig | 2 | +from ..utils.apps import ColabPluginAppConfig |
3 | 3 | ||
4 | 4 | ||
5 | -class ProxyMezuroAppConfig(ColabProxiedAppConfig): | 5 | +class MezuroPluginAppConfig(ColabPluginAppConfig): |
6 | name = 'colab.plugins.mezuro' | 6 | name = 'colab.plugins.mezuro' |
7 | - verbose_name = 'Mezuro Proxy' | 7 | + verbose_name = 'Mezuro Plugin' |
colab/plugins/mezuro/views.py
colab/plugins/noosfero/__init__.py
colab/plugins/noosfero/apps.py
1 | 1 | ||
2 | -from ..utils.apps import ColabProxiedAppConfig | 2 | +from ..utils.apps import ColabPluginAppConfig |
3 | 3 | ||
4 | 4 | ||
5 | -class ProxyNoosferoAppConfig(ColabProxiedAppConfig): | 5 | +class NoosferoPluginAppConfig(ColabPluginAppConfig): |
6 | name = 'colab.plugins.noosfero' | 6 | name = 'colab.plugins.noosfero' |
7 | - verbose_name = 'Noosfero Proxy' | 7 | + verbose_name = 'Noosfero Plugin' |
colab/plugins/noosfero/data_api.py
@@ -1,109 +0,0 @@ | @@ -1,109 +0,0 @@ | ||
1 | -import json | ||
2 | -import urllib | ||
3 | -import urllib2 | ||
4 | -import logging | ||
5 | - | ||
6 | -from dateutil.parser import parse | ||
7 | - | ||
8 | -from django.conf import settings | ||
9 | -from django.db.models.fields import DateTimeField | ||
10 | - | ||
11 | -from colab.plugins.noosfero.models import (NoosferoArticle, NoosferoCommunity, | ||
12 | - NoosferoCategory) | ||
13 | -from colab.plugins.utils.proxy_data_api import ProxyDataAPI | ||
14 | - | ||
15 | -LOGGER = logging.getLogger('colab.plugin.debug') | ||
16 | - | ||
17 | - | ||
18 | -class NoosferoDataAPI(ProxyDataAPI): | ||
19 | - | ||
20 | - def get_request_url(self, path, **kwargs): | ||
21 | - proxy_config = settings.PROXIED_APPS.get(self.app_label, {}) | ||
22 | - | ||
23 | - upstream = proxy_config.get('upstream') | ||
24 | - kwargs['private_token'] = proxy_config.get('private_token') | ||
25 | - params = urllib.urlencode(kwargs) | ||
26 | - | ||
27 | - if upstream[-1] == '/': | ||
28 | - upstream = upstream[:-1] | ||
29 | - | ||
30 | - return u'{}{}?{}'.format(upstream, path, params) | ||
31 | - | ||
32 | - def get_json_data(self, api_url, page, pages=1000): | ||
33 | - url = self.get_request_url(api_url, per_page=pages, | ||
34 | - page=page) | ||
35 | - try: | ||
36 | - data = urllib2.urlopen(url, timeout=10) | ||
37 | - json_data = json.load(data) | ||
38 | - except urllib2.URLError: | ||
39 | - LOGGER.exception("Connection timeout: " + url) | ||
40 | - json_data = [] | ||
41 | - | ||
42 | - return json_data | ||
43 | - | ||
44 | - def fill_object_data(self, element, _object): | ||
45 | - for field in _object._meta.fields: | ||
46 | - try: | ||
47 | - if field.name == "user": | ||
48 | - _object.update_user( | ||
49 | - element["author"]["name"]) | ||
50 | - continue | ||
51 | - | ||
52 | - if field.name == "profile_identifier": | ||
53 | - _object.profile_identifier = \ | ||
54 | - element["profile"]["identifier"] | ||
55 | - continue | ||
56 | - | ||
57 | - if isinstance(field, DateTimeField): | ||
58 | - value = parse(element[field.name]) | ||
59 | - else: | ||
60 | - value = element[field.name] | ||
61 | - | ||
62 | - setattr(_object, field.name, value) | ||
63 | - except KeyError: | ||
64 | - continue | ||
65 | - except TypeError: | ||
66 | - continue | ||
67 | - | ||
68 | - return _object | ||
69 | - | ||
70 | - def fetch_communities(self): | ||
71 | - json_data = self.get_json_data('/api/v1/communities', 1) | ||
72 | - | ||
73 | - json_data = json_data['communities'] | ||
74 | - for element in json_data: | ||
75 | - community = NoosferoCommunity() | ||
76 | - self.fill_object_data(element, community) | ||
77 | - community.save() | ||
78 | - | ||
79 | - if 'categories' in element: | ||
80 | - for category_json in element["categories"]: | ||
81 | - category = NoosferoCategory.objects.get_or_create( | ||
82 | - id=category_json["id"], name=category_json["name"])[0] | ||
83 | - community.categories.add(category.id) | ||
84 | - | ||
85 | - def fetch_articles(self): | ||
86 | - json_data = self.get_json_data('/api/v1/articles', 1) | ||
87 | - | ||
88 | - json_data = json_data['articles'] | ||
89 | - | ||
90 | - for element in json_data: | ||
91 | - article = NoosferoArticle() | ||
92 | - self.fill_object_data(element, article) | ||
93 | - article.save() | ||
94 | - | ||
95 | - for category_json in element["categories"]: | ||
96 | - category = NoosferoCategory.objects.get_or_create( | ||
97 | - id=category_json["id"], name=category_json["name"])[0] | ||
98 | - article.categories.add(category.id) | ||
99 | - | ||
100 | - def fetch_data(self): | ||
101 | - LOGGER.info("Importing Communities") | ||
102 | - self.fetch_communities() | ||
103 | - | ||
104 | - LOGGER.info("Importing Articles") | ||
105 | - self.fetch_articles() | ||
106 | - | ||
107 | - @property | ||
108 | - def app_label(self): | ||
109 | - return 'noosfero' |
@@ -0,0 +1,106 @@ | @@ -0,0 +1,106 @@ | ||
1 | +import json | ||
2 | +import urllib | ||
3 | +import urllib2 | ||
4 | +import logging | ||
5 | + | ||
6 | +from dateutil.parser import parse | ||
7 | + | ||
8 | +from django.db.models.fields import DateTimeField | ||
9 | + | ||
10 | +from colab.plugins.data import PluginDataImporter | ||
11 | + | ||
12 | +from .models import NoosferoArticle, NoosferoCommunity, NoosferoCategory | ||
13 | + | ||
14 | +LOGGER = logging.getLogger('colab.plugin.debug') | ||
15 | + | ||
16 | + | ||
17 | +class NoosferoDataImporter(PluginDataImporter): | ||
18 | + | ||
19 | + def get_request_url(self, path, **kwargs): | ||
20 | + upstream = self.config.get('upstream') | ||
21 | + kwargs['private_token'] = self.config.get('private_token') | ||
22 | + params = urllib.urlencode(kwargs) | ||
23 | + | ||
24 | + if upstream[-1] == '/': | ||
25 | + upstream = upstream[:-1] | ||
26 | + | ||
27 | + return u'{}{}?{}'.format(upstream, path, params) | ||
28 | + | ||
29 | + def get_json_data(self, api_url, page, pages=1000): | ||
30 | + url = self.get_request_url(api_url, per_page=pages, | ||
31 | + page=page) | ||
32 | + try: | ||
33 | + data = urllib2.urlopen(url, timeout=10) | ||
34 | + json_data = json.load(data) | ||
35 | + except urllib2.URLError: | ||
36 | + LOGGER.exception("Connection timeout: " + url) | ||
37 | + json_data = [] | ||
38 | + | ||
39 | + return json_data | ||
40 | + | ||
41 | + def fill_object_data(self, element, _object): | ||
42 | + for field in _object._meta.fields: | ||
43 | + try: | ||
44 | + if field.name == "user": | ||
45 | + _object.update_user( | ||
46 | + element["author"]["name"]) | ||
47 | + continue | ||
48 | + | ||
49 | + if field.name == "profile_identifier": | ||
50 | + _object.profile_identifier = \ | ||
51 | + element["profile"]["identifier"] | ||
52 | + continue | ||
53 | + | ||
54 | + if isinstance(field, DateTimeField): | ||
55 | + value = parse(element[field.name]) | ||
56 | + else: | ||
57 | + value = element[field.name] | ||
58 | + | ||
59 | + setattr(_object, field.name, value) | ||
60 | + except KeyError: | ||
61 | + continue | ||
62 | + except TypeError: | ||
63 | + continue | ||
64 | + | ||
65 | + return _object | ||
66 | + | ||
67 | + def fetch_communities(self): | ||
68 | + json_data = self.get_json_data('/api/v1/communities', 1) | ||
69 | + | ||
70 | + json_data = json_data['communities'] | ||
71 | + for element in json_data: | ||
72 | + community = NoosferoCommunity() | ||
73 | + self.fill_object_data(element, community) | ||
74 | + community.save() | ||
75 | + | ||
76 | + if 'categories' in element: | ||
77 | + for category_json in element["categories"]: | ||
78 | + category = NoosferoCategory.objects.get_or_create( | ||
79 | + id=category_json["id"], name=category_json["name"])[0] | ||
80 | + community.categories.add(category.id) | ||
81 | + | ||
82 | + def fetch_articles(self): | ||
83 | + json_data = self.get_json_data('/api/v1/articles', 1) | ||
84 | + | ||
85 | + json_data = json_data['articles'] | ||
86 | + | ||
87 | + for element in json_data: | ||
88 | + article = NoosferoArticle() | ||
89 | + self.fill_object_data(element, article) | ||
90 | + article.save() | ||
91 | + | ||
92 | + for category_json in element["categories"]: | ||
93 | + category = NoosferoCategory.objects.get_or_create( | ||
94 | + id=category_json["id"], name=category_json["name"])[0] | ||
95 | + article.categories.add(category.id) | ||
96 | + | ||
97 | + def fetch_data(self): | ||
98 | + LOGGER.info("Importing Communities") | ||
99 | + self.fetch_communities() | ||
100 | + | ||
101 | + LOGGER.info("Importing Articles") | ||
102 | + self.fetch_articles() | ||
103 | + | ||
104 | + @property | ||
105 | + def app_label(self): | ||
106 | + return 'noosfero' |
colab/plugins/noosfero/views.py
colab/plugins/templatetags/plugins.py
@@ -11,10 +11,11 @@ register = template.Library() | @@ -11,10 +11,11 @@ register = template.Library() | ||
11 | @register.simple_tag(takes_context=True) | 11 | @register.simple_tag(takes_context=True) |
12 | def plugins_menu(context): | 12 | def plugins_menu(context): |
13 | 13 | ||
14 | + # TODO: Cache has to take language into account | ||
14 | if context['user'].is_authenticated(): | 15 | if context['user'].is_authenticated(): |
15 | - cache_key = 'colab-proxy-menu-authenticated' | 16 | + cache_key = 'colab-plugin-menu-authenticated' |
16 | else: | 17 | else: |
17 | - cache_key = 'colab-proxy-menu-anonymous' | 18 | + cache_key = 'colab-plugin-menu-anonymous' |
18 | 19 | ||
19 | lang = get_language() | 20 | lang = get_language() |
20 | cache_key += '-{}'.format(lang) | 21 | cache_key += '-{}'.format(lang) |
colab/plugins/utils/apps.py
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | from django.apps import AppConfig | 2 | from django.apps import AppConfig |
3 | 3 | ||
4 | 4 | ||
5 | -class ColabProxiedAppConfig(AppConfig): | 5 | +class ColabPluginAppConfig(AppConfig): |
6 | colab_proxied_app = True | 6 | colab_proxied_app = True |
7 | 7 | ||
8 | def register_signals(self): | 8 | def register_signals(self): |
colab/plugins/utils/proxy_data_api.py
colab/plugins/utils/views.py
@@ -1,37 +0,0 @@ | @@ -1,37 +0,0 @@ | ||
1 | - | ||
2 | -import json | ||
3 | - | ||
4 | -from django.conf import settings | ||
5 | - | ||
6 | -from revproxy.views import DiazoProxyView | ||
7 | - | ||
8 | - | ||
9 | -class ColabProxyView(DiazoProxyView): | ||
10 | - add_remote_user = settings.REVPROXY_ADD_REMOTE_USER | ||
11 | - diazo_theme_template = 'base.html' | ||
12 | - html5 = True | ||
13 | - | ||
14 | - @property | ||
15 | - def upstream(self): | ||
16 | - proxy_config = settings.PROXIED_APPS.get(self.app_label, {}) | ||
17 | - return proxy_config.get('upstream') | ||
18 | - | ||
19 | - @property | ||
20 | - def app_label(self): | ||
21 | - raise NotImplementedError('app_label attribute must be set') | ||
22 | - | ||
23 | - def dispatch(self, request, *args, **kwargs): | ||
24 | - | ||
25 | - if request.user.is_authenticated(): | ||
26 | - | ||
27 | - remote_user_data = {} | ||
28 | - | ||
29 | - remote_user_data['email'] = request.user.email | ||
30 | - remote_user_data['name'] = request.user.get_full_name() | ||
31 | - | ||
32 | - request.META['HTTP_REMOTE_USER_DATA'] = json.dumps( | ||
33 | - remote_user_data, | ||
34 | - sort_keys=True, | ||
35 | - ) | ||
36 | - | ||
37 | - return super(ColabProxyView, self).dispatch(request, *args, **kwargs) |
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | + | ||
2 | +import json | ||
3 | + | ||
4 | +from django.conf import settings | ||
5 | + | ||
6 | +from revproxy.views import DiazoProxyView | ||
7 | + | ||
8 | +from .conf import get_plugin_config | ||
9 | + | ||
10 | + | ||
11 | +class ColabProxyView(DiazoProxyView): | ||
12 | + add_remote_user = settings.REVPROXY_ADD_REMOTE_USER | ||
13 | + diazo_theme_template = 'base.html' | ||
14 | + html5 = True | ||
15 | + | ||
16 | + @property | ||
17 | + def upstream(self): | ||
18 | + config = get_plugin_config(self.app_label) | ||
19 | + return config.get('upstream') | ||
20 | + | ||
21 | + @property | ||
22 | + def app_label(self): | ||
23 | + raise NotImplementedError('app_label attribute must be set') | ||
24 | + | ||
25 | + def dispatch(self, request, *args, **kwargs): | ||
26 | + | ||
27 | + if request.user.is_authenticated(): | ||
28 | + | ||
29 | + remote_user_data = {} | ||
30 | + | ||
31 | + remote_user_data['email'] = request.user.email | ||
32 | + remote_user_data['name'] = request.user.get_full_name() | ||
33 | + | ||
34 | + request.META['HTTP_REMOTE_USER_DATA'] = json.dumps( | ||
35 | + remote_user_data, | ||
36 | + sort_keys=True, | ||
37 | + ) | ||
38 | + | ||
39 | + return super(ColabProxyView, self).dispatch(request, *args, **kwargs) |
@@ -0,0 +1,103 @@ | @@ -0,0 +1,103 @@ | ||
1 | +from __future__ import absolute_import | ||
2 | + | ||
3 | +import celery | ||
4 | +import os | ||
5 | +import sys | ||
6 | + | ||
7 | +from django.core.management.base import BaseCommand | ||
8 | + | ||
9 | +DB_SHARED_THREAD = """\ | ||
10 | +DatabaseWrapper objects created in a thread can only \ | ||
11 | +be used in that same thread. The object with alias '%s' \ | ||
12 | +was created in thread id %s and this is thread id %s.\ | ||
13 | +""" | ||
14 | + | ||
15 | + | ||
16 | +def patch_thread_ident(): | ||
17 | + # monkey patch django. | ||
18 | + # This patch make sure that we use real threads to get the ident which | ||
19 | + # is going to happen if we are using gevent or eventlet. | ||
20 | + # -- patch taken from gunicorn | ||
21 | + if getattr(patch_thread_ident, 'called', False): | ||
22 | + return | ||
23 | + try: | ||
24 | + from django.db.backends import BaseDatabaseWrapper, DatabaseError | ||
25 | + | ||
26 | + if 'validate_thread_sharing' in BaseDatabaseWrapper.__dict__: | ||
27 | + import thread | ||
28 | + _get_ident = thread.get_ident | ||
29 | + | ||
30 | + __old__init__ = BaseDatabaseWrapper.__init__ | ||
31 | + | ||
32 | + def _init(self, *args, **kwargs): | ||
33 | + __old__init__(self, *args, **kwargs) | ||
34 | + self._thread_ident = _get_ident() | ||
35 | + | ||
36 | + def _validate_thread_sharing(self): | ||
37 | + if (not self.allow_thread_sharing | ||
38 | + and self._thread_ident != _get_ident()): | ||
39 | + raise DatabaseError( | ||
40 | + DB_SHARED_THREAD % ( | ||
41 | + self.alias, self._thread_ident, _get_ident()), | ||
42 | + ) | ||
43 | + | ||
44 | + BaseDatabaseWrapper.__init__ = _init | ||
45 | + BaseDatabaseWrapper.validate_thread_sharing = \ | ||
46 | + _validate_thread_sharing | ||
47 | + | ||
48 | + patch_thread_ident.called = True | ||
49 | + except ImportError: | ||
50 | + pass | ||
51 | +patch_thread_ident() | ||
52 | + | ||
53 | + | ||
54 | +class CeleryCommand(BaseCommand): | ||
55 | + options = BaseCommand.option_list | ||
56 | + skip_opts = ['--app', '--loader', '--config'] | ||
57 | + keep_base_opts = False | ||
58 | + | ||
59 | + def get_version(self): | ||
60 | + return 'celery %s' % (celery.__version__) | ||
61 | + | ||
62 | + def execute(self, *args, **options): | ||
63 | + broker = options.get('broker') | ||
64 | + if broker: | ||
65 | + self.set_broker(broker) | ||
66 | + super(CeleryCommand, self).execute(*args, **options) | ||
67 | + | ||
68 | + def set_broker(self, broker): | ||
69 | + os.environ['CELERY_BROKER_URL'] = broker | ||
70 | + | ||
71 | + def run_from_argv(self, argv): | ||
72 | + self.handle_default_options(argv[2:]) | ||
73 | + return super(CeleryCommand, self).run_from_argv(argv) | ||
74 | + | ||
75 | + def handle_default_options(self, argv): | ||
76 | + acc = [] | ||
77 | + broker = None | ||
78 | + for i, arg in enumerate(argv): | ||
79 | + if '--settings=' in arg: | ||
80 | + _, settings_module = arg.split('=') | ||
81 | + os.environ['DJANGO_SETTINGS_MODULE'] = settings_module | ||
82 | + elif '--pythonpath=' in arg: | ||
83 | + _, pythonpath = arg.split('=') | ||
84 | + sys.path.insert(0, pythonpath) | ||
85 | + elif '--broker=' in arg: | ||
86 | + _, broker = arg.split('=') | ||
87 | + elif arg == '-b': | ||
88 | + broker = argv[i + 1] | ||
89 | + else: | ||
90 | + acc.append(arg) | ||
91 | + if broker: | ||
92 | + self.set_broker(broker) | ||
93 | + return argv if self.keep_base_opts else acc | ||
94 | + | ||
95 | + def die(self, msg): | ||
96 | + sys.stderr.write(msg) | ||
97 | + sys.stderr.write('\n') | ||
98 | + sys.exit() | ||
99 | + | ||
100 | + @property | ||
101 | + def option_list(self): | ||
102 | + return [x for x in self.options | ||
103 | + if x._long_opts[0] not in self.skip_opts] |
colab/search/templates/search/includes/search_filters.html
@@ -112,28 +112,6 @@ | @@ -112,28 +112,6 @@ | ||
112 | 112 | ||
113 | <ul class="unstyled-list"> | 113 | <ul class="unstyled-list"> |
114 | 114 | ||
115 | - {% if is_trac %} | ||
116 | - <li> | ||
117 | - <span class="glyphicon glyphicon-book"></span> | ||
118 | - <a href="{% append_to_get type='wiki' %}">{% trans "Wiki" %}</a> | ||
119 | - </li> | ||
120 | - <li> | ||
121 | - <span class="glyphicon glyphicon-tag"></span> | ||
122 | - <a href="{% append_to_get type='ticket' %}">{% trans "Ticket" %}</a> | ||
123 | - </li> | ||
124 | - <li> | ||
125 | - <span class="glyphicon glyphicon-align-right"></span> | ||
126 | - <a href="{% append_to_get type='changeset' %}">{% trans "Changeset" %}</a> | ||
127 | - </li> | ||
128 | - <li> | ||
129 | - <span class="glyphicon glyphicon-user"></span> | ||
130 | - <a href="{% append_to_get type='user' %}">{% trans "User" %}</a> | ||
131 | - </li> | ||
132 | - <li> | ||
133 | - <span class="glyphicon glyphicon-file"></span> | ||
134 | - <a href="{% append_to_get type='attachment' %}">{% trans "Attachment" %}</a> | ||
135 | - </li> | ||
136 | - {% endif %} | ||
137 | <li> | 115 | <li> |
138 | <span class="glyphicon glyphicon-envelope"></span> | 116 | <span class="glyphicon glyphicon-envelope"></span> |
139 | <a href="{% append_to_get type='thread' %}">{% trans "Discussion" %}</a> | 117 | <a href="{% append_to_get type='thread' %}">{% trans "Discussion" %}</a> |
colab/search/utils.py
@@ -57,11 +57,10 @@ def get_collaboration_data(logged_user, filter_by_user=None): | @@ -57,11 +57,10 @@ def get_collaboration_data(logged_user, filter_by_user=None): | ||
57 | 57 | ||
58 | latest_results.extend(messages) | 58 | latest_results.extend(messages) |
59 | 59 | ||
60 | - app_names = settings.PROXIED_APPS.keys() | 60 | + app_names = settings.COLAB_APPS.keys() |
61 | 61 | ||
62 | for app_name in app_names: | 62 | for app_name in app_names: |
63 | - module = importlib \ | ||
64 | - .import_module('colab.plugins.{}.models'.format(app_name)) | 63 | + module = importlib.import_module('{}.models'.format(app_name)) |
65 | 64 | ||
66 | for module_item_name in dir(module): | 65 | for module_item_name in dir(module): |
67 | module_item = getattr(module, module_item_name) | 66 | module_item = getattr(module, module_item_name) |
colab/settings.py
@@ -8,7 +8,8 @@ For the full list of settings and their values, see | @@ -8,7 +8,8 @@ For the full list of settings and their values, see | ||
8 | https://docs.djangoproject.com/en/1.7/ref/settings/ | 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ |
9 | """ | 9 | """ |
10 | 10 | ||
11 | -BROKER_URL = 'amqp://guest:guest@localhost:5672/' | 11 | +BROKER_URL = 'redis://localhost:6379/0' |
12 | +CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' | ||
12 | 13 | ||
13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | 14 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) |
14 | import os | 15 | import os |
@@ -49,6 +50,7 @@ INSTALLED_APPS = ( | @@ -49,6 +50,7 @@ INSTALLED_APPS = ( | ||
49 | 'taggit', | 50 | 'taggit', |
50 | 51 | ||
51 | # Own apps | 52 | # Own apps |
53 | + 'colab', | ||
52 | 'colab.home', | 54 | 'colab.home', |
53 | 'colab.plugins', | 55 | 'colab.plugins', |
54 | 'colab.super_archives', | 56 | 'colab.super_archives', |
@@ -257,7 +259,6 @@ locals().update(conf.load_py_settings()) | @@ -257,7 +259,6 @@ locals().update(conf.load_py_settings()) | ||
257 | locals().update(conf.load_colab_apps()) | 259 | locals().update(conf.load_colab_apps()) |
258 | 260 | ||
259 | COLAB_APPS = locals().get('COLAB_APPS') or {} | 261 | COLAB_APPS = locals().get('COLAB_APPS') or {} |
260 | -PROXIED_APPS = {} | ||
261 | 262 | ||
262 | for app_name, app in COLAB_APPS.items(): | 263 | for app_name, app in COLAB_APPS.items(): |
263 | if 'dependencies' in app: | 264 | if 'dependencies' in app: |
@@ -268,9 +269,6 @@ for app_name, app in COLAB_APPS.items(): | @@ -268,9 +269,6 @@ for app_name, app in COLAB_APPS.items(): | ||
268 | if app_name not in INSTALLED_APPS: | 269 | if app_name not in INSTALLED_APPS: |
269 | INSTALLED_APPS += (app_name,) | 270 | INSTALLED_APPS += (app_name,) |
270 | 271 | ||
271 | - if app.get('upstream'): | ||
272 | - PROXIED_APPS[app_name.split('.')[-1]] = app | ||
273 | - | ||
274 | if 'middlewares' in app: | 272 | if 'middlewares' in app: |
275 | for middleware in app.get('middlewares'): | 273 | for middleware in app.get('middlewares'): |
276 | if middleware not in MIDDLEWARE_CLASSES: | 274 | if middleware not in MIDDLEWARE_CLASSES: |
colab/super_archives/templates/superarchives/thread-dashboard.html
@@ -12,9 +12,6 @@ | @@ -12,9 +12,6 @@ | ||
12 | <h3><b>{{ listname|title|lower }} {% if description %} ({{ description }}){% endif %}</b></h3> | 12 | <h3><b>{{ listname|title|lower }} {% if description %} ({{ description }}){% endif %}</b></h3> |
13 | <div class="btn-group btn-group-sm"> | 13 | <div class="btn-group btn-group-sm"> |
14 | <a href="#" class="btn btn-default" disabled="disabled">{% blocktrans %}{{ number_of_users }} members{% endblocktrans %}</a> | 14 | <a href="#" class="btn btn-default" disabled="disabled">{% blocktrans %}{{ number_of_users }} members{% endblocktrans %}</a> |
15 | - {% if proxy.trac %} | ||
16 | - <a href="/wiki/grupos/{{ listname }}" class="btn btn-default">Wiki</a> | ||
17 | - {% endif %} | ||
18 | </div> | 15 | </div> |
19 | <hr/> | 16 | <hr/> |
20 | 17 |
colab/utils/conf.py
@@ -3,7 +3,6 @@ import os | @@ -3,7 +3,6 @@ import os | ||
3 | import sys | 3 | import sys |
4 | import logging | 4 | import logging |
5 | import importlib | 5 | import importlib |
6 | -import warnings | ||
7 | 6 | ||
8 | from django.core.exceptions import ImproperlyConfigured | 7 | from django.core.exceptions import ImproperlyConfigured |
9 | 8 | ||
@@ -96,17 +95,21 @@ def load_colab_apps(): | @@ -96,17 +95,21 @@ def load_colab_apps(): | ||
96 | return {'COLAB_APPS': COLAB_APPS} | 95 | return {'COLAB_APPS': COLAB_APPS} |
97 | 96 | ||
98 | for file_name in os.listdir(plugins_dir): | 97 | for file_name in os.listdir(plugins_dir): |
99 | - if not file_name.endswith('.py'): | ||
100 | - continue | ||
101 | - | ||
102 | file_module = file_name.split('.')[0] | 98 | file_module = file_name.split('.')[0] |
99 | + | ||
100 | + logger.info('Loaded plugin settings: %s%s', plugins_dir, file_name) | ||
103 | py_settings_d = _load_py_file(file_module, plugins_dir) | 101 | py_settings_d = _load_py_file(file_module, plugins_dir) |
104 | - logger.info('Loaded plugin settings: %s/%s', plugins_dir, file_name) | ||
105 | 102 | ||
106 | - app_name = py_settings_d.get('name') | 103 | + if os.path.isdir(os.path.join(plugins_dir, file_name)): |
104 | + app_name = file_name | ||
105 | + | ||
106 | + elif file_name.endswith('.py'): | ||
107 | + app_name = py_settings_d.get('name') | ||
108 | + | ||
107 | if not app_name: | 109 | if not app_name: |
108 | - warnings.warn("Plugin missing name variable") | 110 | + logger.warning("Plugin missing name variable (%s)", file_name) |
109 | continue | 111 | continue |
112 | + | ||
110 | try: | 113 | try: |
111 | importlib.import_module(app_name) | 114 | importlib.import_module(app_name) |
112 | except ImportError: | 115 | except ImportError: |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | + | ||
2 | +import os | ||
3 | + | ||
4 | +from django.core.management import ManagementUtility | ||
5 | + | ||
6 | + | ||
7 | +def execute_from_command_line(argv=None): | ||
8 | + """ | ||
9 | + A simple method that runs a ManagementUtility. | ||
10 | + """ | ||
11 | + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "colab.settings") | ||
12 | + | ||
13 | + utility = ManagementUtility(argv) | ||
14 | + utility.execute() |
docs/source/plugindev.rst
@@ -43,16 +43,16 @@ signals structure, some steps are required: | @@ -43,16 +43,16 @@ signals structure, some steps are required: | ||
43 | * With signals registered and handling method defined you must connect them. | 43 | * With signals registered and handling method defined you must connect them. |
44 | To do it you must call connect_signal passing signal name, sender and handling | 44 | To do it you must call connect_signal passing signal name, sender and handling |
45 | method as arguments. These should be implemented on plugin's apps.py. It must | 45 | method as arguments. These should be implemented on plugin's apps.py. It must |
46 | - be said that the plugin app class must extend ColabProxiedAppConfig. An | 46 | + be said that the plugin app class must extend ColabPluginAppConfig. An |
47 | example of this configuration can be seen below: | 47 | example of this configuration can be seen below: |
48 | 48 | ||
49 | 49 | ||
50 | .. code-block:: python | 50 | .. code-block:: python |
51 | - from colab.plugins.utils.apps import ColabProxiedAppConfig | 51 | + from colab.plugins.utils.apps import ColabPluginAppConfig |
52 | from colab.signals.signals import register_signal, connect_signal | 52 | from colab.signals.signals import register_signal, connect_signal |
53 | from colab.plugins.PLUGIN.tasks import HANDLING_METHOD | 53 | from colab.plugins.PLUGIN.tasks import HANDLING_METHOD |
54 | 54 | ||
55 | - class PluginApps(ColabProxiedAppConfig): | 55 | + class PluginApps(ColabPluginAppConfig): |
56 | short_name = PLUGIN_NAME | 56 | short_name = PLUGIN_NAME |
57 | signals_list = [SIGNAL1, SIGNAL2] | 57 | signals_list = [SIGNAL1, SIGNAL2] |
58 | 58 |
setup.py
@@ -16,7 +16,7 @@ REQUIREMENTS = [ | @@ -16,7 +16,7 @@ REQUIREMENTS = [ | ||
16 | 'diazo>=1.0.5', | 16 | 'diazo>=1.0.5', |
17 | 17 | ||
18 | # Async Signals | 18 | # Async Signals |
19 | - 'celery>=3.1', | 19 | + 'celery[redis]>=3.1', |
20 | 20 | ||
21 | ### Move out of colab (as plugins): | 21 | ### Move out of colab (as plugins): |
22 | 22 | ||
@@ -55,8 +55,7 @@ setup( | @@ -55,8 +55,7 @@ setup( | ||
55 | packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES), | 55 | packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES), |
56 | include_package_data=True, | 56 | include_package_data=True, |
57 | entry_points={'console_scripts': [ | 57 | entry_points={'console_scripts': [ |
58 | - 'colab-admin = colab.management:execute_from_command_line', | ||
59 | - 'colab-init-config = colab.management:initconfig', | 58 | + 'colab-admin = colab.utils.runner:execute_from_command_line', |
60 | ]}, | 59 | ]}, |
61 | zip_safe=False, | 60 | zip_safe=False, |
62 | long_description=open('README.rst').read(), | 61 | long_description=open('README.rst').read(), |
vagrant/centos.sh
@@ -22,9 +22,9 @@ yum install -y epel-release | @@ -22,9 +22,9 @@ yum install -y epel-release | ||
22 | 22 | ||
23 | yum -y groupinstall "Development tools" | 23 | yum -y groupinstall "Development tools" |
24 | 24 | ||
25 | -yum install -y git unzip gettext libxml2-devel libxslt-devel openssl-devel libffi-devel python-devel python-pip python-virtualenvwrapper rabbitmq-server | 25 | +yum install -y git unzip gettext libxml2-devel libxslt-devel openssl-devel libffi-devel python-devel python-pip python-virtualenvwrapper redis |
26 | 26 | ||
27 | 27 | ||
28 | -### Init Rabbitmq | ||
29 | -chkconfig rabbitmq-server on | ||
30 | -systemctl start rabbitmq-server | 28 | +### Init Redis |
29 | +systemctl enable redis | ||
30 | +systemctl start redis |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +# Absolute or relative path to the 'celery' command: | ||
2 | +CELERY_BIN="/home/vagrant/.virtualenvs/colab/bin/celery" | ||
3 | + | ||
4 | +# App instance to use | ||
5 | +# comment out this line if you don't use an app | ||
6 | +CELERY_APP="colab.celery:app" | ||
7 | + | ||
8 | +# Where to chdir at start. | ||
9 | +CELERYBEAT_CHDIR="/vagrant/" | ||
10 | + | ||
11 | +# Extra arguments to celerybeat | ||
12 | +CELERYBEAT_OPTS="--schedule=/var/run/celery/celerybeat-schedule" | ||
13 | + | ||
14 | +CELERTBEAT_LOG_FILE="/var/log/celery/beat.log" | ||
15 | +CELERYBEAT_PID_FILE="/var/run/celery/beat.pid" | ||
16 | + | ||
17 | +CELERYBEAT_USER="vagrant" | ||
18 | +CELERYBEAT_GROUP="vagrant" | ||
19 | + | ||
20 | +# If enabled pid and log directories will be created if missing, | ||
21 | +# and owned by the userid/group configured. | ||
22 | +CELERY_CREATE_DIRS=1 |
vagrant/misc/etc/default/celeryd
@@ -5,13 +5,13 @@ CELERYD_NODES="worker1" | @@ -5,13 +5,13 @@ CELERYD_NODES="worker1" | ||
5 | CELERY_BIN="/home/vagrant/.virtualenvs/colab/bin/celery" | 5 | CELERY_BIN="/home/vagrant/.virtualenvs/colab/bin/celery" |
6 | 6 | ||
7 | # comment out this line if you don't use an app | 7 | # comment out this line if you don't use an app |
8 | -CELERY_APP="colab" | 8 | +CELERY_APP="colab.celery:app" |
9 | 9 | ||
10 | # Where to chdir at start. | 10 | # Where to chdir at start. |
11 | CELERYD_CHDIR="/vagrant/" | 11 | CELERYD_CHDIR="/vagrant/" |
12 | 12 | ||
13 | # Extra command-line arguments to the worker | 13 | # Extra command-line arguments to the worker |
14 | -CELERYD_OPTS="--time-limit=300 --concurrency=8" | 14 | +CELERYD_OPTS="--time-limit=300 --concurrency=2" |
15 | 15 | ||
16 | # %N will be replaced with the first part of the nodename. | 16 | # %N will be replaced with the first part of the nodename. |
17 | CELERYD_LOG_FILE="/var/log/celery/%N.log" | 17 | CELERYD_LOG_FILE="/var/log/celery/%N.log" |
@@ -0,0 +1,318 @@ | @@ -0,0 +1,318 @@ | ||
1 | +#!/bin/sh -e | ||
2 | +# ========================================================= | ||
3 | +# celerybeat - Starts the Celery periodic task scheduler. | ||
4 | +# ========================================================= | ||
5 | +# | ||
6 | +# :Usage: /etc/init.d/celerybeat {start|stop|force-reload|restart|try-restart|status} | ||
7 | +# :Configuration file: /etc/default/celerybeat or /etc/default/celeryd | ||
8 | +# | ||
9 | +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts | ||
10 | + | ||
11 | +### BEGIN INIT INFO | ||
12 | +# Provides: celerybeat | ||
13 | +# Required-Start: $network $local_fs $remote_fs | ||
14 | +# Required-Stop: $network $local_fs $remote_fs | ||
15 | +# Default-Start: 2 3 4 5 | ||
16 | +# Default-Stop: 0 1 6 | ||
17 | +# Short-Description: celery periodic task scheduler | ||
18 | +### END INIT INFO | ||
19 | + | ||
20 | +# Cannot use set -e/bash -e since the kill -0 command will abort | ||
21 | +# abnormally in the absence of a valid process ID. | ||
22 | +#set -e | ||
23 | +VERSION=10.1 | ||
24 | +echo "celery init v${VERSION}." | ||
25 | + | ||
26 | +if [ $(id -u) -ne 0 ]; then | ||
27 | + echo "Error: This program can only be used by the root user." | ||
28 | + echo " Unpriviliged users must use 'celery beat --detach'" | ||
29 | + exit 1 | ||
30 | +fi | ||
31 | + | ||
32 | + | ||
33 | +# May be a runlevel symlink (e.g. S02celeryd) | ||
34 | +if [ -L "$0" ]; then | ||
35 | + SCRIPT_FILE=$(readlink "$0") | ||
36 | +else | ||
37 | + SCRIPT_FILE="$0" | ||
38 | +fi | ||
39 | +SCRIPT_NAME="$(basename "$SCRIPT_FILE")" | ||
40 | + | ||
41 | +# /etc/init.d/celerybeat: start and stop the celery periodic task scheduler daemon. | ||
42 | + | ||
43 | +# Make sure executable configuration script is owned by root | ||
44 | +_config_sanity() { | ||
45 | + local path="$1" | ||
46 | + local owner=$(ls -ld "$path" | awk '{print $3}') | ||
47 | + local iwgrp=$(ls -ld "$path" | cut -b 6) | ||
48 | + local iwoth=$(ls -ld "$path" | cut -b 9) | ||
49 | + | ||
50 | + if [ "$(id -u $owner)" != "0" ]; then | ||
51 | + echo "Error: Config script '$path' must be owned by root!" | ||
52 | + echo | ||
53 | + echo "Resolution:" | ||
54 | + echo "Review the file carefully and make sure it has not been " | ||
55 | + echo "modified with mailicious intent. When sure the " | ||
56 | + echo "script is safe to execute with superuser privileges " | ||
57 | + echo "you can change ownership of the script:" | ||
58 | + echo " $ sudo chown root '$path'" | ||
59 | + exit 1 | ||
60 | + fi | ||
61 | + | ||
62 | + if [ "$iwoth" != "-" ]; then # S_IWOTH | ||
63 | + echo "Error: Config script '$path' cannot be writable by others!" | ||
64 | + echo | ||
65 | + echo "Resolution:" | ||
66 | + echo "Review the file carefully and make sure it has not been " | ||
67 | + echo "modified with malicious intent. When sure the " | ||
68 | + echo "script is safe to execute with superuser privileges " | ||
69 | + echo "you can change the scripts permissions:" | ||
70 | + echo " $ sudo chmod 640 '$path'" | ||
71 | + exit 1 | ||
72 | + fi | ||
73 | + if [ "$iwgrp" != "-" ]; then # S_IWGRP | ||
74 | + echo "Error: Config script '$path' cannot be writable by group!" | ||
75 | + echo | ||
76 | + echo "Resolution:" | ||
77 | + echo "Review the file carefully and make sure it has not been " | ||
78 | + echo "modified with malicious intent. When sure the " | ||
79 | + echo "script is safe to execute with superuser privileges " | ||
80 | + echo "you can change the scripts permissions:" | ||
81 | + echo " $ sudo chmod 640 '$path'" | ||
82 | + exit 1 | ||
83 | + fi | ||
84 | +} | ||
85 | + | ||
86 | +scripts="" | ||
87 | + | ||
88 | +if test -f /etc/default/celeryd; then | ||
89 | + scripts="/etc/default/celeryd" | ||
90 | + _config_sanity /etc/default/celeryd | ||
91 | + . /etc/default/celeryd | ||
92 | +fi | ||
93 | + | ||
94 | +EXTRA_CONFIG="/etc/default/${SCRIPT_NAME}" | ||
95 | +if test -f "$EXTRA_CONFIG"; then | ||
96 | + scripts="$scripts, $EXTRA_CONFIG" | ||
97 | + _config_sanity "$EXTRA_CONFIG" | ||
98 | + . "$EXTRA_CONFIG" | ||
99 | +fi | ||
100 | + | ||
101 | +echo "Using configuration: $scripts" | ||
102 | + | ||
103 | +CELERY_BIN=${CELERY_BIN:-"celery"} | ||
104 | +DEFAULT_USER="celery" | ||
105 | +DEFAULT_PID_FILE="/var/run/celery/beat.pid" | ||
106 | +DEFAULT_LOG_FILE="/var/log/celery/beat.log" | ||
107 | +DEFAULT_LOG_LEVEL="INFO" | ||
108 | +DEFAULT_CELERYBEAT="$CELERY_BIN beat" | ||
109 | + | ||
110 | +CELERYBEAT=${CELERYBEAT:-$DEFAULT_CELERYBEAT} | ||
111 | +CELERYBEAT_LOG_LEVEL=${CELERYBEAT_LOG_LEVEL:-${CELERYBEAT_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} | ||
112 | + | ||
113 | +# Sets --app argument for CELERY_BIN | ||
114 | +CELERY_APP_ARG="" | ||
115 | +if [ ! -z "$CELERY_APP" ]; then | ||
116 | + CELERY_APP_ARG="--app=$CELERY_APP" | ||
117 | +fi | ||
118 | + | ||
119 | +CELERYBEAT_USER=${CELERYBEAT_USER:-${CELERYD_USER:-$DEFAULT_USER}} | ||
120 | + | ||
121 | +# Set CELERY_CREATE_DIRS to always create log/pid dirs. | ||
122 | +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} | ||
123 | +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS | ||
124 | +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS | ||
125 | +if [ -z "$CELERYBEAT_PID_FILE" ]; then | ||
126 | + CELERYBEAT_PID_FILE="$DEFAULT_PID_FILE" | ||
127 | + CELERY_CREATE_RUNDIR=1 | ||
128 | +fi | ||
129 | +if [ -z "$CELERYBEAT_LOG_FILE" ]; then | ||
130 | + CELERYBEAT_LOG_FILE="$DEFAULT_LOG_FILE" | ||
131 | + CELERY_CREATE_LOGDIR=1 | ||
132 | +fi | ||
133 | + | ||
134 | +export CELERY_LOADER | ||
135 | + | ||
136 | +CELERYBEAT_OPTS="$CELERYBEAT_OPTS -f $CELERYBEAT_LOG_FILE -l $CELERYBEAT_LOG_LEVEL" | ||
137 | + | ||
138 | +if [ -n "$2" ]; then | ||
139 | + CELERYBEAT_OPTS="$CELERYBEAT_OPTS $2" | ||
140 | +fi | ||
141 | + | ||
142 | +CELERYBEAT_LOG_DIR=`dirname $CELERYBEAT_LOG_FILE` | ||
143 | +CELERYBEAT_PID_DIR=`dirname $CELERYBEAT_PID_FILE` | ||
144 | + | ||
145 | +# Extra start-stop-daemon options, like user/group. | ||
146 | + | ||
147 | +CELERYBEAT_CHDIR=${CELERYBEAT_CHDIR:-$CELERYD_CHDIR} | ||
148 | +if [ -n "$CELERYBEAT_CHDIR" ]; then | ||
149 | + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYBEAT_CHDIR" | ||
150 | +fi | ||
151 | + | ||
152 | + | ||
153 | +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" | ||
154 | + | ||
155 | +check_dev_null() { | ||
156 | + if [ ! -c /dev/null ]; then | ||
157 | + echo "/dev/null is not a character device!" | ||
158 | + exit 75 # EX_TEMPFAIL | ||
159 | + fi | ||
160 | +} | ||
161 | + | ||
162 | +maybe_die() { | ||
163 | + if [ $? -ne 0 ]; then | ||
164 | + echo "Exiting: $*" | ||
165 | + exit 77 # EX_NOPERM | ||
166 | + fi | ||
167 | +} | ||
168 | + | ||
169 | +create_default_dir() { | ||
170 | + if [ ! -d "$1" ]; then | ||
171 | + echo "- Creating default directory: '$1'" | ||
172 | + mkdir -p "$1" | ||
173 | + maybe_die "Couldn't create directory $1" | ||
174 | + echo "- Changing permissions of '$1' to 02755" | ||
175 | + chmod 02755 "$1" | ||
176 | + maybe_die "Couldn't change permissions for $1" | ||
177 | + if [ -n "$CELERYBEAT_USER" ]; then | ||
178 | + echo "- Changing owner of '$1' to '$CELERYBEAT_USER'" | ||
179 | + chown "$CELERYBEAT_USER" "$1" | ||
180 | + maybe_die "Couldn't change owner of $1" | ||
181 | + fi | ||
182 | + if [ -n "$CELERYBEAT_GROUP" ]; then | ||
183 | + echo "- Changing group of '$1' to '$CELERYBEAT_GROUP'" | ||
184 | + chgrp "$CELERYBEAT_GROUP" "$1" | ||
185 | + maybe_die "Couldn't change group of $1" | ||
186 | + fi | ||
187 | + fi | ||
188 | +} | ||
189 | + | ||
190 | +check_paths() { | ||
191 | + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then | ||
192 | + create_default_dir "$CELERYBEAT_LOG_DIR" | ||
193 | + fi | ||
194 | + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then | ||
195 | + create_default_dir "$CELERYBEAT_PID_DIR" | ||
196 | + fi | ||
197 | +} | ||
198 | + | ||
199 | + | ||
200 | +create_paths () { | ||
201 | + create_default_dir "$CELERYBEAT_LOG_DIR" | ||
202 | + create_default_dir "$CELERYBEAT_PID_DIR" | ||
203 | +} | ||
204 | + | ||
205 | + | ||
206 | +wait_pid () { | ||
207 | + pid=$1 | ||
208 | + forever=1 | ||
209 | + i=0 | ||
210 | + while [ $forever -gt 0 ]; do | ||
211 | + kill -0 $pid 1>/dev/null 2>&1 | ||
212 | + if [ $? -eq 1 ]; then | ||
213 | + echo "OK" | ||
214 | + forever=0 | ||
215 | + else | ||
216 | + kill -TERM "$pid" | ||
217 | + i=$((i + 1)) | ||
218 | + if [ $i -gt 60 ]; then | ||
219 | + echo "ERROR" | ||
220 | + echo "Timed out while stopping (30s)" | ||
221 | + forever=0 | ||
222 | + else | ||
223 | + sleep 0.5 | ||
224 | + fi | ||
225 | + fi | ||
226 | + done | ||
227 | +} | ||
228 | + | ||
229 | + | ||
230 | +stop_beat () { | ||
231 | + echo -n "Stopping ${SCRIPT_NAME}... " | ||
232 | + if [ -f "$CELERYBEAT_PID_FILE" ]; then | ||
233 | + wait_pid $(cat "$CELERYBEAT_PID_FILE") | ||
234 | + else | ||
235 | + echo "NOT RUNNING" | ||
236 | + fi | ||
237 | +} | ||
238 | + | ||
239 | +_chuid () { | ||
240 | + su "$CELERYBEAT_USER" -c "$CELERYBEAT $*" | ||
241 | +} | ||
242 | + | ||
243 | +start_beat () { | ||
244 | + echo "Starting ${SCRIPT_NAME}..." | ||
245 | + _chuid $CELERY_APP_ARG $CELERYBEAT_OPTS $DAEMON_OPTS --detach \ | ||
246 | + --pidfile="$CELERYBEAT_PID_FILE" | ||
247 | +} | ||
248 | + | ||
249 | + | ||
250 | +check_status () { | ||
251 | + local failed= | ||
252 | + local pid_file=$CELERYBEAT_PID_FILE | ||
253 | + if [ ! -e $pid_file ]; then | ||
254 | + echo "${SCRIPT_NAME} is up: no pid file found" | ||
255 | + failed=true | ||
256 | + elif [ ! -r $pid_file ]; then | ||
257 | + echo "${SCRIPT_NAME} is in unknown state, user cannot read pid file." | ||
258 | + failed=true | ||
259 | + else | ||
260 | + local pid=`cat "$pid_file"` | ||
261 | + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` | ||
262 | + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then | ||
263 | + echo "${SCRIPT_NAME}: bad pid file ($pid_file)" | ||
264 | + failed=true | ||
265 | + else | ||
266 | + local failed= | ||
267 | + kill -0 $pid 2> /dev/null || failed=true | ||
268 | + if [ "$failed" ]; then | ||
269 | + echo "${SCRIPT_NAME} (pid $pid) is down, but pid file exists!" | ||
270 | + failed=true | ||
271 | + else | ||
272 | + echo "${SCRIPT_NAME} (pid $pid) is up..." | ||
273 | + fi | ||
274 | + fi | ||
275 | + fi | ||
276 | + | ||
277 | + [ "$failed" ] && exit 1 || exit 0 | ||
278 | +} | ||
279 | + | ||
280 | + | ||
281 | +case "$1" in | ||
282 | + start) | ||
283 | + check_dev_null | ||
284 | + check_paths | ||
285 | + start_beat | ||
286 | + ;; | ||
287 | + stop) | ||
288 | + check_paths | ||
289 | + stop_beat | ||
290 | + ;; | ||
291 | + reload|force-reload) | ||
292 | + echo "Use start+stop" | ||
293 | + ;; | ||
294 | + status) | ||
295 | + check_status | ||
296 | + ;; | ||
297 | + restart) | ||
298 | + echo "Restarting celery periodic task scheduler" | ||
299 | + check_paths | ||
300 | + stop_beat | ||
301 | + check_dev_null | ||
302 | + start_beat | ||
303 | + ;; | ||
304 | + create-paths) | ||
305 | + check_dev_null | ||
306 | + create_paths | ||
307 | + ;; | ||
308 | + check-paths) | ||
309 | + check_dev_null | ||
310 | + check_paths | ||
311 | + ;; | ||
312 | + *) | ||
313 | + echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|create-paths|status}" | ||
314 | + exit 64 # EX_USAGE | ||
315 | + ;; | ||
316 | +esac | ||
317 | + | ||
318 | +exit 0 |
vagrant/provision.sh
@@ -36,15 +36,19 @@ sudo mkdir -p /etc/colab | @@ -36,15 +36,19 @@ sudo mkdir -p /etc/colab | ||
36 | sudo chown vagrant:vagrant /etc/colab | 36 | sudo chown vagrant:vagrant /etc/colab |
37 | 37 | ||
38 | if [ ! -s /etc/colab/settings.py ]; then | 38 | if [ ! -s /etc/colab/settings.py ]; then |
39 | - colab-init-config > /etc/colab/settings.py | 39 | + colab-admin initconfig > /etc/colab/settings.py |
40 | fi | 40 | fi |
41 | 41 | ||
42 | colab-admin migrate | 42 | colab-admin migrate |
43 | colab-admin loaddata /vagrant/tests/test_data.json | 43 | colab-admin loaddata /vagrant/tests/test_data.json |
44 | 44 | ||
45 | # Init.d Celery files | 45 | # Init.d Celery files |
46 | -sudo cp $basedir/vagrant/misc/etc/init.d/celeryd /etc/init.d/ | ||
47 | -sudo cp $basedir/vagrant/misc/etc/default/celeryd /etc/default/ | 46 | +sudo cp $basedir/vagrant/misc/etc/init.d/celery* /etc/init.d/ |
47 | +sudo cp $basedir/vagrant/misc/etc/default/celery* /etc/default/ | ||
48 | +sudo service celeryd stop || echo | ||
49 | +sudo service celerybeat stop || echo | ||
50 | +sleep 2 | ||
48 | sudo service celeryd start | 51 | sudo service celeryd start |
52 | +sudo service celerybeat start | ||
49 | 53 | ||
50 | colab-admin rebuild_index --noinput | 54 | colab-admin rebuild_index --noinput |
vagrant/ubuntu.sh
@@ -5,4 +5,4 @@ set -ex | @@ -5,4 +5,4 @@ set -ex | ||
5 | ### Install dependencies | 5 | ### Install dependencies |
6 | apt-get update | 6 | apt-get update |
7 | 7 | ||
8 | -apt-get install curl git unzip build-essential gettext libxml2-dev libxslt1-dev libssl-dev libffi-dev python-dev virtualenvwrapper python-pip rabbitmq-server -y | 8 | +apt-get install curl git unzip build-essential gettext libxml2-dev libxslt1-dev libssl-dev libffi-dev python-dev virtualenvwrapper python-pip redis-server -y |