From 4601158ca0965c8845e590f3a63249e67c7b766a Mon Sep 17 00:00:00 2001 From: rreben Date: Thu, 10 Aug 2017 08:23:11 +0200 Subject: [PATCH 1/3] Add basket4py support (not customized yet) --- .gitignore | 17 + Vagrantfile | 51 ++ notebooks/Text Analytics with Python.ipynb | 756 ++++++++++++++++++ notebooks/basket4py.ipynb | 188 +++++ notebooks/requirements.txt | 4 + .../Lessons/images/ButtonToExecute.png | Bin 0 -> 24434 bytes notebooks/ressources/Lessons/tips.csv | 3 + provision/files/jupyter-notebook | 170 ++++ provision/files/jupyter_notebook_config.py | 7 + provision/library/ansible-conda/LICENSE.txt | 23 + provision/library/ansible-conda/README.md | 56 ++ provision/library/ansible-conda/conda.py | 302 +++++++ provision/playbook.yml | 54 ++ provision/requirements.yml | 3 + 14 files changed, 1634 insertions(+) create mode 100644 Vagrantfile create mode 100644 notebooks/Text Analytics with Python.ipynb create mode 100644 notebooks/basket4py.ipynb create mode 100644 notebooks/requirements.txt create mode 100644 notebooks/ressources/Lessons/images/ButtonToExecute.png create mode 100644 notebooks/ressources/Lessons/tips.csv create mode 100644 provision/files/jupyter-notebook create mode 100644 provision/files/jupyter_notebook_config.py create mode 100644 provision/library/ansible-conda/LICENSE.txt create mode 100644 provision/library/ansible-conda/README.md create mode 100644 provision/library/ansible-conda/conda.py create mode 100644 provision/playbook.yml create mode 100644 provision/requirements.yml diff --git a/.gitignore b/.gitignore index 72364f9..a01e242 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,20 @@ ENV/ # Rope project settings .ropeproject + +# vagrant and atom +.vagrant/ +provision/*.retry +provision/roles/andrewrothstein.anaconda +provision/roles/andrewrothstein.bash +provision/roles/andrewrothstein.unarchive-deps +.DS_Store +Thumbs.db +*.pyc +out +.ipynb_checkpoints +vagrant/bootstrap_complete.txt +vagrant/.vagrant/ +twitter.oauth_access +Berksfile.lock +._* diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..4313869 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,51 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +########################################################################### +# This configuration file is the starting point for understanding how the +# virtual machine is configured and provides a default provider that uses +# Virtualbox to provide virtualization. +# +# See http://docs.vagrantup.com/v2/vagrantfile/index.html for additional +# details on Vagrantfile configuration in general. +########################################################################### + +Vagrant.configure("2") do |config| + + # SSH forwarding: See https://help.github.com/articles/using-ssh-agent-forwarding + config.ssh.forward_agent = true + + config.vm.box = "ubuntu/trusty64" + + # You might have to play around with the following two lines if you have to install + # the virtual mashine on a flash or usb-flash drive. + # See https://github.com/rreben/Mining-the-Social-Web-2nd-Edition for details. + config.ssh.private_key_path = "/Users/rupertrebentisch/certificates/text_analytics_with_python_key" + config.ssh.insert_key = false + + # jupyter Notebook + config.vm.synced_folder "notebooks/", "/home/vagrant/notebooks" + config.vm.network "private_network", ip: "192.168.33.12" + + ######################################################################### + # Virtualbox configuration - the default provider for running a local VM + ######################################################################### + + config.vm.provider :virtualbox do |vb, override| + # You can increase the default amount of memory used by your VM by + # adjusting this value below (in MB) and reprovisioning. + vb.customize ["modifyvm", :id, "--memory", "1024"] + end + + # Ansible provisioning - + # this will run ansible on the VM so its not necessary to install on the host + config.vm.provision "ansible_local" do |ansible| + ansible.playbook = "provision/playbook.yml" + ansible.install_mode = "default" + #ansible.version = "latest" + ansible.verbose = "true" + ansible.galaxy_role_file = "provision/requirements.yml" + ansible.raw_arguments = ["--module-path", "/vagrant/provision/library/ansible-conda"] + end + +end diff --git a/notebooks/Text Analytics with Python.ipynb b/notebooks/Text Analytics with Python.ipynb new file mode 100644 index 0000000..96a3157 --- /dev/null +++ b/notebooks/Text Analytics with Python.ipynb @@ -0,0 +1,756 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Text Analytics with Python\n", + "## Text Summarization\n", + "\n", + "In order to be able to run the code from a jupyter notebook, we have to \"inline\" the contraction notebook in a cell.\n", + "\n", + "Pls check:\n", + "I used `vagrant ssh` and `sudo -i pip install htmlparser` is this really necessary, when you change to html.parser in python 3?" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# -*- coding: utf-8 -*-\n", + "\"\"\"\n", + "Created on Mon Aug 01 01:11:02 2016\n", + "\n", + "@author: DIP\n", + "\"\"\"\n", + "\n", + "CONTRACTION_MAP = {\n", + "\"ain't\": \"is not\",\n", + "\"aren't\": \"are not\",\n", + "\"can't\": \"cannot\",\n", + "\"can't've\": \"cannot have\",\n", + "\"'cause\": \"because\",\n", + "\"could've\": \"could have\",\n", + "\"couldn't\": \"could not\",\n", + "\"couldn't've\": \"could not have\",\n", + "\"didn't\": \"did not\",\n", + "\"doesn't\": \"does not\",\n", + "\"don't\": \"do not\",\n", + "\"hadn't\": \"had not\",\n", + "\"hadn't've\": \"had not have\",\n", + "\"hasn't\": \"has not\",\n", + "\"haven't\": \"have not\",\n", + "\"he'd\": \"he would\",\n", + "\"he'd've\": \"he would have\",\n", + "\"he'll\": \"he will\",\n", + "\"he'll've\": \"he he will have\",\n", + "\"he's\": \"he is\",\n", + "\"how'd\": \"how did\",\n", + "\"how'd'y\": \"how do you\",\n", + "\"how'll\": \"how will\",\n", + "\"how's\": \"how is\",\n", + "\"I'd\": \"I would\",\n", + "\"I'd've\": \"I would have\",\n", + "\"I'll\": \"I will\",\n", + "\"I'll've\": \"I will have\",\n", + "\"I'm\": \"I am\",\n", + "\"I've\": \"I have\",\n", + "\"i'd\": \"i would\",\n", + "\"i'd've\": \"i would have\",\n", + "\"i'll\": \"i will\",\n", + "\"i'll've\": \"i will have\",\n", + "\"i'm\": \"i am\",\n", + "\"i've\": \"i have\",\n", + "\"isn't\": \"is not\",\n", + "\"it'd\": \"it would\",\n", + "\"it'd've\": \"it would have\",\n", + "\"it'll\": \"it will\",\n", + "\"it'll've\": \"it will have\",\n", + "\"it's\": \"it is\",\n", + "\"let's\": \"let us\",\n", + "\"ma'am\": \"madam\",\n", + "\"mayn't\": \"may not\",\n", + "\"might've\": \"might have\",\n", + "\"mightn't\": \"might not\",\n", + "\"mightn't've\": \"might not have\",\n", + "\"must've\": \"must have\",\n", + "\"mustn't\": \"must not\",\n", + "\"mustn't've\": \"must not have\",\n", + "\"needn't\": \"need not\",\n", + "\"needn't've\": \"need not have\",\n", + "\"o'clock\": \"of the clock\",\n", + "\"oughtn't\": \"ought not\",\n", + "\"oughtn't've\": \"ought not have\",\n", + "\"shan't\": \"shall not\",\n", + "\"sha'n't\": \"shall not\",\n", + "\"shan't've\": \"shall not have\",\n", + "\"she'd\": \"she would\",\n", + "\"she'd've\": \"she would have\",\n", + "\"she'll\": \"she will\",\n", + "\"she'll've\": \"she will have\",\n", + "\"she's\": \"she is\",\n", + "\"should've\": \"should have\",\n", + "\"shouldn't\": \"should not\",\n", + "\"shouldn't've\": \"should not have\",\n", + "\"so've\": \"so have\",\n", + "\"so's\": \"so as\",\n", + "\"that'd\": \"that would\",\n", + "\"that'd've\": \"that would have\",\n", + "\"that's\": \"that is\",\n", + "\"there'd\": \"there would\",\n", + "\"there'd've\": \"there would have\",\n", + "\"there's\": \"there is\",\n", + "\"they'd\": \"they would\",\n", + "\"they'd've\": \"they would have\",\n", + "\"they'll\": \"they will\",\n", + "\"they'll've\": \"they will have\",\n", + "\"they're\": \"they are\",\n", + "\"they've\": \"they have\",\n", + "\"to've\": \"to have\",\n", + "\"wasn't\": \"was not\",\n", + "\"we'd\": \"we would\",\n", + "\"we'd've\": \"we would have\",\n", + "\"we'll\": \"we will\",\n", + "\"we'll've\": \"we will have\",\n", + "\"we're\": \"we are\",\n", + "\"we've\": \"we have\",\n", + "\"weren't\": \"were not\",\n", + "\"what'll\": \"what will\",\n", + "\"what'll've\": \"what will have\",\n", + "\"what're\": \"what are\",\n", + "\"what's\": \"what is\",\n", + "\"what've\": \"what have\",\n", + "\"when's\": \"when is\",\n", + "\"when've\": \"when have\",\n", + "\"where'd\": \"where did\",\n", + "\"where's\": \"where is\",\n", + "\"where've\": \"where have\",\n", + "\"who'll\": \"who will\",\n", + "\"who'll've\": \"who will have\",\n", + "\"who's\": \"who is\",\n", + "\"who've\": \"who have\",\n", + "\"why's\": \"why is\",\n", + "\"why've\": \"why have\",\n", + "\"will've\": \"will have\",\n", + "\"won't\": \"will not\",\n", + "\"won't've\": \"will not have\",\n", + "\"would've\": \"would have\",\n", + "\"wouldn't\": \"would not\",\n", + "\"wouldn't've\": \"would not have\",\n", + "\"y'all\": \"you all\",\n", + "\"y'all'd\": \"you all would\",\n", + "\"y'all'd've\": \"you all would have\",\n", + "\"y'all're\": \"you all are\",\n", + "\"y'all've\": \"you all have\",\n", + "\"you'd\": \"you would\",\n", + "\"you'd've\": \"you would have\",\n", + "\"you'll\": \"you will\",\n", + "\"you'll've\": \"you will have\",\n", + "\"you're\": \"you are\",\n", + "\"you've\": \"you have\"\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'nltk' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnltk\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdownload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"stopwords\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'nltk' is not defined" + ] + } + ], + "source": [ + "nltk.download(\"stopwords\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# -*- coding: utf-8 -*-\n", + "\"\"\"\n", + "Created on Fri Aug 26 20:45:10 2016\n", + "\n", + "@author: DIP\n", + "\"\"\"\n", + "\n", + "# not as an import to get a clean install from basket4py\n", + "# from contractions import CONTRACTION_MAP\n", + "import re\n", + "import nltk\n", + "import string\n", + "from nltk.stem import WordNetLemmatizer\n", + "# changed from HTMLParser to html.parser (because basic changes from python 2 to python 3)\n", + "from html.parser import HTMLParser\n", + "import unicodedata\n", + "\n", + "stopword_list = nltk.corpus.stopwords.words('english')\n", + "wnl = WordNetLemmatizer()\n", + "html_parser = HTMLParser()\n", + "\n", + "def tokenize_text(text):\n", + " tokens = nltk.word_tokenize(text) \n", + " tokens = [token.strip() for token in tokens]\n", + " return tokens\n", + "\n", + "def expand_contractions(text, contraction_mapping):\n", + " \n", + " contractions_pattern = re.compile('({})'.format('|'.join(contraction_mapping.keys())), \n", + " flags=re.IGNORECASE|re.DOTALL)\n", + " def expand_match(contraction):\n", + " match = contraction.group(0)\n", + " first_char = match[0]\n", + " expanded_contraction = contraction_mapping.get(match)\\\n", + " if contraction_mapping.get(match)\\\n", + " else contraction_mapping.get(match.lower()) \n", + " expanded_contraction = first_char+expanded_contraction[1:]\n", + " return expanded_contraction\n", + " \n", + " expanded_text = contractions_pattern.sub(expand_match, text)\n", + " expanded_text = re.sub(\"'\", \"\", expanded_text)\n", + " return expanded_text\n", + " \n", + "#this seems not to be necessary and is not compatible with python 3. The pattern module is not supported by python 3. \n", + "#from pattern.en import tag\n", + "from nltk.corpus import wordnet as wn\n", + "\n", + "# Annotate text tokens with POS tags\n", + "def pos_tag_text(text):\n", + " \n", + " def penn_to_wn_tags(pos_tag):\n", + " if pos_tag.startswith('J'):\n", + " return wn.ADJ\n", + " elif pos_tag.startswith('V'):\n", + " return wn.VERB\n", + " elif pos_tag.startswith('N'):\n", + " return wn.NOUN\n", + " elif pos_tag.startswith('R'):\n", + " return wn.ADV\n", + " else:\n", + " return None\n", + " \n", + " tagged_text = tag(text)\n", + " tagged_lower_text = [(word.lower(), penn_to_wn_tags(pos_tag))\n", + " for word, pos_tag in\n", + " tagged_text]\n", + " return tagged_lower_text\n", + " \n", + "# lemmatize text based on POS tags \n", + "def lemmatize_text(text):\n", + " \n", + " pos_tagged_text = pos_tag_text(text)\n", + " lemmatized_tokens = [wnl.lemmatize(word, pos_tag) if pos_tag\n", + " else word \n", + " for word, pos_tag in pos_tagged_text]\n", + " lemmatized_text = ' '.join(lemmatized_tokens)\n", + " return lemmatized_text\n", + " \n", + "\n", + "def remove_special_characters(text):\n", + " tokens = tokenize_text(text)\n", + " pattern = re.compile('[{}]'.format(re.escape(string.punctuation)))\n", + " filtered_tokens = filter(None, [pattern.sub(' ', token) for token in tokens])\n", + " filtered_text = ' '.join(filtered_tokens)\n", + " return filtered_text\n", + " \n", + " \n", + "def remove_stopwords(text):\n", + " tokens = tokenize_text(text)\n", + " filtered_tokens = [token for token in tokens if token not in stopword_list]\n", + " filtered_text = ' '.join(filtered_tokens) \n", + " return filtered_text\n", + "\n", + "\n", + "def unescape_html(parser, text):\n", + " \n", + " return parser.unescape(text)\n", + "\n", + "def normalize_corpus(corpus, lemmatize=True, tokenize=False):\n", + " \n", + " normalized_corpus = [] \n", + " for text in corpus:\n", + " text = html_parser.unescape(text)\n", + " text = expand_contractions(text, CONTRACTION_MAP)\n", + " if lemmatize:\n", + " text = lemmatize_text(text)\n", + " else:\n", + " text = text.lower()\n", + " text = remove_special_characters(text)\n", + " text = remove_stopwords(text)\n", + " if tokenize:\n", + " text = tokenize_text(text)\n", + " normalized_corpus.append(text)\n", + " else:\n", + " normalized_corpus.append(text)\n", + " \n", + " return normalized_corpus\n", + "\n", + "\n", + "def parse_document(document):\n", + " document = re.sub('\\n', ' ', document)\n", + " if isinstance(document, str):\n", + " document = document\n", + " elif isinstance(document, unicode):\n", + " return unicodedata.normalize('NFKD', document).encode('ascii', 'ignore')\n", + " else:\n", + " raise ValueError('Document is not string or unicode!')\n", + " document = document.strip()\n", + " sentences = nltk.sent_tokenize(document)\n", + " sentences = [sentence.strip() for sentence in sentences]\n", + " return sentences\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# -*- coding: utf-8 -*-\n", + "\"\"\"\n", + "Created on Sat Sep 03 12:38:45 2016\n", + "\n", + "@author: DIP\n", + "\"\"\"\n", + "\n", + "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\n", + "\n", + "def build_feature_matrix(documents, feature_type='frequency'):\n", + "\n", + " feature_type = feature_type.lower().strip() \n", + " \n", + " if feature_type == 'binary':\n", + " vectorizer = CountVectorizer(binary=True, min_df=1, \n", + " ngram_range=(1, 1))\n", + " elif feature_type == 'frequency':\n", + " vectorizer = CountVectorizer(binary=False, min_df=1, \n", + " ngram_range=(1, 1))\n", + " elif feature_type == 'tfidf':\n", + " vectorizer = TfidfVectorizer(min_df=1, \n", + " ngram_range=(1, 1))\n", + " else:\n", + " raise Exception(\"Wrong feature type entered. Possible values: 'binary', 'frequency', 'tfidf'\")\n", + "\n", + " feature_matrix = vectorizer.fit_transform(documents).astype(float)\n", + " \n", + " return vectorizer, feature_matrix\n", + "\n", + "\n", + "from scipy.sparse.linalg import svds\n", + " \n", + "def low_rank_svd(matrix, singular_count=2):\n", + " \n", + " u, s, vt = svds(matrix, k=singular_count)\n", + " return u, s, vt\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "\"\"\"\n", + "Created on Sun Sep 04 15:24:26 2016\n", + "\n", + "@author: DIP\n", + "\"\"\"\n", + "\n", + "# We \"inlined\" the modules in the cells above, so we do not have to / must not import them here\n", + "# from normalization import normalize_corpus, parse_document\n", + "# from utils import build_feature_matrix, low_rank_svd\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "toy_text = \"\"\"\n", + "Elephants are large mammals of the family Elephantidae \n", + "and the order Proboscidea. Two species are traditionally recognised, \n", + "the African elephant and the Asian elephant. Elephants are scattered \n", + "throughout sub-Saharan Africa, South Asia, and Southeast Asia. Male \n", + "African elephants are the largest extant terrestrial animals. All \n", + "elephants have a long trunk used for many purposes, \n", + "particularly breathing, lifting water and grasping objects. Their \n", + "incisors grow into tusks, which can serve as weapons and as tools \n", + "for moving objects and digging. Elephants' large ear flaps help \n", + "to control their body temperature. Their pillar-like legs can \n", + "carry their great weight. African elephants have larger ears \n", + "and concave backs while Asian elephants have smaller ears \n", + "and convex or level backs. \n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We need to install `sudo -i conda install gensim`" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[nltk_data] Downloading package punkt to /home/vagrant/nltk_data...\n", + "[nltk_data] Unzipping tokenizers/punkt.zip.\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nltk.download(\"punkt\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Automated document summarization with the gensim summarization module\n", + "We will now leverage gensims summarization module to get an automated document summarization." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "from gensim.summarization import summarize, keywords\n", + "\n", + "def text_summarization_gensim(text, summary_ratio=0.5):\n", + " \n", + " summary = summarize(text, split=True, ratio=summary_ratio)\n", + " for sentence in summary:\n", + " print (sentence)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remove newlines and tokenize for sentences. Get a list of sentences of the text.\n", + "\n", + "Then join the sentences back together with a space between the sentences. The text is now formatted for gensims summarization module." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Elephants are large mammals of the family Elephantidae and the order Proboscidea.', 'Two species are traditionally recognised, the African elephant and the Asian elephant.', 'Elephants are scattered throughout sub-Saharan Africa, South Asia, and Southeast Asia.', 'Male African elephants are the largest extant terrestrial animals.', 'All elephants have a long trunk used for many purposes, particularly breathing, lifting water and grasping objects.', 'Their incisors grow into tusks, which can serve as weapons and as tools for moving objects and digging.', \"Elephants' large ear flaps help to control their body temperature.\", 'Their pillar-like legs can carry their great weight.', 'African elephants have larger ears and concave backs while Asian elephants have smaller ears and convex or level backs.']\n", + "Elephants are large mammals of the family Elephantidae and the order Proboscidea. Two species are traditionally recognised, the African elephant and the Asian elephant. Elephants are scattered throughout sub-Saharan Africa, South Asia, and Southeast Asia. Male African elephants are the largest extant terrestrial animals. All elephants have a long trunk used for many purposes, particularly breathing, lifting water and grasping objects. Their incisors grow into tusks, which can serve as weapons and as tools for moving objects and digging. Elephants' large ear flaps help to control their body temperature. Their pillar-like legs can carry their great weight. African elephants have larger ears and concave backs while Asian elephants have smaller ears and convex or level backs.\n" + ] + } + ], + "source": [ + "docs = parse_document(toy_text)\n", + "print (docs)\n", + "text = ' '.join(docs)\n", + "print (text)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Two species are traditionally recognised, the African elephant and the Asian elephant.\n", + "All elephants have a long trunk used for many purposes, particularly breathing, lifting water and grasping objects.\n", + "African elephants have larger ears and concave backs while Asian elephants have smaller ears and convex or level backs.\n" + ] + } + ], + "source": [ + "text_summarization_gensim(text, summary_ratio=0.4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Latent semantic analysis (extraction based automated summarization)\n", + "First let us prepare our document." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Elephants are large mammals of the family Elephantidae and the order Proboscidea.', 'Two species are traditionally recognised, the African elephant and the Asian elephant.', 'Elephants are scattered throughout sub-Saharan Africa, South Asia, and Southeast Asia.', 'Male African elephants are the largest extant terrestrial animals.', 'All elephants have a long trunk used for many purposes, particularly breathing, lifting water and grasping objects.', 'Their incisors grow into tusks, which can serve as weapons and as tools for moving objects and digging.', \"Elephants' large ear flaps help to control their body temperature.\", 'Their pillar-like legs can carry their great weight.', 'African elephants have larger ears and concave backs while Asian elephants have smaller ears and convex or level backs.']\n", + "['elephants large mammals family elephantidae order proboscidea', 'two species traditionally recognised african elephant asian elephant', 'elephants scattered throughout sub saharan africa south asia southeast asia', 'male african elephants largest extant terrestrial animals', 'elephants long trunk used many purposes particularly breathing lifting water grasping objects', 'incisors grow tusks serve weapons tools moving objects digging', 'elephants large ear flaps help control body temperature', 'pillar like legs carry great weight', 'african elephants larger ears concave backs asian elephants smaller ears convex level backs']\n", + "Total Sentences in Document: 9\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/Anaconda3-4.4.0-Linux-x86_64/lib/python3.6/site-packages/ipykernel_launcher.py:101: DeprecationWarning: The unescape method is deprecated and will be removed in 3.5, use html.unescape() instead.\n" + ] + } + ], + "source": [ + "sentences = parse_document(toy_text)\n", + "print (sentences)\n", + "\n", + "norm_sentences = normalize_corpus(sentences,lemmatize=False) \n", + "print (norm_sentences)\n", + "\n", + "total_sentences = len(norm_sentences)\n", + "print ('Total Sentences in Document:', total_sentences) " + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 2.02 2.07 1.53 1.4 2.1 4.48 0.7 1.2 4.65]\n", + "[4 5 8]\n", + "All elephants have a long trunk used for many purposes, particularly breathing, lifting water and grasping objects.\n", + "Their incisors grow into tusks, which can serve as weapons and as tools for moving objects and digging.\n", + "African elephants have larger ears and concave backs while Asian elephants have smaller ears and convex or level backs.\n" + ] + } + ], + "source": [ + "num_sentences = 3\n", + "num_topics = 2\n", + "\n", + "vec, dt_matrix = build_feature_matrix(sentences, \n", + " feature_type='frequency')\n", + "\n", + "td_matrix = dt_matrix.transpose()\n", + "td_matrix = td_matrix.multiply(td_matrix > 0)\n", + "\n", + "u, s, vt = low_rank_svd(td_matrix, singular_count=num_topics) \n", + " \n", + "sv_threshold = 0.5\n", + "min_sigma_value = max(s) * sv_threshold\n", + "s[s < min_sigma_value] = 0\n", + "\n", + "salience_scores = np.sqrt(np.dot(np.square(s), np.square(vt)))\n", + "print (np.round(salience_scores, 2))\n", + "\n", + "top_sentence_indices = salience_scores.argsort()[-num_sentences:][::-1]\n", + "top_sentence_indices.sort()\n", + "print (top_sentence_indices)\n", + "\n", + "for index in top_sentence_indices:\n", + " print (sentences[index])\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + " \n", + "def lsa_text_summarizer(documents, num_sentences=2,\n", + " num_topics=2, feature_type='frequency',\n", + " sv_threshold=0.5):\n", + " \n", + " vec, dt_matrix = build_feature_matrix(documents, \n", + " feature_type=feature_type)\n", + "\n", + " td_matrix = dt_matrix.transpose()\n", + " td_matrix = td_matrix.multiply(td_matrix > 0)\n", + "\n", + " u, s, vt = low_rank_svd(td_matrix, singular_count=num_topics) \n", + " min_sigma_value = max(s) * sv_threshold\n", + " s[s < min_sigma_value] = 0\n", + " \n", + " salience_scores = np.sqrt(np.dot(np.square(s), np.square(vt)))\n", + " top_sentence_indices = salience_scores.argsort()[-num_sentences:][::-1]\n", + " top_sentence_indices.sort()\n", + " \n", + " for index in top_sentence_indices:\n", + " print sentences[index]\n", + " \n", + " \n", + " \n", + "\n", + "import networkx\n", + "\n", + "num_sentences = 3\n", + "vec, dt_matrix = build_feature_matrix(norm_sentences, \n", + " feature_type='tfidf')\n", + "similarity_matrix = (dt_matrix * dt_matrix.T)\n", + "print np.round(similarity_matrix.todense(), 2)\n", + "\n", + "similarity_graph = networkx.from_scipy_sparse_matrix(similarity_matrix)\n", + "\n", + "networkx.draw_networkx(similarity_graph)\n", + "\n", + "scores = networkx.pagerank(similarity_graph)\n", + "\n", + "ranked_sentences = sorted(((score, index) \n", + " for index, score \n", + " in scores.items()), \n", + " reverse=True)\n", + "ranked_sentences\n", + "\n", + "top_sentence_indices = [ranked_sentences[index][1] \n", + " for index in range(num_sentences)]\n", + "top_sentence_indices.sort()\n", + "print top_sentence_indices\n", + "\n", + "for index in top_sentence_indices:\n", + " print sentences[index]\n", + " \n", + "\n", + "def textrank_text_summarizer(documents, num_sentences=2,\n", + " feature_type='frequency'):\n", + " \n", + " vec, dt_matrix = build_feature_matrix(norm_sentences, \n", + " feature_type='tfidf')\n", + " similarity_matrix = (dt_matrix * dt_matrix.T)\n", + " \n", + " similarity_graph = networkx.from_scipy_sparse_matrix(similarity_matrix)\n", + " scores = networkx.pagerank(similarity_graph) \n", + " \n", + " ranked_sentences = sorted(((score, index) \n", + " for index, score \n", + " in scores.items()), \n", + " reverse=True)\n", + "\n", + " top_sentence_indices = [ranked_sentences[index][1] \n", + " for index in range(num_sentences)]\n", + " top_sentence_indices.sort()\n", + " \n", + " for index in top_sentence_indices:\n", + " print sentences[index] \n", + " \n", + "\n", + "DOCUMENT = \"\"\"\n", + "The Elder Scrolls V: Skyrim is an open world action role-playing video game \n", + "developed by Bethesda Game Studios and published by Bethesda Softworks. \n", + "It is the fifth installment in The Elder Scrolls series, following \n", + "The Elder Scrolls IV: Oblivion. Skyrim's main story revolves around \n", + "the player character and their effort to defeat Alduin the World-Eater, \n", + "a dragon who is prophesied to destroy the world. \n", + "The game is set two hundred years after the events of Oblivion \n", + "and takes place in the fictional province of Skyrim. The player completes quests \n", + "and develops the character by improving skills. \n", + "Skyrim continues the open world tradition of its predecessors by allowing the \n", + "player to travel anywhere in the game world at any time, and to \n", + "ignore or postpone the main storyline indefinitely. The player may freely roam \n", + "over the land of Skyrim, which is an open world environment consisting \n", + "of wilderness expanses, dungeons, cities, towns, fortresses and villages. \n", + "Players may navigate the game world more quickly by riding horses, \n", + "or by utilizing a fast-travel system which allows them to warp to previously \n", + "Players have the option to develop their character. At the beginning of the game, \n", + "players create their character by selecting one of several races, \n", + "including humans, orcs, elves and anthropomorphic cat or lizard-like creatures, \n", + "and then customizing their character's appearance.discovered locations. Over the \n", + "course of the game, players improve their character's skills, which are numerical \n", + "representations of their ability in certain areas. There are eighteen skills \n", + "divided evenly among the three schools of combat, magic, and stealth. \n", + "Skyrim is the first entry in The Elder Scrolls to include Dragons in the game's \n", + "wilderness. Like other creatures, Dragons are generated randomly in the world \n", + "and will engage in combat. \n", + "\"\"\"\n", + "\n", + "\n", + "sentences = parse_document(DOCUMENT)\n", + "norm_sentences = normalize_corpus(sentences,lemmatize=True) \n", + "print \"Total Sentences:\", len(norm_sentences) \n", + "\n", + "lsa_text_summarizer(norm_sentences, num_sentences=3,\n", + " num_topics=5, feature_type='frequency',\n", + " sv_threshold=0.5) \n", + "\n", + "textrank_text_summarizer(norm_sentences, num_sentences=3,\n", + " feature_type='tfidf') " + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/basket4py.ipynb b/notebooks/basket4py.ipynb new file mode 100644 index 0000000..b6fa41b --- /dev/null +++ b/notebooks/basket4py.ipynb @@ -0,0 +1,188 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# basket4py\n", + "\n", + "Congratulations! If you can see this, you managed to install everything you need to start coding. Place the cursor in the cell (\"hello world\") beneath and press the \"play\" button on top of this web page (look at the following image).\n", + "\n", + "![Press this button to execute your code](ressources/Lessons/images/ButtonToExecute.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print (\" hello world! \" * 8)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let us do some symbolic computer algebra:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sympy\n", + "from sympy import I, pi\n", + "\n", + "n1=sympy.Symbol(\"n\")\n", + "n2=sympy.Symbol(\"n\",integer=True)\n", + "n3=sympy.Symbol(\"n\",odd=True)\n", + "\n", + "sympy.cos(n2*pi)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we use sympy to calculate with fractions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r1=sympy.Rational(11,13)\n", + "r2=sympy.Rational(5,26)\n", + "\n", + "print(r1*r2)\n", + "print(r1/r2)\n", + "print(r1+r2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we use sympy to work with terms" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x=sympy.Symbol(\"x\")\n", + "expr=2*(x**2-x)-x*(x+1)\n", + "expr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "expr.simplify()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We use matplotlib to visualize some functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib as mpl\n", + "#c.IPKernelApp.pyplot = u'inline'\n", + "import matplotlib.pyplot as plt\n", + "#from mpl_toolkits.mplot3d.axes3d import Axes3D\n", + "import numpy as np\n", + "import sympy \n", + "x=np.linspace(-5,2,100)\n", + "y1=x**3 + 5*x**2 + 10\n", + "y2=10*x**2 + 10*x\n", + "y3=6*x + 10\n", + "fig, ax = plt.subplots()\n", + "ax.plot(x,y1, color='blue', label=\"y(x)\")\n", + "ax.plot(x,y2, color='red', label=\"y'(x)\")\n", + "ax.plot(x,y3, color='green', label=\"y''(x)\")\n", + "ax.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import snuggs\n", + "snuggs.eval('(+ 1 2)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from prettytable import PrettyTable\n", + "import csv\n", + "\n", + "x = PrettyTable([\"City name\", \"Area\", \"Population\", \"Annual Rainfall\"])\n", + "x.align[\"City name\"] = \"l\" # Left align city names\n", + "x.padding_width = 1 # One space between column edges and contents (default)\n", + "x.add_row([\"Adelaide\",1295, 1158259, 600.5])\n", + "x.add_row([\"Brisbane\",5905, 1857594, 1146.4])\n", + "x.add_row([\"Darwin\", 112, 120900, 1714.7])\n", + "x.add_row([\"Hobart\", 1357, 205556, 619.5])\n", + "x.add_row([\"Sydney\", 2058, 4336374, 1214.8])\n", + "x.add_row([\"Melbourne\", 1566, 3806092, 646.9])\n", + "x.add_row([\"Perth\", 5386, 1554769, 869.4])\n", + "print (x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/notebooks/requirements.txt b/notebooks/requirements.txt new file mode 100644 index 0000000..8ad97dc --- /dev/null +++ b/notebooks/requirements.txt @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +prettytable==0.7.2 +twitter==1.17.1 \ No newline at end of file diff --git a/notebooks/ressources/Lessons/images/ButtonToExecute.png b/notebooks/ressources/Lessons/images/ButtonToExecute.png new file mode 100644 index 0000000000000000000000000000000000000000..36edc9fab2be6a9e150187d722cfefc2185ae246 GIT binary patch literal 24434 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 literal 0 HcmV?d00001 diff --git a/notebooks/ressources/Lessons/tips.csv b/notebooks/ressources/Lessons/tips.csv new file mode 100644 index 0000000..c4eca88 --- /dev/null +++ b/notebooks/ressources/Lessons/tips.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e54cc4d2ce1bff65d32ca60b3e4b802e06bde1d7e7caf6f796f6bf7370e863b0 +size 9729 diff --git a/provision/files/jupyter-notebook b/provision/files/jupyter-notebook new file mode 100644 index 0000000..2c64e81 --- /dev/null +++ b/provision/files/jupyter-notebook @@ -0,0 +1,170 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: jupyter-notebook +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start jupyter +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Frederic Henri +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# This version was forked from jmtatsch/jupyter. +# 1. The problem of not stopping the jupyter service is fixed. +# It was caused by the exec problem. More details are found in +# https://chris-lamb.co.uk/posts/start-stop-daemon-exec-vs-startas +# 2. Previously the jupyter was run as root, but it is changed to +# be executed as a non-privilege user like 1000:1000 (UID:GID) +# 3. The working dir is added as an argument. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/anaconda/bin/ +DESC="Start Jupyter notebook server as a service" +UID=1000 +GID=1000 +NAME=jupyter-notebook +DAEMON=/usr/local/anaconda/bin/jupyter-notebook +DAEMON_ARGS="--config=/home/vagrant/.jupyter/jupyter_notebook_config.py --notebook-dir=/home/vagrant/notebooks" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +LOG_PATH=/var/log + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --background --make-pidfile --quiet --chuid $UID:$GID --pidfile $PIDFILE \ + --no-close --startas $DAEMON -- $DAEMON_ARGS >> ${LOG_PATH}/${NAME}.out 2>&1\ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --pidfile $PIDFILE --startas $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: \ No newline at end of file diff --git a/provision/files/jupyter_notebook_config.py b/provision/files/jupyter_notebook_config.py new file mode 100644 index 0000000..869ef6a --- /dev/null +++ b/provision/files/jupyter_notebook_config.py @@ -0,0 +1,7 @@ +c.JupyterApp.answer_yes = True +c.NotebookApp.allow_origin = "*" +c.NotebookApp.ip = "0.0.0.0" +c.NotebookApp.open_browser = False +# default password is 'password' +c.NotebookApp.password = "sha1:0a454ce0899d:6550f8fe9b111f264b5a8175b33a050bca6bf621" +c.NotebookApp.port = 8888; \ No newline at end of file diff --git a/provision/library/ansible-conda/LICENSE.txt b/provision/library/ansible-conda/LICENSE.txt new file mode 100644 index 0000000..b095658 --- /dev/null +++ b/provision/library/ansible-conda/LICENSE.txt @@ -0,0 +1,23 @@ +Copyright (c) 2016, UrbanSim Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/provision/library/ansible-conda/README.md b/provision/library/ansible-conda/README.md new file mode 100644 index 0000000..e5bd502 --- /dev/null +++ b/provision/library/ansible-conda/README.md @@ -0,0 +1,56 @@ +# Conda Ansible Module + +Manage [conda][] installations of Python packages in [Ansible][] playbooks. +Put this module somewhere Ansible will find it +(like the `library/` directory next to your top level playbooks). +Usage is much like the built-in Ansible pip module. +This requires `conda` to already be installed somehow. + +Examples: + +```yaml +- name: install numpy via conda + conda: name=numpy state=latest + +- name: install scipy 0.14 via conda + conda: name=scipy version="0.14" + +- name: remove matplotlib from conda + conda: name=matplotlib state=absent +``` + +From `ansible-doc`: + +``` +> CONDA + + Manage Python libraries via conda. Can install, update, and remove + packages. + +Options (= is mandatory): + +- channels + Extra channels to use when installing packages [Default: None] + +- executable + Full path to the conda executable [Default: None] + +- extra_args + Extra arguments passed to conda [Default: None] + += name + The name of a Python library to install [Default: None] + +- state + State in which to leave the Python package (Choices: present, + absent, latest) [Default: present] + +- version + A specific version of a library to install [Default: None] + +Notes: Requires conda to already be installed. Will look under the home + directory for a conda executable. +``` + +[conda]: http://conda.pydata.org/ +[Ansible]: http://docs.ansible.com/index.html diff --git a/provision/library/ansible-conda/conda.py b/provision/library/ansible-conda/conda.py new file mode 100644 index 0000000..90dca53 --- /dev/null +++ b/provision/library/ansible-conda/conda.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +DOCUMENTATION = """ +--- +module: conda +short_description: Manage Python libraries via conda +description: + > + Manage Python libraries via conda. + Can install, update, and remove packages. +author: Synthicity +notes: + > + Requires conda to already be installed. + Will look under the home directory for a conda executable. +options: + name: + description: The name of a Python library to install + required: true + default: null + version: + description: A specific version of a library to install + required: false + default: null + state: + description: State in which to leave the Python package + required: false + default: present + choices: [ "present", "absent", "latest" ] + channels: + description: Extra channels to use when installing packages + required: false + default: null + executable: + description: Full path to the conda executable + required: false + default: null + extra_args: + description: Extra arguments passed to conda + required: false + default: null +""" + +EXAMPLES = """ +- name: install numpy via conda + conda: name=numpy state=latest + +- name: install scipy 0.14 via conda + conda: name=scipy version="0.14" + +- name: remove matplotlib from conda + conda: name=matplotlib state=absent +""" + +from distutils.spawn import find_executable +import os.path +import json + + +def _find_conda(module, executable): + """ + If `executable` is not None, checks whether it points to a valid file + and returns it if this is the case. Otherwise tries to find the `conda` + executable in the path. Calls `fail_json` if either of these fail. + + """ + if not executable: + conda = find_executable('conda') + if conda: + return conda + else: + if os.path.isfile(executable): + return executable + + module.fail_json(msg="could not find conda executable") + + +def _add_channels_to_command(command, channels): + """ + Add extra channels to a conda command by splitting the channels + and putting "--channel" before each one. + + """ + if channels: + channels = channels.strip().split() + dashc = [] + for channel in channels: + dashc.append('--channel') + dashc.append(channel) + + return command[:2] + dashc + command[2:] + else: + return command + + +def _add_extras_to_command(command, extras): + """ + Add extra arguments to a conda command by splitting the arguments + on white space and inserting them after the second item in the command. + + """ + if extras: + extras = extras.strip().split() + return command[:2] + extras + command[2:] + else: + return command + + +def _check_installed(module, conda, name): + """ + Check whether a package is installed. Returns (bool, version_str). + + """ + command = [ + conda, + 'list', + '^' + name + '$', + '--json' + ] + command = _add_extras_to_command(command, module.params['extra_args']) + + rc, stdout, stderr = module.run_command(command) + + if rc != 0: + return False, None + + installed = False + version = None + + data = json.loads(stdout) + if data: + # At this point data will be a list of len 1, with the element of + # the format: "channel::package-version-py35_1" + line = data[0] + if "::" in line: + channel, other = line.split('::') + else: + other = line + + if isinstance(other, dict): + pname = other.get('name', '') + pversion = other.get('version', '') + else: + # split carefully as some package names have "-" in them (scikit-learn) + pname, pversion, pdist = other.rsplit('-', 2) + + if pname == name: # verify match for safety + installed = True + version = pversion + + return installed, version + + +def _remove_package(module, conda, installed, name): + """ + Use conda to remove a given package if it is installed. + + """ + if module.check_mode and installed: + module.exit_json(changed=True) + + if not installed: + module.exit_json(changed=False) + + command = [ + conda, + 'remove', + '--yes', + name + ] + command = _add_extras_to_command(command, module.params['extra_args']) + + rc, stdout, stderr = module.run_command(command) + + if rc != 0: + module.fail_json(msg='failed to remove package ' + name) + + module.exit_json(changed=True, name=name, stdout=stdout, stderr=stderr) + + +def _install_package( + module, conda, installed, name, version, installed_version): + """ + Install a package at a specific version, or install a missing package at + the latest version if no version is specified. + + """ + if installed and (version is None or installed_version == version): + module.exit_json(changed=False, name=name, version=version) + + if module.check_mode: + if not installed or (installed and installed_version != version): + module.exit_json(changed=True) + + if version: + install_str = name + '=' + version + else: + install_str = name + + command = [ + conda, + 'install', + '--yes', + install_str + ] + command = _add_channels_to_command(command, module.params['channels']) + command = _add_extras_to_command(command, module.params['extra_args']) + + rc, stdout, stderr = module.run_command(command) + + if rc != 0: + module.fail_json(msg='failed to install package ' + name) + + module.exit_json( + changed=True, name=name, version=version, stdout=stdout, stderr=stderr) + + +def _update_package(module, conda, installed, name): + """ + Make sure an installed package is at its latest version. + + """ + if not installed: + module.fail_json(msg='can\'t update a package that is not installed') + + # see if it's already installed at the latest version + command = [ + conda, + 'update', + '--dry-run', + name + ] + command = _add_channels_to_command(command, module.params['channels']) + command = _add_extras_to_command(command, module.params['extra_args']) + + rc, stdout, stderr = module.run_command(command) + + if rc != 0: + module.fail_json(msg='can\'t update a package that is not installed') + + if 'requested packages already installed' in stdout: + module.exit_json(changed=False, name=name) + + # now we're definitely gonna update the package + if module.check_mode: + module.exit_json(changed=True, name=name) + + command = [ + conda, + 'update', + '--yes', + name + ] + command = _add_channels_to_command(command, module.params['channels']) + command = _add_extras_to_command(command, module.params['extra_args']) + + rc, stdout, stderr = module.run_command(command) + + if rc != 0: + module.fail_json(msg='failed to update package ' + name) + + module.exit_json(changed=True, name=name, stdout=stdout, stderr=stderr) + + +def main(): + module = AnsibleModule( + argument_spec={ + 'name': {'required': True, 'type': 'str'}, + 'version': {'default': None, 'required': False, 'type': 'str'}, + 'state': { + 'default': 'present', + 'required': False, + 'choices': ['present', 'absent', 'latest'] + }, + 'channels': {'default': None, 'required': False}, + 'executable': {'default': None, 'required': False}, + 'extra_args': {'default': None, 'required': False, 'type': 'str'} + }, + supports_check_mode=True) + + conda = _find_conda(module, module.params['executable']) + name = module.params['name'] + state = module.params['state'] + version = module.params['version'] + + installed, installed_version = _check_installed(module, conda, name) + + if state == 'absent': + _remove_package(module, conda, installed, name) + elif state == 'present' or (state == 'latest' and not installed): + _install_package( + module, conda, installed, name, version, installed_version) + elif state == 'latest': + _update_package(module, conda, installed, name) + + +# import module snippets +from ansible.module_utils.basic import * + +if __name__ == '__main__': + main() diff --git a/provision/playbook.yml b/provision/playbook.yml new file mode 100644 index 0000000..3f24c15 --- /dev/null +++ b/provision/playbook.yml @@ -0,0 +1,54 @@ +- hosts: all + become: true + + vars: + - anaconda_make_sys_default: True + + roles: + - andrewrothstein.anaconda + + post_tasks: + + - name: Create jupyter notebook configuration directory + file: + path: /home/vagrant/.jupyter + state: directory + owner: vagrant + group: vagrant + + - name: Install jupyter configuration file + copy: + src: files/jupyter_notebook_config.py + dest: /home/vagrant/.jupyter + owner: vagrant + group: vagrant + + - name: Create jupyter notebook directory + file: + path: /home/vagrant/notebooks + state: directory + owner: vagrant + group: vagrant + + - name: Install jupyter notebook initd service + template: + src: files/jupyter-notebook + dest: /etc/init.d + mode: 0755 + + + - name: Enable and start jupyter notebook service + service: + name: jupyter-notebook + enabled: yes + state: started + + - name: Install python library through pip requirements + pip: + requirements: /home/vagrant/notebooks/requirements.txt + executable: /usr/local/anaconda/bin/pip + + - name: install python library via conda + conda: name={{ item }} state=latest executable="/usr/local/anaconda/bin/conda" + with_items: + - snuggs diff --git a/provision/requirements.yml b/provision/requirements.yml new file mode 100644 index 0000000..6ae3854 --- /dev/null +++ b/provision/requirements.yml @@ -0,0 +1,3 @@ +--- +- src: andrewrothstein.anaconda + From 68995cf441f97034abc77ab10ede59073de45bcd Mon Sep 17 00:00:00 2001 From: rreben Date: Thu, 10 Aug 2017 08:28:10 +0200 Subject: [PATCH 2/3] added anaconda provisioning packages --- .gitignore | 3 - .../roles/andrewrothstein.anaconda/.gitignore | 3 + .../andrewrothstein.anaconda/.travis.yml | 40 +++++++++++++ .../roles/andrewrothstein.anaconda/LICENSE | 22 +++++++ .../roles/andrewrothstein.anaconda/README.md | 41 +++++++++++++ .../roles/andrewrothstein.anaconda/circle.yml | 36 +++++++++++ .../defaults/main.yml | 46 +++++++++++++++ .../handlers/main.yml | 2 + .../meta/.galaxy_install_info | 1 + .../andrewrothstein.anaconda/meta/main.yml | 32 ++++++++++ .../andrewrothstein.anaconda/tasks/main.yml | 59 +++++++++++++++++++ .../templates/anaconda.sh.j2 | 2 + .../roles/andrewrothstein.anaconda/test.yml | 7 +++ .../andrewrothstein.anaconda/vars/main.yml | 11 ++++ .../roles/andrewrothstein.bash/.gitignore | 3 + .../roles/andrewrothstein.bash/.travis.yml | 45 ++++++++++++++ provision/roles/andrewrothstein.bash/LICENSE | 22 +++++++ .../roles/andrewrothstein.bash/README.md | 39 ++++++++++++ .../roles/andrewrothstein.bash/circle.yml | 41 +++++++++++++ .../andrewrothstein.bash/defaults/main.yml | 2 + .../andrewrothstein.bash/handlers/main.yml | 2 + .../meta/.galaxy_install_info | 1 + .../roles/andrewrothstein.bash/meta/main.yml | 29 +++++++++ .../roles/andrewrothstein.bash/tasks/main.yml | 8 +++ provision/roles/andrewrothstein.bash/test.yml | 6 ++ .../andrewrothstein.bash/tests/inventory | 1 + .../roles/andrewrothstein.bash/tests/test.yml | 5 ++ .../roles/andrewrothstein.bash/vars/main.yml | 2 + .../andrewrothstein.unarchive-deps/.gitignore | 3 + .../andrewrothstein.unarchive-deps/LICENSE | 22 +++++++ .../andrewrothstein.unarchive-deps/README.md | 24 ++++++++ .../andrewrothstein.unarchive-deps/circle.yml | 39 ++++++++++++ .../defaults/main.yml | 5 ++ .../meta/.galaxy_install_info | 1 + .../meta/main.yml | 32 ++++++++++ .../tasks/main.yml | 23 ++++++++ .../andrewrothstein.unarchive-deps/test.yml | 4 ++ .../vars/Darwin.yml | 3 + .../vars/Debian.yml | 2 + 39 files changed, 666 insertions(+), 3 deletions(-) create mode 100644 provision/roles/andrewrothstein.anaconda/.gitignore create mode 100644 provision/roles/andrewrothstein.anaconda/.travis.yml create mode 100644 provision/roles/andrewrothstein.anaconda/LICENSE create mode 100644 provision/roles/andrewrothstein.anaconda/README.md create mode 100644 provision/roles/andrewrothstein.anaconda/circle.yml create mode 100644 provision/roles/andrewrothstein.anaconda/defaults/main.yml create mode 100644 provision/roles/andrewrothstein.anaconda/handlers/main.yml create mode 100644 provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info create mode 100644 provision/roles/andrewrothstein.anaconda/meta/main.yml create mode 100644 provision/roles/andrewrothstein.anaconda/tasks/main.yml create mode 100644 provision/roles/andrewrothstein.anaconda/templates/anaconda.sh.j2 create mode 100644 provision/roles/andrewrothstein.anaconda/test.yml create mode 100644 provision/roles/andrewrothstein.anaconda/vars/main.yml create mode 100644 provision/roles/andrewrothstein.bash/.gitignore create mode 100644 provision/roles/andrewrothstein.bash/.travis.yml create mode 100644 provision/roles/andrewrothstein.bash/LICENSE create mode 100644 provision/roles/andrewrothstein.bash/README.md create mode 100644 provision/roles/andrewrothstein.bash/circle.yml create mode 100644 provision/roles/andrewrothstein.bash/defaults/main.yml create mode 100644 provision/roles/andrewrothstein.bash/handlers/main.yml create mode 100644 provision/roles/andrewrothstein.bash/meta/.galaxy_install_info create mode 100644 provision/roles/andrewrothstein.bash/meta/main.yml create mode 100644 provision/roles/andrewrothstein.bash/tasks/main.yml create mode 100644 provision/roles/andrewrothstein.bash/test.yml create mode 100644 provision/roles/andrewrothstein.bash/tests/inventory create mode 100644 provision/roles/andrewrothstein.bash/tests/test.yml create mode 100644 provision/roles/andrewrothstein.bash/vars/main.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/.gitignore create mode 100644 provision/roles/andrewrothstein.unarchive-deps/LICENSE create mode 100644 provision/roles/andrewrothstein.unarchive-deps/README.md create mode 100644 provision/roles/andrewrothstein.unarchive-deps/circle.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/defaults/main.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/meta/.galaxy_install_info create mode 100644 provision/roles/andrewrothstein.unarchive-deps/meta/main.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/tasks/main.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/test.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/vars/Darwin.yml create mode 100644 provision/roles/andrewrothstein.unarchive-deps/vars/Debian.yml diff --git a/.gitignore b/.gitignore index a01e242..b88db07 100644 --- a/.gitignore +++ b/.gitignore @@ -91,9 +91,6 @@ ENV/ # vagrant and atom .vagrant/ provision/*.retry -provision/roles/andrewrothstein.anaconda -provision/roles/andrewrothstein.bash -provision/roles/andrewrothstein.unarchive-deps .DS_Store Thumbs.db *.pyc diff --git a/provision/roles/andrewrothstein.anaconda/.gitignore b/provision/roles/andrewrothstein.anaconda/.gitignore new file mode 100644 index 0000000..9c8285f --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/.gitignore @@ -0,0 +1,3 @@ +**~ +Dockerfile.* +requirements.yml \ No newline at end of file diff --git a/provision/roles/andrewrothstein.anaconda/.travis.yml b/provision/roles/andrewrothstein.anaconda/.travis.yml new file mode 100644 index 0000000..70fddd1 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/.travis.yml @@ -0,0 +1,40 @@ +--- +sudo: required + +services: + - docker + +language: python +python: "2.7" + +addons: + apt: + packages: + - python-pip + +branches: + except: + - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ + +env: + - OS=centos_7 + - OS=fedora_23 + - OS=fedora_24 + - OS=fedora_25 + - OS=debian_jessie + - OS=ubuntu_trusty + - OS=ubuntu_xenial + +before_install: + - pip install ansible-galaxy-local-deps dcb==0.0.14 + - ansible-galaxy-local-deps-write + +script: + - >- + dcb + --upstreamgroup andrewrothstein + --upstreamapp docker-ansible-role + --pull ${OS} + --write ${OS} + --build ${OS} + --push ${OS} diff --git a/provision/roles/andrewrothstein.anaconda/LICENSE b/provision/roles/andrewrothstein.anaconda/LICENSE new file mode 100644 index 0000000..38ae2c1 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Rothstein + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/provision/roles/andrewrothstein.anaconda/README.md b/provision/roles/andrewrothstein.anaconda/README.md new file mode 100644 index 0000000..1a8557e --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/README.md @@ -0,0 +1,41 @@ +andrewrothstein.anaconda +========= +[![Build Status](https://travis-ci.org/andrewrothstein/ansible-anaconda.svg?branch=master)](https://travis-ci.org/andrewrothstein/ansible-anaconda) + +[![Join the chat at https://gitter.im/andrewrothstein/ansible-anaconda](https://badges.gitter.im/andrewrothstein/ansible-anaconda.svg)](https://gitter.im/andrewrothstein/ansible-anaconda?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +A role that installs [Anaconda](https://www.continuum.io/anaconda-overview) + +Requirements +------------ + +See [meta/main.yml](meta/main.yml) + +Role Variables +-------------- + +See [defaults/main.yml](defaults/main.yml) + +Dependencies +------------ + +See [meta/main.yml](meta/main.yml) + +Example Playbook +---------------- + +```yml +- hosts: servers + roles: + - andrewrothstein.anaconda +``` + +License +------- + +MIT + +Author Information +------------------ + +Andrew Rothstein diff --git a/provision/roles/andrewrothstein.anaconda/circle.yml b/provision/roles/andrewrothstein.anaconda/circle.yml new file mode 100644 index 0000000..ec067df --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/circle.yml @@ -0,0 +1,36 @@ +--- +machine: + services: + - docker + +dependencies: + pre: + - sudo -H pip install --upgrade pip + - sudo -H pip install circleci-helpers ansible-galaxy-local-deps dcb==0.0.14 + - ansible-galaxy-local-deps-write + +test: + override: + - ? | + circle-matrix <<"EHD" + env: + - OS=centos_7 + - OS=fedora_23 + - OS=fedora_24 + - OS=fedora_25 + - OS=debian_jessie + - OS=ubuntu_trusty + - OS=ubuntu_xenial + + script: + - >- + dcb + --upstreamgroup andrewrothstein + --upstreamapp docker-ansible-role + --write ${OS} + --build ${OS} + --push ${OS} + + EHD + : + parallel: true diff --git a/provision/roles/andrewrothstein.anaconda/defaults/main.yml b/provision/roles/andrewrothstein.anaconda/defaults/main.yml new file mode 100644 index 0000000..27b8bd6 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/defaults/main.yml @@ -0,0 +1,46 @@ +--- +# main Anaconda download server +anaconda_mirror : https://repo.continuum.io/archive + +# version of python (2|3) +anaconda_python_ver : 3 + +# anaconda version +anaconda_ver: '4.4.0' + +# anaconda checksums... +# https://docs.continuum.io/anaconda/hashes/Anaconda2-4.4.0-Linux-x86_64.sh-hash +anaconda_checksums : + Anaconda2-4.2.0-Linux-x86_64.sh : sha256:beee286d24fb37dd6555281bba39b3deb5804baec509a9dc5c69185098cf661a + Anaconda3-4.2.0-Linux-x86_64.sh : sha256:73b51715a12b6382dd4df3dd1905b531bd6792d4aa7273b2377a0436d45f0e78 + Anaconda2-4.2.0-MacOSX-x86_64.sh : sha256:a8b3ef86233635d9dcc3499dc384980762a0b42d354a318f8307029c399db452 + Anaconda3-4.2.0-MacOSX-x86_64.sh : sha256:95448921601e1952e01a17ba9767cd3621c154af7fc52dd6b7f57d462155a358 + Anaconda2-4.3.0-Linux-x86_64.sh : sha256:7c52e6e99aabb24a49880130615a48e685da444c3c14eb48d6a65f3313bf745c + Anaconda3-4.3.0-Linux-x86_64.sh : sha256:e9169c3a5029aa820393ac92704eb9ee0701778a085ca7bdc3c57b388ac1beb6 + Anaconda2-4.3.0-MacOSX-x86_64.sh : sha256:834ac0287062929ab5930661735ee617fd379bdfe79f3e0a20aebd614835b6c5 + Anaconda3-4.3.0-MacOSX-x86_64.sh : sha256:c53059b810c5e7a9a5ef9c46a7ed76675dfc7183f4ea867b4d81449cbd5a093d + Anaconda2-4.3.1-Linux-x86_64.sh : sha256:e9b8f2645df6b1527ba56d61343162e0794acc3ee8dde2a6bba353719e2d878d + Anaconda3-4.3.1-Linux-x86_64.sh : sha256:4447b93d2c779201e5fb50cfc45de0ec96c3804e7ad0fe201ab6b99f73e90302 + Anaconda2-4.3.1-MacOSX-x86_64.sh : sha256:35261360f2b01793f441b29715a94052dceaef1137866b7323c76be83c5bcc1a + Anaconda3-4.3.1-MacOSX-x86_64.sh : sha256:a42267203e207cb5e0f539e0d879ead12e436311825c7114d0edd880d001b539 + Anaconda2-4.4.0-Linux-x86_64.sh : sha256:2d30b91ed4d215b6b4a15162a3389e9057b15445a0c02da71bd7bd272e7b824e + Anaconda3-4.4.0-Linux-x86_64.sh : sha256:3301b37e402f3ff3df216fe0458f1e6a4ccbb7e67b4d626eae9651de5ea3ab63 + Anaconda2-4.4.0-MacOSX-x86_64.sh : sha256:ab95aef1110c2a385fd39a17e5f11dfbaabce25c1a5944598de164d7a2772969 + Anaconda3-4.4.0-MacOSX-x86_64.sh : sha256:10fe58f09ae524df2548d17b8bb1e75db17da597a6ec10d695ce01387a2d742 + +# when downloading the anaconda binary it might take a while +# don't let you less great network connection cause the roll to falter +anaconda_timeout_seconds : 600 + +# add the anaconda python onto the front of your path. +# Caveat Emptor: if your OS package manager is python based careful about jamming it +anaconda_make_sys_default : False + +# when installed inside a container delete all of the installer artifacts. won't be +# idempotent but will be smaller in terms of layer/image size +anaconda_cleanup : False + +anaconda_parent_dir: /usr/local +anaconda_link_subdir: anaconda + +anaconda_pkg_update: True diff --git a/provision/roles/andrewrothstein.anaconda/handlers/main.yml b/provision/roles/andrewrothstein.anaconda/handlers/main.yml new file mode 100644 index 0000000..0fee6e8 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for anaconda diff --git a/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info b/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info new file mode 100644 index 0000000..b533493 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Thu Aug 10 05:35:57 2017', version: v3.1.4} diff --git a/provision/roles/andrewrothstein.anaconda/meta/main.yml b/provision/roles/andrewrothstein.anaconda/meta/main.yml new file mode 100644 index 0000000..2b04e31 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/meta/main.yml @@ -0,0 +1,32 @@ +--- +galaxy_info: + author: Andrew Rothstein + description: installs Anaconda + company: BlackRock + license: + - MIT + min_ansible_version: 2.0 + platforms: + - name: EL + versions: + - all + - name: Fedora + versions: + - all + - name: Debian + versions: + - jessie + - name: Ubuntu + versions: + - all + + galaxy_tags: + - python + - conda + - anaconda + +dependencies: + - src: andrewrothstein.bash + version: v1.0.3 + - src: andrewrothstein.unarchive-deps + version: v1.0.8 diff --git a/provision/roles/andrewrothstein.anaconda/tasks/main.yml b/provision/roles/andrewrothstein.anaconda/tasks/main.yml new file mode 100644 index 0000000..b4e15b2 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/tasks/main.yml @@ -0,0 +1,59 @@ +--- +- name: download installer... + become: yes + become_user: root + get_url: + url: '{{anaconda_installer_url}}' + dest: /tmp/{{anaconda_installer_sh}} + timeout: '{{anaconda_timeout_seconds}}' + checksum: '{{anaconda_checksum}}' + mode: 0755 + +- name: installing... + become: yes + become_user: root + command: /tmp/{{anaconda_installer_sh}} -b -p {{anaconda_install_dir}} + args: + creates: '{{anaconda_install_dir}}' + +- name: deleting installer... + become: yes + become_user: root + when: anaconda_cleanup + file: + path: /tmp/{{anaconda_installer_sh}} + state: absent + +- name: link anaconda... + become: yes + become_user: root + file: + src: '{{anaconda_install_dir}}' + dest: '{{anaconda_link_dir}}' + state: link + +- name: update conda pkgs... + become: yes + become_user: root + when: anaconda_pkg_update + command: '{{anaconda_link_dir}}/bin/conda update -y --all' + +- name: remove conda-curl since it conflicts with the system curl + become: yes + become_user: root + command: '{{anaconda_conda_bin}} remove -y curl' + args: + removes: '{{anaconda_link_dir}}/lib/libcurl.a' + +- name: make system default python etc... + become: yes + become_user: root + when: anaconda_make_sys_default + with_items: + - d: /etc/profile.d + f: anaconda.sh + template: + src: '{{item.f}}.j2' + dest: '{{item.d}}/{{item.f}}' + mode: '{{item.m|default("0644")}}' + diff --git a/provision/roles/andrewrothstein.anaconda/templates/anaconda.sh.j2 b/provision/roles/andrewrothstein.anaconda/templates/anaconda.sh.j2 new file mode 100644 index 0000000..0dd931d --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/templates/anaconda.sh.j2 @@ -0,0 +1,2 @@ +export ANACONDA_DIR={{anaconda_link_dir}} +export PATH=${ANACONDA_DIR}/bin:${PATH} diff --git a/provision/roles/andrewrothstein.anaconda/test.yml b/provision/roles/andrewrothstein.anaconda/test.yml new file mode 100644 index 0000000..d3be9f7 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/test.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + roles: + - role: '{{playbook_dir}}' + anaconda_make_sys_default: True + anaconda_cleanup: True + anaconda_pkg_update: False diff --git a/provision/roles/andrewrothstein.anaconda/vars/main.yml b/provision/roles/andrewrothstein.anaconda/vars/main.yml new file mode 100644 index 0000000..07e5716 --- /dev/null +++ b/provision/roles/andrewrothstein.anaconda/vars/main.yml @@ -0,0 +1,11 @@ +--- +anaconda_platform : '{{ansible_system}}-{{ansible_machine}}' +anaconda_name : 'Anaconda{{anaconda_python_ver}}-{{anaconda_ver}}-{{anaconda_platform}}' +anaconda_installer_sh : '{{anaconda_name}}.sh' +anaconda_installer_url : '{{anaconda_mirror}}/{{anaconda_installer_sh}}' +anaconda_checksum : '{{anaconda_checksums[anaconda_installer_sh]}}' + +anaconda_install_dir: '{{anaconda_parent_dir}}/{{anaconda_name}}' +anaconda_link_dir: '{{anaconda_parent_dir}}/{{anaconda_link_subdir}}' + +anaconda_conda_bin: '{{anaconda_link_dir}}/bin/conda' diff --git a/provision/roles/andrewrothstein.bash/.gitignore b/provision/roles/andrewrothstein.bash/.gitignore new file mode 100644 index 0000000..ac9aec3 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/.gitignore @@ -0,0 +1,3 @@ +**~ +*.retry +.ansible-roles/ \ No newline at end of file diff --git a/provision/roles/andrewrothstein.bash/.travis.yml b/provision/roles/andrewrothstein.bash/.travis.yml new file mode 100644 index 0000000..dcbea66 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/.travis.yml @@ -0,0 +1,45 @@ +--- +sudo: required + +services: + - docker + +language: python +python: "2.7" + +addons: + apt: + packages: + - python-pip + +branches: + except: + - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ + +env: + - OS=alpine_3.3 + - OS=alpine_3.4 + - OS=alpine_3.5 + - OS=alpine_3.6 + - OS=alpine_edge + - OS=centos_7 + - OS=fedora_23 + - OS=fedora_24 + - OS=fedora_25 + - OS=debian_jessie + - OS=ubuntu_trusty + - OS=ubuntu_xenial + +before_install: + - pip install ansible-galaxy-local-deps dcb==0.0.14 + - ansible-galaxy-local-deps-write + +script: + - >- + dcb + --upstreamgroup andrewrothstein + --upstreamapp docker-ansible-role + --pull ${OS} + --write ${OS} + --build ${OS} + --push ${OS} diff --git a/provision/roles/andrewrothstein.bash/LICENSE b/provision/roles/andrewrothstein.bash/LICENSE new file mode 100644 index 0000000..97695a4 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Andrew Rothstein + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/provision/roles/andrewrothstein.bash/README.md b/provision/roles/andrewrothstein.bash/README.md new file mode 100644 index 0000000..0ed01a9 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/README.md @@ -0,0 +1,39 @@ +andrewrothstein.bash +========= +[![Build Status](https://travis-ci.org/andrewrothstein/ansible-bash.svg?branch=master)](https://travis-ci.org/andrewrothstein/ansible-bash) + +Installs [Bash](https://www.gnu.org/software/bash/) + +Requirements +------------ + +See [meta/main.yml](meta/main.yml) + +Role Variables +-------------- + +See [defaults/main.yml](defaults/main.yml) + +Dependencies +------------ + +See [meta/main.yml](meta/main.yml) + +Example Playbook +---------------- + +```yml +- hosts: servers + roles: + - andrewrothstein.bash +``` + +License +------- + +MIT + +Author Information +------------------ + +Andrew Rothstein diff --git a/provision/roles/andrewrothstein.bash/circle.yml b/provision/roles/andrewrothstein.bash/circle.yml new file mode 100644 index 0000000..797cbd7 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/circle.yml @@ -0,0 +1,41 @@ +--- +machine: + services: + - docker + +dependencies: + pre: + - sudo -H pip install --upgrade pip + - sudo -H pip install circleci-helpers ansible-galaxy-local-deps dcb==0.0.14 + - ansible-galaxy-local-deps-write + +test: + override: + - ? | + circle-matrix <<"EHD" + env: + - OS=alpine_3.3 + - OS=alpine_3.4 + - OS=alpine_3.5 + - OS=alpine_3.6 + - OS=alpine_edge + - OS=centos_7 + - OS=fedora_23 + - OS=fedora_24 + - OS=fedora_25 + - OS=debian_jessie + - OS=ubuntu_trusty + - OS=ubuntu_xenial + + script: + - >- + dcb + --upstreamgroup andrewrothstein + --upstreamapp docker-ansible-role + --write ${OS} + --build ${OS} + --push ${OS} + + EHD + : + parallel: true diff --git a/provision/roles/andrewrothstein.bash/defaults/main.yml b/provision/roles/andrewrothstein.bash/defaults/main.yml new file mode 100644 index 0000000..92c92f8 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for bash diff --git a/provision/roles/andrewrothstein.bash/handlers/main.yml b/provision/roles/andrewrothstein.bash/handlers/main.yml new file mode 100644 index 0000000..99349ab --- /dev/null +++ b/provision/roles/andrewrothstein.bash/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for bash diff --git a/provision/roles/andrewrothstein.bash/meta/.galaxy_install_info b/provision/roles/andrewrothstein.bash/meta/.galaxy_install_info new file mode 100644 index 0000000..33ec63d --- /dev/null +++ b/provision/roles/andrewrothstein.bash/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Tue Jul 18 06:01:36 2017', version: v1.0.3} diff --git a/provision/roles/andrewrothstein.bash/meta/main.yml b/provision/roles/andrewrothstein.bash/meta/main.yml new file mode 100644 index 0000000..28fb78e --- /dev/null +++ b/provision/roles/andrewrothstein.bash/meta/main.yml @@ -0,0 +1,29 @@ +--- +galaxy_info: + author: Andrew Rothstein + description: installs bash + company: BlackRock + license: + - MIT + min_ansible_version: 2.0 + platforms: + - name: EL + versions: + - all + - name: Fedora + versions: + - all + - name: Ubuntu + versions: + - all + - name: Alpine + versions: + - all + - name: Debian + versions: + - jessie + galaxy_tags: + - development + +dependencies: [] + diff --git a/provision/roles/andrewrothstein.bash/tasks/main.yml b/provision/roles/andrewrothstein.bash/tasks/main.yml new file mode 100644 index 0000000..f01e972 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- name: install bash + become: yes + become_user: root + package: + name: bash + state: present + diff --git a/provision/roles/andrewrothstein.bash/test.yml b/provision/roles/andrewrothstein.bash/test.yml new file mode 100644 index 0000000..9087ea4 --- /dev/null +++ b/provision/roles/andrewrothstein.bash/test.yml @@ -0,0 +1,6 @@ + +--- +- hosts: all + roles: + - role: '{{playbook_dir}}' + \ No newline at end of file diff --git a/provision/roles/andrewrothstein.bash/tests/inventory b/provision/roles/andrewrothstein.bash/tests/inventory new file mode 100644 index 0000000..d18580b --- /dev/null +++ b/provision/roles/andrewrothstein.bash/tests/inventory @@ -0,0 +1 @@ +localhost \ No newline at end of file diff --git a/provision/roles/andrewrothstein.bash/tests/test.yml b/provision/roles/andrewrothstein.bash/tests/test.yml new file mode 100644 index 0000000..aaa9f3d --- /dev/null +++ b/provision/roles/andrewrothstein.bash/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - bash \ No newline at end of file diff --git a/provision/roles/andrewrothstein.bash/vars/main.yml b/provision/roles/andrewrothstein.bash/vars/main.yml new file mode 100644 index 0000000..9aefd1a --- /dev/null +++ b/provision/roles/andrewrothstein.bash/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for bash diff --git a/provision/roles/andrewrothstein.unarchive-deps/.gitignore b/provision/roles/andrewrothstein.unarchive-deps/.gitignore new file mode 100644 index 0000000..9c8285f --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/.gitignore @@ -0,0 +1,3 @@ +**~ +Dockerfile.* +requirements.yml \ No newline at end of file diff --git a/provision/roles/andrewrothstein.unarchive-deps/LICENSE b/provision/roles/andrewrothstein.unarchive-deps/LICENSE new file mode 100644 index 0000000..38ae2c1 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Rothstein + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/provision/roles/andrewrothstein.unarchive-deps/README.md b/provision/roles/andrewrothstein.unarchive-deps/README.md new file mode 100644 index 0000000..6311c38 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/README.md @@ -0,0 +1,24 @@ +[![CircleCI](https://circleci.com/gh/andrewrothstein/ansible-unarchive-deps.svg?style=svg)](https://circleci.com/gh/andrewrothstein/ansible-unarchive-deps) +andrewrothstein.unarchive-deps +============== + +Dependencies for the Ansible unarchive module, et. al. + +Example Playbook +---------------- + +```yml +- hosts: servers + roles: + - andrewrothstein.unarchive-deps +``` + +License +------- + +MIT + +Author Information +------------------ + +Andrew Rothstein diff --git a/provision/roles/andrewrothstein.unarchive-deps/circle.yml b/provision/roles/andrewrothstein.unarchive-deps/circle.yml new file mode 100644 index 0000000..d0d1e18 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/circle.yml @@ -0,0 +1,39 @@ +--- +machine: + services: + - docker + +dependencies: + pre: + - sudo -H pip install --upgrade pip + - sudo -H pip install circleci-helpers ansible-galaxy-local-deps dcb==0.0.13 + - ansible-galaxy-local-deps-write + +test: + override: + - ? | + circle-matrix <<"EHD" + env: + - OS=alpine_3.3 + - OS=alpine_3.4 + - OS=alpine_3.5 + - OS=alpine_edge + - OS=centos_7 + - OS=fedora_23 + - OS=fedora_24 + - OS=fedora_25 + - OS=ubuntu_trusty + - OS=ubuntu_xenial + + script: + - >- + dcb + --upstreamgroup andrewrothstein + --upstreamapp docker-ansible-role + --write ${OS} + --build ${OS} + --push ${OS} + + EHD + : + parallel: true diff --git a/provision/roles/andrewrothstein.unarchive-deps/defaults/main.yml b/provision/roles/andrewrothstein.unarchive-deps/defaults/main.yml new file mode 100644 index 0000000..381b1bc --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/defaults/main.yml @@ -0,0 +1,5 @@ +--- +unarchive_deps_all_pkgs: + - unzip + - gzip + - bzip2 diff --git a/provision/roles/andrewrothstein.unarchive-deps/meta/.galaxy_install_info b/provision/roles/andrewrothstein.unarchive-deps/meta/.galaxy_install_info new file mode 100644 index 0000000..c4a4b8e --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Tue Jul 18 06:01:44 2017', version: v1.0.8} diff --git a/provision/roles/andrewrothstein.unarchive-deps/meta/main.yml b/provision/roles/andrewrothstein.unarchive-deps/meta/main.yml new file mode 100644 index 0000000..df8176f --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/meta/main.yml @@ -0,0 +1,32 @@ +--- +galaxy_info: + author: Andrew Rothstein + description: role to install deps needed for the Ansible unarchive module + company: BlackRock + license: + - MIT + min_ansible_version: 2.0 + platforms: + - name: EL + versions: + - all + - name: Fedora + versions: + - all + - name: Ubuntu + versions: + - all + - name: MacOSX + versions: + - all + - name: Alpine + version: + - all + + galaxy_tags: + - archive + - unarchive + - packaging + +dependencies: [] + diff --git a/provision/roles/andrewrothstein.unarchive-deps/tasks/main.yml b/provision/roles/andrewrothstein.unarchive-deps/tasks/main.yml new file mode 100644 index 0000000..3bb6be2 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: resolve platform specific vars + include_vars: "{{item}}" + with_first_found: + - files: + - "{{ansible_distribution}}-{{ansible_distribution_release}}.yml" + - "{{ansible_distribution}}.yml" + - "{{ansible_os_family}}.yml" + skip: true + paths: + - '{{role_path}}/vars' + +- name: install common pkgs... + become: '{{unarchive_deps_privilege_escalation | default(True)}}' + become_user: root + with_items: + - '{{unarchive_deps_all_pkgs}}' + - '{{unarchive_deps_tar_pkg|default("tar")}}' + - '{{unarchive_deps_xz_pkg|default("xz")}}' + package: + name: '{{item}}' + state: present + diff --git a/provision/roles/andrewrothstein.unarchive-deps/test.yml b/provision/roles/andrewrothstein.unarchive-deps/test.yml new file mode 100644 index 0000000..e938742 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/test.yml @@ -0,0 +1,4 @@ +--- +- hosts: all + roles: + - role: '{{playbook_dir}}' diff --git a/provision/roles/andrewrothstein.unarchive-deps/vars/Darwin.yml b/provision/roles/andrewrothstein.unarchive-deps/vars/Darwin.yml new file mode 100644 index 0000000..e1d5b53 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/vars/Darwin.yml @@ -0,0 +1,3 @@ +--- +unarchive_deps_privilege_escalation: False +unarchive_deps_tar_pkg: gnu-tar diff --git a/provision/roles/andrewrothstein.unarchive-deps/vars/Debian.yml b/provision/roles/andrewrothstein.unarchive-deps/vars/Debian.yml new file mode 100644 index 0000000..c31fc64 --- /dev/null +++ b/provision/roles/andrewrothstein.unarchive-deps/vars/Debian.yml @@ -0,0 +1,2 @@ +--- +unarchive_deps_xz_pkg: xz-utils From 7d06540ef3b1dd6cc4b0e707a57aeb88f332d2fa Mon Sep 17 00:00:00 2001 From: rreben Date: Thu, 10 Aug 2017 22:23:44 +0200 Subject: [PATCH 3/3] updated repo to use python 2 --- notebooks/basket4py.ipynb | 10 +++++----- provision/playbook.yml | 1 + provision/roles/andrewrothstein.anaconda/.travis.yml | 2 +- .../andrewrothstein.anaconda/meta/.galaxy_install_info | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/notebooks/basket4py.ipynb b/notebooks/basket4py.ipynb index b6fa41b..de7c9d9 100644 --- a/notebooks/basket4py.ipynb +++ b/notebooks/basket4py.ipynb @@ -166,21 +166,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 2", "language": "python", - "name": "python3" + "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3 + "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" + "pygments_lexer": "ipython2", + "version": "2.7.13" } }, "nbformat": 4, diff --git a/provision/playbook.yml b/provision/playbook.yml index 3f24c15..ef2837e 100644 --- a/provision/playbook.yml +++ b/provision/playbook.yml @@ -2,6 +2,7 @@ become: true vars: + - anaconda_python_ver : 2 - anaconda_make_sys_default: True roles: diff --git a/provision/roles/andrewrothstein.anaconda/.travis.yml b/provision/roles/andrewrothstein.anaconda/.travis.yml index 70fddd1..8b1fba9 100644 --- a/provision/roles/andrewrothstein.anaconda/.travis.yml +++ b/provision/roles/andrewrothstein.anaconda/.travis.yml @@ -24,7 +24,7 @@ env: - OS=debian_jessie - OS=ubuntu_trusty - OS=ubuntu_xenial - + before_install: - pip install ansible-galaxy-local-deps dcb==0.0.14 - ansible-galaxy-local-deps-write diff --git a/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info b/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info index b533493..e859d50 100644 --- a/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info +++ b/provision/roles/andrewrothstein.anaconda/meta/.galaxy_install_info @@ -1 +1 @@ -{install_date: 'Thu Aug 10 05:35:57 2017', version: v3.1.4} +{install_date: 'Thu Aug 10 19:59:54 2017', version: v3.1.4}