<commit_before>#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-

import logging
from pprint import pprint, pformat
from argparse import ArgumentParser
from getpass import getpass

import dill as pickle
from deepdiff import DeepDiff

from atlassian.service_model import MyLittleAtlassianWorld
from atlassian.confluence import Confluence
from atlassian.jira import Jira
from atlassian.stash import Stash

from view.csv import WorldCsvView
from view.text import WorldTextView
from view.html import WorldHtmlView

l = logging.getLogger(__name__)

class CliController:
    @staticmethod
    def run():
        controller = CliController()
        controller.prepare_arguments()
        controller.parse_arguments()
        controller.run_action()
        return controller

    def __init__(self):
        self.parser = ArgumentParser()
        self.args = None

        """A MyLittleAtlassianWold object serving as root of our model"""
        self.world = None

    def prepare_arguments(self):
        auth = self.parser.add_argument_group("Authentication",
                                         "Please provide administrative credentials so this script can access your Atlassian services.")
        auth.add_argument('--user', '-u', help='User to log in with')
        passwordargs = auth.add_mutually_exclusive_group()
        passwordargs.add_argument('--password', '-p', help='Password')
        passwordargs.add_argument('--passfile', '-P', help='Password file')

        services = self.parser.add_argument_group("Services",
                                             '''\
                                             Add at least one Atlassian service to check. You can provide multiple instances of each kind.
                                             Please provide the complete URL including either http:// or https://, e.g. https://confluence.myserver.example.com.
                                             You can add a hint telling us the Confluence version you're running like this:
                                             https://confluence.myserver.example.com,version=5.8.14''')
        services.add_argument('--confluence', '-c', help='Add Confluence instance.', action='append')
        services.add_argument('--jira', '-j', help='Add JIRA instance.', action='append')
        services.add_argument('--stash', '-s', help='Add Bitbucket Server instance, formerly known as Stash.', action='append')

        action = self.parser.add_argument_group("Action", "What do you actually want to do?")
        action.add_argument('--csv', action='store_true', help='Export permissions as CSV')
        action.add_argument('--print', action='store_true', help='Pretty-Print permissions (plain text)')
        action.add_argument('--html', action='store_true', help='Export permissions as HTML')

        optional = self.parser.add_argument_group("optional arguments")
        optional.add_argument('--compare', '-cmp',
                              help="Compare to previous state, show changes only." +
                                   "Will compare to a file previously saved with --save." +
                                   "Provide this file's name here.")
        optional.add_argument('--save', '-S', help='Save to internal file. This allows you to do further analysis with this script without re-crawling everything.')
        optional.add_argument('--load', '-L', help='Load from file. This allows you to do further analysis with this script without re-crawling everything.')
        optional.add_argument('--output', '-o', help='Write output to this file. Will print to console if omitted.')
        optional.add_argument('--loglevel', '-l', default='WARNING', help="Loglevel", action='store')
        optional.add_argument('--header', help='For CSV export, include a header line', action='store_true')

    def parse_arguments(self):
        self.args = self.parser.parse_args()

        if not (self.args.print or self.args.csv or self.args.save or self.args.html):
            self.parser.error("Error: Please specify at least one action. You do want this script to actually do something, right?")

        # Can't output diff as CSV as we're currently using DeepDiff's output format and our CSV exporter doesn't support it.
        # TODO: fix this
        if self.args.csv and self.args.compare:
            self.parser.error("Error: This tool currently can't export comparisons as CSV. Use --print instead.")

        # Set log level
        loglevel = getattr(logging, self.args.loglevel.upper(), None)
        if not isinstance(loglevel, int):
            raise ValueError('Invalid log level: {}'.format(self.args.loglevel))
        logging.basicConfig(level=loglevel)

        # Create model
        if self.args.load:   # ...or get a ready-made one from disk?
            with open(self.args.load, 'rb') as fd:
                self.world = pickle.load(fd)
        else:
            password = <PASSWORD>.get_password()
            self.world = self.create_services(self.args.confluence, self.args.jira, self.args.stash)
            for service in self.world.services:  # TODO beautify
                service.login(self.args.user, password)

    def run_action(self):
        if self.args.compare:  # special case, we prevented any other output than plain text in parse_arguments()
            with open(self.args.compare, 'rb') as pickle_file:
                current_permissions = self.world.permissions
                previous_permissions = pickle.load(pickle_file).permissions
                permissions = DeepDiff(previous_permissions, current_permissions, ignore_order=True)

                if self.args.output:
                    with open(self.args.output, 'w') as out_file:
                        out_file.write(pformat(permissions))
                else:
                    pprint(permissions)

        # TODO: beautify this block similar to what we did in create_services()
        if self.args.csv:
            view = WorldCsvView(self.world)
            if self.args.output:
                view.export(self.args.output)
            else:
                view.print()

        if self.args.print:
            view = WorldTextView(self.world)
            if self.args.output:
                view.export(self.args.output)
            else:
                view.print()

        if self.args.html:
            view = WorldHtmlView(self.world)
            if self.args.output:
                view.export(self.args.output)
            else:
                view.print()

        if self.args.save:  # Save model as pickle. Independent of any other action.
            with open(self.args.save, 'wb') as fd:
                self.world.logout()
                pickle.dump(self.world, fd)

    def get_password(self):
        password = <PASSWORD>
        if self.args.password is not None:
            password = self.args.password
        elif self.args.passfile is not None:
            with open(self.args.passfile, 'r') as fd:
                password = fd.read()
        else:  # get it interactively
            password = getpass()
        if password:
            return password
        else:
            self.parser.error("Please provide a valid password.")

    def create_services(self, confluence, jira, stash):
        """
        :rtype MyLittleAtlassianWorld
        :return An object representing an ecosystem of Atlassian services<commit_msg>Fix minor CLI regression<commit_after>#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-

import logging
from pprint import pprint, pformat
from argparse import ArgumentParser
from getpass import getpass

import dill as pickle
from deepdiff import DeepDiff

from atlassian.service_model import MyLittleAtlassianWorld
from atlassian.confluence import Confluence
from atlassian.jira import Jira
from atlassian.stash import Stash

from view.csv import WorldCsvView
from view.text import WorldTextView
from view.html import WorldHtmlView

l = logging.getLogger(__name__)

class CliController:
    @staticmethod
    def run():
        controller = CliController()
        controller.prepare_arguments()
        controller.parse_arguments()
        controller.run_action()
        return controller

    def __init__(self):
        self.parser = ArgumentParser()
        self.args = None

        """A MyLittleAtlassianWold object serving as root of our model"""
        self.world = None

    def prepare_arguments(self):
        auth = self.parser.add_argument_group("Authentication",
                                         "Please provide administrative credentials so this script can access your Atlassian services.")
        auth.add_argument('--user', '-u', help='User to log in with')
        passwordargs = auth.add_mutually_exclusive_group()
        passwordargs.add_argument('--password', '-p', help='Password')
        passwordargs.add_argument('--passfile', '-P', help='Password file')

        services = self.parser.add_argument_group("Services",
                                             '''\
                                             Add at least one Atlassian service to check. You can provide multiple instances of each kind.
                                             Please provide the complete URL including either http:// or https://, e.g. https://confluence.myserver.example.com.
                                             You can add a hint telling us the Confluence version you're running like this:
                                             https://confluence.myserver.example.com,version=5.8.14''')
        services.add_argument('--confluence', '-c', help='Add Confluence instance.', action='append')
        services.add_argument('--jira', '-j', help='Add JIRA instance.', action='append')
        services.add_argument('--stash', '-s', help='Add Bitbucket Server instance, formerly known as Stash.', action='append')

        action = self.parser.add_argument_group("Action", "What do you actually want to do?")
        action.add_argument('--csv', action='store_true', help='Export permissions as CSV')
        action.add_argument('--print', action='store_true', help='Pretty-Print permissions (plain text)')
        action.add_argument('--html', action='store_true', help='Export permissions as HTML')

        optional = self.parser.add_argument_group("optional arguments")
        optional.add_argument('--compare', '-cmp',
                              help="Compare to previous state, show changes only." +
                                   "Will compare to a file previously saved with --save." +
                                   "Provide this file's name here.")
        optional.add_argument('--save', '-S', help='Save to internal file. This allows you to do further analysis with this script without re-crawling everything.')
        optional.add_argument('--load', '-L', help='Load from file. This allows you to do further analysis with this script without re-crawling everything.')
        optional.add_argument('--output', '-o', help='Write output to this file. Will print to console if omitted.')
        optional.add_argument('--loglevel', '-l', default='WARNING', help="Loglevel", action='store')
        optional.add_argument('--header', help='For CSV export, include a header line', action='store_true')

    def parse_arguments(self):
        self.args = self.parser.parse_args()

        if not (self.args.print or self.args.csv or self.args.save or self.args.html):
            self.parser.error("Error: Please specify at least one action. You do want this script to actually do something, right?")

        # Can't output diff as CSV as we're currently using DeepDiff's output format and our CSV exporter doesn't support it.
        # TODO: fix this
        if self.args.compare and (self.args.csv or self.args.html):
            self.parser.error("Error: This tool currently can't export comparisons as CSV or HTML. Use --print instead.")

        # Set log level
        loglevel = getattr(logging, self.args.loglevel.upper(), None)
        if not isinstance(loglevel, int):
            raise ValueError('Invalid log level: {}'.format(self.args.loglevel))
        logging.basicConfig(level=loglevel)

        # Create model
        if self.args.load:   # ...or get a ready-made one from disk?
            with open(self.args.load, 'rb') as fd:
                self.world = pickle.load(fd)
        else:
            password = <PASSWORD>()
            self.world = self.create_services(self.args.confluence, self.args.jira, self.args.stash)
            for service in self.world.services:  # TODO beautify
                service.login(self.args.user, password)

    def run_action(self):
        if self.args.compare:  # special case, we prevented any other output than plain text in parse_arguments()
            with open(self.args.compare, 'rb') as pickle_file:
                current_permissions = self.world.permissions
                previous_world = pickle.load(pickle_file)
                previous_permissions = previous_world.permissions
                permissions = DeepDiff(previous_permissions, current_permissions, ignore_order=True)
                test = DeepDiff(previous_world, self.world)
                if self.args.output:
                    with open(self.args.output, 'w') as out_file:
                        out_file.write(pformat(permissions))
                else:
                    pprint(permissions)
        else:

            # TODO: beautify this block similar to what we did in create_services()
            if self.args.csv:
                view = WorldCsvView(self.world)
                if self.args.output:
                    view.export(self.args.output)
                else:
                    view.print()

            if self.args.print:
                view = WorldTextView(self.world)
                if self.args.output:
                    view.export(self.args.output)
                else:
                    view.print()

            if self.args.html:
                view = WorldHtmlView(self.world)
                if self.args.output:
                    view.export(self.args.output)
                else:
                    view.print()

        if self.args.save:  # Save model as pickle. Independent of any other action.
            with open(self.args.save, 'wb') as fd:
                self.world.logout()
                pickle.dump(self.world, fd)

    def get_password(self):
        password = <PASSWORD>
        if self.args.password is not None:
            password = self.args.password
        elif self.args.passfile is not None:
            with open(self.args.passfile, 'r') as fd:
                password = fd.read()
        else:  # get it interactively
            password = <PASSWORD>()
        if password:
            return password
        else:
            self.parser.error("Please provide a valid password.")

    def create_services(self, confluence, jira, stash):
        """
        :rtype MyLittleAtlassianWorld
        :return An object representing an ecosystem of Atlassian services
{'fraction_non_alphanumeric': 0.09969467675560865, 'fraction_numerical': 0.0009292446568432231, 'mean_word_length': 2.140921409214092, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2529391', 'n_tokens_mistral': 3899, 'n_tokens_neox': 3748, 'n_words': 1156}