Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Refactoring part 2. New views, templates, classmethods in models etc.…

… New beta release is very close.
  • Loading branch information...
commit 2ef613aa67da3a04708d775340fb9f707159966f 1 parent b803957
authored
43  whiskers/__init__.py
@@ -2,6 +2,9 @@
2 2
 from sqlalchemy import engine_from_config
3 3
 
4 4
 from whiskers.models import initialize_sql
  5
+from whiskers.views.buildouts import BuildoutsView
  6
+from whiskers.views.hosts import HostsView
  7
+from whiskers.views.packages import PackagesView
5 8
 
6 9
 
7 10
 def main(global_config, **settings):
@@ -12,34 +15,36 @@ def main(global_config, **settings):
12 15
     config = Configurator(settings=settings)
13 16
     config.add_static_view('static', 'whiskers:static', cache_max_age=3600)
14 17
 
15  
-    config.add_route('home', '/')
  18
+    config.add_route('home', '/', request_method='GET')
  19
+    config.add_route('post_buildout', '/',
  20
+                     request_method='POST')
16 21
     config.add_view('whiskers.views.main.whiskers_view',
17 22
                     route_name='home',
18 23
                     renderer='views/templates/main.pt')
19 24
 
20  
-    config.add_route('buildouts', '/buildouts')
21  
-    # config.add_view('whiskers.views.buildout.buildouts_view',
22  
-    #                 route_name='buildouts',
23  
-    #                 renderer='views/templates/buildouts.pt')
  25
+    config.add_route('buildouts', '/buildouts', request_method="GET")
  26
+    config.add_view(BuildoutsView, route_name='buildouts',
  27
+                    renderer='views/templates/buildouts.pt')
24 28
 
25  
-    config.add_route('add_buildout', '/buildouts/add')
26  
-    # config.add_view('whiskers.views.buildout.add_buildout_view',
27  
-    #                 route_name='add')
  29
+    # BBB
  30
+    config.add_route('add_buildout', '/buildouts/add',
  31
+                     request_method='POST')
  32
+    config.add_view(BuildoutsView, route_name='post_buildout', attr="post")
  33
+    # BBB
  34
+    config.add_view(BuildoutsView, route_name='add_buildout', attr="post")
28 35
 
29 36
     config.add_route('buildout_view', '/buildouts/{buildout_id}')
30  
-    # config.add_view('whiskers.views.buildout.buildout_view',
31  
-    #                 route_name='buildout_view',
32  
-    #                 renderer='views/templates/buildout.pt')
  37
+    config.add_view(BuildoutsView, route_name='buildout_view',
  38
+                    renderer='views/templates/buildout.pt',
  39
+                    attr="buildout_view")
33 40
 
34 41
     config.add_route('packages', '/packages')
35  
-    config.add_view('whiskers.views.packages.packages_view',
36  
-                    route_name='packages',
  42
+    config.add_view(PackagesView, route_name='packages',
37 43
                     renderer='views/templates/packages.pt')
38 44
 
39 45
     config.add_route('package_view', '/packages/{package_name}*id')
40  
-    config.add_view('whiskers.views.package.package_view',
41  
-                    route_name='package_view',
42  
-                    renderer='views/templates/package.pt')
  46
+    config.add_view(PackagesView, route_name='package_view',
  47
+                    attr='package_view', renderer='views/templates/package.pt')
43 48
 
44 49
     config.add_route('about', '/about')
45 50
     config.add_view('whiskers.views.about.about_view',
@@ -47,11 +52,11 @@ def main(global_config, **settings):
47 52
                     renderer='views/templates/about.pt')
48 53
 
49 54
     config.add_route('hosts', '/hosts')
  55
+    config.add_view(HostsView, route_name='hosts',
  56
+                    renderer='views/templates/hosts.pt')
50 57
 
51 58
     config.add_route('host', '/hosts/{host_id}')
52  
-    config.add_view('whiskers.views.host.host_view',
53  
-                    route_name='host',
  59
+    config.add_view(HostsView, route_name='host', attr="host_view",
54 60
                     renderer='views/templates/host.pt')
55 61
 
56  
-    config.scan()
57 62
     return config.make_wsgi_app()
65  whiskers/jsonwrapper.py
... ...
@@ -0,0 +1,65 @@
  1
+import json
  2
+
  3
+
  4
+class JsonDataWrapper(object):
  5
+    """Wrapper for json-data."""
  6
+
  7
+    def __init__(self, data):
  8
+        self.data = json.loads(data)
  9
+
  10
+    @property
  11
+    def hostname(self):
  12
+        return self.data.get('hostname', None)
  13
+
  14
+    @property
  15
+    def name(self):
  16
+        return self.data.get('buildoutname', None) or\
  17
+            self.path.rsplit('/', 1)[-1]
  18
+
  19
+    @property
  20
+    def path(self):
  21
+        return self.data.get('directory', None)
  22
+
  23
+    @property
  24
+    def packages(self):
  25
+        for package in self.data['packages'].keys():
  26
+            package_dict = {
  27
+                'name': package,
  28
+                'requirements': self.data['packages'][package]['requirements'],
  29
+                'version': self.get_package_version(package)}
  30
+            yield package_dict
  31
+
  32
+    @property
  33
+    def executable(self):
  34
+        return self.data.get('executable', None)
  35
+
  36
+    @property
  37
+    def allow_picked_versions(self):
  38
+        return self.data.get('allow_picked_versions', None)
  39
+
  40
+    @property
  41
+    def newest(self):
  42
+        return self.data.get('newest', None)
  43
+
  44
+    @property
  45
+    def versionmap(self):
  46
+        return self.data.get('versionmap', None)
  47
+
  48
+    @property
  49
+    def config(self):
  50
+        tmp = self.data.copy()
  51
+        tmp.pop('packages')
  52
+        tmp.pop('versionmap')
  53
+        return json.dumps(tmp)
  54
+
  55
+    def get_package_version(self, package):
  56
+        """Return package version."""
  57
+
  58
+        if not self.data['packages'][package]['version']:
  59
+            try:
  60
+                version = self.versionmap[package['name']]
  61
+            except KeyError:
  62
+                version = 'stdlib'
  63
+        else:
  64
+            version = self.data['packages'][package]['version']
  65
+        return version
118  whiskers/models.py
... ...
@@ -1,12 +1,16 @@
  1
+from datetime import datetime
  2
+
1 3
 from sqlalchemy import (
2 4
     Column,
3 5
     Text,
4 6
     Integer,
  7
+    DateTime,
5 8
     Unicode,
6 9
     ForeignKey,
7 10
     Table)
8 11
 
9 12
 from sqlalchemy.ext.declarative import declarative_base
  13
+from sqlalchemy.orm.exc import NoResultFound
10 14
 
11 15
 from sqlalchemy.orm import (
12 16
     scoped_session,
@@ -30,11 +34,6 @@
30 34
     Column('package_id', Integer, ForeignKey('package.id'))
31 35
 )
32 36
 
33  
-# packageversion_table = Table('packageversion_table', Base.metadata,
34  
-#     Column('package_id', Integer, ForeignKey('package.id')),
35  
-#     Column('version_id', Integer, ForeignKey('version.id'))
36  
-# )
37  
-
38 37
 packagerequires_table = Table(
39 38
     'packagerequires_table',
40 39
     Base.metadata,
@@ -50,19 +49,41 @@ class Buildout(Base):
50 49
 
51 50
     id = Column(Integer, primary_key=True)
52 51
     name = Column(Unicode(255))
  52
+    datetime = Column(DateTime)
  53
+    checksum = Column(Integer, unique=True)
53 54
     host_id = Column(Integer, ForeignKey('host.id'))
54  
-    host = relationship("Host", backref=backref('buildouts', order_by=id))
  55
+    host = relationship("Host",
  56
+                        backref=backref('buildouts',
  57
+                                        order_by=datetime.desc()))
55 58
     packages = relationship("Package", secondary=buildoutpackage_table,
56  
-                            backref=backref('buildouts', order_by=id))
  59
+                            backref=backref('buildouts', order_by=name))
57 60
     config = Column(Text)
58 61
 
59  
-    def __init__(self, name, host, packages=None, config=None):
  62
+    def __init__(self, name, host, checksum, packages=None, config=None):
60 63
         self.name = name
61 64
         self.host = host
62 65
         if packages:
63 66
             self.packages = packages
64 67
         if config:
65 68
             self.config = config
  69
+        self.datetime = datetime.now()
  70
+        self.checksum = checksum
  71
+
  72
+    @classmethod
  73
+    def get_by_checksum(klass, checksum):
  74
+        query = DBSession.query(klass).\
  75
+            filter(klass.checksum == checksum).first()
  76
+        return query
  77
+
  78
+    @classmethod
  79
+    def by_name(klass):
  80
+        query = DBSession.query(klass).order_by(klass.name)
  81
+        return query
  82
+
  83
+    @classmethod
  84
+    def by_host(klass):
  85
+        query = DBSession.query(klass).order_by(klass.host)
  86
+        return query
66 87
 
67 88
 
68 89
 @implementer(interfaces.IHost)
@@ -76,6 +97,33 @@ class Host(Base):
76 97
     def __init__(self, name):
77 98
         self.name = name
78 99
 
  100
+    @classmethod
  101
+    def all_by_name(klass):
  102
+        query = DBSession.query(klass).order_by(klass.name)
  103
+        return query
  104
+
  105
+    @classmethod
  106
+    def get_by_name(klass, name):
  107
+        host = DBSession.query(klass).\
  108
+            join(klass.buildouts).\
  109
+            filter(klass.name == name).\
  110
+            order_by(klass.name).first()
  111
+        return host
  112
+
  113
+    @classmethod
  114
+    def get_by_id(klass, id):
  115
+        host = DBSession.query(klass).\
  116
+            join(klass.buildouts).\
  117
+            filter(klass.id == id).\
  118
+            order_by(klass.name)
  119
+        return host.one()
  120
+
  121
+    @classmethod
  122
+    def add(klass, hostname):
  123
+        host = klass(hostname)
  124
+        DBSession.add(host)
  125
+        return host
  126
+
79 127
 
80 128
 @implementer(interfaces.IPackage)
81 129
 class Package(Base):
@@ -95,7 +143,8 @@ class Package(Base):
95 143
         secondary=packagerequires_table,
96 144
         primaryjoin=id == packagerequires_table.c.package_id,
97 145
         secondaryjoin=id == packagerequires_table.c.required_package_id,
98  
-        backref="required_by")
  146
+        backref="required_by",
  147
+        order_by=name)
99 148
 
100 149
     def __init__(self, name, version=None, requires=None):
101 150
         self.name = name
@@ -109,6 +158,45 @@ def __init__(self, name, version=None, requires=None):
109 158
                 # We don't know what requires is so we just ignore it
110 159
                 pass
111 160
 
  161
+    @classmethod
  162
+    def get_packages_by_name(klass, name):
  163
+        """Return package filtered by name."""
  164
+        try:
  165
+            query = DBSession.query(klass).join(Version).\
  166
+                filter(klass.name == name).\
  167
+                order_by(Version.version.desc())
  168
+            return query
  169
+        except NoResultFound:
  170
+            return None
  171
+
  172
+    @classmethod
  173
+    def by_name(klass):
  174
+        """Return packages grouped and ordered by name."""
  175
+        query = DBSession.query(klass).group_by(klass.name).\
  176
+            order_by(klass.name)
  177
+        return query
  178
+
  179
+    @classmethod
  180
+    def get_by_nameversion(klass, name, version=None):
  181
+        query = DBSession.query(klass).join(klass.version).\
  182
+            filter(klass.name == name)
  183
+        if version:
  184
+            query = query.filter(Version.version == version)
  185
+        return query.first()
  186
+
  187
+    @classmethod
  188
+    def get_by_id(klass, id):
  189
+        package = DBSession.query(klass).filter_by(id)
  190
+
  191
+        if package.count():
  192
+            return package.first().id
  193
+
  194
+    @classmethod
  195
+    def add(klass, name, version=None, requires=None):
  196
+        package = klass(name, version=version, requires=requires)
  197
+        DBSession.add(package)
  198
+        return package
  199
+
112 200
 
113 201
 @implementer(interfaces.IVersion)
114 202
 class Version(Base):
@@ -125,6 +213,18 @@ def __init__(self, version, equation=None):
125 213
         if equation:
126 214
             self.equation = equation
127 215
 
  216
+    @classmethod
  217
+    def get_by_version(klass, version):
  218
+        version = DBSession.query(klass).\
  219
+            filter(klass.version == version).first()
  220
+        return version
  221
+
  222
+    @classmethod
  223
+    def add(klass, version, equation):
  224
+        version = klass(version, equation=equation)
  225
+        DBSession.add(version)
  226
+        return version
  227
+
128 228
 
129 229
 def initialize_sql(engine):
130 230
     DBSession.configure(bind=engine)
22  whiskers/scripts/initializedb.py
... ...
@@ -1,21 +1,21 @@
1 1
 import os
2 2
 import sys
3  
-import transaction
  3
+# import transaction
4 4
 
5 5
 from sqlalchemy import engine_from_config
6 6
 
7 7
 from pyramid.paster import (
8 8
     get_appsettings,
9 9
     setup_logging,
10  
-    )
  10
+)
11 11
 
12 12
 from ..models import (
13 13
     DBSession,
14  
-    Host,
15  
-    Buildout,
  14
+    # Host,
  15
+    # Buildout,
16 16
     # MyModel,
17 17
     Base,
18  
-    )
  18
+)
19 19
 
20 20
 
21 21
 def usage(argv):
@@ -34,9 +34,9 @@ def main(argv=sys.argv):
34 34
     engine = engine_from_config(settings, 'sqlalchemy.')
35 35
     DBSession.configure(bind=engine)
36 36
     Base.metadata.create_all(engine)
37  
-    with transaction.manager:
38  
-        model = Host('latitude')
39  
-        DBSession.add(model)
40  
-        buildout = Buildout('testbuildout', '/path/to/buildout', host=model,
41  
-                            packages=[])
42  
-        DBSession.add(buildout)
  37
+    # with transaction.manager:
  38
+    #     model = Host('latitude')
  39
+    #     DBSession.add(model)
  40
+    #     buildout = Buildout('testbuildout', '/path/to/buildout', host=model,
  41
+    #                         packages=[])
  42
+    #     DBSession.add(buildout)
9  whiskers/static/whiskers.css
@@ -29,12 +29,3 @@ body {
29 29
 .package-details a.all-versions {
30 30
     font-size: 80%;
31 31
 }
32  
-
33  
-.package-details ul, p {
34  
-    margin-left: 20pt;
35  
-    margin-top: 5pt;
36  
-}
37  
-
38  
-.footer {
39  
-    background-color: #ccc;
40  
-}
73  whiskers/tests/test_views.py
... ...
@@ -1,6 +1,5 @@
1 1
 import transaction
2 2
 import unittest
3  
-import json
4 3
 import os
5 4
 
6 5
 from pyramid import testing
@@ -101,52 +100,26 @@ def get_testjson(self):
101 100
 class PackagesViewTests(unittest.TestCase):
102 101
 
103 102
     def setUp(self):
104  
-        self.session = _initTestinDB()
105  
-# class AddBuildoutTests(unittest.TestCase):
106  
-#     def setUp(self):
107  
-#         self.session = _initTestingDB()
108  
-#         self.config = testing.setUp()
109  
-#
110  
-#     def tearDown(self):
111  
-#         self.session.remove()
112  
-#         testing.tearDown()
113  
-#
114  
-#     def _callFUT(self, request):
115  
-#         from whiskers.views import add_buildout_view
116  
-#         return add_buildout_view(request)
117  
-#
118  
-#     def add_buildout(self, test_data=test_data):
119  
-#         _registerRoutes(self.config)
120  
-#         request = testing.DummyRequest()
121  
-#         request.params = {'data': json.dumps(test_data)}
122  
-#         self._callFUT(request)
123  
-#
124  
-#     def test_it_nodata(self):
125  
-#         _registerRoutes(self.config)
126  
-#         request = testing.DummyRequest()
127  
-#         info = self._callFUT(request)
128  
-#         self.assertEqual(info.status, u'200 OK')
129  
-#         self.assertEqual(info.text, u'No data. Nothing added.')
130  
-#
131  
-#     def test_it_submitted(self):
132  
-#         self.add_buildout()
133  
-#         from whiskers.models import Buildout
134  
-#         buildout = self.session.query(Buildout).filter_by(name='test').one()
135  
-#         self.assertEqual(buildout.name, u'test')
136  
-#         packages = [i.name for i in buildout.packages]
137  
-#         for p in ['distribute', 'nose', 'zc.buildout', 'zc.recipe.egg']:
138  
-#             self.assertTrue(p in packages)
139  
-#
140  
-#     def test_update_data(self):
141  
-#         from whiskers.models import Buildout
142  
-#         # First we add default data and check it's there
143  
-#         self.add_buildout()
144  
-#         buildout = self.session.query(Buildout).filter_by(name='test').one()
145  
-#         packages = [(i.name, i.version.version) for i in buildout.packages]
146  
-#         self.assertTrue((u'distribute', u'0.6.24') in packages)
147  
-#         # Lets update our data
148  
-#         test_data['packages'][0]['version'] = '0.6.25'
149  
-#         self.add_buildout(test_data)
150  
-#         buildout = self.session.query(Buildout).filter_by(name='test').one()
151  
-#         packages = [(i.name, i.version.version) for i in buildout.packages]
152  
-#         self.assertTrue((u'distribute', u'0.6.25') in packages)
  103
+        self.session = _initTestingDB()
  104
+
  105
+    def _createDummyContent(self):
  106
+        from whiskers.models import Package, Version
  107
+        version = Version('1.0')
  108
+        package = Package('req-package-1', version)
  109
+        self.session.add(package)
  110
+        self.session.flush()
  111
+        transaction.commit()
  112
+
  113
+    def _callFUT(self, request):
  114
+        from whiskers.views.packages import PackagesView
  115
+        return PackagesView(request).package_view()
  116
+
  117
+    def test_it(self):
  118
+        from sqlalchemy import create_engine
  119
+        engine = create_engine('sqlite:///:memory:')
  120
+        self._callFUT(engine)
  121
+        self._createDummyContent()
  122
+        _registerRoutes(self.config)
  123
+        request = testing.DummyRequest()
  124
+        info = self._callFUT(request)
  125
+        import pdb; pdb.set_trace()
284  whiskers/views/buildout.py
... ...
@@ -1,284 +0,0 @@
1  
-import json
2  
-# import transaction
3  
-
4  
-from pyramid.view import view_config
5  
-from pyramid.response import Response
6  
-from pyramid.renderers import get_renderer
7  
-from sqlalchemy.orm.exc import NoResultFound
8  
-from whiskers.models import DBSession
9  
-from whiskers.models import Buildout, Package, Host, Version
10  
-from whiskers.views.version import get_version
11  
-from whiskers.views.package import get_package
12  
-
13  
-
14  
-class BuildoutView(object):
15  
-    """Buildout views."""
16  
-
17  
-    def __init__(self, request):
18  
-        self.main = get_renderer(
19  
-            'whiskers:views/templates/master.pt').implementation()
20  
-        self.request = request
21  
-        self.session = DBSession()
22  
-
23  
-    @view_config(route_name='buildouts')
24  
-    def buildouts_view(self):
25  
-        """Main view for whiskers/buildouts."""
26  
-
27  
-        return {'buildouts': self.buildouts,
28  
-                'project': 'whiskers',
29  
-                'main': self.main}
30  
-
31  
-    @property
32  
-    def buildouts(self):
33  
-        """Return all buildouts"""
34  
-        return self.session.query(Buildout).order_by(Buildout.name).all()
35  
-
36  
-    @view_config(route_name='add_buildout')
37  
-    def add_buildout_view(self):
38  
-        """Add a new buildout to database."""
39  
-
40  
-        try:
41  
-            self.jsondata = JsonDataWrapper(self.request.params['data'])
42  
-        except KeyError:
43  
-            return Response('No data. Nothing added.')
44  
-
45  
-        host = self.get_host(self.jsondata.hostname)
46  
-
47  
-        if host:
48  
-            buildout = self.get_existing_buildout(self.jsondata)
49  
-            if buildout:
50  
-                # self.update(buildout, data)
51  
-                pass
52  
-            else:
53  
-                self.add_buildout(self.jsondata, host)
54  
-        else:
55  
-            host = self.add_host(self.jsondata.hostname)
56  
-            buildout = self.add_buildout(self.jsondata, host)
57  
-
58  
-        return Response('OK. Added buildout')
59  
-
60  
-    def add_package(self, data):
61  
-        package = Package(data['name'])
62  
-
63  
-        version = self.get_version(data)
64  
-        package.version = version
65  
-
66  
-        if 'requirements' in data:
67  
-            requirements = []
68  
-            for req in data['requirements']:
69  
-                requirements.append(self.get_package(req))
70  
-            package.requires = requirements
71  
-
72  
-        self.session.add(package)
73  
-        return package
74  
-
75  
-    def get_package(self, package):
76  
-        try:
77  
-            if not 'version' in package:
78  
-                try:
79  
-                    version = self.jsondata.versionmap[package['name']]
80  
-                except KeyError:
81  
-                    version = 'stdlib'
82  
-            else:
83  
-                version = package['version']
84  
-
85  
-            package = self.session.query(Package).join(Package.version).\
86  
-                filter(Package.name == package['name']).\
87  
-                filter(Version.version == version).one()
88  
-        except NoResultFound:
89  
-            package = self.add_package(package)
90  
-
91  
-        return package
92  
-
93  
-    def get_version(self, data):
94  
-        try:
95  
-            if not 'version' in data:
96  
-                try:
97  
-                    data['version'] = self.jsondata.versionmap[data['name']]
98  
-                except KeyError:
99  
-                    # maybe we have standardlib requirement (eg. unittest2)
100  
-                    data['version'] = 'stdlib'
101  
-            version = self.session.query(Version).filter(
102  
-                Version.version == data['version']).one()
103  
-        except NoResultFound:
104  
-            version = self.add_version(data)
105  
-
106  
-        return version
107  
-
108  
-    def add_version(self, data):
109  
-        version = Version(data['version'])
110  
-        if 'equation' in data:
111  
-            version.equation = data['equation']
112  
-        self.session.add(version)
113  
-
114  
-        return version
115  
-
116  
-    def add_host(self, hostname):
117  
-        host = Host(hostname)
118  
-        self.session.add(host)
119  
-        return host
120  
-
121  
-    def add_buildout(self, data, host):
122  
-        packages = []
123  
-
124  
-        for package in data.packages:
125  
-            packages.append(self.get_package(package))
126  
-
127  
-        buildout = Buildout(data.name, data.path, host, packages,
128  
-                            data.config)
129  
-        self.session.add(buildout)
130  
-        return buildout
131  
-
132  
-    def get_host(self, hostname):
133  
-        """Return host object if found."""
134  
-        try:
135  
-            host = self.session.query(Host).filter(Host.name == hostname).one()
136  
-            return host
137  
-        except NoResultFound:
138  
-            return None
139  
-
140  
-    def get_existing_buildout(self, data):
141  
-        """Return buildout if it already exists in db."""
142  
-        try:
143  
-            buildout = self.session.query(Host).join(Host.buildouts).\
144  
-                filter(Host.name == data.hostname).\
145  
-                filter(Buildout.name == data.name).one()
146  
-            return buildout
147  
-        except NoResultFound:
148  
-            return None
149  
-
150  
-    # def add(self, data):
151  
-    #     """Add buildout to db."""
152  
-
153  
-    #     if packages:
154  
-    #         packages_list = []
155  
-    #         for package in packages:
156  
-    #             requires = []
157  
-    #             if package.get('requires'):
158  
-    #                 for req in package['requires']:
159  
-    #                     pass
160  
-
161  
-    #     buildout = Buildout(name, path, hostname, packages)
162  
-    #     return buildout
163  
-
164  
-    @view_config(route_name='buildout_view',
165  
-                 renderer='templates/buildout.pt')
166  
-    def get(self):
167  
-        """Return a buildout specified by buildout_id."""
168  
-        buildout_id = self.request.matchdict['buildout_id']
169  
-        buildout = self.session.query(Buildout).filter_by(
170  
-            id=int(buildout_id)).one()
171  
-        packages = self.session.query(Package).join(Package.buildouts).filter(
172  
-            Buildout.id == buildout_id).order_by(Package.name).all()
173  
-        config = json.loads(buildout.config)
174  
-
175  
-        return {'buildout': buildout, 'main': self.main, 'config': config}
176  
-
177  
-    def update(self, buildout_id):
178  
-        """Update existing buildout."""
179  
-        # Update buildout path information
180  
-        # path = data.get('buildoutpath', None)
181  
-        # if path:
182  
-        #     buildout.path = path
183  
-
184  
-        # # Update hostname information
185  
-        # hostname = data.get('hostname', None)
186  
-        # if hostname:
187  
-        #     buildout.hostname = hostname
188  
-
189  
-        # # Update packages used by buildout
190  
-        # packages = data.get('packages', None)
191  
-        # if packages:
192  
-        #     buildout.packages = update_buildout_packages(buildout, packages)
193  
-        pass
194  
-
195  
-    def delete(self, buildout_id):
196  
-        """Delete existing buildout."""
197  
-        pass
198  
-
199  
-    def get_buildouts(self):
200  
-        """Return all buildouts from database."""
201  
-        pass
202  
-
203  
-
204  
-def update_existing_buildout(buildout, data):
205  
-    """Updates buildout information"""
206  
-
207  
-    # Update buildout path information
208  
-    path = data.get('buildoutpath', None)
209  
-    if path:
210  
-        buildout.path = path
211  
-
212  
-    # Update hostname information
213  
-    hostname = data.get('hostname', None)
214  
-    if hostname:
215  
-        buildout.hostname = hostname
216  
-
217  
-    # Update packages used by buildout
218  
-    packages = data.get('packages', None)
219  
-    if packages:
220  
-        buildout.packages = update_buildout_packages(buildout, packages)
221  
-
222  
-
223  
-def update_buildout_packages(buildout, packages):
224  
-    """Update buildout packages"""
225  
-
226  
-    packages_list = []
227  
-
228  
-    for package in packages:
229  
-        version_id = get_version(package['version'])
230  
-        package_id = get_package(package['name'], version_id)
231  
-        packages_list.append(package_id)
232  
-
233  
-    return packages_list
234  
-
235  
-
236  
-class JsonDataWrapper(object):
237  
-    """Wrapper for json-data."""
238  
-
239  
-    def __init__(self, data):
240  
-        self.data = json.loads(data)
241  
-
242  
-    @property
243  
-    def hostname(self):
244  
-        return self.data.get('hostname', None)
245  
-
246  
-    @property
247  
-    def name(self):
248  
-        return self.data.get('buildoutname', None) or\
249  
-            self.path.rsplit('/', 1)[-1]
250  
-
251  
-    @property
252  
-    def path(self):
253  
-        return self.data.get('directory', None)
254  
-
255  
-    @property
256  
-    def packages(self):
257  
-        for package in self.data['packages'].keys():
258  
-            yield {'name': package,
259  
-                   'version': self.data['packages'][package]['version'],
260  
-                   'requirements':
261  
-                   self.data['packages'][package]['requirements']}
262  
-
263  
-    @property
264  
-    def executable(self):
265  
-        return self.data.get('executable', None)
266  
-
267  
-    @property
268  
-    def allow_picked_versions(self):
269  
-        return self.data.get('allow_picked_versions', None)
270  
-
271  
-    @property
272  
-    def newest(self):
273  
-        return self.data.get('newest', None)
274  
-
275  
-    @property
276  
-    def versionmap(self):
277  
-        return self.data.get('versionmap', None)
278  
-
279  
-    @property
280  
-    def config(self):
281  
-        tmp = self.data.copy()
282  
-        tmp.pop('packages')
283  
-        tmp.pop('versionmap')
284  
-        return json.dumps(tmp)
140  whiskers/views/buildouts.py
... ...
@@ -0,0 +1,140 @@
  1
+import json
  2
+import zlib
  3
+import logging
  4
+
  5
+from datetime import datetime
  6
+
  7
+from pyramid.response import Response
  8
+from pyramid.renderers import get_renderer
  9
+from whiskers.jsonwrapper import JsonDataWrapper
  10
+from whiskers.models import (
  11
+    Buildout,
  12
+    Package,
  13
+    Host,
  14
+    Version,
  15
+    DBSession)
  16
+
  17
+
  18
+class BuildoutsView(object):
  19
+    """Buildout views."""
  20
+
  21
+    def __init__(self, request):
  22
+        self.main = get_renderer(
  23
+            'whiskers:views/templates/master.pt').implementation()
  24
+        self.request = request
  25
+
  26
+    def __call__(self):
  27
+        """Main view for whiskers/buildouts."""
  28
+
  29
+        buildouts = self.get_buildouts_info()
  30
+
  31
+        return {'buildouts': buildouts,
  32
+                'project': 'whiskers',
  33
+                'main': self.main}
  34
+
  35
+    def get_buildouts_info(self):
  36
+        """Return list of dicts containing Buildout info."""
  37
+
  38
+        query = DBSession.query(Buildout).join(Buildout.host).\
  39
+            group_by(Buildout.name).order_by(Buildout.datetime).\
  40
+            all()
  41
+
  42
+        return query
  43
+
  44
+    def post(self):
  45
+        """Add a new buildout to database."""
  46
+        try:
  47
+            data = self.request.params['data']
  48
+            incoming = data.encode('utf-8')
  49
+            checksum = zlib.adler32(incoming)
  50
+            checksum_buildout = Buildout.get_by_checksum(checksum)
  51
+            if checksum_buildout:
  52
+                logging.info("Checksum matched")
  53
+                logging.info("Updating datetime..")
  54
+                checksum_buildout.datetime = datetime.now()
  55
+                DBSession.flush()
  56
+                raise Exception("No changes with existing data.")
  57
+            logging.info("New checksum")
  58
+            jsondata = JsonDataWrapper(data)
  59
+        except KeyError:
  60
+            return Response('No data. Nothing added.')
  61
+        except Exception as e:
  62
+            return Response(str(e))
  63
+
  64
+        host = Host.get_by_name(jsondata.hostname)
  65
+
  66
+        if not host:
  67
+            host = Host.add(jsondata.hostname)
  68
+
  69
+        self.add_buildout(jsondata, host, checksum)
  70
+
  71
+        return Response('Added buildout info to Whiskers.')
  72
+
  73
+    def add_buildout(self, data, host, checksum):
  74
+        packages = []
  75
+
  76
+        for package_info in data.packages:
  77
+            package = Package.get_by_nameversion(package_info['name'],
  78
+                                                 package_info['version'])
  79
+            if not package:
  80
+                equation = package_info.get('equation', None)
  81
+                version = Version.get_by_version(package_info['version']) or\
  82
+                    Version.add(package_info['version'], equation)
  83
+                requirements = self.get_requirements(
  84
+                    package_info['requirements'])
  85
+                package = Package.add(package_info['name'],
  86
+                                      version,
  87
+                                      requirements)
  88
+
  89
+            packages.append(package)
  90
+
  91
+        buildout = Buildout(data.name, host, checksum, packages,
  92
+                            data.config)
  93
+
  94
+        DBSession.add(buildout)
  95
+        return buildout
  96
+
  97
+    def get_requirements(self, requirements):
  98
+        """Return list of package requirements."""
  99
+        packages = []
  100
+
  101
+        for req in requirements:
  102
+            name = req.get('name')
  103
+            version = req.get('version')
  104
+            package = Package.get_by_nameversion(name,
  105
+                                                 version)
  106
+            if not package:
  107
+                equation = req.get('equation', None)
  108
+                version = req.get('version', 'stdlib')
  109
+                version = Version.get_by_version(version) or\
  110
+                    Version.add(version, equation)
  111
+                package = Package.add(req['name'], version)
  112
+            packages.append(package)
  113
+
  114
+        return packages
  115
+
  116
+    def buildout_view(self):
  117
+        """Return a buildout specified by buildout_id."""
  118
+        buildout_id = self.request.matchdict['buildout_id']
  119
+        buildout = DBSession.query(Buildout).filter_by(
  120
+            id=int(buildout_id)).one()
  121
+
  122
+        new_buildouts = DBSession.query(Buildout).join(Buildout.host).\
  123
+            filter(Buildout.host == buildout.host,
  124
+                   Buildout.name == buildout.name,
  125
+                   Buildout.id != buildout_id,
  126
+                   Buildout.datetime > buildout.datetime).\
  127
+            order_by(Buildout.datetime).all()
  128
+
  129
+        older_buildouts = DBSession.query(Buildout).join(Buildout.host).\
  130
+            filter(Buildout.host == buildout.host,
  131
+                   Buildout.name == buildout.name,
  132
+                   Buildout.datetime < buildout.datetime,
  133
+                   Buildout.id != buildout_id).\
  134
+            order_by(Buildout.datetime).all()
  135
+
  136
+        config = json.loads(buildout.config)
  137
+
  138
+        return {'buildout': buildout, 'main': self.main, 'config': config,
  139
+                'older_buildouts': older_buildouts,
  140
+                'new_buildouts': new_buildouts}
21  whiskers/views/host.py
... ...
@@ -1,11 +1,16 @@
1 1
 from pyramid.renderers import get_renderer
2  
-from whiskers.models import (DBSession,
3  
-                             Host)
  2
+from whiskers.models import DBSession
  3
+from whiskers.models import Host
4 4
 
5 5
 
6  
-def host_view(request):
7  
-    main = get_renderer('whiskers:views/templates/master.pt').implementation()
8  
-    session = DBSession()
9  
-    host_id = request.matchdict['host_id']
10  
-    host = session.query(Host).filter_by(id=int(host_id)).one()
11  
-    return {'host': host, 'main': main}
  6
+class HostView(object):
  7
+
  8
+    def __init__(self, request):
  9
+        self.main = get_renderer(
  10
+            'whiskers:views/templates/master.pt').implementation()
  11
+        self.request = request
  12
+
  13
+    def __call__(self):
  14
+        host_id = self.request.matchdict['host_id']
  15
+        host = DBSession.query(Host).filter_by(id=int(host_id)).one()
  16
+        return {'host': host, 'main': self.main}
49  whiskers/views/hosts.py
... ...
@@ -1,15 +1,10 @@
1  
-import json
2  
-
3  
-from pyramid.view import view_config
4  
-from pyramid.response import Response
  1
+from sqlalchemy.sql.expression import func
5 2
 from pyramid.renderers import get_renderer
6  
-from sqlalchemy.orm.exc import NoResultFound
7 3
 from whiskers.models import DBSession
8  
-from whiskers.models import Host
  4
+from whiskers.models import Host, Buildout
9 5
 
10 6
 
11 7
 class HostsView(object):
12  
-    """Hosts views."""
13 8
 
14 9
     def __init__(self, request):
15 10
         self.main = get_renderer(
@@ -17,20 +12,28 @@ def __init__(self, request):
17 12
         self.request = request
18 13
         self.session = DBSession()
19 14
 
20  
-    @view_config(route_name='hosts',
21  
-                 renderer='templates/hosts.pt')
22  
-    def hosts_view(self):
  15
+    def __call__(self):
23 16
         """Hosts main view."""
24  
-        hosts = self.get_hosts()
25  
-        return {'hosts': hosts, 'project': 'whiskers', 'main': self.main}
26  
-
27  
-    def get_host(self, host):
28  
-        """Return host"""
29  
-        result = DBSession.query(Host).filter_by(name=host)
30  
-        if result.count() > 0:
31  
-            return result
32  
-        else:
33  
-            return None
34  
-
35  
-    def get_hosts(self):
36  
-        return self.session.query(Host).order_by(Host.name).all()
  17
+        query = self.get_hosts_info()
  18
+        return {'results': query, 'main': self.main}
  19
+
  20
+    def host_view(self):
  21
+        host_id = self.request.matchdict['host_id']
  22
+        host = Host.get_by_id(int(host_id))
  23
+        return {'host': host, 'main': self.main}
  24
+
  25
+    def get_hosts_info(self):
  26
+        """Return list of dicts containing Host info."""
  27
+
  28
+        result_list = []
  29
+
  30
+        query = DBSession.query(Host, func.count(Buildout.id)).join(Buildout).\
  31
+            filter(Host.id == Buildout.host).all()
  32
+
  33
+        for result in query:
  34
+            tmp = {}
  35
+            tmp['host'] = result[0]
  36
+            tmp['count'] = result[1]
  37
+            result_list.append(tmp)
  38
+
  39
+        return result_list
60  whiskers/views/package.py
... ...
@@ -1,60 +0,0 @@
1  
-from whiskers.models import DBSession
2  
-from whiskers.models import Package
3  
-from pyramid.renderers import get_renderer
4  
-from pkg_resources import parse_version
5  
-
6  
-
7  
-def package_view(request):
8  
-    main = get_renderer('whiskers:views/templates/master.pt').implementation()
9  
-    package_name = request.matchdict['package_name']
10  
-    package_id = request.matchdict['id']
11  
-
12  
-    results = DBSession.query(Package).filter(
13  
-        Package.name == package_name)
14  
-    packages = results.all()
15  
-    packages = sort_versionnumbers(packages)
16  
-    requires = None
17  
-
18  
-    if len(package_id) > 0:
19  
-        package = results.filter_by(id=int(package_id[0])).first()
20  
-        requires = package.requires
21  
-    else:
22  
-        package = None
23  
-    if results.count() > 1:
24  
-        other_versions = True
25  
-    else:
26  
-        other_versions = False
27  
-
28  
-    return {'packages': packages, 'package': package,
29  
-            'package_name': package_name, 'main': main,
30  
-            'other_versions': other_versions,
31  
-            'requires': requires}
32  
-
33  
-
34  
-def sort_versionnumbers(packages):
35  
-    try:
36  
-        packages.sort(key=lambda x: [
37  
-            parse_version(x.version.version)])
38  
-    except AttributeError:
39  
-        pass
40  
-    return packages
41  
-
42  
-
43  
-def get_package(name, version):
44  
-    """Returns package id"""
45  
-
46  
-    package = DBSession.query(Package).filter_by(name).\
47  
-            filter_by(version=version)
48  
-
49  
-    if package.count():
50  
-        return package.first().id
51  
-    else:
52  
-        new_package_id = add_package(name, version)
53  
-        return new_package_id
54  
-
55  
-
56  
-def add_package(name, version):
57  
-    """Adds new package and returns its id"""
58  
-
59  
-    package = Package(name, version)
60  
-    return package.id
42  whiskers/views/packages.py
... ...
@@ -1,15 +1,39 @@
1  
-from whiskers.models import DBSession
2 1
 from whiskers.models import Package
3 2
 from pyramid.renderers import get_renderer
4 3
 
5 4
 
6  
-def packages_view(request):
7  
-    main = get_renderer('whiskers:views/templates/master.pt').implementation()
8  
-    packages = get_packages()
9  
-    return {'packages': packages, 'project': 'whiskers', 'main': main}
  5
+class PackagesView(object):
10 6
 
  7
+    def __init__(self, request):
  8
+        self.request = request
  9
+        self.main = get_renderer(
  10
+            'whiskers:views/templates/master.pt').implementation()
11 11
 
12  
-def get_packages():
13  
-    """Return packages"""
14  
-    session = DBSession()
15  
-    return session.query(Package).group_by(Package.name).all()
  12
+    def __call__(self):
  13
+        """Main view for packages."""
  14
+        packages = Package.by_name()
  15
+        return {'packages': packages, 'project': 'whiskers', 'main': self.main}
  16
+
  17
+    def package_view(self):
  18
+        """View for individual package."""
  19
+        package_name = self.request.matchdict.get('package_name', None)
  20
+        package_id = self.request.matchdict.get('id', None)
  21
+
  22
+        packages = Package.get_packages_by_name(package_name)
  23
+        requires = None
  24
+        other_versions = False
  25
+
  26
+        if len(package_id) > 0:
  27
+            package = packages.filter(Package.id == int(package_id[0])).first()
  28
+            if package and package.requires:
  29
+                requires = package.requires
  30
+        else:
  31
+            package = None
  32
+
  33
+        if packages.count() > 1:
  34
+            other_versions = True
  35
+
  36
+        return {'packages': packages.all(), 'package': package,
  37
+                'package_name': package_name, 'main': self.main,
  38
+                'other_versions': other_versions,
  39
+                'requires': requires}
37  whiskers/views/templates/buildout.pt
@@ -7,7 +7,17 @@
7 7
 <tal:block metal:use-macro="main.macros['body']">
8 8
   <div metal:fill-slot="content">
9 9
     <div class="container">
10  
-      <h2 class="title">${buildout.name}</h2>
  10
+      <h2 class="title">Buildout name: ${buildout.name}</h2>
  11
+      <dl>
  12
+        <dt>Host</dt>
  13
+        <dd>
  14
+          <a href="${request.route_url('host', host_id=buildout.host.id)}">
  15
+            ${buildout.host.name}
  16
+          </a>
  17
+        </dd>
  18
+        <dt>Added or modified</dt>
  19
+        <dd>${buildout.datetime.strftime("%Y-%m-%d %H:%M:%S")}</dd>
  20
+      </dl>
11 21
       <div class="config">
12 22
         <a href="#buildoutConfig" role="button" class="" data-toggle="modal">Show buildout config</a>
13 23
 
@@ -45,6 +55,31 @@
45 55
           </p>
46 56
         </div>
47 57
       </div>
  58
+
  59
+      <div tal:condition="new_buildouts">
  60
+        <hr />
  61
+        <h4>View new versions of this buildout</h4>
  62
+        <ul tal:repeat="buildout new_buildouts">
  63
+          <li>
  64
+            <a href="${request.route_url('buildout_view', buildout_id=buildout.id)}">
  65
+              ${buildout.datetime.strftime("%Y-%m-%d %H:%M:%S")}
  66
+            </a>
  67
+          </li>
  68
+        </ul>
  69
+      </div>
  70
+
  71
+      <div tal:condition="older_buildouts">
  72
+        <hr />
  73
+        <h4>View older versions of this buildout</h4>
  74
+        <ul tal:repeat="buildout older_buildouts">
  75
+          <li>
  76
+            <a href="${request.route_url('buildout_view', buildout_id=buildout.id)}">
  77
+              ${buildout.datetime.strftime("%Y-%m-%d %H:%M:%S")}
  78
+            </a>
  79
+          </li>
  80
+        </ul>
  81
+
  82
+      </div>
48 83
     </div>
49 84
   </div>
50 85
 </tal:block>
26  whiskers/views/templates/buildouts.pt
@@ -7,11 +7,27 @@
7 7
 <tal:block metal:use-macro="main.macros['body']">
8 8
   <div metal:fill-slot="content">
9