From 3467699d7157915a905894f8ce95eafa36b9f8fc Mon Sep 17 00:00:00 2001 From: Benjamin Bengfort Date: Fri, 11 Mar 2016 15:09:09 -0500 Subject: [PATCH] packaging stuff --- .travis.yml | 27 ++++++++++ DESCRIPTION.txt | 7 +++ Makefile | 8 ++- README.md | 69 +++++++++++++++--------- setup.py | 132 +++++++++++++++++++++++++++++++++++++++++++++- tests/__init__.py | 14 +++++ tribe-admin.py | 8 +-- tribe/__init__.py | 9 ++++ tribe/version.py | 40 ++++++++++++++ 9 files changed, 281 insertions(+), 33 deletions(-) create mode 100644 .travis.yml create mode 100644 DESCRIPTION.txt mode change 100644 => 100755 setup.py create mode 100644 tribe/version.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7156dd5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ + +language: python + +python: + - '2.7' + +before_install: + - pip install nose + - pip install mock + - pip install coverage + - pip install coveralls + +install: pip install -r requirements.txt + +script: make test + +after_script: coveralls + +notifications: + email: + recipients: + - bbengfort@districtdatalabs.com + - tojeda@districtdatalabs.com + - rbilbro@districtdatalabs.com + + on_success: change + on_failure: always diff --git a/DESCRIPTION.txt b/DESCRIPTION.txt new file mode 100644 index 0000000..1a9a751 --- /dev/null +++ b/DESCRIPTION.txt @@ -0,0 +1,7 @@ +Social networks are not new, even though websites like Facebook and Twitter might make you want to believe they are; and trust me- I’m not talking about Myspace! Social networks are extremely interesting models for human behavior, whose study dates back to the early twentieth century. However, because of those websites, data scientists have access to much more data than the anthropologists who studied the networks of tribes! + +Because networks take a relationship-centered view of the world, the data structures that we will analyze model real world behaviors and community. Through a suite of algorithms derived from mathematical Graph theory we are able to compute and predict behavior of individuals and communities through these types of analyses. Clearly this has a number of practical applications from recommendation to law enforcement to election prediction, and more. + +Tribe is a utility that will allow you to extract a network (a graph) from a communication network that we all use often - our email. Tribe is designed to read an email mbox (a native format for email in Python)and write the resulting graph to a GraphML file on disk. This utility is generally used for District Data Labs' Graph Analytics with Python and NetworkX course, but can be used for anyone interested in studying networks. + +For more, please see the full documentation at: http://ddl-tribe.readthedocs.org/en/latest/ diff --git a/Makefile b/Makefile index 313839b..d438a67 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Shell to use with Make -SHELL := /bin/sh +SHELL := /bin/bash # Set important Paths PROJECT := tribe @@ -8,7 +8,7 @@ PYTHONPATH := $(LOCALPATH)/ PYTHON_BIN := $(VIRTUAL_ENV)/bin # Export targets not associated with files -.PHONY: test coverage bootstrap pip virtualenv clean virtual_env_set +.PHONY: test coverage pip virtualenv clean publish # Clean build files clean: @@ -22,3 +22,7 @@ clean: # Targets for Coruscate testing test: $(PYTHON_BIN)/nosetests -v --with-coverage --cover-package=$(PROJECT) --cover-inclusive --cover-erase tests + +# Publish to gh-pages +publish: + git subtree push --prefix=site origin gh-pages diff --git a/README.md b/README.md index 26d079b..77cc843 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ **Social Network Analysis of Email** + +[![Build Status][travis_img]][travis_href] +[![Coverage Status][coveralls_img]][coveralls_href] + +[![Documentation Status][rtfd_img]][rtfd_href] +[![Stories in Ready][waffle_img]][waffle_href] + [![SNA Visualization](docs/images/sna_viz.png)](docs/images/sna_viz.png) This repository contains code for the Social Network Analysis with Python @@ -28,58 +35,72 @@ For more information: [Download your Data: Per-service information](https://supp ## Getting Started -To work with this code, you'll need to do a few things to set up your environment, follow these steps to put together a _development ready environment_. Note that there are some variations of the methodology for various operating systems, the notes below are for Linux/Unix (including Mac OS X). Feel free to add Windows/Powershell instructions to help out as well. +To work with this code, you'll need to do a few things to set up your environment, follow these steps to put together a _development ready environment_. Note that there are some variations of the methodology for various operating systems, the notes below are for Linux/Unix (including Mac OS X). Feel free to add Windows/Powershell instructions to help out as well. 1. Clone this repository Using the git command line tool, this is a pretty simple step: - + $ git clone https://github.com/DistrictDataLabs/tribe.git - - Optionally, you can fork this repository into your own user directory, and clone that instead. + + Optionally, you can fork this repository into your own user directory, and clone that instead. 2. Change directories (cd) into the project directory $ cd tribe -3. (Optional, Recommended) Create a virtual environment for the code and dependencies +3. (Optional, Recommended) Create a virtual environment for the code and dependencies Using `virtualenv` by itself: - + $ virtualenv venv - + Using `virtualenvwrapper` (configured correctly): - + $ mkvirtualenv -a $(pwd) tribe - + 4. Install the required third party packages using `pip`: - $ pip install -r requirements.txt - + $ pip install -r requirements.txt + Note, this may take a little while, but if you already have `matplotlib` and `pygraphviz` installed already, you should have little trouble. 5. Add the `tribe` module to the Python path for easy import There are a variety of ways to accomplish this, you could set the `$PYTHONPATH` environment variable, you could edit the `scripts/tribe-admin.py` file to modify the `sys.path` at runtime; choose your favorite approach. Mine is to use `.pth` files as below: - + First, find out the location of your Python cite packages with the following command: - + $ python -c 'from distutils.sysconfig import get_python_lib; print get_python_lib();' - + This is such a useful command, that I have it aliased in my .profile. Once you know this directory, create a .pth file with the current working directory in it as follows: - + $ echo $(pwd) > tribe.pth $ mv tribe.pth /path/to/site/packages/ - - Then simply move the .pth file to the location that was specified in the first command. Open up a new terminal window, and cd into your home directory. Open up a python iterpreter and type in `import tribe` - this should work with no errors. - -5. (Optional) Add a symlink to the `scripts/tribe-admin.py` script to your `$PATH`. - The simplest way to deal with this is to create a directory, `$HOME/bin` then add that directory to your `$PATH` using your bash profile. Of course, there are a variety of ways to do this as well, you could add the `scripts` directory to your `$PATH`, symlink that directory to `/usr/local/bin` or a similar place on Windows. Whatever you're most comfortable with is fine. - + Then simply move the .pth file to the location that was specified in the first command. Open up a new terminal window, and cd into your home directory. Open up a python iterpreter and type in `import tribe` - this should work with no errors. + +5. (Optional) Add a symlink to the `scripts/tribe-admin.py` script to your `$PATH`. + + The simplest way to deal with this is to create a directory, `$HOME/bin` then add that directory to your `$PATH` using your bash profile. Of course, there are a variety of ways to do this as well, you could add the `scripts` directory to your `$PATH`, symlink that directory to `/usr/local/bin` or a similar place on Windows. Whatever you're most comfortable with is fine. + 6. Test everything is working: $ scripts/tribe-admin.py --help - - You should see a help screen printed out (if you have import errors, see step 5, if you have a non executable error, make sure that you chmod tribe-admin.py to be executable). - + + You should see a help screen printed out (if you have import errors, see step 5, if you have a non executable error, make sure that you chmod tribe-admin.py to be executable). + + + +[pypi_img]: https://badge.fury.io/py/ddl-tribe.svg +[pypi_href]: https://badge.fury.io/py/ddl-tribe +[travis_img]: https://travis-ci.org/DistrictDataLabs/tribe.svg?branch=master +[travis_href]: https://travis-ci.org/DistrictDataLabs/tribe/ +[coveralls_img]: https://coveralls.io/repos/github/DistrictDataLabs/tribe/badge.svg?branch=master +[coveralls_href]: https://coveralls.io/github/DistrictDataLabs/tribe?branch=master +[health_img]: https://landscape.io/github/DistrictDataLabs/tribe/master/landscape.svg?style=flat +[health_href]: https://landscape.io/github/DistrictDataLabs/tribe/master +[waffle_img]: https://badge.waffle.io/DistrictDataLabs/tribe.png?label=ready&title=Ready +[waffle_href]: https://waffle.io/DistrictDataLabs/tribe +[rtfd_img]: https://readthedocs.org/projects/ddl-tribe/badge/?version=latest +[rtfd_href]: http://ddl-tribe.readthedocs.org/ diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 82d7ffc..297f9b5 --- a/setup.py +++ b/setup.py @@ -1,4 +1,134 @@ #!/usr/bin/env python +# setup +# Setup script for installing tribe +# +# Author: Benjamin Bengfort +# Created: Fri Mar 11 14:57:10 2016 -0500 +# +# Copyright (C) 2016 District Data Labs +# For license information, see LICENSE.txt and NOTICE.md +# +# ID: setup.py [] benjamin@bengfort.com $ + +""" +Setup script for installing tribe. +See http://bbengfort.github.io/programmer/2016/01/20/packaging-with-pypi.html +""" + +########################################################################## +## Imports +########################################################################## + +import os +import re +import codecs + +from setuptools import setup +from setuptools import find_packages + +########################################################################## +## Package Information +########################################################################## + +## Basic information +NAME = "tribe" +DESCRIPTION = "An graph extraction tool for email MBox files" +AUTHOR = "Benjamin Bengfort" +EMAIL = "bbengfort@districtdatalabs.com" +LICENSE = "MIT" +REPOSITORY = "https://github.com/DistrictDataLabs/tribe" +PACKAGE = "tribe" + +## Define the keywords +KEYWORDS = ('graph', 'networkx', 'data science', 'analysis', 'social network') + +## Define the classifiers +## See https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = ( + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Utilities', +) + +## Important Paths +PROJECT = os.path.abspath(os.path.dirname(__file__)) +REQUIRE_PATH = "requirements.txt" +VERSION_PATH = os.path.join(PACKAGE, "version.py") +PKG_DESCRIBE = "DESCRIPTION.txt" + +## Directories to ignore in find_packages +EXCLUDES = ( + "tests", "bin", "docs", "fixtures", "register", "notebooks", +) + +########################################################################## +## Helper Functions +########################################################################## + +def read(*parts): + """ + Assume UTF-8 encoding and return the contents of the file located at the + absolute path from the REPOSITORY joined with *parts. + """ + with codecs.open(os.path.join(PROJECT, *parts), 'rb', 'utf-8') as f: + return f.read() + + +def get_version(path=VERSION_PATH): + """ + Reads the __init__.py defined in the VERSION_PATH to find the get_version + function, and executes it to ensure that it is loaded correctly. + """ + namespace = {} + exec(read(path), namespace) + return namespace['get_version']() + + +def get_requires(path=REQUIRE_PATH): + """ + Yields a generator of requirements as defined by the REQUIRE_PATH which + should point to a requirements.txt output by `pip freeze`. + """ + for line in read(path).splitlines(): + line = line.strip() + if line and not line.startswith('#'): + yield line + +########################################################################## +## Define the configuration +########################################################################## + +config = { + "name": NAME, + "version": get_version(), + "description": DESCRIPTION, + "long_description": read(PKG_DESCRIBE), + "license": LICENSE, + "author": AUTHOR, + "author_email": EMAIL, + "maintainer": AUTHOR, + "maintainer_email": EMAIL, + "url": REPOSITORY, + "download_url": "{}/tarball/v{}".format(REPOSITORY, get_version()), + "packages": find_packages(where=PROJECT, exclude=EXCLUDES), + "install_requires": list(get_requires()), + "classifiers": CLASSIFIERS, + "keywords": KEYWORDS, + "zip_safe": False, + "scripts": ['bin/baleen'], +} + +########################################################################## +## Run setup script +########################################################################## if __name__ == '__main__': - raise NotImplementedError("Setup has not been created yet.") + setup(**config) diff --git a/tests/__init__.py b/tests/__init__.py index 6b81f7b..2ae0664 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -19,6 +19,13 @@ import unittest + +########################################################################## +## Module Constants +########################################################################## + +TEST_VERSION = "1.1.2" ## Also the expected version of the package + ########################################################################## ## Initialization Tests ########################################################################## @@ -39,3 +46,10 @@ def test_import(self): import tribe except ImportError: self.fail("Could not import the tribe module") + + def test_version(self): + """ + Assert that the version is sane + """ + import tribe + self.assertEqual(TEST_VERSION, tribe.__version__) diff --git a/tribe-admin.py b/tribe-admin.py index 0c49b52..343014b 100755 --- a/tribe-admin.py +++ b/tribe-admin.py @@ -20,12 +20,8 @@ import os import sys - -PROJECT = os.path.join(os.path.dirname(__file__), "..") - -sys.path.append(PROJECT) - import json +import tribe import argparse import networkx as nx @@ -40,7 +36,7 @@ DESCRIPTION = "An administrative utility for the Tribe Social Network Analysis" EPILOG = "If there are any bugs or concerns, submit an issue on Github" -VERSION = "1.1.2" +VERSION = "tribe v{}".format(tribe.__version__) ########################################################################## ## Commands diff --git a/tribe/__init__.py b/tribe/__init__.py index fc1c0f7..828d542 100644 --- a/tribe/__init__.py +++ b/tribe/__init__.py @@ -16,3 +16,12 @@ ########################################################################## ## Imports ########################################################################## + + +from .version import * + +########################################################################## +## Package Version +########################################################################## + +__version__ = get_version() diff --git a/tribe/version.py b/tribe/version.py new file mode 100644 index 0000000..77df682 --- /dev/null +++ b/tribe/version.py @@ -0,0 +1,40 @@ +# tribe.version +# Stores version information such that it can be read by setuptools. +# +# Author: Benjamin Bengfort +# Created: Thu Feb 18 20:14:16 2016 -0500 +# +# Copyright (C) 2016 District Data Labs +# For license information, see LICENSE.txt +# +# ID: version.py [] benjamin@bengfort.com $ + +""" +Stores version information such that it can be read by setuptools. +""" + +########################################################################## +## Imports +########################################################################## + +__version_info__ = { + 'major': 1, + 'minor': 1, + 'micro': 2, + 'releaselevel': 'final', + 'serial': 0, +} + + +def get_version(short=False): + """ + Computes a string representation of the version from __version_info__. + """ + assert __version_info__['releaselevel'] in ('alpha', 'beta', 'final') + vers = ["%(major)i.%(minor)i" % __version_info__, ] + if __version_info__['micro']: + vers.append(".%(micro)i" % __version_info__) + if __version_info__['releaselevel'] != 'final' and not short: + vers.append('%s%i' % (__version_info__['releaselevel'][0], + __version_info__['serial'])) + return ''.join(vers)