diff --git a/tools/changelog_1.0.4.md b/tools/changelog_1.0.4.md new file mode 100644 index 00000000..e127fc33 --- /dev/null +++ b/tools/changelog_1.0.4.md @@ -0,0 +1,38 @@ +# Changes + +Version 1.0.4 (2018-05-10) + +We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15. + +## Issues Closed + - error when installing using pip (#15) + - add RTD yaml (#14) + - add docs reqs (#13) + - begin docs (#12) + - Order of connectivity may be incorrect in Spatial_Prox_Prof (#5) + - add lorenz (#10) + - add working decomposition func and CDF plotting func (#7) + - start counterfactual (#6) + - directly specifiy Miniconda3 (#3) + - add spatial information theory index (#2) + - add second install option to README.md (#1) + +## Pull Requests + - add RTD yaml (#14) + - add docs reqs (#13) + - begin docs (#12) + - add lorenz (#10) + - add working decomposition func and CDF plotting func (#7) + - start counterfactual (#6) + - directly specifiy Miniconda3 (#3) + - add second install option to README.md (#1) + +The following individuals contributed to this release: + + - Renan Xavier Cortes + - Eli Knaap + - James Gaboardi + - Ljwolf + - Serge Rey + - Wei Kang + - Levi John Wolf \ No newline at end of file diff --git a/tools/gitcount.ipynb b/tools/gitcount.ipynb new file mode 100644 index 00000000..964ee97e --- /dev/null +++ b/tools/gitcount.ipynb @@ -0,0 +1,783 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## PySAL Change Log Statistics\n", + "\n", + "This notebook generates the summary statistics for a package. \n", + "\n", + "It assumes you are running this under the `tools` directory at the toplevel of the package\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Change the values only in the next cell" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "package_name = 'segregation'\n", + "release_date = '2018-05-10'\n", + "start_date = '2018-04-15'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook will generate a file in the current directory with the name \"changelog_VERSION.md\". You can edit and append this on front of the CHANGELOG file for the package release." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "import os\n", + "import json\n", + "import re\n", + "import sys\n", + "import pandas\n", + "\n", + "from datetime import datetime, timedelta\n", + "from time import sleep\n", + "from subprocess import check_output\n", + "try:\n", + " from urllib import urlopen\n", + "except:\n", + " from urllib.request import urlopen\n", + "\n", + "import ssl\n", + "import yaml\n", + "\n", + "context = ssl._create_unverified_context()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "CWD = os.path.abspath(os.path.curdir)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'C:\\\\Users\\\\renan\\\\Desktop\\\\segregation\\\\tools'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "CWD" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2018, 4, 15, 0, 0)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "since_date = '--since=\"{start}\"'.format(start=start_date)\n", + "since_date\n", + "since = datetime.strptime(start_date+\" 0:0:0\", \"%Y-%m-%d %H:%M:%S\")\n", + "since" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# get __version__\n", + "f = \"../{package}/__init__.py\".format(package=package_name)\n", + "\n", + "with open(f, 'r') as initfile:\n", + " exec(initfile.readline())\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Total commits by subpackage" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "cmd = ['git', 'log', '--oneline', since_date]\n", + "ncommits = len(check_output(cmd).splitlines())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "406" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ncommits" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## List Contributors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some of our contributors have many aliases for the same identity. So, we've added a mapping to make sure that individuals are listed once (and only once). " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "identities = {'Renan Xavier Cortes': ('renanxcortes', 'Renan Xavier Cortes'),\n", + " 'Serge Rey': ('Serge Rey', 'Sergio Rey', 'sjsrey', 'serge'),\n", + " 'Elijah Knaap': ('Elijah Knaap', 'knaaptime')\n", + "}\n", + "\n", + "def regularize_identity(string):\n", + " string = string.decode()\n", + " for name, aliases in identities.items():\n", + " for alias in aliases:\n", + " if alias in string:\n", + " string = string.replace(alias, name)\n", + " if len(string.split(' '))>1:\n", + " string = string.title()\n", + " return string.lstrip('* ')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "author_cmd = ['git', 'log', '--format=* %aN', since_date]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import Counter" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "ncommits = len(check_output(cmd).splitlines())\n", + "all_authors = check_output(author_cmd).splitlines()\n", + "counter = Counter([regularize_identity(author) for author in all_authors])\n", + "# global_counter += counter\n", + "# counters.update({'.'.join((package,subpackage)): counter})\n", + "unique_authors = sorted(set(all_authors))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "unique_authors = counter.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['Renan Xavier Cortes', 'Eli Knaap', 'James Gaboardi', 'Ljwolf', 'Serge Rey', 'Wei Kang', 'Levi John Wolf'])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "unique_authors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Disaggregate by PR, Issue" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime, timedelta\n", + "ISO8601 = \"%Y-%m-%dT%H:%M:%SZ\"\n", + "PER_PAGE = 100\n", + "element_pat = re.compile(r'<(.+?)>')\n", + "rel_pat = re.compile(r'rel=[\\'\"](\\w+)[\\'\"]')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def parse_link_header(headers):\n", + " link_s = headers.get('link', '')\n", + " urls = element_pat.findall(link_s)\n", + " rels = rel_pat.findall(link_s)\n", + " d = {}\n", + " for rel,url in zip(rels, urls):\n", + " d[rel] = url\n", + " return d\n", + "\n", + "def get_paged_request(url):\n", + " \"\"\"get a full list, handling APIv3's paging\"\"\"\n", + " results = []\n", + " while url:\n", + " #print(\"fetching %s\" % url, file=sys.stderr)\n", + " f = urlopen(url)\n", + " results.extend(json.load(f))\n", + " links = parse_link_header(f.headers)\n", + " url = links.get('next')\n", + " return results\n", + "\n", + "def get_issues(project=\"pysal/pysal\", state=\"closed\", pulls=False):\n", + " \"\"\"Get a list of the issues from the Github API.\"\"\"\n", + " which = 'pulls' if pulls else 'issues'\n", + " url = \"https://api.github.com/repos/%s/%s?state=%s&per_page=%i\" % (project, which, state, PER_PAGE)\n", + " return get_paged_request(url)\n", + "\n", + "\n", + "def _parse_datetime(s):\n", + " \"\"\"Parse dates in the format returned by the Github API.\"\"\"\n", + " if s:\n", + " return datetime.strptime(s, ISO8601)\n", + " else:\n", + " return datetime.fromtimestamp(0)\n", + "\n", + "\n", + "def issues2dict(issues):\n", + " \"\"\"Convert a list of issues to a dict, keyed by issue number.\"\"\"\n", + " idict = {}\n", + " for i in issues:\n", + " idict[i['number']] = i\n", + " return idict\n", + "\n", + "\n", + "def is_pull_request(issue):\n", + " \"\"\"Return True if the given issue is a pull request.\"\"\"\n", + " return 'pull_request_url' in issue\n", + "\n", + "\n", + "def issues_closed_since(period=timedelta(days=365), project=\"pysal/pysal\", pulls=False):\n", + " \"\"\"Get all issues closed since a particular point in time. period\n", + "can either be a datetime object, or a timedelta object. In the\n", + "latter case, it is used as a time before the present.\"\"\"\n", + "\n", + " which = 'pulls' if pulls else 'issues'\n", + "\n", + " if isinstance(period, timedelta):\n", + " period = datetime.now() - period\n", + " url = \"https://api.github.com/repos/%s/%s?state=closed&sort=updated&since=%s&per_page=%i\" % (project, which, period.strftime(ISO8601), PER_PAGE)\n", + " allclosed = get_paged_request(url)\n", + " # allclosed = get_issues(project=project, state='closed', pulls=pulls, since=period)\n", + " filtered = [i for i in allclosed if _parse_datetime(i['closed_at']) > period]\n", + "\n", + " # exclude rejected PRs\n", + " if pulls:\n", + " filtered = [ pr for pr in filtered if pr['merged_at'] ]\n", + "\n", + " return filtered\n", + "\n", + "\n", + "def sorted_by_field(issues, field='closed_at', reverse=False):\n", + " \"\"\"Return a list of issues sorted by closing date date.\"\"\"\n", + " return sorted(issues, key = lambda i:i[field], reverse=reverse)\n", + "\n", + "\n", + "def report(issues, show_urls=False):\n", + " \"\"\"Summary report about a list of issues, printing number and title.\n", + " \"\"\"\n", + " # titles may have unicode in them, so we must encode everything below\n", + " if show_urls:\n", + " for i in issues:\n", + " role = 'ghpull' if 'merged_at' in i else 'ghissue'\n", + " print('* :%s:`%d`: %s' % (role, i['number'],\n", + " i['title'].encode('utf-8')))\n", + " else:\n", + " for i in issues:\n", + " print('* %d: %s' % (i['number'], i['title'].encode('utf-8')))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "all_issues = {}\n", + "all_pulls = {}\n", + "total_commits = 0\n", + "#prj='pysal/libpysal'\n", + "prj = 'pysal/{package}'.format(package=package_name)\n", + "issues = issues_closed_since(since, project=prj,pulls=False)\n", + "pulls = issues_closed_since(since, project=prj,pulls=True)\n", + "issues = sorted_by_field(issues, reverse=True)\n", + "pulls = sorted_by_field(pulls, reverse=True)\n", + "n_issues, n_pulls = map(len, (issues, pulls))\n", + "n_total = n_issues + n_pulls\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "issue_listing = []\n", + "for issue in issues:\n", + " entry = \"{title} (#{number})\".format(title=issue['title'],number=issue['number'])\n", + " issue_listing.append(entry)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "pull_listing = []\n", + "for pull in pulls:\n", + " entry = \"{title} (#{number})\".format(title=pull['title'],number=pull['number'])\n", + " pull_listing.append(entry)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['add RTD yaml (#14)',\n", + " 'add docs reqs (#13)',\n", + " 'begin docs (#12)',\n", + " 'add lorenz (#10)',\n", + " 'add working decomposition func and CDF plotting func (#7)',\n", + " 'start counterfactual (#6)',\n", + " 'directly specifiy Miniconda3 (#3)',\n", + " 'add second install option to README.md (#1)']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pull_listing" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "message = \"We closed a total of {total} issues (enhancements and bug fixes) through {pr} pull requests\".format(total=n_total, pr=n_pulls)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "message = \"{msg}, since our last release on {previous}.\".format(msg=message, previous=str(start_date))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15.'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "message" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "message += \"\\n\\n## Issues Closed\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15.\n", + "\n", + "## Issues Closed\n", + "\n" + ] + } + ], + "source": [ + "print(message)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "issues = \"\\n\".join([\" - \"+issue for issue in issue_listing])\n", + "message += issues\n", + "message += \"\\n\\n## Pull Requests\\n\"\n", + "pulls = \"\\n\".join([\" - \"+pull for pull in pull_listing])\n", + "message += pulls" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15.\n", + "\n", + "## Issues Closed\n", + " - error when installing using pip (#15)\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - Order of connectivity may be incorrect in Spatial_Prox_Prof (#5)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add spatial information theory index (#2)\n", + " - add second install option to README.md (#1)\n", + "\n", + "## Pull Requests\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add second install option to README.md (#1)\n" + ] + } + ], + "source": [ + "print(message)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "people = \"\\n\".join([\" - \"+person for person in unique_authors])" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " - Renan Xavier Cortes\n", + " - Eli Knaap\n", + " - James Gaboardi\n", + " - Ljwolf\n", + " - Serge Rey\n", + " - Wei Kang\n", + " - Levi John Wolf\n" + ] + } + ], + "source": [ + "print(people)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "message +=\"\\n\\nThe following individuals contributed to this release:\\n\\n{people}\".format(people=people)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15.\n", + "\n", + "## Issues Closed\n", + " - error when installing using pip (#15)\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - Order of connectivity may be incorrect in Spatial_Prox_Prof (#5)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add spatial information theory index (#2)\n", + " - add second install option to README.md (#1)\n", + "\n", + "## Pull Requests\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add second install option to README.md (#1)\n", + "\n", + "The following individuals contributed to this release:\n", + "\n", + " - Renan Xavier Cortes\n", + " - Eli Knaap\n", + " - James Gaboardi\n", + " - Ljwolf\n", + " - Serge Rey\n", + " - Wei Kang\n", + " - Levi John Wolf\n" + ] + } + ], + "source": [ + "print(message)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "head = \"# Changes\\n\\nVersion {version} ({release_date})\\n\\n\".format(version=__version__, release_date=release_date)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Changes\n", + "\n", + "Version 1.0.4 (2018-05-10)\n", + "\n", + "We closed a total of 19 issues (enhancements and bug fixes) through 8 pull requests, since our last release on 2018-04-15.\n", + "\n", + "## Issues Closed\n", + " - error when installing using pip (#15)\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - Order of connectivity may be incorrect in Spatial_Prox_Prof (#5)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add spatial information theory index (#2)\n", + " - add second install option to README.md (#1)\n", + "\n", + "## Pull Requests\n", + " - add RTD yaml (#14)\n", + " - add docs reqs (#13)\n", + " - begin docs (#12)\n", + " - add lorenz (#10)\n", + " - add working decomposition func and CDF plotting func (#7)\n", + " - start counterfactual (#6)\n", + " - directly specifiy Miniconda3 (#3)\n", + " - add second install option to README.md (#1)\n", + "\n", + "The following individuals contributed to this release:\n", + "\n", + " - Renan Xavier Cortes\n", + " - Eli Knaap\n", + " - James Gaboardi\n", + " - Ljwolf\n", + " - Serge Rey\n", + " - Wei Kang\n", + " - Levi John Wolf\n" + ] + } + ], + "source": [ + "print(head+message)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "outfile = 'changelog_{version}.md'.format(version=__version__)\n", + "with open(outfile, 'w') as of:\n", + " of.write(head+message)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}