<commit_before>"""Tests for assess_constraints module."""

from contextlib import contextmanager
import logging
from mock import call, Mock, patch
import os
import StringIO

from assess_constraints import (
    assess_virt_type_constraints,
    assess_instance_type_constraints,
    Constraints,
    deploy_constraint,
    deploy_charm_constraint,
    juju_show_machine_hardware,
    parse_args,
    main,
    mem_to_int,
    INSTANCE_TYPES,
    )
from tests import (
    parse_error,
    TestCase,
    )
from tests.test_jujupy import fake_juju_client
from utility import (
    JujuAssertionError,
    temp_dir,
    )

class TestParseArgs(TestCase):

    def test_common_args(self):
        args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"])
        self.assertEqual("an-env", args.env)
        self.assertEqual("/bin/juju", args.juju_bin)
        self.assertEqual("/tmp/logs", args.logs)
        self.assertEqual("an-env-mod", args.temp_env_name)
        self.assertEqual(False, args.debug)

    def test_help(self):
        fake_stdout = StringIO.StringIO()
        with parse_error(self) as fake_stderr:
            with patch("sys.stdout", fake_stdout):
                parse_args(["--help"])
        self.assertEqual("", fake_stderr.getvalue())

class TestConstraints(TestCase):

    def test_mem_to_int(self):
        self.assertEqual(1, mem_to_int('1'))
        self.assertEqual(1, mem_to_int('1M'))
        self.assertEqual(1024, mem_to_int('1G'))
        self.assertEqual(4096, mem_to_int('4G'))
        with self.assertRaises(JujuAssertionError):
            mem_to_int('40XB')

    def test_str_operator(self):
        constraints = Constraints(mem='2G', root_disk='4G', virt_type='lxd')
        self.assertEqual('mem=2G virt-type=lxd root-disk=4G',
                         str(constraints))

    def test_str_operator_none(self):
        self.assertEqual('', str(Constraints()))
        self.assertEqual('', str(Constraints(arch=None)))

    def test__meets_string(self):
        meets_string = Constraints._meets_string
        self.assertTrue(meets_string(None, 'amd64'))
        self.assertTrue(meets_string('amd64', 'amd64'))
        self.assertFalse(meets_string('i32', 'amd64'))

    def test__meets_min_int(self):
        meets_min_int = Constraints._meets_min_int
        self.assertTrue(meets_min_int(None, '2'))
        self.assertFalse(meets_min_int('2', '1'))
        self.assertTrue(meets_min_int('2', '2'))
        self.assertTrue(meets_min_int('2', '3'))

    def test__meets_min_mem(self):
        meets_min_mem = Constraints._meets_min_mem
        self.assertTrue(meets_min_mem(None, '64'))
        self.assertFalse(meets_min_mem('1G', '512M'))
        self.assertTrue(meets_min_mem('1G', '1024'))
        self.assertTrue(meets_min_mem('1G', '1G'))
        self.assertTrue(meets_min_mem('1G', '2G'))

    def test_meets_instance_type(self):
        constraints = Constraints(instance_type='t2.micro')
        data1 = {'root-disk': '1G', 'cpu-power': '10', 'cores': '1'}
        self.assertTrue(constraints.meets_instance_type(data1))
        data2 = {'root-disk': '8G', 'cpu-power': '20', 'cores': '1'}
        self.assertFalse(constraints.meets_instance_type(data2))
        data3 = dict(data1, arch='amd64')
        self.assertTrue(constraints.meets_instance_type(data3))
        data4 = {'cpu-power': '10', 'cores': '1'}
        with self.assertRaises(JujuAssertionError):
            constraints.meets_instance_type(data4)

    def test_meets_instance_type_fix(self):
        constraints = Constraints(instance_type='t2.micro')
        data = {'root-disk': '1G', 'cpu-power': '10', 'cpu-cores': '1'}
        self.assertTrue(constraints.meets_instance_type(data))

class TestMain(TestCase):

    def test_main(self):
        argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
        client = Mock(spec=["is_jes_enabled"])
        with patch("assess_constraints.configure_logging",
                   autospec=True) as mock_cl:
            with patch("assess_constraints.BootstrapManager.booted_context",
                       autospec=True) as mock_bc:
                with patch('deploy_stack.client_from_config',
                           return_value=client) as mock_cfc:
                    with patch("assess_constraints.assess_constraints",
                               autospec=True) as mock_assess:
                        main(argv)
        mock_cl.assert_called_once_with(logging.DEBUG)
        mock_cfc.assert_called_once_with('an-env', "/bin/juju", debug=False)
        self.assertEqual(mock_bc.call_count, 1)
        mock_assess.assert_called_once_with(client, False)

class TestAssess(TestCase):

    @contextmanager
    def prepare_deploy_mock(self):
        # Using fake_client means that deploy and get_status have plausible
        # results.  Wrapping it in a Mock causes every call to be recorded, and
        # allows assertions to be made about calls.  Mocks and the fake client
        # can also be used separately.
        """Mock a client and the deploy function."""
        fake_client = Mock(wraps=fake_juju_client())
        fake_client.bootstrap()
        with patch('jujupy.EnvJujuClient.deploy',
                   autospec=True) as deploy_mock:
            yield fake_client, deploy_mock

    def gather_constraint_args(self, mock):
        """Create a list of the constraint arguments passed to the mock."""
        constraint_args = [
            args[1]["constraints"] for args in mock.call_args_list]
        return constraint_args

    @contextmanager
    def patch_hardware(self, return_values):
        """Patch juju_show_machine_hardware with a series of return values."""
        def pop_hardware(client, machine):
            return return_values.pop(0)
        with patch('assess_constraints.juju_show_machine_hardware',
                   autospec=True, side_effect=pop_hardware) as hardware_mock:
            yield hardware_mock

    def test_virt_type_constraints_with_kvm(self):
        assert_constraints_calls = ["virt-type=lxd", "virt-type=kvm"]
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_virt_type_constraints(fake_client, True)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)

    def test_virt_type_constraints_without_kvm(self):
        assert_constraints_calls = ["virt-type=lxd"]
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_virt_type_constraints(fake_client, False)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)

    @contextmanager
    def patch_instance_spec(self, fake_provider):
        fake_instance_types = ['bar', 'baz']
        bar_data = {'cpu-power': '20'}
        baz_data = {'cpu-power': '30'}

        def mock_get_instance_spec(instance_type):
            if 'bar' == instance_type:
                return bar_data
            elif 'baz' == instance_type:
                return baz_data
            else:
                raise ValueError('instance-type not in mock.')
        with patch.dict(INSTANCE_TYPES, {fake_provider: fake_instance_types}):
            with patch('assess_constraints.get_instance_spec', autospec=True,
                       side_effect=mock_get_instance_spec) as spec_mock:
                with self.patch_hardware([bar_data, baz_data]):
                    yield spec_mock

    def test_instance_type_constraints(self):
        assert_constraints_calls = ['instance-type=bar', 'instance-type=baz']
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            fake_provider = fake_client.env.config.get('type')
            with self.patch_instance_spec(fake_provider) as spec_mock:
                assess_instance_type_constraints(fake_client)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)
        self.assertEqual(spec_mock.call_args_list, [call('bar'), call('baz')])

    def test_instance_type_constraints_missing(self):
        fake_client = Mock(wraps=fake_juju_client())
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_instance_type_constraints(fake_client)
        self.assertFalse(deploy_mock.called)

class TestDeploy(TestCase):

    def test_deploy_constraint(self):
        fake_client = Mock(wraps=fake_juju_client())
        fake_client.attach_mock(Mock(), 'deploy')
        fake_client.attach_mock(Mock(), 'wait_for_workloads')
        charm_name = 'test-constraint'
        charm_series = 'xenial'
        constraints = Constraints(mem='10GB')
        with temp_dir() as charm_dir:
            charm = os.path.join(charm_dir, charm_series, charm_name)
            deploy_constraint(fake_client, constraints, charm, charm_series,
                              charm_dir)
        fake_client.deploy.assert_called_once_with(
            charm, series=charm_series, repository=charm_dir,
            constraints=str(constraints))
        fake_client.wait_for_workloads.assert_called_once_with()

    def test_deploy_charm_constraint(self):
        fake_client = Mock(wraps=fake_juju_client())
        charm_name = 'test-constraint'
        charm_series = 'xenial'
        constraints = Constraints(mem='10GB')
        with temp_dir() as charm_dir:
            with patch('assess_constraints.deploy_constraint',
                       autospec=True) as deploy_mock:
                deploy_charm_constraint(fake_client, constraints, charm_name,
                                        charm_series, charm_dir)
        charm = os.path.join(charm_dir, charm_series, charm_name)
        deploy_mock.assert_called_once_with(fake_client, constraints, charm,
                                            charm_series, charm_dir)

class TestJujuWrappers(TestCase):

    SAMPLE_JUJU_SHOW_MACHINE_OUTPUT = """\
model:
  name: controller
  controller: assessconstraints-20160914122952-temp-env
  cloud: lxd
  region: localhost
  version: 2.0-beta18
machines:
  "0":
    juju-status:
      current: started
      since: 14 Sep 2016 12:32:17-04:00
      version: 2.0-beta18
    dns-name: 10.252.22.39
    instance-id: juju-7d249e-0
    machine-status:
      current: pending
      since: 14 Sep 2016 12:32:07-04:00
    series: xenial
    hardware: arch=amd64 cpu-cores=0 mem=0M
    controller-member-status: has-vote
applications: {}"""

    def test_juju_show_machine_hardware(self):
        """Check the juju_show_machine_hardware data translation."""
        output_mock = Mock(
            return_value=self.SAMPLE_JUJU_SHOW_MACHINE_OUTPUT)
        fake_client = Mock(get_juju_output=output_mock)
        data = juju_show_machine_hardware(fake_client, '0')
        output_mock.assert_called_once_with('show-machine', '0',
                                            '--format', 'yaml')
        self.assertEqual({'arch': 'amd64', 'cpu-cores': '0', 'mem': '0M'},
                         data)
<commit_msg>Added unit tests for the update assess_instance_type_constraints function.<commit_after>"""Tests for assess_constraints module."""

from contextlib import contextmanager
import logging
from mock import call, Mock, patch
import os
import StringIO

from assess_constraints import (
    assess_virt_type_constraints,
    assess_instance_type_constraints,
    Constraints,
    deploy_constraint,
    deploy_charm_constraint,
    juju_show_machine_hardware,
    parse_args,
    main,
    mem_to_int,
    INSTANCE_TYPES,
    )
from tests import (
    parse_error,
    TestCase,
    )
from tests.test_jujupy import fake_juju_client
from utility import (
    JujuAssertionError,
    temp_dir,
    )

class TestParseArgs(TestCase):

    def test_common_args(self):
        args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"])
        self.assertEqual("an-env", args.env)
        self.assertEqual("/bin/juju", args.juju_bin)
        self.assertEqual("/tmp/logs", args.logs)
        self.assertEqual("an-env-mod", args.temp_env_name)
        self.assertEqual(False, args.debug)

    def test_help(self):
        fake_stdout = StringIO.StringIO()
        with parse_error(self) as fake_stderr:
            with patch("sys.stdout", fake_stdout):
                parse_args(["--help"])
        self.assertEqual("", fake_stderr.getvalue())

class TestConstraints(TestCase):

    def test_mem_to_int(self):
        self.assertEqual(1, mem_to_int('1'))
        self.assertEqual(1, mem_to_int('1M'))
        self.assertEqual(1024, mem_to_int('1G'))
        self.assertEqual(4096, mem_to_int('4G'))
        with self.assertRaises(JujuAssertionError):
            mem_to_int('40XB')

    def test_str_operator(self):
        constraints = Constraints(mem='2G', root_disk='4G', virt_type='lxd')
        self.assertEqual('mem=2G virt-type=lxd root-disk=4G',
                         str(constraints))

    def test_str_operator_none(self):
        self.assertEqual('', str(Constraints()))
        self.assertEqual('', str(Constraints(arch=None)))

    def test__meets_string(self):
        meets_string = Constraints._meets_string
        self.assertTrue(meets_string(None, 'amd64'))
        self.assertTrue(meets_string('amd64', 'amd64'))
        self.assertFalse(meets_string('i32', 'amd64'))

    def test__meets_min_int(self):
        meets_min_int = Constraints._meets_min_int
        self.assertTrue(meets_min_int(None, '2'))
        self.assertFalse(meets_min_int('2', '1'))
        self.assertTrue(meets_min_int('2', '2'))
        self.assertTrue(meets_min_int('2', '3'))

    def test__meets_min_mem(self):
        meets_min_mem = Constraints._meets_min_mem
        self.assertTrue(meets_min_mem(None, '64'))
        self.assertFalse(meets_min_mem('1G', '512M'))
        self.assertTrue(meets_min_mem('1G', '1024'))
        self.assertTrue(meets_min_mem('1G', '1G'))
        self.assertTrue(meets_min_mem('1G', '2G'))

    def test_meets_instance_type(self):
        constraints = Constraints(instance_type='t2.micro')
        data1 = {'mem': '1G', 'cpu-power': '10', 'cores': '1'}
        self.assertTrue(constraints.meets_instance_type(data1))
        data2 = {'mem': '8G', 'cpu-power': '20', 'cores': '1'}
        self.assertFalse(constraints.meets_instance_type(data2))
        data3 = dict(data1, arch='amd64')
        self.assertTrue(constraints.meets_instance_type(data3))
        data4 = {'root-disk': '1G', 'cpu-power': '10', 'cores': '1'}
        with self.assertRaises(JujuAssertionError):
            constraints.meets_instance_type(data4)

    def test_meets_instance_type_fix(self):
        constraints = Constraints(instance_type='t2.micro')
        data = {'mem': '1G', 'cpu-power': '10', 'cpu-cores': '1'}
        self.assertTrue(constraints.meets_instance_type(data))

class TestMain(TestCase):

    def test_main(self):
        argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
        client = Mock(spec=["is_jes_enabled"])
        with patch("assess_constraints.configure_logging",
                   autospec=True) as mock_cl:
            with patch("assess_constraints.BootstrapManager.booted_context",
                       autospec=True) as mock_bc:
                with patch('deploy_stack.client_from_config',
                           return_value=client) as mock_cfc:
                    with patch("assess_constraints.assess_constraints",
                               autospec=True) as mock_assess:
                        main(argv)
        mock_cl.assert_called_once_with(logging.DEBUG)
        mock_cfc.assert_called_once_with('an-env', "/bin/juju", debug=False)
        self.assertEqual(mock_bc.call_count, 1)
        mock_assess.assert_called_once_with(client, False)

class TestAssess(TestCase):

    @contextmanager
    def prepare_deploy_mock(self):
        # Using fake_client means that deploy and get_status have plausible
        # results.  Wrapping it in a Mock causes every call to be recorded, and
        # allows assertions to be made about calls.  Mocks and the fake client
        # can also be used separately.
        """Mock a client and the deploy function."""
        fake_client = Mock(wraps=fake_juju_client())
        fake_client.bootstrap()
        with patch('jujupy.EnvJujuClient.deploy',
                   autospec=True) as deploy_mock:
            yield fake_client, deploy_mock

    def gather_constraint_args(self, mock):
        """Create a list of the constraint arguments passed to the mock."""
        constraint_args = [
            args[1]["constraints"] for args in mock.call_args_list]
        return constraint_args

    @contextmanager
    def patch_hardware(self, return_values):
        """Patch juju_show_machine_hardware with a series of return values."""
        def pop_hardware(client, machine):
            return return_values.pop(0)
        with patch('assess_constraints.juju_show_machine_hardware',
                   autospec=True, side_effect=pop_hardware) as hardware_mock:
            yield hardware_mock

    def test_virt_type_constraints_with_kvm(self):
        assert_constraints_calls = ["virt-type=lxd", "virt-type=kvm"]
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_virt_type_constraints(fake_client, True)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)

    def test_virt_type_constraints_without_kvm(self):
        assert_constraints_calls = ["virt-type=lxd"]
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_virt_type_constraints(fake_client, False)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)

    @contextmanager
    def patch_instance_spec(self, fake_provider, passing=True):
        fake_instance_types = ['bar', 'baz']
        bar_data = {'cpu-power': '20'}
        baz_data = {'cpu-power': '30'}

        def mock_get_instance_spec(instance_type):
            if 'bar' == instance_type:
                return bar_data
            elif 'baz' == instance_type and passing:
                return baz_data
            elif 'baz' == instance_type:
                return {'cpu-power': '40'}
            else:
                raise ValueError('instance-type not in mock.')
        with patch.dict(INSTANCE_TYPES, {fake_provider: fake_instance_types}):
            with patch('assess_constraints.get_instance_spec', autospec=True,
                       side_effect=mock_get_instance_spec) as spec_mock:
                with self.patch_hardware([bar_data, baz_data]):
                    yield spec_mock

    def test_instance_type_constraints(self):
        assert_constraints_calls = ['instance-type=bar', 'instance-type=baz']
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            fake_provider = fake_client.env.config.get('type')
            with self.patch_instance_spec(fake_provider) as spec_mock:
                assess_instance_type_constraints(fake_client)
        constraints_calls = self.gather_constraint_args(deploy_mock)
        self.assertEqual(constraints_calls, assert_constraints_calls)
        self.assertEqual(spec_mock.call_args_list, [call('bar'), call('baz')])

    def test_instance_type_constraints_fail(self):
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            fake_provider = fake_client.env.config.get('type')
            with self.patch_instance_spec(fake_provider, False) as spec_mock:
                with self.assertRaises(ValueError):
                    assess_instance_type_constraints(fake_client)

    def test_instance_type_constraints_missing(self):
        fake_client = Mock(wraps=fake_juju_client())
        with self.prepare_deploy_mock() as (fake_client, deploy_mock):
            assess_instance_type_constraints(fake_client)
        self.assertFalse(deploy_mock.called)

class TestDeploy(TestCase):

    def test_deploy_constraint(self):
        fake_client = Mock(wraps=fake_juju_client())
        fake_client.attach_mock(Mock(), 'deploy')
        fake_client.attach_mock(Mock(), 'wait_for_workloads')
        charm_name = 'test-constraint'
        charm_series = 'xenial'
        constraints = Constraints(mem='10GB')
        with temp_dir() as charm_dir:
            charm = os.path.join(charm_dir, charm_series, charm_name)
            deploy_constraint(fake_client, constraints, charm, charm_series,
                              charm_dir)
        fake_client.deploy.assert_called_once_with(
            charm, series=charm_series, repository=charm_dir,
            constraints=str(constraints))
        fake_client.wait_for_workloads.assert_called_once_with()

    def test_deploy_charm_constraint(self):
        fake_client = Mock(wraps=fake_juju_client())
        charm_name = 'test-constraint'
        charm_series = 'xenial'
        constraints = Constraints(mem='10GB')
        with temp_dir() as charm_dir:
            with patch('assess_constraints.deploy_constraint',
                       autospec=True) as deploy_mock:
                deploy_charm_constraint(fake_client, constraints, charm_name,
                                        charm_series, charm_dir)
        charm = os.path.join(charm_dir, charm_series, charm_name)
        deploy_mock.assert_called_once_with(fake_client, constraints, charm,
                                            charm_series, charm_dir)

class TestJujuWrappers(TestCase):

    SAMPLE_JUJU_SHOW_MACHINE_OUTPUT = """\
model:
  name: controller
  controller: assessconstraints-20160914122952-temp-env
  cloud: lxd
  region: localhost
  version: 2.0-beta18
machines:
  "0":
    juju-status:
      current: started
      since: 14 Sep 2016 12:32:17-04:00
      version: 2.0-beta18
    dns-name: 10.252.22.39
    instance-id: juju-7d249e-0
    machine-status:
      current: pending
      since: 14 Sep 2016 12:32:07-04:00
    series: xenial
    hardware: arch=amd64 cpu-cores=0 mem=0M
    controller-member-status: has-vote
applications: {}"""

    def test_juju_show_machine_hardware(self):
        """Check the juju_show_machine_hardware data translation."""
        output_mock = Mock(
            return_value=self.SAMPLE_JUJU_SHOW_MACHINE_OUTPUT)
        fake_client = Mock(get_juju_output=output_mock)
        data = juju_show_machine_hardware(fake_client, '0')
        output_mock.assert_called_once_with('show-machine', '0',
                                            '--format', 'yaml')
        self.assertEqual({'arch': 'amd64', 'cpu-cores': '0', 'mem': '0M'},
                         data)
<|endoftext|>


<commit_before>from flask import *
from . import web_module as mod_web
from . import controllers
from application import cache

@cache.cached()
@mod_web.route("/", methods = ["GET", "POST"])
def index():
    schedule = controllers.get_schedule()
    return render_template("web.index.html", schedule = schedule)

@mod_web.route("/schedule")
def forward_schedule():
    return redirect("/#schedule")

@cache.cached()
@mod_web.route("/rules", methods = ["GET", "POST"])
def rules():
    return render_template("web.rules.html")

@mod_web.route("/team", methods = ["GET"])
def team():
    board = controllers.get_team_members_by_team(["board"])[0]
    team_list = ["finance", "logistics", "outreach", "promotions", "sponsors", "tech"]
    teams = controllers.get_team_members_by_team(team_list)
    return render_template("web.team.html", board = board, teams = teams)

@cache.cached()
@mod_web.route("/sponsors", methods = ["GET"])
def sponsors():
    return mod_web.send_static_file("sponsors.pdf")

@mod_web.route("/sponsors.pdf", methods = ["GET"])
@mod_web.route("/sponsor.pdf", methods = ["GET"])
@mod_web.route("/sponsor", methods = ["GET"])
def foward_sponsors():
    return redirect("/sponsors")

@cache.cached()
@mod_web.route("/waiver", methods = ["GET"])
def waiver():
    return mod_web.send_static_file("waiver.pdf")

@mod_web.route("/waiver.pdf", methods = ["GET"])
def foward_waiver():
    return redirect("/waiver")

@mod_web.route("/hardware", method = ["GET"])
def hardware():
    return mod_web.send_static_file("hardware.pdf")

@cache.cached()
@mod_web.route("/map", methods = ["GET"])
def map():
    return mod_web.send_static_file("map.jpg")

@mod_web.route("/map.jpeg", methods = ["GET"])
@mod_web.route("/map.jpg", methods = ["GET"])
def foward_map():
    return redirect("/map")

@mod_web.route("/privacy", methods = ["GET"])
def privacy():
    return mod_web.send_static_file("privacystatement.pdf")

@mod_web.route("/privacy.pdf", methods = ["GET"])
@mod_web.route("/privacystatement.pdf", methods = ["GET"])
def forward_privacy():
    return redirect("/privacy")

@mod_web.route("/chaperones", methods = ["GET"])
def chaperones():
    return redirect("https://docs.google.com/forms/d/13l1ToVeDeBSEF4OBdHt6gSbYan8xnMFVG6BTRcKrEQw/viewform")

#For mailing list
#mod_web.route("/confirm/<token>")
# def confirm_email(token):
# 	controllers.confirm_email(token)
# 	return redirect("/?status=confirmed")
<commit_msg>Forgot workshops route.<commit_after>from flask import *
from . import web_module as mod_web
from . import controllers
from application import cache

@cache.cached()
@mod_web.route("/", methods = ["GET", "POST"])
def index():
    schedule = controllers.get_schedule()
    return render_template("web.index.html", schedule = schedule)

@mod_web.route("/schedule")
def forward_schedule():
    return redirect("/#schedule")

@cache.cached()
@mod_web.route("/rules", methods = ["GET", "POST"])
def rules():
    return render_template("web.rules.html")

@mod_web.route("/team", methods = ["GET"])
def team():
    board = controllers.get_team_members_by_team(["board"])[0]
    team_list = ["finance", "logistics", "outreach", "promotions", "sponsors", "tech"]
    teams = controllers.get_team_members_by_team(team_list)
    return render_template("web.team.html", board = board, teams = teams)

@cache.cached()
@mod_web.route("/sponsors", methods = ["GET"])
def sponsors():
    return mod_web.send_static_file("sponsors.pdf")

@mod_web.route("/sponsors.pdf", methods = ["GET"])
@mod_web.route("/sponsor.pdf", methods = ["GET"])
@mod_web.route("/sponsor", methods = ["GET"])
def foward_sponsors():
    return redirect("/sponsors")

@cache.cached()
@mod_web.route("/waiver", methods = ["GET"])
def waiver():
    return mod_web.send_static_file("waiver.pdf")

@mod_web.route("/waiver.pdf", methods = ["GET"])
def foward_waiver():
    return redirect("/waiver")

@mod_web.route("/hardware", method = ["GET"])
def hardware():
    return mod_web.send_static_file("hardware.pdf")

@mod_web.route("/workshops", method = ["GET"])
def workshops():
    return mod_web.send_static_file("workshops.pdf")

@mod_web.route("/workshop", method = ["GET"])
def workshop():
    return redirect("workshops")

@cache.cached()
@mod_web.route("/map", methods = ["GET"])
def map():
    return mod_web.send_static_file("map.jpg")

@mod_web.route("/map.jpeg", methods = ["GET"])
@mod_web.route("/map.jpg", methods = ["GET"])
def foward_map():
    return redirect("/map")

@mod_web.route("/privacy", methods = ["GET"])
def privacy():
    return mod_web.send_static_file("privacystatement.pdf")

@mod_web.route("/privacy.pdf", methods = ["GET"])
@mod_web.route("/privacystatement.pdf", methods = ["GET"])
def forward_privacy():
    return redirect("/privacy")

@mod_web.route("/chaperones", methods = ["GET"])
def chaperones():
    return redirect("https://docs.google.com/forms/d/13l1ToVeDeBSEF4OBdHt6gSbYan8xnMFVG6BTRcKrEQw/viewform")

#For mailing list
#mod_web.route("/confirm/<token>")
# def confirm_email(token):
# 	controllers.confirm_email(token)
# 	return redirect("/?status=confirmed")
<|endoftext|>


<commit_before>from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login_manager

class Employee(UserMixin, db.Model):
    """
    Create an Employee table
    """

    # Ensures table will be named in plural and not in singular
    # as is the name of the model
    __tablename__ = 'employees'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(60), index=True, unique=True)
    username = db.Column(db.String(60), index=True, unique=True)
    first_name = db.Column(db.String(60), index=True)
    last_name = db.Column(db.String(60), index=True)
    password_hash = db.Column(db.String(128))
    department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    is_admin = db.Column(db.Boolean, default=False)

    @property
    def password(self):
        """
        Prevent pasword from being accessed
        """
        raise AttributeError('password is not a readable attribute.')

    @password.setter
    def password(self, password):
        """
        Set password to a hashed password
        """
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        """
        Check if hashed password matches actual password
        """
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return '<Employee: {}>'.format(self.username)

# Set up user_loader
@login_manager.user_loader
def load_user(user_id):
    return Employee.query.get(int(user_id))

class Department(db.Model):
    """
    Create a Department table
    """

    __tablename__ = 'departments'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    description = db.Column(db.String(200))
    employees = db.relationship('Employee', backref='department',
                                lazy='dynamic')

    def __repr__(self):
        return '<Department: {}>'.format(self.name)

class Role(db.Model):
    """
    Create a Role table
    """

    __tablename__ = 'roles'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    description = db.Column(db.String(200))
    employees = db.relationship('Employee', backref='role',
                                lazy='dynamic')

    def __repr__(self):
        return '<Role: {}>'.format(self.name)

class Thing(db.Model):
    """
    Create a Thing table
    for physical objects
    """

    __tablename__ = 'thing'

    id = db.Column(db.Integer, primary_key=True)
    barcode = db.Column(db.String, unique=True)
    name = db.Column(db.String)
    description = db.Column(db.String)

    def __repr__(selfself):
        return '<Item>: {}'.format(self.name)<commit_msg>Update<commit_after>from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login_manager

class Employee(UserMixin, db.Model):
    """
    Create an Employee table
    """

    # Ensures table will be named in plural and not in singular
    # as is the name of the model
    __tablename__ = 'employees'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(60), index=True, unique=True)
    username = db.Column(db.String(60), index=True, unique=True)
    first_name = db.Column(db.String(60), index=True)
    last_name = db.Column(db.String(60), index=True)
    password_hash = db.Column(db.String(128))
    department_id = db.Column(db.Integer, db.ForeignKey('vendors.id'))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    is_admin = db.Column(db.Boolean, default=False)

    @property
    def password(self):
        """
        Prevent pasword from being accessed
        """
        raise AttributeError('password is not a readable attribute.')

    @password.setter
    def password(self, password):
        """
        Set password to a hashed password
        """
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        """
        Check if hashed password matches actual password
        """
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return '<Employee: {}>'.format(self.username)

# Set up user_loader
@login_manager.user_loader
def load_user(user_id):
    return Employee.query.get(int(user_id))

class Vendor(db.Model):
    """
    Create a Vendor table
    """

    __tablename__ = 'vendors'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    description = db.Column(db.String(200))
    employees = db.relationship('Employee', backref='vendor',
                                lazy='dynamic')

    def __repr__(self):
        return '<Department: {}>'.format(self.name)

class Role(db.Model):
    """
    Create a Role table
    """

    __tablename__ = 'roles'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    description = db.Column(db.String(200))
    employees = db.relationship('Employee', backref='role',
                                lazy='dynamic')

    def __repr__(self):
        return '<Role: {}>'.format(self.name)

class Thing(db.Model):
    """
    Create a Thing table
    for physical objects
    """

    __tablename__ = 'thing'

    id = db.Column(db.Integer, primary_key=True)
    barcode = db.Column(db.String, unique=True)
    name = db.Column(db.String)
    description = db.Column(db.String)

    def __repr__(self):
        return '<Item>: {}'.format(self.name)<|endoftext|>


<commit_before>#!/usr/bin/env python

"""Wrapper for the Strava (http://www.strava.com) API.

See https://stravasite-main.pbworks.com/w/browse/ for API documentation."""

__author__    = "Brian Landers"
__contact__   = "brian@packetslave.com"
__copyright__ = "Copyright 2012, Brian Landers"
__license__   = "Apache"
__version__   = "1.0"

BASE_API = "http://www.strava.com/api/v1"

from collections import defaultdict
from datetime import date, timedelta
import json

try:
    import urllib2
except ImportError:
    import urllib
    urllib2 = False

class APIError(Exception):
    pass

class StravaObject(object):
    """Base class for interacting with the Strava API endpoint."""

    def __init__(self, oid):
        self._id = oid

    #noinspection PyUnresolvedReferences
    def load(self, url, key):
        if  urllib2:
            try:
                req = urllib2.Request(BASE_API + url)
                rsp = urllib2.urlopen(req)
            except urllib2.HTTPError as e:
                raise APIError("%s: request failed: %s" % (url, e))
        else:
            try:
                rsp = urllib.request.urlopen(BASE_API + url)
            except urllib.error.HTTPError as e:
                raise APIError("%s: request failed: %s" % (url, e))
        txt = rsp.read().decode('utf-8')

        try:
            return json.loads(txt)[key]
        except (ValueError, KeyError) as e:
            raise APIError("%s: parsing response failed: %s" % (url, e))

    @property
    def id(self):
        return self._id

class Athlete(StravaObject):
    """Encapsulates data about a single Athlete.

    Note that the athlete's name is NOT available through this API. You have to
    load a ride or effort to get that data from the service, and we don't want
    to make that heavy a query at the top level.
    """
    def __init__(self, oid):
        super(Athlete, self).__init__(oid)
        self._url = "/rides?athleteId=%s" % self.id

    def rides(self, start_date=None):
        out = []

        url = self._url
        if start_date:
            url += "&startDate=%s" % start_date.isoformat()

        for ride in self.load(url, "rides"):
            out.append(Ride(ride["id"], ride["name"]))

        return out

    def ride_stats(self, days=7):
        """Get number of rides, time, and distance for the past N days."""
        start = date.today() - timedelta(days=days)
        stats = defaultdict(float)

        for ride in self.rides(start_date=start):
            stats["rides"] += 1
            stats["moving_time"] += ride.detail.moving_time
            stats["distance"] += ride.detail.distance

        return stats

class Ride(StravaObject):
    """Information about a single ride.

    Most of the ride data is encapsulated in a RideDetail instance, accessible
    via the "detail" property. This lets us lazy-load the details, and saves an
    API round-trip if all we care about is the ID or name of the ride.
    """
    def __init__(self, oid, name):
        super(Ride, self).__init__(oid)
        self._name = name
        self._detail = None
        self._segments = []

    @property
    def name(self):
        return self._name

    @property
    def detail(self):
        if not self._detail:
            self._detail = RideDetail(self.id)
        return self._detail

    @property
    def segments(self):
        if not self._segments:
            for effort in self.load("/rides/%s/efforts" % self.id, "efforts"):
                self._segments.append(Segment(effort))
        return self._segments

class RideDetail(StravaObject):

    def __init__(self, oid):
        super(RideDetail, self).__init__(oid)
        self._attr = self.load("/rides/%s" % oid, 'ride')

    @property
    def athlete(self):
        return self._attr["athlete"]["name"]

    @property
    def athlete_id(self):
        return self._attr["athlete"]["id"]

    @property
    def bike(self):
        return self._attr["bike"]["name"]

    @property
    def bike_id(self):
        return self._attr["bike"]["id"]

    @property
    def location(self):
        return self._attr["location"]

    @property
    def distance(self):
        return self._attr["distance"]

    @property
    def moving_time(self):
        return self._attr["movingTime"]

class Segment(StravaObject):
    """Information about a single ride segment.

    Most of the data is encapsulated in a SegmentDetail instance, accessible via
    the "detail" property. This lets us lazy-load the details, and saves an API
    round-trip if all we care about is the ID or name of the segment.

    Note that this class combines the "effort" and "segment" as Strava defines
    them. They both ultimately pertain to a given portion of a ride, so it makes
    sense to access them both through the same interface.

    This does have the side effect, however, of requiring two API round-trips to
    load the "detail" property.  It's lazy-loaded, so if you just care about the
    segment name or ID, you won't take the it.
    """
    def __init__(self, attr):
        super(Segment, self).__init__(attr["id"])
        self._segment = attr["segment"]
        self._time = attr["elapsed_time"]
        self._detail = None

    @property
    def time(self):
        return self._time

    @property
    def name(self):
        return self._segment["name"]

    @property
    def detail(self):
        if not self._detail:
            self._detail = SegmentDetail(self._segment["id"], self.id)
        return self._detail

class SegmentDetail(StravaObject):
    def __init__(self, segment_id, effort_id):
        super(SegmentDetail, self).__init__(segment_id)
        self._effort_attr = self.load("/efforts/%s" % effort_id, "effort")
        self._segment_attr = self.load("/segments/%s" % segment_id, "segment")

    @property
    def distance(self):
        return self._segment_attr["distance"]

    @property
    def elapsed_time(self):
        return self._effort_attr["elapsedTime"]

    @property
    def moving_time(self):
        return self._effort_attr["movingTime"]

    @property
    def average_speed(self):
        return self._effort_attr["averageSpeed"]

    @property
    def maximum_speed(self):
        return self._effort_attr["maximumSpeed"]

    @property
    def average_watts(self):
        return self._effort_attr["averageWatts"]

    @property
    def average_grade(self):
        return self._segment_attr["averageGrade"]

    @property
    def climb_category(self):
        return self._segment_attr["climbCategory"]

    @property
    def elevations(self):
        return (self._segment_attr["elevationLow"],
                self._segment_attr["elevationHigh"],
                self._segment_attr["elevationGain"])
<commit_msg>added average_speed<commit_after>#!/usr/bin/env python

"""Wrapper for the Strava (http://www.strava.com) API.

See https://stravasite-main.pbworks.com/w/browse/ for API documentation."""

__author__    = "Brian Landers"
__contact__   = "brian@packetslave.com"
__copyright__ = "Copyright 2012, Brian Landers"
__license__   = "Apache"
__version__   = "1.0"

BASE_API = "http://www.strava.com/api/v1"

from collections import defaultdict
from datetime import date, timedelta
import json

try:
    import urllib2
except ImportError:
    import urllib
    urllib2 = False

class APIError(Exception):
    pass

class StravaObject(object):
    """Base class for interacting with the Strava API endpoint."""

    def __init__(self, oid):
        self._id = oid

    #noinspection PyUnresolvedReferences
    def load(self, url, key):
        if  urllib2:
            try:
                req = urllib2.Request(BASE_API + url)
                rsp = urllib2.urlopen(req)
            except urllib2.HTTPError as e:
                raise APIError("%s: request failed: %s" % (url, e))
        else:
            try:
                rsp = urllib.request.urlopen(BASE_API + url)
            except urllib.error.HTTPError as e:
                raise APIError("%s: request failed: %s" % (url, e))
        txt = rsp.read().decode('utf-8')

        try:
            return json.loads(txt)[key]
        except (ValueError, KeyError) as e:
            raise APIError("%s: parsing response failed: %s" % (url, e))

    @property
    def id(self):
        return self._id

class Athlete(StravaObject):
    """Encapsulates data about a single Athlete.

    Note that the athlete's name is NOT available through this API. You have to
    load a ride or effort to get that data from the service, and we don't want
    to make that heavy a query at the top level.
    """
    def __init__(self, oid):
        super(Athlete, self).__init__(oid)
        self._url = "/rides?athleteId=%s" % self.id

    def rides(self, start_date=None):
        out = []

        url = self._url
        if start_date:
            url += "&startDate=%s" % start_date.isoformat()

        for ride in self.load(url, "rides"):
            out.append(Ride(ride["id"], ride["name"]))

        return out

    def ride_stats(self, days=7):
        """Get number of rides, time, and distance for the past N days."""
        start = date.today() - timedelta(days=days)
        stats = defaultdict(float)

        for ride in self.rides(start_date=start):
            stats["rides"] += 1
            stats["moving_time"] += ride.detail.moving_time
            stats["distance"] += ride.detail.distance

        return stats

class Ride(StravaObject):
    """Information about a single ride.

    Most of the ride data is encapsulated in a RideDetail instance, accessible
    via the "detail" property. This lets us lazy-load the details, and saves an
    API round-trip if all we care about is the ID or name of the ride.
    """
    def __init__(self, oid, name):
        super(Ride, self).__init__(oid)
        self._name = name
        self._detail = None
        self._segments = []

    @property
    def name(self):
        return self._name

    @property
    def detail(self):
        if not self._detail:
            self._detail = RideDetail(self.id)
        return self._detail

    @property
    def segments(self):
        if not self._segments:
            for effort in self.load("/rides/%s/efforts" % self.id, "efforts"):
                self._segments.append(Segment(effort))
        return self._segments

class RideDetail(StravaObject):

    def __init__(self, oid):
        super(RideDetail, self).__init__(oid)
        self._attr = self.load("/rides/%s" % oid, 'ride')

    @property
    def athlete(self):
        return self._attr["athlete"]["name"]

    @property
    def athlete_id(self):
        return self._attr["athlete"]["id"]

    @property
    def bike(self):
        return self._attr["bike"]["name"]

    @property
    def bike_id(self):
        return self._attr["bike"]["id"]

    @property
    def location(self):
        return self._attr["location"]

    @property
    def distance(self):
        return self._attr["distance"]

    @property
    def average_speed(self):
        return self._attr["averageSpeed"]

    @property
    def moving_time(self):
        return self._attr["movingTime"]

class Segment(StravaObject):
    """Information about a single ride segment.

    Most of the data is encapsulated in a SegmentDetail instance, accessible via
    the "detail" property. This lets us lazy-load the details, and saves an API
    round-trip if all we care about is the ID or name of the segment.

    Note that this class combines the "effort" and "segment" as Strava defines
    them. They both ultimately pertain to a given portion of a ride, so it makes
    sense to access them both through the same interface.

    This does have the side effect, however, of requiring two API round-trips to
    load the "detail" property.  It's lazy-loaded, so if you just care about the
    segment name or ID, you won't take the it.
    """
    def __init__(self, attr):
        super(Segment, self).__init__(attr["id"])
        self._segment = attr["segment"]
        self._time = attr["elapsed_time"]
        self._detail = None

    @property
    def time(self):
        return self._time

    @property
    def name(self):
        return self._segment["name"]

    @property
    def detail(self):
        if not self._detail:
            self._detail = SegmentDetail(self._segment["id"], self.id)
        return self._detail

class SegmentDetail(StravaObject):
    def __init__(self, segment_id, effort_id):
        super(SegmentDetail, self).__init__(segment_id)
        self._effort_attr = self.load("/efforts/%s" % effort_id, "effort")
        self._segment_attr = self.load("/segments/%s" % segment_id, "segment")

    @property
    def distance(self):
        return self._segment_attr["distance"]

    @property
    def elapsed_time(self):
        return self._effort_attr["elapsedTime"]

    @property
    def moving_time(self):
        return self._effort_attr["movingTime"]

    @property
    def average_speed(self):
        return self._effort_attr["averageSpeed"]

    @property
    def maximum_speed(self):
        return self._effort_attr["maximumSpeed"]

    @property
    def average_watts(self):
        return self._effort_attr["averageWatts"]

    @property
    def average_grade(self):
        return self._segment_attr["averageGrade"]

    @property
    def climb_category(self):
        return self._segment_attr["climbCategory"]

    @property
    def elevations(self):
        return (self._segment_attr["elevationLow"],
                self._segment_attr["elevationHigh"],
                self._segment_attr["elevationGain"])
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Created on Fri Sep 23 10:49:39 2016

@author: adam
"""
import pytest

from rickshaw.choose_commodities import up_hierarchy, choose_commodity, choose_commodities

@pytest.mark.parametrize("key, expected", [
    [("mine", "enrichment"), "natural_uranium"],
    [("mine:uranium", "enrichment"), "natural_uranium"],
    [("mine", "enrichment:uranium"), "natural_uranium"],
    [("mine:uranium", "enrichment:uranium"), "natural_uranium"],
    [("reactor:lwr", "separations"), "used_uox"],
    [("fuel_fab:triso", "reactor:pb"), "fresh_triso"],
    [("fuel_fab", "reactor:lwr"), "fresh_fuel"],

])
def test_up_hierarchy(key, expected):
    observed = up_hierarchy(key)
    assert expected == observed
"""
@pytest.mark.parametrize("keyfrom, keyto, unique_commods, expected", [
    [("mine", "enrichment", [], "natural_uranium")],
])
def test_choose_commodity(keyfrom, keyto, unique_commods, expected):
    observed = choose_commodity(keyfrom, keyto, unique_commods)
    assert expected == observed
"""

"""
@pytest.mark.parametrize("niches, expected", [
    [["mine", "enrichment", "fuel_fab", "reactor:lwr", "storage", "separations", "storage", "repository"],
      ["natural_uranium", "low_enriched_uranium", "fresh_fuel", "used_uox", "stored_used_fuel",
       "separated_waste", "stored_used_fuel1"]],
])
"""

@pytest.mark.parametrize("niches, expected", [
    [["mine", "enrichment", "fuel_fab", "reactor:lwr", "storage", "separations", "storage", "repository"],
      ["natural_uranium", "low_enriched_uranium", "fresh_fuel", "used_uox", "stored_used_fuel",
       "separated_waste", "stored_used_fuel1"]],
    [["storage", "separations", "storage", "separations", "storage", "repository"],
     ["stored_used_fuel", "separated_waste", "stored_used_fuel1", "separated_waste1", "stored_used_fuel2"]],

])
def test_choose_commodities(niches, expected):
    observed = choose_commodities(niches)
    assert expected == observed
<commit_msg>fixed test<commit_after># -*- coding: utf-8 -*-
"""
Created on Fri Sep 23 10:49:39 2016

@author: adam
"""
import pytest

from rickshaw.choose_commodities import up_hierarchy, choose_commodity, choose_commodities

@pytest.mark.parametrize("key, expected", [
    [("mine", "enrichment"), "natural_uranium"],
    [("mine:uranium", "enrichment"), "natural_uranium"],
    [("mine", "enrichment:uranium"), "natural_uranium"],
    [("mine:uranium", "enrichment:uranium"), "natural_uranium"],
    [("reactor:lwr", "separations"), "used_uox"],
    [("fuel_fab:triso", "reactor:pb"), "fresh_triso"],
    [("fuel_fab", "reactor:lwr"), "fresh_fuel"],

])
def test_up_hierarchy(key, expected):
    observed = up_hierarchy(key)
    assert expected == observed

@pytest.mark.parametrize("keyfrom, keyto, expected", [
    ("mine", "enrichment", "natural_uranium"),
])
def test_choose_commodity(keyfrom, keyto, expected):
    unique_commods = set()
    observed = choose_commodity(keyfrom, keyto, unique_commods)
    assert expected == observed

@pytest.mark.parametrize("niches, expected", [
    [["mine", "enrichment", "fuel_fab", "reactor:lwr", "storage", "separations", "storage", "repository"],
      ["natural_uranium", "low_enriched_uranium", "fresh_fuel", "used_uox", "stored_used_fuel",
       "separated_waste", "stored_used_fuel1"]],
    [["storage", "separations", "storage", "separations", "storage", "repository"],
     ["stored_used_fuel", "separated_waste", "stored_used_fuel1", "separated_waste1", "stored_used_fuel2"]],

])
def test_choose_commodities(niches, expected):
    observed = choose_commodities(niches)
    assert expected == observed
<|endoftext|>


<commit_before># coding=utf-8

import sys
import platform
import time
import json
from datetime import datetime
from threading import Lock

import regex
import requests
from requests.auth import HTTPBasicAuth

from urllib.parse import quote_plus
from globalvars import GlobalVars
if GlobalVars.on_windows:
    # noinspection PyPep8Naming
    from classes import Git as git, GitError
else:
    from sh.contrib import git
    from sh import ErrorReturnCode as GitError

from helpers import log, log_exception, only_blacklists_changed
from blacklists import *

class GitHubManager:
    auth = HTTPBasicAuth(GlobalVars.github_username, GlobalVars.github_password)
    repo = GlobalVars.bot_repo_slug

    @classmethod
    def create_pull_request(cls, payload):
        """
        Creates a pull request on GitHub, returns the json'd response
        """
        if isinstance(payload, dict):
            payload = json.dumps(payload)
        response = requests.post("https://api.github.com/repos/{}/pulls".format(cls.repo),
                                 auth=cls.auth, data=payload)
        return response.json()

    @classmethod
    def comment_on_thread(cls, thread_id, body):
        url = "https://api.github.com/repos/{}/issues/{}/comments".format(cls.repo, thread_id)
        payload = json.dumps({'body': body})
        response = requests.post(url, auth=cls.auth, data=payload)
        return response.json()

# noinspection PyRedundantParentheses,PyClassHasNoInit,PyBroadException
class GitManager:
    gitmanager_lock = Lock()

    @classmethod
    def add_to_blacklist(cls, blacklist='', item_to_blacklist='', username='', chat_profile_link='',
                         code_permissions=False, metasmoke_down=False):
        if git.config("--get", "user.name", _ok_code=[0, 1]) == "":
            return (False, 'Tell someone to run `git config user.name "SmokeDetector"`')

        if git.config("--get", "user.email", _ok_code=[0, 1]) == "":
            return (False, 'Tell someone to run `git config user.email "smokey@erwaysoftware.com"`')

        if blacklist == "":
            return (False, 'GitManager: blacklist is not defined. Blame a developer.')

        if item_to_blacklist == "":
            return (False, 'GitManager: item_to_blacklist is not defined. Blame a developer.')

        # item_to_blacklist = item_to_blacklist.replace("\\s", " ")

        if blacklist == "website":
            blacklist_type = Blacklist.WEBSITES
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "keyword":
            blacklist_type = Blacklist.KEYWORDS
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "username":
            blacklist_type = Blacklist.USERNAMES
            ms_search_option = "&username_is_regex=1&username="
        elif blacklist == "number":
            blacklist_type = Blacklist.NUMBERS
            ms_search_option = "&body="
        elif blacklist == "watch_keyword":
            blacklist_type = Blacklist.WATCHED_KEYWORDS
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "watch_number":
            blacklist_type = Blacklist.WATCHED_NUMBERS
            ms_search_option = "&body="
        else:
            return (False, 'GitManager: blacklist is not recognized. Blame a developer.')

        blacklister = Blacklist(blacklist_type)
        blacklist_file_name = blacklist_type[0]

        try:
            cls.gitmanager_lock.acquire()
            status, message = cls.prepare_git_for_operation(blacklist_file_name)
            if not status:
                return (False, message)

            now = str(int(time.time()))

            if blacklist_type in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                op = 'watch'
                item = item_to_blacklist
                item_to_blacklist = "\t".join([now, username, item])
            else:
                op = 'blacklist'
                item = item_to_blacklist

            exists, line = blacklister.exists(item_to_blacklist)
            if exists:
                return (False, 'Already {}ed on line {} of {}'.format(op, line, blacklist_file_name))

            watch_removed = False
            if blacklist_type not in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                for watcher_type in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                    watcher = Blacklist(watcher_type)
                    if watcher.exists(item_to_blacklist):
                        watch_removed = True
                        watcher.remove(item_to_blacklist)

            blacklister.add(item_to_blacklist)

            branch = "auto-blacklist-{0}".format(now)
            git.checkout("-b", branch)

            git.reset("HEAD")

            git.add(blacklist_file_name)
            if watch_removed:
                git.add('watched_keywords.txt', 'watched_numbers.txt')

            git("-c", "user.name=" + GlobalVars.git_name,
                "-c", "user.email=" + GlobalVars.git_email,
                "commit",
                "--author={} <{}>".format(GlobalVars.git_name, GlobalVars.git_email),
                "-m", "Auto {0} of `{1}` by {2}".format(op, item, username))

            if code_permissions:
                git.checkout("master")
                git.merge(branch)
                git.push("origin", "master")
                git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
            else:
                git.push("origin", branch)
                git.checkout("master")

                if GlobalVars.github_username is None or GlobalVars.github_password is None:
                    return (False, "Tell someone to set a GH password")

                payload = {"title": "{0}: {1} {2}".format(username, op.title(), item),
                           "body": "[{0}]({1}) requests the {2} of the {3} `{4}`. See the MS search [here]"
                                   "(https://metasmoke.erwaysoftware.com/search?utf8=%E2%9C%93{5}{6}) and the "
                                   "Stack Exchange search [here](https://stackexchange.com/search?q=%22{7}%22).\n"
                                   "<!-- METASMOKE-BLACKLIST-{8} {4} -->".format(
                                       username, chat_profile_link, op, blacklist,                # 0 1 2 3
                                       item, ms_search_option,                                    # 4 5
                                       quote_plus(item),                                          # 6
                                       quote_plus(item.replace("\\W", " ").replace("\\.", ".")),  # 7
                                       blacklist.upper()),                                        # 8
                           "head": branch,
                           "base": "master"}
                response = GitHubManager.create_pull_request(payload)
                log('debug', response)
                try:
                    git.checkout("deploy")  # Return to deploy, pending the accept of the PR in Master.
                    git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
                    url, pr_num = response["html_url"], response["number"]
                    if metasmoke_down:
                        return (True,
                                "MS is not reachable, so I can't see if you have code privileges, but I've "
                                "[created PR#{1} for you]({0}).".format(
                                    url, pr_num))
                    else:
                        return (True,
                                "You don't have code privileges, but I've [created PR#{1} for you]({0}).".format(
                                    url, pr_num))

                except KeyError:
                    git.checkout("deploy")  # Return to deploy

                    try:
                        # Delete the branch in the local git tree, we'll create it again if the
                        # command is run again. This way, we keep things a little more clean in
                        # the local git tree
                        git.branch('-D', branch)
                    except GitError:
                        # It's OK if the branch doesn't get deleted, so long as we switch back to
                        # deploy, which we do in the finally block...
                        pass

                    # Error capture/checking for any "invalid" GH reply without an 'html_url' item,
                    # which will throw a KeyError.
                    if "bad credentials" in str(response['message']).lower():
                        # Capture the case when GH credentials are bad or invalid
                        return (False, "Something is wrong with the GH credentials, tell someone to check them.")
                    else:
                        # Capture any other invalid response cases.
                        return (False, "A bad or invalid reply was received from GH, the message was: %s" %
                                response['message'])
        except Exception as err:
            log_exception(*sys.exc_info())
            return (False, "Git functions failed for unspecified reasons, details may be in error log.")
        finally:
            # Always return to `deploy` branch when done with anything.
            git.checkout("deploy")
            cls.gitmanager_lock.release()

        if op == 'blacklist':
            return (True, "Blacklisted `{0}`".format(item))
        elif op == 'watch':
            return (True, "Added `{0}` to watchlist".format(item))

    @classmethod
    def remove_from_blacklist(cls, item, username, blacklist_type="", code_privileged=False, metasmoke_down=False):
        if not code_privileged:
            if metasmoke_down:
                return False, "MS is offline, and I can't determine if you are a code admin or not. " \
                              "If you are a code admin, then wait for MS to be back up before running this command."
            else:
                return False, "Ask a code admin to run that for you. Use `!!/whois code_admin` to find out who's here."

        try:
            cls.gitmanager_lock.acquire()
            git.checkout("master")

            if blacklist_type == "watch":
                blacklists = [Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS]
                list_type = "watchlist"
            elif blacklist_type == "blacklist":
                blacklists = [Blacklist.KEYWORDS, Blacklist.WEBSITES, Blacklist.USERNAMES, Blacklist.NUMBERS]
                list_type = "blacklist"
            else:
                return False, "`blacklist_type` not set, blame a developer."

            for blacklist in blacklists:
                file_name = blacklist[0]
                manager = Blacklist(blacklist)

                exists, _line = manager.exists(item)
                if exists:
                    break

            if not exists:
                return False, 'No such item `{}` in {}.'.format(item, list_type)

            status, message = cls.prepare_git_for_operation(file_name)
            if not status:
                return False, message

            branch = 'auto-un{}-{}'.format(blacklist_type, time.time())
            git.checkout('-b', branch)
            git.reset('HEAD')

            manager.remove(item)

            git.add(file_name)
            git.commit("--author='SmokeDetector <smokey@erwaysoftware.com>'",
                       '-m', 'Auto un{} of `{}` by {}'.format(blacklist_type, item, username))

            git.checkout('master')
            git.merge(branch)
            git.push('origin', 'master')

            try:
                git.branch('-D', branch)
            except GitError:
                # It's OK if the branch doesn't get deleted, so long as we switch back to
                # deploy, which we do in the finally block...
                pass

        except Exception as e:
            log('error', '{}: {}'.format(type(e).__name__, e))
            log_exception(*sys.exc_info())
            return False, 'Git operations failed for unspecified reasons.'
        finally:
            git.checkout('deploy')
            cls.gitmanager_lock.release()

        # With no exception raised, list_type should be set
        return True, 'Removed `{}` from {}'.format(item, list_type)

    @classmethod
    def merge_pull_request(cls, pr_id, comment=""):
        response = requests.get("https://api.github.com/repos/{}/pulls/{}".format(GlobalVars.bot_repo_slug, pr_id))
        if not response:
            raise ConnectionError("Cannot connect to GitHub API")
        pr_info = response.json()
        if pr_info["user"]["login"] != "SmokeDetector":
            raise ValueError("PR #{} is not created by me, so I can't approve it".format(pr_id))
        if "<!-- METASMOKE-BLACKLIST" not in pr_info["body"]:
            raise ValueError("PR description is malformed. Blame a developer")
        ref = pr_info['head']['ref']

        if comment:  # yay we have comments now
            GitHubManager.comment_on_thread(pr_id, comment)

        try:
            # Remote checks passed, good to go here
            cls.gitmanager_lock.acquire()
            git.checkout('master')
            git.fetch('origin', '+refs/pull/{}/head'.format(pr_id))
            git.merge('FETCH_HEAD', '--no-ff', '-m', 'Merge pull request #{} from {}/{}'.format(
                      pr_id, GlobalVars.bot_repo_slug.split("/")[0], ref))
            git.push('origin', 'master')
            try:
                git.push('-d', 'origin', ref)
            except GitError as e:
                # TODO: PR merged, but branch deletion has something wrong, generate some text
                pass
            return "Merged pull request [#{0}](https://github.com/{1}/pull/{0}).".format(
                pr_id, GlobalVars.bot_repo_slug)
        finally:
            git.checkout('deploy')
            cls.gitmanager_lock.release()

    @staticmethod
    def prepare_git_for_operation(blacklist_file_name):
        try:
            git.checkout('master')
            git.remote.update()
            git.reset('--hard', 'origin/master')
        except GitError as e:
            if GlobalVars.on_windows:
                return False, "Not doing this, we're on Windows."
            log_exception(*sys.exc_info())
            return False, "`git pull` has failed. This shouldn't happen. Details have been logged."

        if GlobalVars.on_windows:
            remote_ref = git.rev_parse("refs/remotes/origin/master").strip()
            local_ref = git.rev_parse("master").strip()
        else:
            remote_ref = git("rev-parse", "refs/remotes/origin/master").strip()
            local_ref = git("rev-parse", "master").strip()
        if local_ref != remote_ref:
            local_log = git.log(r"--pretty=`[%h]` *%cn*: %s", "-1", str(local_ref)).strip()
            remote_log = git.log(r"--pretty=`[%h]` *%cn*: %s", "-1", str(remote_ref)).strip()
            return False, "HEAD isn't at tip of origin's master branch (local {}, remote {})".format(
                local_log, remote_log)

        return True, None

    @staticmethod
    def current_git_status():
        if GlobalVars.on_windows:
            return git.status_stripped()
        else:
            return str(git.status())

    @staticmethod
    def current_branch():
        return str(git('rev-parse', '--abbrev-ref', 'HEAD')).strip()

    @staticmethod
    def merge_abort():
        if GlobalVars.on_windows:
            return  # No we don't do Windows
        git.merge("--abort")

    @staticmethod
    def reset_head():
        if GlobalVars.on_windows:
            return  # No we don't do Windows
        git.reset("--hard", "HEAD")
        git.clean("-f")

    @staticmethod
    def get_remote_diff():
        git.fetch()
        if GlobalVars.on_windows:
            return git.diff_filenames("HEAD", "origin/deploy")
        else:
            return git.diff("--name-only", "HEAD", "origin/deploy")

    @staticmethod
    def get_local_diff():
        if GlobalVars.on_windows:
            return git.diff_filenames("HEAD", "master")
        else:
            return git.diff("--name-only", "HEAD", "master")

    @staticmethod
    def pull_remote():
        git.pull()

    @staticmethod
    def pull_local():
        diff = GitManager.get_local_diff()
        if not only_blacklists_changed(diff):
            return
        try:
            git.merge("--ff-only", "master")
            git.push("origin", "deploy")
        except GitError:
            return

    @staticmethod
    def sync_remote():
        if GlobalVars.on_windows:
            return False, "We don't do Windows"

        try:
            if GlobalVars.on_branch != "deploy":
                git.checkout("deploy")
            git.branch('--create-reflog', '-f', 'master', '-t', 'origin/master')
            return True, "wow, this is working!"
        except Exception as e:
            return False, str(e)
<commit_msg>These git config checks are no longer needed<commit_after># coding=utf-8

import sys
import platform
import time
import json
from datetime import datetime
from threading import Lock

import regex
import requests
from requests.auth import HTTPBasicAuth

from urllib.parse import quote_plus
from globalvars import GlobalVars
if GlobalVars.on_windows:
    # noinspection PyPep8Naming
    from classes import Git as git, GitError
else:
    from sh.contrib import git
    from sh import ErrorReturnCode as GitError

from helpers import log, log_exception, only_blacklists_changed
from blacklists import *

class GitHubManager:
    auth = HTTPBasicAuth(GlobalVars.github_username, GlobalVars.github_password)
    repo = GlobalVars.bot_repo_slug

    @classmethod
    def create_pull_request(cls, payload):
        """
        Creates a pull request on GitHub, returns the json'd response
        """
        if isinstance(payload, dict):
            payload = json.dumps(payload)
        response = requests.post("https://api.github.com/repos/{}/pulls".format(cls.repo),
                                 auth=cls.auth, data=payload)
        return response.json()

    @classmethod
    def comment_on_thread(cls, thread_id, body):
        url = "https://api.github.com/repos/{}/issues/{}/comments".format(cls.repo, thread_id)
        payload = json.dumps({'body': body})
        response = requests.post(url, auth=cls.auth, data=payload)
        return response.json()

# noinspection PyRedundantParentheses,PyClassHasNoInit,PyBroadException
class GitManager:
    gitmanager_lock = Lock()

    @classmethod
    def add_to_blacklist(cls, blacklist='', item_to_blacklist='', username='', chat_profile_link='',
                         code_permissions=False, metasmoke_down=False):
        if blacklist == "":
            return (False, 'GitManager: blacklist is not defined. Blame a developer.')

        if item_to_blacklist == "":
            return (False, 'GitManager: item_to_blacklist is not defined. Blame a developer.')

        # item_to_blacklist = item_to_blacklist.replace("\\s", " ")

        if blacklist == "website":
            blacklist_type = Blacklist.WEBSITES
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "keyword":
            blacklist_type = Blacklist.KEYWORDS
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "username":
            blacklist_type = Blacklist.USERNAMES
            ms_search_option = "&username_is_regex=1&username="
        elif blacklist == "number":
            blacklist_type = Blacklist.NUMBERS
            ms_search_option = "&body="
        elif blacklist == "watch_keyword":
            blacklist_type = Blacklist.WATCHED_KEYWORDS
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "watch_number":
            blacklist_type = Blacklist.WATCHED_NUMBERS
            ms_search_option = "&body="
        else:
            return (False, 'GitManager: blacklist is not recognized. Blame a developer.')

        blacklister = Blacklist(blacklist_type)
        blacklist_file_name = blacklist_type[0]

        try:
            cls.gitmanager_lock.acquire()
            status, message = cls.prepare_git_for_operation(blacklist_file_name)
            if not status:
                return (False, message)

            now = str(int(time.time()))

            if blacklist_type in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                op = 'watch'
                item = item_to_blacklist
                item_to_blacklist = "\t".join([now, username, item])
            else:
                op = 'blacklist'
                item = item_to_blacklist

            exists, line = blacklister.exists(item_to_blacklist)
            if exists:
                return (False, 'Already {}ed on line {} of {}'.format(op, line, blacklist_file_name))

            watch_removed = False
            if blacklist_type not in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                for watcher_type in {Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS}:
                    watcher = Blacklist(watcher_type)
                    if watcher.exists(item_to_blacklist):
                        watch_removed = True
                        watcher.remove(item_to_blacklist)

            blacklister.add(item_to_blacklist)

            branch = "auto-blacklist-{0}".format(now)
            git.checkout("-b", branch)

            git.reset("HEAD")

            git.add(blacklist_file_name)
            if watch_removed:
                git.add('watched_keywords.txt', 'watched_numbers.txt')

            git("-c", "user.name=" + GlobalVars.git_name,
                "-c", "user.email=" + GlobalVars.git_email,
                "commit",
                "--author={} <{}>".format(GlobalVars.git_name, GlobalVars.git_email),
                "-m", "Auto {0} of `{1}` by {2}".format(op, item, username))

            if code_permissions:
                git.checkout("master")
                git.merge(branch)
                git.push("origin", "master")
                git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
            else:
                git.push("origin", branch)
                git.checkout("master")

                if GlobalVars.github_username is None or GlobalVars.github_password is None:
                    return (False, "Tell someone to set a GH password")

                payload = {"title": "{0}: {1} {2}".format(username, op.title(), item),
                           "body": "[{0}]({1}) requests the {2} of the {3} `{4}`. See the MS search [here]"
                                   "(https://metasmoke.erwaysoftware.com/search?utf8=%E2%9C%93{5}{6}) and the "
                                   "Stack Exchange search [here](https://stackexchange.com/search?q=%22{7}%22).\n"
                                   "<!-- METASMOKE-BLACKLIST-{8} {4} -->".format(
                                       username, chat_profile_link, op, blacklist,                # 0 1 2 3
                                       item, ms_search_option,                                    # 4 5
                                       quote_plus(item),                                          # 6
                                       quote_plus(item.replace("\\W", " ").replace("\\.", ".")),  # 7
                                       blacklist.upper()),                                        # 8
                           "head": branch,
                           "base": "master"}
                response = GitHubManager.create_pull_request(payload)
                log('debug', response)
                try:
                    git.checkout("deploy")  # Return to deploy, pending the accept of the PR in Master.
                    git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
                    url, pr_num = response["html_url"], response["number"]
                    if metasmoke_down:
                        return (True,
                                "MS is not reachable, so I can't see if you have code privileges, but I've "
                                "[created PR#{1} for you]({0}).".format(
                                    url, pr_num))
                    else:
                        return (True,
                                "You don't have code privileges, but I've [created PR#{1} for you]({0}).".format(
                                    url, pr_num))

                except KeyError:
                    git.checkout("deploy")  # Return to deploy

                    try:
                        # Delete the branch in the local git tree, we'll create it again if the
                        # command is run again. This way, we keep things a little more clean in
                        # the local git tree
                        git.branch('-D', branch)
                    except GitError:
                        # It's OK if the branch doesn't get deleted, so long as we switch back to
                        # deploy, which we do in the finally block...
                        pass

                    # Error capture/checking for any "invalid" GH reply without an 'html_url' item,
                    # which will throw a KeyError.
                    if "bad credentials" in str(response['message']).lower():
                        # Capture the case when GH credentials are bad or invalid
                        return (False, "Something is wrong with the GH credentials, tell someone to check them.")
                    else:
                        # Capture any other invalid response cases.
                        return (False, "A bad or invalid reply was received from GH, the message was: %s" %
                                response['message'])
        except Exception as err:
            log_exception(*sys.exc_info())
            return (False, "Git functions failed for unspecified reasons, details may be in error log.")
        finally:
            # Always return to `deploy` branch when done with anything.
            git.checkout("deploy")
            cls.gitmanager_lock.release()

        if op == 'blacklist':
            return (True, "Blacklisted `{0}`".format(item))
        elif op == 'watch':
            return (True, "Added `{0}` to watchlist".format(item))

    @classmethod
    def remove_from_blacklist(cls, item, username, blacklist_type="", code_privileged=False, metasmoke_down=False):
        if not code_privileged:
            if metasmoke_down:
                return False, "MS is offline, and I can't determine if you are a code admin or not. " \
                              "If you are a code admin, then wait for MS to be back up before running this command."
            else:
                return False, "Ask a code admin to run that for you. Use `!!/whois code_admin` to find out who's here."

        try:
            cls.gitmanager_lock.acquire()
            git.checkout("master")

            if blacklist_type == "watch":
                blacklists = [Blacklist.WATCHED_KEYWORDS, Blacklist.WATCHED_NUMBERS]
                list_type = "watchlist"
            elif blacklist_type == "blacklist":
                blacklists = [Blacklist.KEYWORDS, Blacklist.WEBSITES, Blacklist.USERNAMES, Blacklist.NUMBERS]
                list_type = "blacklist"
            else:
                return False, "`blacklist_type` not set, blame a developer."

            for blacklist in blacklists:
                file_name = blacklist[0]
                manager = Blacklist(blacklist)

                exists, _line = manager.exists(item)
                if exists:
                    break

            if not exists:
                return False, 'No such item `{}` in {}.'.format(item, list_type)

            status, message = cls.prepare_git_for_operation(file_name)
            if not status:
                return False, message

            branch = 'auto-un{}-{}'.format(blacklist_type, time.time())
            git.checkout('-b', branch)
            git.reset('HEAD')

            manager.remove(item)

            git.add(file_name)
            git("-c", "user.name=" + GlobalVars.git_name,
                "-c", "user.email=" + GlobalVars.git_email,
                "commit",
                "--author={} <{}>".format(GlobalVars.git_name, GlobalVars.git_email),
                '-m', 'Auto un{} of `{}` by {}'.format(blacklist_type, item, username))

            git.checkout('master')
            git.merge(branch)
            git.push('origin', 'master')

            try:
                git.branch('-D', branch)
            except GitError:
                # It's OK if the branch doesn't get deleted, so long as we switch back to
                # deploy, which we do in the finally block...
                pass

        except Exception as e:
            log('error', '{}: {}'.format(type(e).__name__, e))
            log_exception(*sys.exc_info())
            return False, 'Git operations failed for unspecified reasons.'
        finally:
            git.checkout('deploy')
            cls.gitmanager_lock.release()

        # With no exception raised, list_type should be set
        return True, 'Removed `{}` from {}'.format(item, list_type)

    @classmethod
    def merge_pull_request(cls, pr_id, comment=""):
        response = requests.get("https://api.github.com/repos/{}/pulls/{}".format(GlobalVars.bot_repo_slug, pr_id))
        if not response:
            raise ConnectionError("Cannot connect to GitHub API")
        pr_info = response.json()
        if pr_info["user"]["login"] != "SmokeDetector":
            raise ValueError("PR #{} is not created by me, so I can't approve it".format(pr_id))
        if "<!-- METASMOKE-BLACKLIST" not in pr_info["body"]:
            raise ValueError("PR description is malformed. Blame a developer")
        ref = pr_info['head']['ref']

        if comment:  # yay we have comments now
            GitHubManager.comment_on_thread(pr_id, comment)

        try:
            # Remote checks passed, good to go here
            cls.gitmanager_lock.acquire()
            git.checkout('master')
            git.fetch('origin', '+refs/pull/{}/head'.format(pr_id))
            git.merge('FETCH_HEAD', '--no-ff', '-m', 'Merge pull request #{} from {}/{}'.format(
                      pr_id, GlobalVars.bot_repo_slug.split("/")[0], ref))
            git.push('origin', 'master')
            try:
                git.push('-d', 'origin', ref)
            except GitError as e:
                # TODO: PR merged, but branch deletion has something wrong, generate some text
                pass
            return "Merged pull request [#{0}](https://github.com/{1}/pull/{0}).".format(
                pr_id, GlobalVars.bot_repo_slug)
        finally:
            git.checkout('deploy')
            cls.gitmanager_lock.release()

    @staticmethod
    def prepare_git_for_operation(blacklist_file_name):
        try:
            git.checkout('master')
            git.remote.update()
            git.reset('--hard', 'origin/master')
        except GitError as e:
            if GlobalVars.on_windows:
                return False, "Not doing this, we're on Windows."
            log_exception(*sys.exc_info())
            return False, "`git pull` has failed. This shouldn't happen. Details have been logged."

        if GlobalVars.on_windows:
            remote_ref = git.rev_parse("refs/remotes/origin/master").strip()
            local_ref = git.rev_parse("master").strip()
        else:
            remote_ref = git("rev-parse", "refs/remotes/origin/master").strip()
            local_ref = git("rev-parse", "master").strip()
        if local_ref != remote_ref:
            local_log = git.log(r"--pretty=`[%h]` *%cn*: %s", "-1", str(local_ref)).strip()
            remote_log = git.log(r"--pretty=`[%h]` *%cn*: %s", "-1", str(remote_ref)).strip()
            return False, "HEAD isn't at tip of origin's master branch (local {}, remote {})".format(
                local_log, remote_log)

        return True, None

    @staticmethod
    def current_git_status():
        if GlobalVars.on_windows:
            return git.status_stripped()
        else:
            return str(git.status())

    @staticmethod
    def current_branch():
        return str(git('rev-parse', '--abbrev-ref', 'HEAD')).strip()

    @staticmethod
    def merge_abort():
        if GlobalVars.on_windows:
            return  # No we don't do Windows
        git.merge("--abort")

    @staticmethod
    def reset_head():
        if GlobalVars.on_windows:
            return  # No we don't do Windows
        git.reset("--hard", "HEAD")
        git.clean("-f")

    @staticmethod
    def get_remote_diff():
        git.fetch()
        if GlobalVars.on_windows:
            return git.diff_filenames("HEAD", "origin/deploy")
        else:
            return git.diff("--name-only", "HEAD", "origin/deploy")

    @staticmethod
    def get_local_diff():
        if GlobalVars.on_windows:
            return git.diff_filenames("HEAD", "master")
        else:
            return git.diff("--name-only", "HEAD", "master")

    @staticmethod
    def pull_remote():
        git.pull()

    @staticmethod
    def pull_local():
        diff = GitManager.get_local_diff()
        if not only_blacklists_changed(diff):
            return
        try:
            git.merge("--ff-only", "master")
            git.push("origin", "deploy")
        except GitError:
            return

    @staticmethod
    def sync_remote():
        if GlobalVars.on_windows:
            return False, "We don't do Windows"

        try:
            if GlobalVars.on_branch != "deploy":
                git.checkout("deploy")
            git.branch('--create-reflog', '-f', 'master', '-t', 'origin/master')
            return True, "wow, this is working!"
        except Exception as e:
            return False, str(e)
<|endoftext|>


<commit_before>from __future__ import with_statement
from fabric.state import env
from fabric.api import local, run, abort, task
from fabric.context_managers import settings

@task
def allow_dirty():
    '''allow pushing even when the working copy is dirty'''
    env.gitric_allow_dirty = True

@task
def force_push():
    '''allow pushing even when history will be lost'''
    env.gitric_force_push = True

def git_seed(repo_path, commit=None):
    '''seed a remote git repository'''
    commit = _get_commit(commit)
    force = ('gitric_force_push' in env) and '-f' or ''
    dirty_working_copy = local('git status --porcelain', capture=True)
    if dirty_working_copy and 'gitric_allow_dirty' not in env:
        abort(
            'Working copy is dirty. This check can be overridden by\n'
            'importing gitric.api.allow_dirty and adding allow_dirty to your '
            'call.')
    # initialize the remote repository (idempotent)
    run('git init %s' % repo_path)
    # silence git complaints about pushes coming in on the current branch
    # the pushes only seed the immutable object store and do not modify the
    # working copy
    run('GIT_DIR=%s/.git git config receive.denyCurrentBranch ignore' %
        repo_path)
    # a target doesn't need to keep track of which branch it is on so we always
    # push to its "master"
    with settings(warn_only=True):
        push = local(
            'git push git+ssh://%s@%s:%s%s %s:refs/heads/master %s' % (
                env.user, env.host, env.port, repo_path, commit, force))
    if push.failed:
        abort(
            '%s is a non-fast-forward\n'
            'push. The seed will abort so you don\'t lose information. '
            'If you are doing this\nintentionally import '
            'gitric.api.force_push and add it to your call.' % commit)

def git_reset(repo_path, commit=None):
    '''checkout a sha1 on a remote git repo'''
    commit = _get_commit(commit)
    run('cd %s && git reset --hard %s' % (repo_path, commit))

def _get_commit(commit):
    if commit is None:
        # if no commit is specified we will push HEAD
        commit = local('git rev-parse HEAD', capture=True)
    return commit
<commit_msg>Ignore untracked files when checking for dirty working copy<commit_after>from __future__ import with_statement
from fabric.state import env
from fabric.api import local, run, abort, task
from fabric.context_managers import settings

@task
def allow_dirty():
    '''allow pushing even when the working copy is dirty'''
    env.gitric_allow_dirty = True

@task
def force_push():
    '''allow pushing even when history will be lost'''
    env.gitric_force_push = True

def git_seed(repo_path, commit=None):
    '''seed a remote git repository'''
    commit = _get_commit(commit)
    force = ('gitric_force_push' in env) and '-f' or ''
    dirty_working_copy = local('git status --untracked-files=no --porcelain', capture=True)
    if dirty_working_copy and 'gitric_allow_dirty' not in env:
        abort(
            'Working copy is dirty. This check can be overridden by\n'
            'importing gitric.api.allow_dirty and adding allow_dirty to your '
            'call.')
    # initialize the remote repository (idempotent)
    run('git init %s' % repo_path)
    # silence git complaints about pushes coming in on the current branch
    # the pushes only seed the immutable object store and do not modify the
    # working copy
    run('GIT_DIR=%s/.git git config receive.denyCurrentBranch ignore' %
        repo_path)
    # a target doesn't need to keep track of which branch it is on so we always
    # push to its "master"
    with settings(warn_only=True):
        push = local(
            'git push git+ssh://%s@%s:%s%s %s:refs/heads/master %s' % (
                env.user, env.host, env.port, repo_path, commit, force))
    if push.failed:
        abort(
            '%s is a non-fast-forward\n'
            'push. The seed will abort so you don\'t lose information. '
            'If you are doing this\nintentionally import '
            'gitric.api.force_push and add it to your call.' % commit)

def git_reset(repo_path, commit=None):
    '''checkout a sha1 on a remote git repo'''
    commit = _get_commit(commit)
    run('cd %s && git reset --hard %s' % (repo_path, commit))

def _get_commit(commit):
    if commit is None:
        # if no commit is specified we will push HEAD
        commit = local('git rev-parse HEAD', capture=True)
    return commit
<|endoftext|>