Updated ROADMAP
This commit is contained in:
parent
ccf50d0515
commit
6874060fc3
63
ROADMAP.md
63
ROADMAP.md
|
@ -1,45 +1,58 @@
|
|||
# Roadmap
|
||||
|
||||
|
||||
### 1.0a1 Milestone (first alpha release on Sep '14)
|
||||
### 1.0a1 Milestone (first alpha release on Oct '14)
|
||||
|
||||
1. [x] Automated deployment of the development environment
|
||||
2. [x] Automated installation and upgrading
|
||||
2. [ ] Testing framework for running unittests and functional tests
|
||||
2. [ ] Continuous integration environment
|
||||
2. [x] Admin interface based on django.contrib.admin foundations
|
||||
3. [x] REST API based on django-rest-framework foundations
|
||||
2. [ ] Testing framework for running unittests and functional tests with LXC containers
|
||||
2. [ ] Continuous integration with Jenkins
|
||||
2. [x] Admin interface based on django.contrib.admin
|
||||
3. [x] REST API for users
|
||||
2. [x] [Orchestra-orm](https://github.com/glic3rinu/orchestra-orm) a Python library for easily interacting with the REST API
|
||||
3. [x] Service orchestration framework
|
||||
4. [ ] Data model, input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and some documentation of:
|
||||
1. [x] Web applications
|
||||
2. [ ] FTP accounts
|
||||
2. [ ] Databases
|
||||
1. [ ] Mail accounts, aliases, forwards
|
||||
1. [x] DNS
|
||||
1. [ ] Mailing lists
|
||||
4. [ ] Data model, input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and documentation of:
|
||||
1. [x] PHP/static Web applications
|
||||
1. [x] Websites with Apache
|
||||
2. [-] FTP/rsync/scp/shell system accounts
|
||||
2. [-] Databases and database users
|
||||
1. [-] Mail accounts, aliases, forwards with Postfix and Dovecot
|
||||
1. [x] DNS with Bind
|
||||
1. [-] Mailing lists with Mailman
|
||||
1. [x] Contact management and service contraction
|
||||
1. [ ] Object level permissions system
|
||||
1. [ ] Unittests of all the logic
|
||||
2. [ ] Functional tests of all Admin and REST interations
|
||||
1. [-] Unittests of the bussines logic logic
|
||||
2. [-] Functional tests of all Admin UI and REST interations
|
||||
1. [ ] Initial documentation
|
||||
|
||||
|
||||
### 1.0b1 Milestone (first beta release on Nov '14)
|
||||
### 1.0b1 Milestone (first beta release on Dec '14)
|
||||
|
||||
1. [x] Resource monitoring
|
||||
1. [ ] Orders
|
||||
2. [ ] Pricing
|
||||
3. [ ] Billing
|
||||
1. [ ] Payment methods
|
||||
2. [ ] Scheduling of service cancellations and deactivations
|
||||
1. [x] Resource allocation and monitoring
|
||||
1. [x] Order tracking
|
||||
2. [x] Service definition, service plans and pricing
|
||||
3. [-] Billing
|
||||
3. [x] Invoice
|
||||
3. [x] Membership fee
|
||||
3. [-] Amendment invoice
|
||||
3. [-] Amendment fee
|
||||
3. [x] Pro Forma
|
||||
3. [ ] Advanced bill handling (move lines, undo billing, ...)
|
||||
1. [x] Payment methods
|
||||
1. [x] SEPA Direct Debit
|
||||
2. [x] SEPA Credit Transfer
|
||||
1. [ ] Full documentation
|
||||
2. [-] Additional services
|
||||
2. [-] VPS with Proxmox/OpenVZ
|
||||
2. [-] SaaS (Software as a Service) Redmine/phpList/BSCW/Wordpress/Moodle/Drupal
|
||||
2. [x] Miscellaneous services
|
||||
2. [x] Issue tracking system
|
||||
|
||||
|
||||
### 1.0 Milestone (first stable release on Feb '15)
|
||||
### 1.0 Milestone (first stable release on Apr '15)
|
||||
|
||||
1. [ ] Stabilize data model, internal APIs and REST API
|
||||
1. [ ] Integration with third-party service providers, e.g. Gandi
|
||||
1. [ ] Support for additional services like VPS
|
||||
2. [ ] Issue tracking system
|
||||
3. [ ] Translation to Spanish and Catalan
|
||||
2. [ ] Scheduling of service cancellations and deactivations
|
||||
1. [ ] Object level permissions system
|
||||
2. [ ] API access for superusers
|
||||
|
|
18
TODO.md
18
TODO.md
|
@ -76,7 +76,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
|
||||
* help_text on readonly_fields specialy Bill.state. (eg. A bill is in OPEN state when bla bla )
|
||||
|
||||
|
||||
* Transaction states: CREATED, PROCESSED, EXECUTED, COMMITED, ABORTED (SECURED, REJECTED?)
|
||||
* bill.send() -> transacction.EXECUTED when source=None
|
||||
* transaction.secured() -> bill.paid when bill.total == transaction.value else Error
|
||||
|
@ -84,8 +83,8 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
* bill.bad_debt() -> transaction.ABORTED
|
||||
* transaction.ABORTED -> bill.bad_debt
|
||||
- Issue new transaction when current transaction is ABORTED
|
||||
* underescore *every* private function
|
||||
|
||||
* underescore *every* private function
|
||||
|
||||
* create log file at /var/log/orchestra.log and rotate
|
||||
|
||||
|
@ -94,7 +93,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
def register_on(self):
|
||||
return order.register_at.date()
|
||||
|
||||
|
||||
* mail backend related_models = ('resources__content_type') ??
|
||||
* ignore orders
|
||||
|
||||
|
@ -103,3 +101,17 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
* Domain backend PowerDNS Bind validation support?
|
||||
|
||||
* Maildir billing tests/ webdisk billing tests (avg metric)
|
||||
|
||||
* move icons to apps, and use appconfig to cleanup config stuff
|
||||
|
||||
* when using modeladmin to store shit like self.account, make sure to have a cleanslate in each request
|
||||
|
||||
*jabber with mailbox accounts (dovecto mail notification)
|
||||
|
||||
* rename accounts register to manager register
|
||||
|
||||
* make accounts django auth users
|
||||
- when an account is created a mirrored system user is created
|
||||
- system users are independent users, so they can have different passwords and all.
|
||||
|
||||
* take a look icons from ajenti ;)
|
||||
|
|
|
@ -4,6 +4,8 @@ from django.contrib import admin
|
|||
from django.contrib.admin.utils import unquote
|
||||
from django.forms.models import BaseInlineFormSet
|
||||
from django.shortcuts import render, redirect
|
||||
from django.utils.text import camel_case_to_spaces
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .utils import set_url_query, action_to_view, wrap_admin_view
|
||||
|
||||
|
@ -175,11 +177,24 @@ class SelectPluginAdminMixin(object):
|
|||
self.plugin_value = plugin_value
|
||||
if not plugin_value:
|
||||
self.plugin_value = self.plugin.get_plugins()[0]
|
||||
return super(SelectPluginAdminMixin, self).add_view(request,
|
||||
form_url=form_url, extra_context=extra_context)
|
||||
# TODO add plugin name on title
|
||||
context = {
|
||||
'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value),
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url,
|
||||
extra_context=context)
|
||||
return redirect('./select-plugin/?%s' % request.META['QUERY_STRING'])
|
||||
|
||||
def change_view(self, request, object_id, form_url='', extra_context=None):
|
||||
obj = self.get_object(request, unquote(object_id))
|
||||
plugin_value = getattr(obj, self.plugin_field)
|
||||
context = {
|
||||
'title': _("Change %s") % camel_case_to_spaces(plugin_value),
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
return super(SelectPluginAdminMixin, self).change_view(request, object_id,
|
||||
form_url=form_url, extra_context=context)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not change:
|
||||
setattr(obj, self.plugin_field, self.plugin_value)
|
||||
|
|
|
@ -64,11 +64,12 @@ class ResourceAdmin(ExtendedModelAdmin):
|
|||
|
||||
class ResourceDataAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'id', 'resource', 'used', 'allocated', 'updated_at', 'content_object_link'
|
||||
'id', 'resource_link', 'used', 'allocated', 'updated_at', 'content_object_link'
|
||||
)
|
||||
list_filter = ('resource',)
|
||||
readonly_fields = ('content_object_link',)
|
||||
|
||||
resource_link = admin_link('resource')
|
||||
content_object_link = admin_link('content_object')
|
||||
|
||||
def get_queryset(self, request):
|
||||
|
|
|
@ -18,6 +18,9 @@ class SaaS(models.Model):
|
|||
verbose_name = "SaaS"
|
||||
verbose_name_plural = "SaaS"
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s (%s)" % (self.description, self.service_class.verbose_name)
|
||||
|
||||
@cached_property
|
||||
def service_class(self):
|
||||
return SoftwareService.get_plugin(self.service)
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .options import SoftwareService, SoftwareServiceForm
|
||||
from orchestra.forms import PluginDataForm
|
||||
|
||||
from .options import SoftwareService
|
||||
|
||||
|
||||
class BSCWForm(SoftwareServiceForm):
|
||||
class BSCWForm(PluginDataForm):
|
||||
username = forms.CharField(label=_("Username"), max_length=64)
|
||||
password = forms.CharField(label=_("Password"), max_length=64)
|
||||
quota = forms.IntegerField(label=_("Quota"))
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .options import SoftwareService, SoftwareServiceForm
|
||||
from orchestra.forms import PluginDataForm
|
||||
|
||||
from .options import SoftwareService
|
||||
|
||||
|
||||
class GitLabForm(SoftwareServiceForm):
|
||||
class GitLabForm(PluginDataForm):
|
||||
username = forms.CharField(label=_("Username"), max_length=64)
|
||||
password = forms.CharField(label=_("Password"), max_length=64)
|
||||
project_name = forms.CharField(label=_("Project name"), max_length=64)
|
||||
email = forms.CharField(label=_("Email"), max_length=64)
|
||||
email = forms.EmailField(label=_("Email"))
|
||||
|
||||
|
||||
class GitLabService(SoftwareService):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.forms import PluginDataForm
|
||||
from orchestra.utils import plugins
|
||||
from orchestra.utils.functional import cached
|
||||
from orchestra.utils.python import import_class
|
||||
|
@ -9,17 +8,9 @@ from orchestra.utils.python import import_class
|
|||
from .. import settings
|
||||
|
||||
|
||||
class SoftwareServiceForm(PluginDataForm):
|
||||
username = forms.CharField(label=_("Username"), max_length=64)
|
||||
password = forms.CharField(label=_("Password"), max_length=64)
|
||||
|
||||
class Meta:
|
||||
exclude = ('data', 'service')
|
||||
|
||||
|
||||
class SoftwareService(plugins.Plugin):
|
||||
description_field = ''
|
||||
form = SoftwareServiceForm
|
||||
form = None
|
||||
serializer = None
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -4,4 +4,5 @@ from django.conf import settings
|
|||
SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
|
||||
'orchestra.apps.saas.services.bscw.BSCWService',
|
||||
'orchestra.apps.saas.services.gitlab.GitLabService',
|
||||
'orchestra.apps.saas.services.phplist.PHPListService',
|
||||
))
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
|
||||
{% if not is_popup %}
|
||||
<!-- Header -->
|
||||
{% block header-stetic %}<div id="header">{% endblock %}
|
||||
{% block header-stetic %}<div id="header"><div id="header-wrapper">{% endblock %}
|
||||
<div id="branding">
|
||||
{% block branding %}{% endblock %}
|
||||
{% block branding %}{% endblock %}
|
||||
</div>
|
||||
{% if user.is_active and user.is_staff %}
|
||||
<div id="user-tools">
|
||||
|
@ -69,7 +69,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% block nav-global %}{% endblock %}
|
||||
</div>
|
||||
</div></div>
|
||||
<!-- END Header -->
|
||||
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans 'Home' %}</a>{% if title %} › {{ title }}{% endif %}</div>{% endblock %}
|
||||
{% endif %}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div style="margin:20px;">
|
||||
<ul>
|
||||
{% for name, verbose in plugins %}
|
||||
<li><a href="../?{{ field }}={{ name }}&{{ request.META.QUERY_STRING }}">{{ verbose }}</<a></li>
|
||||
<li><a style="font-size:small;" href="../?{{ field }}={{ name }}&{{ request.META.QUERY_STRING }}">{{ verbose }}</<a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue