Rename package name to snapdiff
authorLingchao Xin <lingchaox.xin@intel.com>
Thu, 28 Nov 2013 08:04:07 +0000 (16:04 +0800)
committerLingchao Xin <lingchaox.xin@intel.com>
Thu, 28 Nov 2013 08:06:49 +0000 (16:06 +0800)
Change-Id: I7edbeed41482b37d968c583ec04a712fecf0df6c

17 files changed:
MANIFEST.in
builddiff/__init__.py [deleted file]
builddiff/render.py [deleted file]
builddiff/repo.py [deleted file]
builddiff/templates/diff.html [deleted file]
builddiff/utils.py [deleted file]
packaging/builddiff.changes [deleted file]
packaging/builddiff.spec [deleted file]
packaging/snapdiff.changes [new file with mode: 0644]
packaging/snapdiff.spec [new file with mode: 0644]
setup.py
snapdiff/__init__.py [new file with mode: 0644]
snapdiff/render.py [new file with mode: 0644]
snapdiff/repo.py [new file with mode: 0644]
snapdiff/templates/diff.html [new file with mode: 0644]
snapdiff/utils.py [new file with mode: 0644]
tools/repo-diff

index ef632508746480a6e762d74989f386941e317ee2..410a988f15afbfec755de428e096dd771d82397b 100644 (file)
@@ -1,2 +1,2 @@
-include builddiff/templates/*.html
+include snapdiff/templates/*.html
 
diff --git a/builddiff/__init__.py b/builddiff/__init__.py
deleted file mode 100644 (file)
index 1e5a5e7..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-
-__all__ = ['render', 'repo']
diff --git a/builddiff/render.py b/builddiff/render.py
deleted file mode 100644 (file)
index 707a806..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-from jinja2 import Environment, FileSystemLoader
-import os
-
-_SETTINGS = {
-    'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
-    }
-
-def output_html(template_name, **kwargs):
-    """Render a template file with given kwargs"""
-    env = Environment(
-        loader = FileSystemLoader(_SETTINGS['template_path']),
-        # auto_reload = True,
-    )
-    template = env.get_template(template_name)
-    return template.render(kwargs)
-
diff --git a/builddiff/repo.py b/builddiff/repo.py
deleted file mode 100644 (file)
index 4d20365..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-from . import utils
-from .render import output_html
-
-import gzip
-import json
-import os
-import tempfile
-import urllib2
-import xml.etree.cElementTree as ET
-
-
-class RepoError(Exception):
-    """Local custom Exception class, handle Repo Errors"""
-    pass
-
-
-def _get_primary_md(url, workspace, name):
-    """Get primary.xml.gz file from remote repodata directory"""
-    tree = ET.ElementTree(file=_download(url + '/repodata/repomd.xml', \
-            workspace, name))
-    for elem in tree.iter(tag='{http://linux.duke.edu/metadata/repo}data'):
-        if elem.attrib['type'] == 'primary':
-            for c in elem:
-                if c.tag == '{http://linux.duke.edu/metadata/repo}location':
-                    href = c.attrib['href']
-                    if href:
-                        return _download(url + href, workspace, \
-                                href.split('/')[-1])
-    else:
-        raise RepoError('Repo primary metadata can\'t be found !')
-
-def _download(url, workspace, name):
-    """Download needed xml file to local by given url"""
-    xs_file = os.path.join(workspace, name)
-
-    try:
-        rf = urllib2.urlopen(url)
-    except urllib2.HTTPError:
-        xs_file = None
-    else:
-        with open(xs_file, 'wb') as xs:
-            xs.write(rf.read())
-
-    return xs_file
-
-
-class Repo(object):
-    """Stuff packages' info"""
-
-    def __init__(self, url):
-        workspace = tempfile.mkdtemp(dir='/var/tmp')
-        primary_md = _get_primary_md(url, workspace, 'repomd.xml')
-        self._et = utils.xml2obj(gzip.open(primary_md))
-
-    @property
-    def packages(self):
-        packages_info = {}
-        for _package in self._et.package:
-            packages_info[_package.name] = (packages_info.get(_package.name) \
-                    or []) + [_package]
-        return packages_info
-
-def diff_to_JSON(old_url, new_url):
-    """Output diffs' json format"""
-
-    if not old_url or not new_url:
-        return
-
-    old, new = Repo(old_url).packages, Repo(new_url).packages
-
-    added, removed, modified, rebuilded = [], [], [], []
-    package_names = set(old.keys() + new.keys())
-
-    def _pair_old_new():
-        for name in package_names:
-            if old.get(name) is None:
-                for pkg in new[name]:
-                    yield (None, pkg)
-            elif new.get(name) is None:
-                for pkg in old[name]:
-                    yield (pkg, None)
-            else:
-                for old_pkg in old[name]:
-                    for new_pkg in new[name]:
-                        if old_pkg.version.ver == new_pkg.version.ver:
-                            yield (old_pkg, new_pkg)
-                            old[name].remove(old_pkg)
-                            new[name].remove(new_pkg)
-                for pair in map(None, old[name], new[name]):
-                    yield pair
-    for old_pkg, new_pkg in _pair_old_new():
-        if old_pkg is None:
-            added.append(new_pkg)
-        elif new_pkg is None:
-            removed.append(old_pkg)
-        elif old_pkg.version.ver == new_pkg.version.ver and \
-                old_pkg.version.vcs == new_pkg.version.vcs:
-            rebuilded.append((old_pkg, new_pkg))
-        else:
-            modified.append((old_pkg, new_pkg))
-
-    obj = {'repo': {'old': old_url, 'new': new_url},
-            'diff': {
-                'added': [{'oldpkg': None, 'newpkg': {'name': _new.name, \
-                        'version': {'epoch': _new.version.epoch, \
-                        'rel': _new.version.rel, 'ver': _new.version.rel}, \
-                        'vcs': _new.version.vcs}, 'codediff': None} \
-                        for _new in added],
-                'removed': [{'oldpkg': {'name': _old.name, \
-                        'version': {'epoch': _old.version.epoch, \
-                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
-                        'vcs': _old.version.vcs}, 'newpkg': None, \
-                        'codediff': None} for _old in added],
-                'modified': [{'oldpkg': {'name': _old.name, \
-                        'version': {'epoch': _old.version.epoch, \
-                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
-                        'vcs': _old.version.vcs}, \
-                        'newpkg': {'name': _new.name, \
-                        'version': {'epoch': _new.version.epoch, \
-                        'rel': _new.version.rel, 'ver': _new.version.ver},
-                            'vcs': _new.version.vcs}, 'codediff': None} \
-                                    for _old, _new in modified],
-                 'rebuilded': [{'oldpkg': {'name': _old.name, \
-                        'version': {'epoch': _old.version.epoch, \
-                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
-                        'vcs': _old.version.vcs}, \
-                        'newpkg': {'name': _new.name, \
-                        'version': {'epoch': _new.version.epoch, \
-                        'rel': _new.version.rel, 'ver': _new.version.ver},
-                            'vcs': _new.version.vcs}, 'codediff': None} \
-                                    for _old, _new in rebuilded],
-                }
-            }
-
-    return json.dumps(obj, indent=4)
-
-def diff_to_HTML(old_url, new_url):
-    """Output diffs' html format"""
-
-    data = json.loads(diff_to_JSON(old_url, new_url))
-
-    context = {'old_url': old_url,
-            'new_url': new_url,
-            'diff': data['diff'],
-            }
-
-    return output_html('diff.html', **context)
-
diff --git a/builddiff/templates/diff.html b/builddiff/templates/diff.html
deleted file mode 100644 (file)
index 664bc0e..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-    <link href="theme/html-reset.css" media="screen" rel="stylesheet" type="text/css" />
-    <link href="theme/layout-liquid.css" media="screen" rel="stylesheet" type="text/css" />
-    <link href="theme/tizen.css" media="screen" rel="stylesheet" type="text/css" />
-    <link href="theme/style.css" media="screen" rel="stylesheet" type="text/css" />
-    <script type="text/javascript" src="theme/js/shCore.js"></script>
-    <script type="text/javascript" src="theme/js/shBrushDiff.js"></script>
-    <link href="theme/css/shCore.css" rel="stylesheet" type="text/css" />
-    <link href="theme/css/shThemeDefault.css" rel="stylesheet" type="text/css" />
-</head>
-<body>
-    <script type="text/javascript">SyntaxHighlighter.all()</script>
-    <div id="page-wrapper">
-      <div id="page">
-        <div id="header">
-          <div class="section clearfix">
-            <h1 id="logo">
-              <a href="http://www.tizen.org"><img alt="Tizen-logo" src="theme/tizen-logo.png" width="180px" height="50px"></a>
-            </h1>
-          </div>
-        </div>
-      </div>
-    </div>
-    <h1>
-        Repository Difference and Changelogs
-    </h1>
-    <a href="index.html">Go back</a>...<br>
-    <p>Difference between
-        <a href="{{old_url}}">{{ old_url }}</a> and <a href="{{new_url}}">{{ new_url }}</a>
-    </p>
-    <h3>Highlights</h3>
-    <ul>
-        <li><a href="#added">Added Packages: {{ diff['added']|count }}</a></li>
-        <li><a href="#removed">Removed Packages: {{ diff['removed']|count }}</a></li>
-        <li><a href="#modified">Modified packages: {{ diff['modified']|count }}</a></li>
-        <li><a href="#rebuilded">Packages with Rebuilds: {{ diff['rebuilded']|count }}</a></li>
-    </ul>
-</body>
-</html>
-
diff --git a/builddiff/utils.py b/builddiff/utils.py
deleted file mode 100644 (file)
index 256c371..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-import re
-import xml.sax.handler
-
-def xml2obj(src):
-    """
-    A simple function to converts XML data into native Python object,
-    from http://goo.gl/Ymc6Nl, Licensed under the PSF License.
-    """
-
-    non_id_char = re.compile('[^_0-9a-zA-Z]')
-    def _name_mangle(name):
-        return non_id_char.sub('_', name)
-
-    class DataNode(object):
-        def __init__(self):
-            self._attrs = {}    # XML attributes and child elements
-            self.data = None    # child text data
-        def __len__(self):
-            # treat single element as a list of 1
-            return 1
-        def __getitem__(self, key):
-            if isinstance(key, basestring):
-                return self._attrs.get(key, None)
-            else:
-                return [self][key]
-        def __contains__(self, name):
-            return self._attrs.has_key(name)
-        def __nonzero__(self):
-            return bool(self._attrs or self.data)
-        def __getattr__(self, name):
-            if name.startswith('__'):
-                # need to do this for Python special methods???
-                raise AttributeError(name)
-            return self._attrs.get(name, None)
-        def _add_xml_attr(self, name, value):
-            if name in self._attrs:
-                # multiple attribute of the same name are represented by a list
-                children = self._attrs[name]
-                if not isinstance(children, list):
-                    children = [children]
-                    self._attrs[name] = children
-                children.append(value)
-            else:
-                self._attrs[name] = value
-        def __str__(self):
-            return self.data or ''
-        def __repr__(self):
-            items = sorted(self._attrs.items())
-            if self.data:
-                items.append(('data', self.data))
-            return u'{%s}' % ', '.join([u'%s:%s' % (k, repr(v)) for k, v in items])
-
-    class TreeBuilder(xml.sax.handler.ContentHandler):
-        def __init__(self):
-            self.stack = []
-            self.root = DataNode()
-            self.current = self.root
-            self.text_parts = []
-        def startElement(self, name, attrs):
-            self.stack.append((self.current, self.text_parts))
-            self.current = DataNode()
-            self.text_parts = []
-            # xml attributes --> python attributes
-            for k, v in attrs.items():
-                self.current._add_xml_attr(_name_mangle(k), v)
-        def endElement(self, name):
-            text = ''.join(self.text_parts).strip()
-            if text:
-                self.current.data = text
-            if self.current._attrs:
-                obj = self.current
-            else:
-                # a text only node is simply represented by the string
-                obj = text or ''
-            self.current, self.text_parts = self.stack.pop()
-            self.current._add_xml_attr(_name_mangle(name), obj)
-        def characters(self, content):
-            self.text_parts.append(content)
-
-    builder = TreeBuilder()
-    if isinstance(src, basestring):
-        xml.sax.parseString(src, builder)
-    else:
-        xml.sax.parse(src, builder)
-    return builder.root._attrs.values()[0]
-
diff --git a/packaging/builddiff.changes b/packaging/builddiff.changes
deleted file mode 100644 (file)
index a90f011..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-* Wed Nov 20 2013 Lingchao Xin <lingchaox.xin@intel.com> - 0.1
-  - Initial packaging
diff --git a/packaging/builddiff.spec b/packaging/builddiff.spec
deleted file mode 100644 (file)
index 1e0dcda..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# spec file for package builddiff
-#
-# Copyright (c) 2013 Lingchao Xin <lingchaox.xin@intel.com>.
-#
-
-Name:           builddiff
-Version:        0.1
-Release:        0
-License:        GPL-2.0+
-Summary:        Generate image and repo diffs
-Url:            http://www.tizen.org
-Group:          Development/Tools/Other
-Source:         %{name}-%{version}.tar.gz
-BuildRequires:  python-devel
-Requires:       python >= 2.7
-Requires:       python-jinja2
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-BuildArch:      noarch
-%{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
-
-%description
-Builddiff is used to generate image and repo diffs.
-
-%prep
-%setup -q
-
-%build
-CFLAGS="%{optflags}" python setup.py build
-
-%install
-python setup.py install --prefix=%{_prefix} --root=%{buildroot}
-
-%clean
-rm -rf %{buildroot}
-
-%files
-%defattr(-,root,root)
-%{_bindir}/*
-%{python_sitelib}/*
-
diff --git a/packaging/snapdiff.changes b/packaging/snapdiff.changes
new file mode 100644 (file)
index 0000000..a90f011
--- /dev/null
@@ -0,0 +1,2 @@
+* Wed Nov 20 2013 Lingchao Xin <lingchaox.xin@intel.com> - 0.1
+  - Initial packaging
diff --git a/packaging/snapdiff.spec b/packaging/snapdiff.spec
new file mode 100644 (file)
index 0000000..b8825f2
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# spec file for package python-snapdiff
+#
+# Copyright (c) 2013 Lingchao Xin <lingchaox.xin@intel.com>.
+#
+
+Name:           python-snapdiff
+Version:        0.1
+Release:        0
+License:        GPL-2.0+
+Summary:        Generate image and repo diffs
+Url:            http://www.tizen.org
+Group:          Development/Tools/Other
+Source:         %{name}-%{version}.tar.gz
+BuildRequires:  python-devel
+Requires:       python >= 2.7
+Requires:       python-jinja2
+BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+BuildArch:      noarch
+%{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+%description
+Builddiff is used to generate image and repo diffs.
+
+%prep
+%setup -q
+
+%build
+CFLAGS="%{optflags}" python setup.py build
+
+%install
+python setup.py install --prefix=%{_prefix} --root=%{buildroot}
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root)
+%{_bindir}/*
+%{python_sitelib}/*
+
index a4c41a702aaea2496c473c008427705bfa5c7a8f..ef7d6ee6802b03e65f64d4b64ec6a7451cc09d2c 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -5,14 +5,14 @@ try:
 except ImportError:
     from distutils.core import setup
 
-setup(name = 'builddiff',
+setup(name = 'python-snapdiff',
       version = '0.1',
       description = 'Generate image and repo diffs',
       author = 'Lingchao Xin',
       author_email = 'lingchaox.xin@intel.com',
       url = 'http://www.tizen.org/',
       scripts = ['tools/repo-diff',],
-      packages=['builddiff',],
+      packages=['snapdiff',],
       include_package_data=True,
       install_requires=['Jinja2>=2.6'],
       zip_safe=False,
diff --git a/snapdiff/__init__.py b/snapdiff/__init__.py
new file mode 100644 (file)
index 0000000..1e5a5e7
--- /dev/null
@@ -0,0 +1,2 @@
+
+__all__ = ['render', 'repo']
diff --git a/snapdiff/render.py b/snapdiff/render.py
new file mode 100644 (file)
index 0000000..707a806
--- /dev/null
@@ -0,0 +1,16 @@
+from jinja2 import Environment, FileSystemLoader
+import os
+
+_SETTINGS = {
+    'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
+    }
+
+def output_html(template_name, **kwargs):
+    """Render a template file with given kwargs"""
+    env = Environment(
+        loader = FileSystemLoader(_SETTINGS['template_path']),
+        # auto_reload = True,
+    )
+    template = env.get_template(template_name)
+    return template.render(kwargs)
+
diff --git a/snapdiff/repo.py b/snapdiff/repo.py
new file mode 100644 (file)
index 0000000..4d20365
--- /dev/null
@@ -0,0 +1,148 @@
+from . import utils
+from .render import output_html
+
+import gzip
+import json
+import os
+import tempfile
+import urllib2
+import xml.etree.cElementTree as ET
+
+
+class RepoError(Exception):
+    """Local custom Exception class, handle Repo Errors"""
+    pass
+
+
+def _get_primary_md(url, workspace, name):
+    """Get primary.xml.gz file from remote repodata directory"""
+    tree = ET.ElementTree(file=_download(url + '/repodata/repomd.xml', \
+            workspace, name))
+    for elem in tree.iter(tag='{http://linux.duke.edu/metadata/repo}data'):
+        if elem.attrib['type'] == 'primary':
+            for c in elem:
+                if c.tag == '{http://linux.duke.edu/metadata/repo}location':
+                    href = c.attrib['href']
+                    if href:
+                        return _download(url + href, workspace, \
+                                href.split('/')[-1])
+    else:
+        raise RepoError('Repo primary metadata can\'t be found !')
+
+def _download(url, workspace, name):
+    """Download needed xml file to local by given url"""
+    xs_file = os.path.join(workspace, name)
+
+    try:
+        rf = urllib2.urlopen(url)
+    except urllib2.HTTPError:
+        xs_file = None
+    else:
+        with open(xs_file, 'wb') as xs:
+            xs.write(rf.read())
+
+    return xs_file
+
+
+class Repo(object):
+    """Stuff packages' info"""
+
+    def __init__(self, url):
+        workspace = tempfile.mkdtemp(dir='/var/tmp')
+        primary_md = _get_primary_md(url, workspace, 'repomd.xml')
+        self._et = utils.xml2obj(gzip.open(primary_md))
+
+    @property
+    def packages(self):
+        packages_info = {}
+        for _package in self._et.package:
+            packages_info[_package.name] = (packages_info.get(_package.name) \
+                    or []) + [_package]
+        return packages_info
+
+def diff_to_JSON(old_url, new_url):
+    """Output diffs' json format"""
+
+    if not old_url or not new_url:
+        return
+
+    old, new = Repo(old_url).packages, Repo(new_url).packages
+
+    added, removed, modified, rebuilded = [], [], [], []
+    package_names = set(old.keys() + new.keys())
+
+    def _pair_old_new():
+        for name in package_names:
+            if old.get(name) is None:
+                for pkg in new[name]:
+                    yield (None, pkg)
+            elif new.get(name) is None:
+                for pkg in old[name]:
+                    yield (pkg, None)
+            else:
+                for old_pkg in old[name]:
+                    for new_pkg in new[name]:
+                        if old_pkg.version.ver == new_pkg.version.ver:
+                            yield (old_pkg, new_pkg)
+                            old[name].remove(old_pkg)
+                            new[name].remove(new_pkg)
+                for pair in map(None, old[name], new[name]):
+                    yield pair
+    for old_pkg, new_pkg in _pair_old_new():
+        if old_pkg is None:
+            added.append(new_pkg)
+        elif new_pkg is None:
+            removed.append(old_pkg)
+        elif old_pkg.version.ver == new_pkg.version.ver and \
+                old_pkg.version.vcs == new_pkg.version.vcs:
+            rebuilded.append((old_pkg, new_pkg))
+        else:
+            modified.append((old_pkg, new_pkg))
+
+    obj = {'repo': {'old': old_url, 'new': new_url},
+            'diff': {
+                'added': [{'oldpkg': None, 'newpkg': {'name': _new.name, \
+                        'version': {'epoch': _new.version.epoch, \
+                        'rel': _new.version.rel, 'ver': _new.version.rel}, \
+                        'vcs': _new.version.vcs}, 'codediff': None} \
+                        for _new in added],
+                'removed': [{'oldpkg': {'name': _old.name, \
+                        'version': {'epoch': _old.version.epoch, \
+                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
+                        'vcs': _old.version.vcs}, 'newpkg': None, \
+                        'codediff': None} for _old in added],
+                'modified': [{'oldpkg': {'name': _old.name, \
+                        'version': {'epoch': _old.version.epoch, \
+                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
+                        'vcs': _old.version.vcs}, \
+                        'newpkg': {'name': _new.name, \
+                        'version': {'epoch': _new.version.epoch, \
+                        'rel': _new.version.rel, 'ver': _new.version.ver},
+                            'vcs': _new.version.vcs}, 'codediff': None} \
+                                    for _old, _new in modified],
+                 'rebuilded': [{'oldpkg': {'name': _old.name, \
+                        'version': {'epoch': _old.version.epoch, \
+                        'rel': _old.version.rel, 'ver': _old.version.ver}, \
+                        'vcs': _old.version.vcs}, \
+                        'newpkg': {'name': _new.name, \
+                        'version': {'epoch': _new.version.epoch, \
+                        'rel': _new.version.rel, 'ver': _new.version.ver},
+                            'vcs': _new.version.vcs}, 'codediff': None} \
+                                    for _old, _new in rebuilded],
+                }
+            }
+
+    return json.dumps(obj, indent=4)
+
+def diff_to_HTML(old_url, new_url):
+    """Output diffs' html format"""
+
+    data = json.loads(diff_to_JSON(old_url, new_url))
+
+    context = {'old_url': old_url,
+            'new_url': new_url,
+            'diff': data['diff'],
+            }
+
+    return output_html('diff.html', **context)
+
diff --git a/snapdiff/templates/diff.html b/snapdiff/templates/diff.html
new file mode 100644 (file)
index 0000000..664bc0e
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <link href="theme/html-reset.css" media="screen" rel="stylesheet" type="text/css" />
+    <link href="theme/layout-liquid.css" media="screen" rel="stylesheet" type="text/css" />
+    <link href="theme/tizen.css" media="screen" rel="stylesheet" type="text/css" />
+    <link href="theme/style.css" media="screen" rel="stylesheet" type="text/css" />
+    <script type="text/javascript" src="theme/js/shCore.js"></script>
+    <script type="text/javascript" src="theme/js/shBrushDiff.js"></script>
+    <link href="theme/css/shCore.css" rel="stylesheet" type="text/css" />
+    <link href="theme/css/shThemeDefault.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+    <script type="text/javascript">SyntaxHighlighter.all()</script>
+    <div id="page-wrapper">
+      <div id="page">
+        <div id="header">
+          <div class="section clearfix">
+            <h1 id="logo">
+              <a href="http://www.tizen.org"><img alt="Tizen-logo" src="theme/tizen-logo.png" width="180px" height="50px"></a>
+            </h1>
+          </div>
+        </div>
+      </div>
+    </div>
+    <h1>
+        Repository Difference and Changelogs
+    </h1>
+    <a href="index.html">Go back</a>...<br>
+    <p>Difference between
+        <a href="{{old_url}}">{{ old_url }}</a> and <a href="{{new_url}}">{{ new_url }}</a>
+    </p>
+    <h3>Highlights</h3>
+    <ul>
+        <li><a href="#added">Added Packages: {{ diff['added']|count }}</a></li>
+        <li><a href="#removed">Removed Packages: {{ diff['removed']|count }}</a></li>
+        <li><a href="#modified">Modified packages: {{ diff['modified']|count }}</a></li>
+        <li><a href="#rebuilded">Packages with Rebuilds: {{ diff['rebuilded']|count }}</a></li>
+    </ul>
+</body>
+</html>
+
diff --git a/snapdiff/utils.py b/snapdiff/utils.py
new file mode 100644 (file)
index 0000000..256c371
--- /dev/null
@@ -0,0 +1,86 @@
+import re
+import xml.sax.handler
+
+def xml2obj(src):
+    """
+    A simple function to converts XML data into native Python object,
+    from http://goo.gl/Ymc6Nl, Licensed under the PSF License.
+    """
+
+    non_id_char = re.compile('[^_0-9a-zA-Z]')
+    def _name_mangle(name):
+        return non_id_char.sub('_', name)
+
+    class DataNode(object):
+        def __init__(self):
+            self._attrs = {}    # XML attributes and child elements
+            self.data = None    # child text data
+        def __len__(self):
+            # treat single element as a list of 1
+            return 1
+        def __getitem__(self, key):
+            if isinstance(key, basestring):
+                return self._attrs.get(key, None)
+            else:
+                return [self][key]
+        def __contains__(self, name):
+            return self._attrs.has_key(name)
+        def __nonzero__(self):
+            return bool(self._attrs or self.data)
+        def __getattr__(self, name):
+            if name.startswith('__'):
+                # need to do this for Python special methods???
+                raise AttributeError(name)
+            return self._attrs.get(name, None)
+        def _add_xml_attr(self, name, value):
+            if name in self._attrs:
+                # multiple attribute of the same name are represented by a list
+                children = self._attrs[name]
+                if not isinstance(children, list):
+                    children = [children]
+                    self._attrs[name] = children
+                children.append(value)
+            else:
+                self._attrs[name] = value
+        def __str__(self):
+            return self.data or ''
+        def __repr__(self):
+            items = sorted(self._attrs.items())
+            if self.data:
+                items.append(('data', self.data))
+            return u'{%s}' % ', '.join([u'%s:%s' % (k, repr(v)) for k, v in items])
+
+    class TreeBuilder(xml.sax.handler.ContentHandler):
+        def __init__(self):
+            self.stack = []
+            self.root = DataNode()
+            self.current = self.root
+            self.text_parts = []
+        def startElement(self, name, attrs):
+            self.stack.append((self.current, self.text_parts))
+            self.current = DataNode()
+            self.text_parts = []
+            # xml attributes --> python attributes
+            for k, v in attrs.items():
+                self.current._add_xml_attr(_name_mangle(k), v)
+        def endElement(self, name):
+            text = ''.join(self.text_parts).strip()
+            if text:
+                self.current.data = text
+            if self.current._attrs:
+                obj = self.current
+            else:
+                # a text only node is simply represented by the string
+                obj = text or ''
+            self.current, self.text_parts = self.stack.pop()
+            self.current._add_xml_attr(_name_mangle(name), obj)
+        def characters(self, content):
+            self.text_parts.append(content)
+
+    builder = TreeBuilder()
+    if isinstance(src, basestring):
+        xml.sax.parseString(src, builder)
+    else:
+        xml.sax.parse(src, builder)
+    return builder.root._attrs.values()[0]
+
index f553cbb5d370aae5ced9090e1a2071a3515b5646..e73d2724bca52311dd4e11957b308e19cb161958 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-from builddiff.repo import diff_to_JSON, diff_to_HTML
+from snapdiff.repo import diff_to_JSON, diff_to_HTML
 
 import argparse
 import sys