<commit_before>import shell
from gitFunctions import Commiter
import shouter

class RTCInitializer:
    @staticmethod
    def initialize(config):
        RTCInitializer.loginandcollectstreams(config)
        WorkspaceHandler(config).createandload(config.earlieststreamname, config.initialcomponentbaselines)

    @staticmethod
    def loginandcollectstreams(config):
        shell.execute("lscm login -r %s -u %s -P %s" % (config.repo, config.user, config.password))
        config.collectstreamuuids()

class WorkspaceHandler:
    def __init__(self, config):
        self.config = config
        self.workspace = config.workspace
        self.repo = config.repo

    def createandload(self, stream, componentbaselineentries=[], create=True):
        if create:
            shell.execute("lscm create workspace -r %s -s %s %s" % (self.config.repo, stream, self.workspace))
        if componentbaselineentries:
            self.setcomponentstobaseline(componentbaselineentries, stream)
        else:
            self.setcomponentstobaseline(ImportHandler(self.config).getcomponentbaselineentriesfromstream(stream),
                                         stream)
        self.load()

    def load(self):
        shouter.shout("Start (re)loading current workspace")
        shell.execute("lscm load -r %s %s --force" % (self.repo, self.workspace))
        shouter.shout("Load of workspace finished")

    def setcomponentstobaseline(self, componentbaselineentries, streamuuid):
        self.setnewflowtargets(streamuuid)
        for entry in componentbaselineentries:
            shouter.shout("Set component '%s' to baseline '%s'" % (entry.componentname, entry.baselinename))

            replacecommand = "lscm set component -r %s -b %s %s stream %s %s --overwrite-uncommitted" % \
                             (self.repo, entry.baseline, self.workspace, streamuuid, entry.component)
            shell.execute(replacecommand)

    def setnewflowtargets(self, streamuuid):
        shouter.shout("Replacing Flowtargets")
        self.removedefaultflowtarget()
        shell.execute("lscm add flowtarget -r %s %s %s"
                      % (self.repo, self.workspace, streamuuid))
        shell.execute("lscm set flowtarget -r %s %s --default --current %s"
                      % (self.repo, self.workspace, streamuuid))

    def removedefaultflowtarget(self):
        flowtargetline = shell.getoutput("lscm --show-alias n list flowtargets -r %s %s"
                                         % (self.repo, self.workspace))[0]
        flowtargetnametoremove = flowtargetline.split("\"")[1]
        shell.execute("lscm remove flowtarget -r %s %s %s"
                      % (self.repo, self.workspace, flowtargetnametoremove))

    def recreateoldestworkspace(self):
        self.createandload(self.config.earlieststreamname, self.config.initialcomponentbaselines, False)

class ImportHandler:
    def __init__(self, config):
        self.config = config

    def getcomponentbaselineentriesfromstream(self, stream):
        filename = self.config.getlogpath("StreamComponents_" + stream + ".txt")
        shell.execute(
            "lscm --show-alias n --show-uuid y list components -v -r " + self.config.repo + " " + stream, filename)
        componentbaselinesentries = []
        skippedfirstrow = False
        islinewithcomponent = 2
        component = ""
        baseline = ""
        componentname = ""
        baselinename = ""
        with open(filename, 'r') as file:
            for line in file:
                cleanedline = line.strip()
                if cleanedline:
                    if not skippedfirstrow:
                        skippedfirstrow = True
                        continue
                    splittedinformationline = line.split("\"")
                    uuidpart = splittedinformationline[0].split(" ")
                    if islinewithcomponent % 2 is 0:
                        component = uuidpart[3].strip()[1:-1]
                        componentname = splittedinformationline[1]
                    else:
                        baseline = uuidpart[5].strip()[1:-1]
                        baselinename = splittedinformationline[1]

                    if baseline and component:
                        componentbaselinesentries.append(
                            ComponentBaseLineEntry(component, baseline, componentname, baselinename))
                        baseline = ""
                        component = ""
                        componentname = ""
                        baselinename = ""
                    islinewithcomponent += 1
        return componentbaselinesentries

    def acceptchangesintoworkspace(self, changeentries):
        git = Commiter
        amountofchanges = len(changeentries)
        shouter.shout("Start accepting %s changesets" % amountofchanges)
        amountofacceptedchanges = 0
        for changeEntry in changeentries:
            amountofacceptedchanges += 1
            revision = changeEntry.revision
            acceptingmsg = "Accepting: " + changeEntry.comment + " (Date: " + changeEntry.date + " Author: " \
                           + changeEntry.author + " Revision: " + revision + ")"
            shouter.shout(acceptingmsg)
            acceptcommand = "lscm accept --changes " + revision + " --overwrite-uncommitted"
            shell.execute(acceptcommand, self.config.getlogpath("accept.txt"), "a")
            git.addandcommit(changeEntry)

            shouter.shout("Accepted change %s/%s" % (amountofacceptedchanges, amountofchanges))

    def getchangeentriesofstreamcomponents(self, componentbaselineentries):
        shouter.shout("Start collecting changeentries")
        changeentries = []
        for componentBaseLineEntry in componentbaselineentries:
            changeentries.extend(self.getchangeentriesofbaseline(componentBaseLineEntry.baseline))
        changeentries.sort(key=lambda change: change.date)
        return changeentries

    @staticmethod
    def getchangeentriesfromfile(outputfilename):
        informationseparator = "@@"
        changeentries = []
        with open(outputfilename, 'r') as file:
            for line in file:
                cleanedline = line.strip()
                if cleanedline:
                    splittedlines = cleanedline.split(informationseparator)
                    revisionwithbrackets = splittedlines[0].strip()
                    revision = revisionwithbrackets[1:-1]
                    author = splittedlines[1].strip()
                    email = splittedlines[2].strip()
                    comment = splittedlines[3].strip()
                    date = splittedlines[4].strip()
                    changeentries.append(ChangeEntry(revision, author, email, date, comment))
        return changeentries

    def getchangeentriesofbaseline(self, baselinetocompare):
        return self.getchangeentriesbytypeandvalue("baseline", baselinetocompare)

    def getchangeentriesofstream(self, streamtocompare):
        shouter.shout("Start collecting changes since baseline creation")
        return self.getchangeentriesbytypeandvalue("stream", streamtocompare)

    def getchangeentriesbytypeandvalue(self, comparetype, value):
        dateformat = "yyyy-MM-dd HH:mm:ss"
        outputfilename = self.config.getlogpath("Compare_" + comparetype + "_" + value + ".txt")
        comparecommand = "lscm --show-alias n --show-uuid y compare ws %s %s %s -r %s -I sw -C @@{name}@@{email}@@ --flow-directions i -D @@\"%s\"@@" \
                         % (self.config.workspace, comparetype, value, self.config.repo, dateformat)
        shell.execute(comparecommand, outputfilename)
        return ImportHandler.getchangeentriesfromfile(outputfilename)

class ChangeEntry:
    def __init__(self, revision, author, email, date, comment):
        self.revision = revision
        self.author = author
        self.email = email
        self.date = date
        self.comment = comment

    def getgitauthor(self):
        authorrepresentation = "%s <%s>" % (self.author, self.email)
        return shell.quote(authorrepresentation)

class ComponentBaseLineEntry:
    def __init__(self, component, baseline, componentname, baselinename):
        self.component = component
        self.baseline = baseline
        self.componentname = componentname
        self.baselinename = baselinename
<commit_msg>added error handling in case changes cant be accepted<commit_after>import sys

import shell
from gitFunctions import Commiter
import shouter

class RTCInitializer:
    @staticmethod
    def initialize(config):
        RTCInitializer.loginandcollectstreams(config)
        WorkspaceHandler(config).createandload(config.earlieststreamname, config.initialcomponentbaselines)

    @staticmethod
    def loginandcollectstreams(config):
        shell.execute("lscm login -r %s -u %s -P %s" % (config.repo, config.user, config.password))
        config.collectstreamuuids()

class WorkspaceHandler:
    def __init__(self, config):
        self.config = config
        self.workspace = config.workspace
        self.repo = config.repo

    def createandload(self, stream, componentbaselineentries=[], create=True):
        if create:
            shell.execute("lscm create workspace -r %s -s %s %s" % (self.config.repo, stream, self.workspace))
        if componentbaselineentries:
            self.setcomponentstobaseline(componentbaselineentries, stream)
        else:
            self.setcomponentstobaseline(ImportHandler(self.config).getcomponentbaselineentriesfromstream(stream),
                                         stream)
        self.load()

    def load(self):
        shouter.shout("Start (re)loading current workspace")
        shell.execute("lscm load -r %s %s --force" % (self.repo, self.workspace))
        shouter.shout("Load of workspace finished")

    def setcomponentstobaseline(self, componentbaselineentries, streamuuid):
        self.setnewflowtargets(streamuuid)
        for entry in componentbaselineentries:
            shouter.shout("Set component '%s' to baseline '%s'" % (entry.componentname, entry.baselinename))

            replacecommand = "lscm set component -r %s -b %s %s stream %s %s --overwrite-uncommitted" % \
                             (self.repo, entry.baseline, self.workspace, streamuuid, entry.component)
            shell.execute(replacecommand)

    def setnewflowtargets(self, streamuuid):
        shouter.shout("Replacing Flowtargets")
        self.removedefaultflowtarget()
        shell.execute("lscm add flowtarget -r %s %s %s"
                      % (self.repo, self.workspace, streamuuid))
        shell.execute("lscm set flowtarget -r %s %s --default --current %s"
                      % (self.repo, self.workspace, streamuuid))

    def removedefaultflowtarget(self):
        flowtargetline = shell.getoutput("lscm --show-alias n list flowtargets -r %s %s"
                                         % (self.repo, self.workspace))[0]
        flowtargetnametoremove = flowtargetline.split("\"")[1]
        shell.execute("lscm remove flowtarget -r %s %s %s"
                      % (self.repo, self.workspace, flowtargetnametoremove))

    def recreateoldestworkspace(self):
        self.createandload(self.config.earlieststreamname, self.config.initialcomponentbaselines, False)

class ImportHandler:
    def __init__(self, config):
        self.config = config

    def getcomponentbaselineentriesfromstream(self, stream):
        filename = self.config.getlogpath("StreamComponents_" + stream + ".txt")
        shell.execute(
            "lscm --show-alias n --show-uuid y list components -v -r " + self.config.repo + " " + stream, filename)
        componentbaselinesentries = []
        skippedfirstrow = False
        islinewithcomponent = 2
        component = ""
        baseline = ""
        componentname = ""
        baselinename = ""
        with open(filename, 'r') as file:
            for line in file:
                cleanedline = line.strip()
                if cleanedline:
                    if not skippedfirstrow:
                        skippedfirstrow = True
                        continue
                    splittedinformationline = line.split("\"")
                    uuidpart = splittedinformationline[0].split(" ")
                    if islinewithcomponent % 2 is 0:
                        component = uuidpart[3].strip()[1:-1]
                        componentname = splittedinformationline[1]
                    else:
                        baseline = uuidpart[5].strip()[1:-1]
                        baselinename = splittedinformationline[1]

                    if baseline and component:
                        componentbaselinesentries.append(
                            ComponentBaseLineEntry(component, baseline, componentname, baselinename))
                        baseline = ""
                        component = ""
                        componentname = ""
                        baselinename = ""
                    islinewithcomponent += 1
        return componentbaselinesentries

    def acceptchangesintoworkspace(self, changeentries):
        git = Commiter
        amountofchanges = len(changeentries)
        shouter.shout("Start accepting %s changesets" % amountofchanges)
        amountofacceptedchanges = 0
        for changeEntry in changeentries:
            amountofacceptedchanges += 1
            revision = changeEntry.revision
            acceptingmsg = "Accepting: " + changeEntry.comment + " (Date: " + changeEntry.date + " Author: " \
                           + changeEntry.author + " Revision: " + revision + ")"
            shouter.shout(acceptingmsg)
            acceptcommand = "lscm accept --changes " + revision + " --overwrite-uncommitted"
            acceptedsuccesfully = shell.execute(acceptcommand, self.config.getlogpath("accept.txt"), "a") is 0
            if not acceptedsuccesfully:
                sys.exit("Change wasnt succesfully accepted into workspace, please check the output")
            git.addandcommit(changeEntry)

            shouter.shout("Accepted change %s/%s" % (amountofacceptedchanges, amountofchanges))

    def getchangeentriesofstreamcomponents(self, componentbaselineentries):
        shouter.shout("Start collecting changeentries")
        changeentries = []
        for componentBaseLineEntry in componentbaselineentries:
            changeentries.extend(self.getchangeentriesofbaseline(componentBaseLineEntry.baseline))
        changeentries.sort(key=lambda change: change.date)
        return changeentries

    @staticmethod
    def getchangeentriesfromfile(outputfilename):
        informationseparator = "@@"
        changeentries = []
        with open(outputfilename, 'r') as file:
            for line in file:
                cleanedline = line.strip()
                if cleanedline:
                    splittedlines = cleanedline.split(informationseparator)
                    revisionwithbrackets = splittedlines[0].strip()
                    revision = revisionwithbrackets[1:-1]
                    author = splittedlines[1].strip()
                    email = splittedlines[2].strip()
                    comment = splittedlines[3].strip()
                    date = splittedlines[4].strip()
                    changeentries.append(ChangeEntry(revision, author, email, date, comment))
        return changeentries

    def getchangeentriesofbaseline(self, baselinetocompare):
        return self.getchangeentriesbytypeandvalue("baseline", baselinetocompare)

    def getchangeentriesofstream(self, streamtocompare):
        shouter.shout("Start collecting changes since baseline creation")
        return self.getchangeentriesbytypeandvalue("stream", streamtocompare)

    def getchangeentriesbytypeandvalue(self, comparetype, value):
        dateformat = "yyyy-MM-dd HH:mm:ss"
        outputfilename = self.config.getlogpath("Compare_" + comparetype + "_" + value + ".txt")
        comparecommand = "lscm --show-alias n --show-uuid y compare ws %s %s %s -r %s -I sw -C @@{name}@@{email}@@ --flow-directions i -D @@\"%s\"@@" \
                         % (self.config.workspace, comparetype, value, self.config.repo, dateformat)
        shell.execute(comparecommand, outputfilename)
        return ImportHandler.getchangeentriesfromfile(outputfilename)

class ChangeEntry:
    def __init__(self, revision, author, email, date, comment):
        self.revision = revision
        self.author = author
        self.email = email
        self.date = date
        self.comment = comment

    def getgitauthor(self):
        authorrepresentation = "%s <%s>" % (self.author, self.email)
        return shell.quote(authorrepresentation)

class ComponentBaseLineEntry:
    def __init__(self, component, baseline, componentname, baselinename):
        self.component = component
        self.baseline = baseline
        self.componentname = componentname
        self.baselinename = baselinename
<|endoftext|>


<commit_before># vim: set expandtab sw=4 ts=4:
#
# Unit tests for Travis env_var related functions
#
# Copyright (C) 2014-2015 Dieter Adriaenssens <ruleant@users.sourceforge.net>
#
# This file is part of buildtimetrend/python-lib
# <https://github.com/buildtimetrend/python-lib/>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import buildtimetrend
from buildtimetrend.settings import Settings
from buildtimetrend.travis.env_var import load_travis_env_vars
from buildtimetrend.travis.env_var import load_build_matrix_env_vars
from buildtimetrend.travis.env_var import load_travis_pr_env_vars
#import constants
import unittest

class TestTravis(unittest.TestCase):

    """Unit tests for Travis CI env var loading functions"""

    def setUp(self):
        """Initialise test environment before each test."""
        # reinit settings singleton
        Settings().__init__()

    def test_load_travis_env_vars(self):
        """Test load_travis_env_vars"""
        settings = Settings()

        self.assertEqual(None, settings.get_setting("ci_platform"))
        self.assertEqual(None, settings.get_setting("build"))
        self.assertEqual(None, settings.get_setting("job"))
        self.assertEqual(None, settings.get_setting("branch"))
        self.assertEqual(None, settings.get_setting("result"))
        self.assertEqual(None, settings.get_setting("build_trigger"))
        self.assertEqual(None, settings.get_setting("pull_request"))
        self.assertEqual(buildtimetrend.NAME, settings.get_project_name())

        #setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            expected_build = os.environ["TRAVIS_BUILD_NUMBER"]
            expected_job = os.environ["TRAVIS_JOB_NUMBER"]
            expected_branch = os.environ["TRAVIS_BRANCH"]
            expected_project_name = os.environ["TRAVIS_REPO_SLUG"]
            copy_pull_request = os.environ["TRAVIS_PULL_REQUEST"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"
            expected_build = os.environ["TRAVIS_BUILD_NUMBER"] = "123"
            expected_job = os.environ["TRAVIS_JOB_NUMBER"] = "123.1"
            expected_branch = os.environ["TRAVIS_BRANCH"] = "branch1"
            expected_project_name = \
                os.environ["TRAVIS_REPO_SLUG"] = "test/project"

        # setup Travis test result
        if "TRAVIS_TEST_RESULT" in os.environ:
            reset_travis_result = False
            copy_result = os.environ["TRAVIS_TEST_RESULT"]
        else:
            reset_travis_result = True

        os.environ["TRAVIS_TEST_RESULT"] = "0"
        os.environ["TRAVIS_PULL_REQUEST"] = "false"

        load_travis_env_vars()

        self.assertEqual("travis", settings.get_setting("ci_platform"))
        self.assertEqual(expected_build, settings.get_setting("build"))
        self.assertEqual(expected_job, settings.get_setting("job"))
        self.assertEqual(expected_branch, settings.get_setting("branch"))
        self.assertEqual(expected_project_name, settings.get_project_name())
        self.assertEqual("passed", settings.get_setting("result"))
        self.assertEqual("push", settings.get_setting("build_trigger"))
        self.assertDictEqual(
            {
                'is_pull_request': False,
                'title': None,
                'number': None
            },
            settings.get_setting("pull_request")
        )

        os.environ["TRAVIS_TEST_RESULT"] = "1"
        # build is a pull request
        expected_pull_request = os.environ["TRAVIS_PULL_REQUEST"] = "123"
        load_travis_env_vars()
        self.assertEqual("failed", settings.get_setting("result"))
        self.assertEqual(
            "pull_request", settings.get_setting("build_trigger")
        )
        self.assertDictEqual(
            {
                'is_pull_request': True,
                'title': "unknown",
                'number': expected_pull_request
            },
            settings.get_setting("pull_request")
        )

        # build is not a pull request
        os.environ["TRAVIS_PULL_REQUEST"] = "false"
        load_travis_env_vars()
        self.assertEqual("push", settings.get_setting("build_trigger"))
        self.assertDictEqual(
            {
                'is_pull_request': False,
                'title': None,
                'number': None
            },
            settings.get_setting("pull_request")
        )

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]
            del os.environ["TRAVIS_BUILD_NUMBER"]
            del os.environ["TRAVIS_JOB_NUMBER"]
            del os.environ["TRAVIS_BRANCH"]
            del os.environ["TRAVIS_REPO_SLUG"]
            del os.environ["TRAVIS_PULL_REQUEST"]
        else:
            os.environ["TRAVIS_PULL_REQUEST"] = copy_pull_request

        # reset Travis test result
        if reset_travis_result:
            del os.environ["TRAVIS_TEST_RESULT"]
        else:
            os.environ["TRAVIS_TEST_RESULT"] = copy_result

    def test_load_build_matrix_env_vars(self):
        """Test load_build_matrix_env_vars"""
        settings = Settings()

        self.assertEqual(None, settings.get_setting("build_matrix"))

        #setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            expected_os = os.environ["TRAVIS_OS_NAME"]
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"
            expected_os = os.environ["TRAVIS_OS_NAME"] = "test_os"

        # temporarily remove PYTHON VERSION
        if "TRAVIS_PYTHON_VERSION" in os.environ:
            reset_python_version = True
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_python_version = False

        # test language and language versions
        test_languages = [
            {
                'env_var': 'TRAVIS_DART_VERSION',
                'language': 'dart',
                'test_value': "1.1"
            },
            {
                'env_var': 'TRAVIS_GO_VERSION',
                'language': 'go',
                'test_value': "1.2"
            },
            {
                'env_var': 'TRAVIS_HAXE_VERSION',
                'language': 'haxe',
                'test_value': "1.3"
            },
            {
                'env_var': 'TRAVIS_JDK_VERSION',
                'language': 'java',
                'test_value': "1.4"
            },
            {
                'env_var': 'TRAVIS_JULIA_VERSION',
                'language': 'julia',
                'test_value': "1.5"
            },
            {
                'env_var': 'TRAVIS_NODE_VERSION',
                'language': 'javascript',
                'test_value': "1.6"
            },
            {
                'env_var': 'TRAVIS_OTP_RELEASE',
                'language': 'erlang',
                'test_value': "1.7"
            },
            {
                'env_var': 'TRAVIS_PERL_VERSION',
                'language': 'perl',
                'test_value': "1.8"
            },
            {
                'env_var': 'TRAVIS_PHP_VERSION',
                'language': 'php',
                'test_value': "1.9"
            },
            {
                'env_var': 'TRAVIS_PYTHON_VERSION',
                'language': 'python',
                'test_value': "1.10"
            },
            {
                'env_var': 'TRAVIS_R_VERSION',
                'language': 'r',
                'test_value': "1.11"
            },
            {
                'env_var': 'TRAVIS_RUBY_VERSION',
                'language': 'ruby',
                'test_value': "1.12"
            },
            {
                'env_var': 'TRAVIS_RUST_VERSION',
                'language': 'rust',
                'test_value': "1.13"
            },
            {
                'env_var': 'TRAVIS_SCALA_VERSION',
                'language': 'scala',
                'test_value': "1.14"
            }
        ]

        # test languages
        for language in test_languages:
            if language['env_var'] in os.environ:
                reset_travis_lang_version = False
                expected_lang_version = os.environ[language['env_var']]
            else:
                reset_travis_lang_version = True
                expected_lang_version = \
                    os.environ[language['env_var']] = language['test_value']

            load_build_matrix_env_vars(settings)

            self.assertDictEqual(
                {
                    'os': expected_os,
                    'language': language['language'],
                    'language_version': expected_lang_version,
                    'summary': "%s %s %s" % (
                        language['language'],
                        expected_lang_version,
                        expected_os
                    )
                },
                settings.get_setting("build_matrix")
            )

            # reset Travis test result
            if reset_travis_lang_version:
                del os.environ[language['env_var']]

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]
            del os.environ["TRAVIS_OS_NAME"]

        # reset removed python version
        if reset_python_version:
            os.environ["TRAVIS_PYTHON_VERSION"] = copy_python

    def test_load_build_matrix_env_vars_parameters(self):
        """Test load_travis_env_vars, optional parameters"""
        # setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            copy_os = os.environ["TRAVIS_OS_NAME"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"

        # temporarily remove OS VERSION
        if "TRAVIS_OS_NAME" in os.environ:
            reset_os = True
            copy_os = os.environ["TRAVIS_OS_NAME"]
            del os.environ["TRAVIS_OS_NAME"]
        else:
            reset_os = False

        # temporarily remove PYTHON VERSION
        if "TRAVIS_PYTHON_VERSION" in os.environ:
            reset_python_version = True
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_python_version = False

        # test optional build matrix parameters
        test_parameters = [
            {
                'env_var': 'TRAVIS_XCODE_SDK',
                'parameter': 'xcode_sdk',
                'test_value': "test_x_sdk"
            },
            {
                'env_var': 'TRAVIS_XCODE_SCHEME',
                'parameter': 'xcode_scheme',
                'test_value': "test_x_scheme"
            },
            {
                'env_var': 'TRAVIS_XCODE_PROJECT',
                'parameter': 'xcode_project',
                'test_value': "test_x_project"
            },
            {
                'env_var': 'TRAVIS_XCODE_WORKSPACE',
                'parameter': 'xcode_workspace',
                'test_value': "test_x_workspace"
            },
            {
                'env_var': 'CC',
                'parameter': 'compiler',
                'test_value': "test_gcc"
            },
            {
                'env_var': 'ENV',
                'parameter': 'parameters',
                'test_value': "test_env"
            }
        ]

        # test parameters
        for parameter in test_parameters:
            Settings().__init__()
            settings = Settings()

            if parameter['env_var'] in os.environ:
                reset_travis_parameter = False
                expected_param_value = os.environ[parameter['env_var']]
            else:
                reset_travis_parameter = True
                expected_param_value = os.environ[parameter['env_var']] = \
                    parameter['test_value']

            load_build_matrix_env_vars(settings)

            self.assertDictEqual(
                {
                    parameter["parameter"]: expected_param_value,
                    'summary': expected_param_value
                },
                settings.get_setting("build_matrix")
            )

            # reset Travis parameters
            if reset_travis_parameter:
                del os.environ[parameter['env_var']]

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]

        # reset removed os name
        if reset_os:
            os.environ["TRAVIS_OS_NAME"] = copy_os

        # reset removed python version
        if reset_python_version:
            os.environ["TRAVIS_PYTHON_VERSION"] = copy_python
<commit_msg>remove unused import, rename test class<commit_after># vim: set expandtab sw=4 ts=4:
#
# Unit tests for Travis env_var related functions
#
# Copyright (C) 2014-2015 Dieter Adriaenssens <ruleant@users.sourceforge.net>
#
# This file is part of buildtimetrend/python-lib
# <https://github.com/buildtimetrend/python-lib/>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import buildtimetrend
from buildtimetrend.settings import Settings
from buildtimetrend.travis.env_var import load_travis_env_vars
from buildtimetrend.travis.env_var import load_build_matrix_env_vars
from buildtimetrend.travis.env_var import load_travis_pr_env_vars
import unittest

class TestTravisEnvVar(unittest.TestCase):

    """Unit tests for Travis CI env var loading functions"""

    def setUp(self):
        """Initialise test environment before each test."""
        # reinit settings singleton
        Settings().__init__()

    def test_load_travis_env_vars(self):
        """Test load_travis_env_vars"""
        settings = Settings()

        self.assertEqual(None, settings.get_setting("ci_platform"))
        self.assertEqual(None, settings.get_setting("build"))
        self.assertEqual(None, settings.get_setting("job"))
        self.assertEqual(None, settings.get_setting("branch"))
        self.assertEqual(None, settings.get_setting("result"))
        self.assertEqual(None, settings.get_setting("build_trigger"))
        self.assertEqual(None, settings.get_setting("pull_request"))
        self.assertEqual(buildtimetrend.NAME, settings.get_project_name())

        #setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            expected_build = os.environ["TRAVIS_BUILD_NUMBER"]
            expected_job = os.environ["TRAVIS_JOB_NUMBER"]
            expected_branch = os.environ["TRAVIS_BRANCH"]
            expected_project_name = os.environ["TRAVIS_REPO_SLUG"]
            copy_pull_request = os.environ["TRAVIS_PULL_REQUEST"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"
            expected_build = os.environ["TRAVIS_BUILD_NUMBER"] = "123"
            expected_job = os.environ["TRAVIS_JOB_NUMBER"] = "123.1"
            expected_branch = os.environ["TRAVIS_BRANCH"] = "branch1"
            expected_project_name = \
                os.environ["TRAVIS_REPO_SLUG"] = "test/project"

        # setup Travis test result
        if "TRAVIS_TEST_RESULT" in os.environ:
            reset_travis_result = False
            copy_result = os.environ["TRAVIS_TEST_RESULT"]
        else:
            reset_travis_result = True

        os.environ["TRAVIS_TEST_RESULT"] = "0"
        os.environ["TRAVIS_PULL_REQUEST"] = "false"

        load_travis_env_vars()

        self.assertEqual("travis", settings.get_setting("ci_platform"))
        self.assertEqual(expected_build, settings.get_setting("build"))
        self.assertEqual(expected_job, settings.get_setting("job"))
        self.assertEqual(expected_branch, settings.get_setting("branch"))
        self.assertEqual(expected_project_name, settings.get_project_name())
        self.assertEqual("passed", settings.get_setting("result"))
        self.assertEqual("push", settings.get_setting("build_trigger"))
        self.assertDictEqual(
            {
                'is_pull_request': False,
                'title': None,
                'number': None
            },
            settings.get_setting("pull_request")
        )

        os.environ["TRAVIS_TEST_RESULT"] = "1"
        # build is a pull request
        expected_pull_request = os.environ["TRAVIS_PULL_REQUEST"] = "123"
        load_travis_env_vars()
        self.assertEqual("failed", settings.get_setting("result"))
        self.assertEqual(
            "pull_request", settings.get_setting("build_trigger")
        )
        self.assertDictEqual(
            {
                'is_pull_request': True,
                'title': "unknown",
                'number': expected_pull_request
            },
            settings.get_setting("pull_request")
        )

        # build is not a pull request
        os.environ["TRAVIS_PULL_REQUEST"] = "false"
        load_travis_env_vars()
        self.assertEqual("push", settings.get_setting("build_trigger"))
        self.assertDictEqual(
            {
                'is_pull_request': False,
                'title': None,
                'number': None
            },
            settings.get_setting("pull_request")
        )

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]
            del os.environ["TRAVIS_BUILD_NUMBER"]
            del os.environ["TRAVIS_JOB_NUMBER"]
            del os.environ["TRAVIS_BRANCH"]
            del os.environ["TRAVIS_REPO_SLUG"]
            del os.environ["TRAVIS_PULL_REQUEST"]
        else:
            os.environ["TRAVIS_PULL_REQUEST"] = copy_pull_request

        # reset Travis test result
        if reset_travis_result:
            del os.environ["TRAVIS_TEST_RESULT"]
        else:
            os.environ["TRAVIS_TEST_RESULT"] = copy_result

    def test_load_build_matrix_env_vars(self):
        """Test load_build_matrix_env_vars"""
        settings = Settings()

        self.assertEqual(None, settings.get_setting("build_matrix"))

        #setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            expected_os = os.environ["TRAVIS_OS_NAME"]
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"
            expected_os = os.environ["TRAVIS_OS_NAME"] = "test_os"

        # temporarily remove PYTHON VERSION
        if "TRAVIS_PYTHON_VERSION" in os.environ:
            reset_python_version = True
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_python_version = False

        # test language and language versions
        test_languages = [
            {
                'env_var': 'TRAVIS_DART_VERSION',
                'language': 'dart',
                'test_value': "1.1"
            },
            {
                'env_var': 'TRAVIS_GO_VERSION',
                'language': 'go',
                'test_value': "1.2"
            },
            {
                'env_var': 'TRAVIS_HAXE_VERSION',
                'language': 'haxe',
                'test_value': "1.3"
            },
            {
                'env_var': 'TRAVIS_JDK_VERSION',
                'language': 'java',
                'test_value': "1.4"
            },
            {
                'env_var': 'TRAVIS_JULIA_VERSION',
                'language': 'julia',
                'test_value': "1.5"
            },
            {
                'env_var': 'TRAVIS_NODE_VERSION',
                'language': 'javascript',
                'test_value': "1.6"
            },
            {
                'env_var': 'TRAVIS_OTP_RELEASE',
                'language': 'erlang',
                'test_value': "1.7"
            },
            {
                'env_var': 'TRAVIS_PERL_VERSION',
                'language': 'perl',
                'test_value': "1.8"
            },
            {
                'env_var': 'TRAVIS_PHP_VERSION',
                'language': 'php',
                'test_value': "1.9"
            },
            {
                'env_var': 'TRAVIS_PYTHON_VERSION',
                'language': 'python',
                'test_value': "1.10"
            },
            {
                'env_var': 'TRAVIS_R_VERSION',
                'language': 'r',
                'test_value': "1.11"
            },
            {
                'env_var': 'TRAVIS_RUBY_VERSION',
                'language': 'ruby',
                'test_value': "1.12"
            },
            {
                'env_var': 'TRAVIS_RUST_VERSION',
                'language': 'rust',
                'test_value': "1.13"
            },
            {
                'env_var': 'TRAVIS_SCALA_VERSION',
                'language': 'scala',
                'test_value': "1.14"
            }
        ]

        # test languages
        for language in test_languages:
            if language['env_var'] in os.environ:
                reset_travis_lang_version = False
                expected_lang_version = os.environ[language['env_var']]
            else:
                reset_travis_lang_version = True
                expected_lang_version = \
                    os.environ[language['env_var']] = language['test_value']

            load_build_matrix_env_vars(settings)

            self.assertDictEqual(
                {
                    'os': expected_os,
                    'language': language['language'],
                    'language_version': expected_lang_version,
                    'summary': "%s %s %s" % (
                        language['language'],
                        expected_lang_version,
                        expected_os
                    )
                },
                settings.get_setting("build_matrix")
            )

            # reset Travis test result
            if reset_travis_lang_version:
                del os.environ[language['env_var']]

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]
            del os.environ["TRAVIS_OS_NAME"]

        # reset removed python version
        if reset_python_version:
            os.environ["TRAVIS_PYTHON_VERSION"] = copy_python

    def test_load_build_matrix_env_vars_parameters(self):
        """Test load_travis_env_vars, optional parameters"""
        # setup Travis env vars
        if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
            reset_travis_vars = False
            copy_os = os.environ["TRAVIS_OS_NAME"]
        else:
            reset_travis_vars = True
            os.environ["TRAVIS"] = "true"

        # temporarily remove OS VERSION
        if "TRAVIS_OS_NAME" in os.environ:
            reset_os = True
            copy_os = os.environ["TRAVIS_OS_NAME"]
            del os.environ["TRAVIS_OS_NAME"]
        else:
            reset_os = False

        # temporarily remove PYTHON VERSION
        if "TRAVIS_PYTHON_VERSION" in os.environ:
            reset_python_version = True
            copy_python = os.environ["TRAVIS_PYTHON_VERSION"]
            del os.environ["TRAVIS_PYTHON_VERSION"]
        else:
            reset_python_version = False

        # test optional build matrix parameters
        test_parameters = [
            {
                'env_var': 'TRAVIS_XCODE_SDK',
                'parameter': 'xcode_sdk',
                'test_value': "test_x_sdk"
            },
            {
                'env_var': 'TRAVIS_XCODE_SCHEME',
                'parameter': 'xcode_scheme',
                'test_value': "test_x_scheme"
            },
            {
                'env_var': 'TRAVIS_XCODE_PROJECT',
                'parameter': 'xcode_project',
                'test_value': "test_x_project"
            },
            {
                'env_var': 'TRAVIS_XCODE_WORKSPACE',
                'parameter': 'xcode_workspace',
                'test_value': "test_x_workspace"
            },
            {
                'env_var': 'CC',
                'parameter': 'compiler',
                'test_value': "test_gcc"
            },
            {
                'env_var': 'ENV',
                'parameter': 'parameters',
                'test_value': "test_env"
            }
        ]

        # test parameters
        for parameter in test_parameters:
            Settings().__init__()
            settings = Settings()

            if parameter['env_var'] in os.environ:
                reset_travis_parameter = False
                expected_param_value = os.environ[parameter['env_var']]
            else:
                reset_travis_parameter = True
                expected_param_value = os.environ[parameter['env_var']] = \
                    parameter['test_value']

            load_build_matrix_env_vars(settings)

            self.assertDictEqual(
                {
                    parameter["parameter"]: expected_param_value,
                    'summary': expected_param_value
                },
                settings.get_setting("build_matrix")
            )

            # reset Travis parameters
            if reset_travis_parameter:
                del os.environ[parameter['env_var']]

        # reset test Travis vars
        if reset_travis_vars:
            del os.environ["TRAVIS"]

        # reset removed os name
        if reset_os:
            os.environ["TRAVIS_OS_NAME"] = copy_os

        # reset removed python version
        if reset_python_version:
            os.environ["TRAVIS_PYTHON_VERSION"] = copy_python
<|endoftext|>


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

# author: greyshell
# description: create blog posts for octopress

import argparse
import json
import os
import subprocess
import sys

class UserInput:
    def __init__(self):
        self.parser = argparse.ArgumentParser(
                description="create blog posts for octopress")
        self.parser.add_argument("-c", "--config", metavar="", help="provide a .json file", required=True)

class Octopress:
    def __init__(self):
        self._src_dir = ""
        self._dst_dir = ""
        self._publish_flag = ""
        self._blacklisted_dirs = list()
        self._whitelisted_filetypes = list()

    def get_parameters(self, config_dict):
        """
        retrieve the value from the input
        :param config_dict: dict
        :return: None
        """

        self._src_dir = config_dict["src_dir"]
        self._dst_dir = config_dict["dst_dir"]
        self._publish_flag = config_dict["publish_flag"]
        self._blacklisted_dirs = config_dict["blacklisted_dirs"]
        self._whitelisted_filetypes = config_dict["whitelisted_filetypes"]

        # validate the input
        if not self._src_dir and self._dst_dir and self._publish_flag:
            print(f"[x] please fill the input in the json config file !!")
            sys.exit(0)

    def clean_dst_dir(self):
        # clean the existing dst dir
        cmd = "cd " + self._dst_dir + " && " + "rm -rf *"
        print(cmd)
        subprocess.check_output(cmd, shell=True)

    @staticmethod
    def _parse_metadata(absolute_file_path):
        """
        parse the metadata from a markdown file
        :return:
        """
        date = comments = ""
        counter = 0
        with open(absolute_file_path) as file:
            for line in file:
                # if first line is not started with '-' then the file is not valid
                if counter == 0 and line[:-1] != '---':
                    return date, comments
                # date
                if counter == 3:
                    date = line.split(" ")[1]
                # comments
                elif counter == 4:
                    comments = line.split(" ")[1]
                elif counter > 4:
                    break
                counter = counter + 1
        return date, comments

    def process_markdown(self):
        """
        process the markdown files
        :return:
        """
        for root, dirs, files in os.walk(self._src_dir):
            for file in files:
                # resolve the / issue at src directory
                if root != self._src_dir:
                    absolute_file_path = root + "/" + file
                else:
                    absolute_file_path = root + file

                # extract the file type
                name, file_type = os.path.splitext(file)

                # extract the directory
                curr_dir = root.split('/').pop()

                if curr_dir in self._blacklisted_dirs:
                    continue

                # check if the file types are whitelisted
                if file_type in self._whitelisted_filetypes and curr_dir:
                    # parse metadata from a markdown file
                    date, comments = self._parse_metadata(absolute_file_path)
                    if date != "" and comments != "true":
                        # prepare the file name
                        new_file_name = date + "-" + name.replace('_', '-') + ".markdown"
                        # copy the fle to the dst dir
                        cmd = "cp " + absolute_file_path + " " + self._dst_dir + new_file_name
                        print(cmd)
                        subprocess.check_output(cmd, shell=True)

if __name__ == "__main__":
    my_input = UserInput()
    args = my_input.parser.parse_args()

    if len(sys.argv) == 1:
        my_input.parser.print_help(sys.stderr)
        sys.exit(0)

    if args.config:
        with open(args.config) as f:
            json_config = json.load(f)

        octo = Octopress()
        octo.get_parameters(json_config)
        octo.clean_dst_dir()
        octo.process_markdown()

    else:
        my_input.parser.print_help(sys.stderr)
<commit_msg>rename and copy the file<commit_after>#!/usr/bin/env python3

# author: greyshell
# description: create blog posts for octopress

import argparse
import json
import os
import subprocess
import sys

class UserInput:
    def __init__(self):
        self.parser = argparse.ArgumentParser(
                description="create blog posts for octopress")
        self.parser.add_argument("-c", "--config", metavar="", help="provide a .json file", required=True)

class Octopress:
    def __init__(self):
        self._src_dir = ""
        self._dst_dir = ""
        self._publish_flag = ""
        self._blacklisted_dirs = list()
        self._whitelisted_filetypes = list()

    def get_parameters(self, config_dict):
        """
        retrieve the value from the input
        :param config_dict: dict
        :return: None
        """

        self._src_dir = config_dict["src_dir"]
        self._dst_dir = config_dict["dst_dir"]
        self._publish_flag = config_dict["publish_flag"]
        self._blacklisted_dirs = config_dict["blacklisted_dirs"]
        self._whitelisted_filetypes = config_dict["whitelisted_filetypes"]

        # validate the input
        if not self._src_dir and self._dst_dir and self._publish_flag:
            print(f"[x] please fill the input in the json config file !!")
            sys.exit(0)

    def clean_dst_dir(self):
        # clean the existing dst dir
        cmd = "cd " + self._dst_dir + " && " + "rm -rf *"
        print(cmd)
        subprocess.check_output(cmd, shell=True)

    @staticmethod
    def _parse_metadata(absolute_file_path):
        """
        parse the metadata from a markdown file
        :return:
        """
        date = comments = ""
        counter = 0
        with open(absolute_file_path) as file:
            for line in file:
                # if first line is not started with '-' then the file is not valid
                if counter == 0 and line[:-1] != '---':
                    return date, comments
                # date
                if counter == 3:
                    date = line.split(" ")[1]
                # comments
                elif counter == 4:
                    comments = line.split(" ")[1]
                elif counter > 4:
                    break
                counter = counter + 1
        return date, comments

    def process_markdown(self):
        """
        process the markdown files
        :return:
        """
        for root, dirs, files in os.walk(self._src_dir):
            for file in files:
                # resolve the / issue at src directory
                if root != self._src_dir:
                    absolute_file_path = root + "/" + file
                else:
                    absolute_file_path = root + file

                # extract the file type
                name, file_type = os.path.splitext(file)

                # extract the directory
                curr_dir = root.split('/').pop()

                if curr_dir in self._blacklisted_dirs:
                    continue

                # check if the file types are whitelisted
                if file_type in self._whitelisted_filetypes:
                    # parse metadata from a markdown file
                    date, comments = self._parse_metadata(absolute_file_path)
                    if date != "" and comments != "true":
                        # prepare the file name
                        new_file_name = date + "-" + name.replace('_', '-') + ".markdown"
                        # copy the fle to the dst dir
                        cmd = "cp " + absolute_file_path + " " + self._dst_dir + new_file_name
                        print(cmd)
                        subprocess.check_output(cmd, shell=True)

if __name__ == "__main__":
    my_input = UserInput()
    args = my_input.parser.parse_args()

    if len(sys.argv) == 1:
        my_input.parser.print_help(sys.stderr)
        sys.exit(0)

    if args.config:
        with open(args.config) as f:
            json_config = json.load(f)

        octo = Octopress()
        octo.get_parameters(json_config)
        octo.clean_dst_dir()
        octo.process_markdown()

    else:
        my_input.parser.print_help(sys.stderr)
<|endoftext|>


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

from __future__ import print_function

import argparse
import os
import subprocess
import sys
import tempfile

INFER_IMPORT_DIR = \
    os.path.dirname(os.path.realpath(__file__)) + '/sdk-module-lists'

INFER_IMPORT_PATH = INFER_IMPORT_DIR + '/infer-imports.py'

def printerr(message):
    print(message, file=sys.stderr)

def fatal_error(message):
    printerr(message)
    sys.exit(1)

def escapeCmdArg(arg):
    if '"' in arg or ' ' in arg:
        return '"%s"' % arg.replace('"', '\\"')
    else:
        return arg

def check_call(cmd, cwd=None, env=os.environ, verbose=False, output=None):
    if verbose:
        print(' '.join([escapeCmdArg(arg) for arg in cmd]))
    try:
        subprocess.check_call(cmd, cwd=cwd, env=env,
                              stderr=None, stdout=output)
        return 0
    except Exception as error:
        printerr(error)
        return 1

def check_output(cmd, verbose=False):
    if verbose:
        print(' '.join([escapeCmdArg(arg) for arg in cmd]))
    return subprocess.check_output(cmd).strip()

def get_sdk_path(platform):
    if platform.startswith('iosmac'):
        platform = 'macosx'
    return check_output(['xcrun', '-sdk', platform, '-show-sdk-path'])

def write_fixed_module(file, platform, infix, verbose):
    common_modules_path = os.path.join(INFER_IMPORT_DIR, 'fixed-' + infix +
                                       '-modules-common.txt')
    platform_modules_path = os.path.join(INFER_IMPORT_DIR, 'fixed-' + infix +
                                         '-modules-' + platform + '.txt')
    with open(common_modules_path, 'r') as extra:
        if verbose:
            print('Including modules in: ' + common_modules_path)
        file.write(extra.read())
    with open(platform_modules_path, 'r') as extra:
        if verbose:
            print('Including modules in: ' + platform_modules_path)
        file.write(extra.read())

def prepare_module_list(platform, file, verbose, module_filter_flags,
                        include_fixed_clang_modules):
    cmd = [INFER_IMPORT_PATH, '-s', get_sdk_path(platform)]
    cmd.extend(module_filter_flags)
    if platform.startswith('iosmac'):
        cmd.extend(['--catalyst'])
    if verbose:
        cmd.extend(['--v'])
    check_call(cmd, verbose=verbose, output=file)
    # Always include fixed swift modules
    write_fixed_module(file, platform, 'swift', verbose)
    # Check if we need fixed clang modules
    if include_fixed_clang_modules:
        write_fixed_module(file, platform, 'clang', verbose)

def get_api_digester_path(tool_path):
    if tool_path:
        return tool_path
    return check_output(['xcrun', '--find', 'swift-api-digester'])

def create_directory(path):
    if not os.path.isdir(path):
        os.makedirs(path)

class DumpConfig:
    def __init__(self, tool_path, platform, platform_alias, abi, verbose):
        target_map = {
            'iphoneos': 'arm64-apple-ios13.0',
            'macosx': 'x86_64-apple-macosx10.15',
            'appletvos': 'arm64-apple-tvos13.0',
            'watchos': 'armv7k-apple-watchos6.0',
            'iosmac': 'x86_64-apple-ios13.0-macabi',
        }
        self.tool_path = get_api_digester_path(tool_path)
        self.platform = platform
        self.target = target_map[platform]
        self.sdk = get_sdk_path(platform)
        self.inputs = []
        self.platform_alias = platform_alias
        self.abi = abi
        if self.platform == 'macosx':
            # We need this input search path for CreateML
            self.inputs.extend([self.sdk + '/usr/lib/swift/'])
        self.frameworks = []
        if self.platform.startswith('iosmac'):
            # Catalyst modules need this extra framework dir
            iOSSupport = self.sdk + \
                '/System/iOSSupport/System/Library/Frameworks'
            self.frameworks.extend([iOSSupport])
        self._environ = dict(os.environ)
        self._environ['SWIFT_FORCE_MODULE_LOADING'] = 'prefer-interface'
        self.verbose = verbose

    def dumpZipperedContent(self, cmd, output, module):
        dir_path = os.path.realpath(output + '/' + module)
        if self.abi:
            dir_path = os.path.join(dir_path, 'ABI')
        else:
            dir_path = os.path.join(dir_path, 'API')
        file_path = os.path.realpath(dir_path + '/' + self.platform_alias +
                                     '.json')
        create_directory(dir_path)
        current_cmd = list(cmd)
        current_cmd.extend(['-module', module])
        current_cmd.extend(['-o', file_path])
        check_call(current_cmd, env=self._environ, verbose=self.verbose)

    def run(self, output, module, swift_ver, opts,
            module_filter_flags, include_fixed_clang_modules,
            separate_by_module, zippered):
        cmd = [self.tool_path, '-sdk', self.sdk, '-target',
               self.target, '-dump-sdk', '-module-cache-path',
               '/tmp/ModuleCache', '-swift-version',
               swift_ver, '-abort-on-module-fail']
        for path in self.frameworks:
            cmd.extend(['-iframework', path])
        for path in self.inputs:
            cmd.extend(['-I', path])
        if self.abi:
            cmd.extend(['-abi'])
        cmd.extend(['-' + o for o in opts])
        if self.verbose:
            cmd.extend(['-v'])
        if module:
            if zippered:
                create_directory(output)
                self.dumpZipperedContent(cmd, output, module)
            else:
                cmd.extend(['-module', module])
                cmd.extend(['-o', output])
                check_call(cmd, env=self._environ, verbose=self.verbose)
        else:
            with tempfile.NamedTemporaryFile() as tmp:
                prepare_module_list(self.platform, tmp, self.verbose,
                                    module_filter_flags,
                                    include_fixed_clang_modules)
                if separate_by_module:
                    tmp.seek(0)
                    create_directory(output)
                    for module in [name.strip() for name in tmp.readlines()]:
                        # Skip comments
                        if module.startswith('//'):
                            continue
                        self.dumpZipperedContent(cmd, output, module)
                else:
                    cmd.extend(['-o', output])
                    cmd.extend(['-module-list-file', tmp.name])
                    check_call(cmd, env=self._environ, verbose=self.verbose)

class DiagnoseConfig:
    def __init__(self, tool_path, abi):
        self.tool_path = get_api_digester_path(tool_path)
        self.abi = abi

    def run(self, opts, before, after, output, verbose):
        cmd = [self.tool_path, '-diagnose-sdk', '-input-paths', before,
               '-input-paths', after, '-print-module']
        if output:
            cmd.extend(['-o', output])
        if self.abi:
            cmd.extend(['-abi'])
        cmd.extend(['-' + o for o in opts])
        if verbose:
            cmd.extend(['-v'])
        check_call(cmd, verbose=verbose)

def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='''
A convenient wrapper for swift-api-digester.
''')

    basic_group = parser.add_argument_group('Basic')

    basic_group.add_argument('--tool-path', default=None, help='''
        the path to a swift-api-digester; if not specified, the script will
        use the one from the toolchain
        ''')

    basic_group.add_argument('--action', default='', help='''
        the action to perform for swift-api-digester
        ''')

    basic_group.add_argument('--target', default=None, help='''
        one of macosx, iphoneos, appletvos, and watchos
        ''')

    basic_group.add_argument('--output', default=None, help='''
        the output file of the module baseline should end with .json
        ''')

    basic_group.add_argument('--swift-version', default='5', help='''
        Swift version to use; default is 5
        ''')

    basic_group.add_argument('--module', default=None, help='''
        name of the module/framework to generate baseline, e.g. Foundation
        ''')

    basic_group.add_argument('--module-filter', default='', help='''
        the action to perform for swift-api-digester
        ''')

    basic_group.add_argument('--opts', nargs='+', default=[], help='''
        additional flags to pass to swift-api-digester
        ''')

    basic_group.add_argument('--v',
                             action='store_true',
                             help='Process verbosely')

    basic_group.add_argument('--dump-before',
                             action=None,
                             help='''
        Path to the json file generated before change'
        ''')

    basic_group.add_argument('--dump-after',
                             action=None,
                             help='''
        Path to the json file generated after change
        ''')

    basic_group.add_argument('--separate-by-module',
                             action='store_true',
                             help='When importing entire SDK, dump content '
                                  'seprately by module names')

    basic_group.add_argument('--zippered',
                             action='store_true',
                             help='dump module content to a dir with files for'
                                  'seprately targets')

    basic_group.add_argument('--abi',
                             action='store_true',
                             help='Process verbosely')

    basic_group.add_argument('--platform-alias', default='', help='''
        Specify a file name to use if using a platform name in json file isn't
        optimal
        ''')

    args = parser.parse_args(sys.argv[1:])

    if args.action == 'dump':
        if not args.target:
            fatal_error("Need to specify --target")
        if not args.output:
            fatal_error("Need to specify --output")
        if args.module_filter == '':
            module_filter_flags = []
            include_fixed_clang_modules = True
        elif args.module_filter == 'swift-frameworks-only':
            module_filter_flags = ['--swift-frameworks-only']
            include_fixed_clang_modules = False
        elif args.module_filter == 'swift-overlay-only':
            module_filter_flags = ['--swift-overlay-only']
            include_fixed_clang_modules = False
        else:
            fatal_error("cannot recognize --module-filter")
        if args.platform_alias == '':
            args.platform_alias = args.target
        runner = DumpConfig(tool_path=args.tool_path, platform=args.target,
                            platform_alias=args.platform_alias,
                            abi=args.abi,
                            verbose=args.v)
        runner.run(output=args.output, module=args.module,
                   swift_ver=args.swift_version, opts=args.opts,
                   module_filter_flags=module_filter_flags,
                   include_fixed_clang_modules=include_fixed_clang_modules,
                   separate_by_module=args.separate_by_module,
                   zippered=args.zippered)
    elif args.action == 'diagnose':
        if not args.dump_before:
            fatal_error("Need to specify --dump-before")
        if not args.dump_after:
            fatal_error("Need to specify --dump-after")
        runner = DiagnoseConfig(tool_path=args.tool_path, abi=args.abi)
        runner.run(opts=args.opts, before=args.dump_before,
                   after=args.dump_after, output=args.output, verbose=args.v)
    else:
        fatal_error('Cannot recognize action: ' + args.action)

if __name__ == '__main__':
    main()
<commit_msg>swift-api-checker.py: add -swift-only flag when checking ABI stability<commit_after>#!/usr/bin/env python

from __future__ import print_function

import argparse
import os
import subprocess
import sys
import tempfile

INFER_IMPORT_DIR = \
    os.path.dirname(os.path.realpath(__file__)) + '/sdk-module-lists'

INFER_IMPORT_PATH = INFER_IMPORT_DIR + '/infer-imports.py'

def printerr(message):
    print(message, file=sys.stderr)

def fatal_error(message):
    printerr(message)
    sys.exit(1)

def escapeCmdArg(arg):
    if '"' in arg or ' ' in arg:
        return '"%s"' % arg.replace('"', '\\"')
    else:
        return arg

def check_call(cmd, cwd=None, env=os.environ, verbose=False, output=None):
    if verbose:
        print(' '.join([escapeCmdArg(arg) for arg in cmd]))
    try:
        subprocess.check_call(cmd, cwd=cwd, env=env,
                              stderr=None, stdout=output)
        return 0
    except Exception as error:
        printerr(error)
        return 1

def check_output(cmd, verbose=False):
    if verbose:
        print(' '.join([escapeCmdArg(arg) for arg in cmd]))
    return subprocess.check_output(cmd).strip()

def get_sdk_path(platform):
    if platform.startswith('iosmac'):
        platform = 'macosx'
    return check_output(['xcrun', '-sdk', platform, '-show-sdk-path'])

def write_fixed_module(file, platform, infix, verbose):
    common_modules_path = os.path.join(INFER_IMPORT_DIR, 'fixed-' + infix +
                                       '-modules-common.txt')
    platform_modules_path = os.path.join(INFER_IMPORT_DIR, 'fixed-' + infix +
                                         '-modules-' + platform + '.txt')
    with open(common_modules_path, 'r') as extra:
        if verbose:
            print('Including modules in: ' + common_modules_path)
        file.write(extra.read())
    with open(platform_modules_path, 'r') as extra:
        if verbose:
            print('Including modules in: ' + platform_modules_path)
        file.write(extra.read())

def prepare_module_list(platform, file, verbose, module_filter_flags,
                        include_fixed_clang_modules):
    cmd = [INFER_IMPORT_PATH, '-s', get_sdk_path(platform)]
    cmd.extend(module_filter_flags)
    if platform.startswith('iosmac'):
        cmd.extend(['--catalyst'])
    if verbose:
        cmd.extend(['--v'])
    check_call(cmd, verbose=verbose, output=file)
    # Always include fixed swift modules
    write_fixed_module(file, platform, 'swift', verbose)
    # Check if we need fixed clang modules
    if include_fixed_clang_modules:
        write_fixed_module(file, platform, 'clang', verbose)

def get_api_digester_path(tool_path):
    if tool_path:
        return tool_path
    return check_output(['xcrun', '--find', 'swift-api-digester'])

def create_directory(path):
    if not os.path.isdir(path):
        os.makedirs(path)

class DumpConfig:
    def __init__(self, tool_path, platform, platform_alias, abi, verbose):
        target_map = {
            'iphoneos': 'arm64-apple-ios13.0',
            'macosx': 'x86_64-apple-macosx10.15',
            'appletvos': 'arm64-apple-tvos13.0',
            'watchos': 'armv7k-apple-watchos6.0',
            'iosmac': 'x86_64-apple-ios13.0-macabi',
        }
        self.tool_path = get_api_digester_path(tool_path)
        self.platform = platform
        self.target = target_map[platform]
        self.sdk = get_sdk_path(platform)
        self.inputs = []
        self.platform_alias = platform_alias
        self.abi = abi
        if self.platform == 'macosx':
            # We need this input search path for CreateML
            self.inputs.extend([self.sdk + '/usr/lib/swift/'])
        self.frameworks = []
        if self.platform.startswith('iosmac'):
            # Catalyst modules need this extra framework dir
            iOSSupport = self.sdk + \
                '/System/iOSSupport/System/Library/Frameworks'
            self.frameworks.extend([iOSSupport])
        self._environ = dict(os.environ)
        self._environ['SWIFT_FORCE_MODULE_LOADING'] = 'prefer-interface'
        self.verbose = verbose

    def dumpZipperedContent(self, cmd, output, module):
        dir_path = os.path.realpath(output + '/' + module)
        if self.abi:
            dir_path = os.path.join(dir_path, 'ABI')
        else:
            dir_path = os.path.join(dir_path, 'API')
        file_path = os.path.realpath(dir_path + '/' + self.platform_alias +
                                     '.json')
        create_directory(dir_path)
        current_cmd = list(cmd)
        current_cmd.extend(['-module', module])
        current_cmd.extend(['-o', file_path])
        check_call(current_cmd, env=self._environ, verbose=self.verbose)

    def run(self, output, module, swift_ver, opts,
            module_filter_flags, include_fixed_clang_modules,
            separate_by_module, zippered):
        cmd = [self.tool_path, '-sdk', self.sdk, '-target',
               self.target, '-dump-sdk', '-module-cache-path',
               '/tmp/ModuleCache', '-swift-version',
               swift_ver, '-abort-on-module-fail']
        for path in self.frameworks:
            cmd.extend(['-iframework', path])
        for path in self.inputs:
            cmd.extend(['-I', path])
        if self.abi:
            cmd.extend(['-abi', '-swift-only'])
        cmd.extend(['-' + o for o in opts])
        if self.verbose:
            cmd.extend(['-v'])
        if module:
            if zippered:
                create_directory(output)
                self.dumpZipperedContent(cmd, output, module)
            else:
                cmd.extend(['-module', module])
                cmd.extend(['-o', output])
                check_call(cmd, env=self._environ, verbose=self.verbose)
        else:
            with tempfile.NamedTemporaryFile() as tmp:
                prepare_module_list(self.platform, tmp, self.verbose,
                                    module_filter_flags,
                                    include_fixed_clang_modules)
                if separate_by_module:
                    tmp.seek(0)
                    create_directory(output)
                    for module in [name.strip() for name in tmp.readlines()]:
                        # Skip comments
                        if module.startswith('//'):
                            continue
                        self.dumpZipperedContent(cmd, output, module)
                else:
                    cmd.extend(['-o', output])
                    cmd.extend(['-module-list-file', tmp.name])
                    check_call(cmd, env=self._environ, verbose=self.verbose)

class DiagnoseConfig:
    def __init__(self, tool_path, abi):
        self.tool_path = get_api_digester_path(tool_path)
        self.abi = abi

    def run(self, opts, before, after, output, verbose):
        cmd = [self.tool_path, '-diagnose-sdk', '-input-paths', before,
               '-input-paths', after, '-print-module']
        if output:
            cmd.extend(['-o', output])
        if self.abi:
            cmd.extend(['-abi'])
        cmd.extend(['-' + o for o in opts])
        if verbose:
            cmd.extend(['-v'])
        check_call(cmd, verbose=verbose)

def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='''
A convenient wrapper for swift-api-digester.
''')

    basic_group = parser.add_argument_group('Basic')

    basic_group.add_argument('--tool-path', default=None, help='''
        the path to a swift-api-digester; if not specified, the script will
        use the one from the toolchain
        ''')

    basic_group.add_argument('--action', default='', help='''
        the action to perform for swift-api-digester
        ''')

    basic_group.add_argument('--target', default=None, help='''
        one of macosx, iphoneos, appletvos, and watchos
        ''')

    basic_group.add_argument('--output', default=None, help='''
        the output file of the module baseline should end with .json
        ''')

    basic_group.add_argument('--swift-version', default='5', help='''
        Swift version to use; default is 5
        ''')

    basic_group.add_argument('--module', default=None, help='''
        name of the module/framework to generate baseline, e.g. Foundation
        ''')

    basic_group.add_argument('--module-filter', default='', help='''
        the action to perform for swift-api-digester
        ''')

    basic_group.add_argument('--opts', nargs='+', default=[], help='''
        additional flags to pass to swift-api-digester
        ''')

    basic_group.add_argument('--v',
                             action='store_true',
                             help='Process verbosely')

    basic_group.add_argument('--dump-before',
                             action=None,
                             help='''
        Path to the json file generated before change'
        ''')

    basic_group.add_argument('--dump-after',
                             action=None,
                             help='''
        Path to the json file generated after change
        ''')

    basic_group.add_argument('--separate-by-module',
                             action='store_true',
                             help='When importing entire SDK, dump content '
                                  'seprately by module names')

    basic_group.add_argument('--zippered',
                             action='store_true',
                             help='dump module content to a dir with files for'
                                  'seprately targets')

    basic_group.add_argument('--abi',
                             action='store_true',
                             help='Process verbosely')

    basic_group.add_argument('--platform-alias', default='', help='''
        Specify a file name to use if using a platform name in json file isn't
        optimal
        ''')

    args = parser.parse_args(sys.argv[1:])

    if args.action == 'dump':
        if not args.target:
            fatal_error("Need to specify --target")
        if not args.output:
            fatal_error("Need to specify --output")
        if args.module_filter == '':
            module_filter_flags = []
            include_fixed_clang_modules = True
        elif args.module_filter == 'swift-frameworks-only':
            module_filter_flags = ['--swift-frameworks-only']
            include_fixed_clang_modules = False
        elif args.module_filter == 'swift-overlay-only':
            module_filter_flags = ['--swift-overlay-only']
            include_fixed_clang_modules = False
        else:
            fatal_error("cannot recognize --module-filter")
        if args.platform_alias == '':
            args.platform_alias = args.target
        runner = DumpConfig(tool_path=args.tool_path, platform=args.target,
                            platform_alias=args.platform_alias,
                            abi=args.abi,
                            verbose=args.v)
        runner.run(output=args.output, module=args.module,
                   swift_ver=args.swift_version, opts=args.opts,
                   module_filter_flags=module_filter_flags,
                   include_fixed_clang_modules=include_fixed_clang_modules,
                   separate_by_module=args.separate_by_module,
                   zippered=args.zippered)
    elif args.action == 'diagnose':
        if not args.dump_before:
            fatal_error("Need to specify --dump-before")
        if not args.dump_after:
            fatal_error("Need to specify --dump-after")
        runner = DiagnoseConfig(tool_path=args.tool_path, abi=args.abi)
        runner.run(opts=args.opts, before=args.dump_before,
                   after=args.dump_after, output=args.output, verbose=args.v)
    else:
        fatal_error('Cannot recognize action: ' + args.action)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>"""Import timetable data "fresh from the cow"
"""
import os
from requests import Session
from ciso8601 import parse_datetime
from django.core.management.base import BaseCommand
from django.conf import settings
from django.utils import timezone
from busstops.models import DataSource, Service, Operator
from .import_gtfs import download_if_modified
from .import_transxchange import Command as TransXChangeCommand
from .import_passenger import handle_file
from ...models import Route, Calendar

def clean_up(operators, sources):
    Route.objects.filter(service__operator__in=operators).exclude(source__in=sources).delete()
    Service.objects.filter(operator__in=operators, current=True, route=None).update(current=False)
    Calendar.objects.filter(trip=None).delete()

def get_command():
    command = TransXChangeCommand()
    command.undefined_holidays = set()
    command.notes = {}
    command.corrections = {}

    return command

def bus_open_data(api_key):
    session = Session()
    command = get_command()

    for operator_id, region_id, operators in settings.BOD_OPERATORS:
        command.operators = operators
        command.region_id = region_id
        command.service_descriptions = {}
        command.service_codes = set()
        command.calendar_cache = {}

        sources = []

        url = 'https://data.bus-data.dft.gov.uk/api/v1/dataset/'
        params = {
            'api_key': api_key,
            'noc': operator_id,
            'status': ['published', 'expiring']
        }

        while url:
            response = session.get(url, params=params)
            json = response.json()

            for result in json['results']:
                filename = result['name']
                url = result['url']
                path = os.path.join(settings.DATA_DIR, filename)

                modified = parse_datetime(result['modified'])

                command.source, created = DataSource.objects.get_or_create({'name': filename}, url=url)

                if command.source.datetime != modified:
                    print(response.url, filename)
                    command.source.datetime = modified
                    download_if_modified(path, url)
                    handle_file(command, filename)

                    if not created:
                        command.source.name = filename
                    command.source.save(update_fields=['name', 'datetime'])

                sources.append(command.source)

            url = json['next']
            params = None

        # print(Operator.objects.filter(service__source__in=sources).distinct().values_list('id', flat=True))

        clean_up(operators.values(), sources)

def first():
    command = get_command()

    for operator, region_id, operators in settings.FIRST_OPERATORS:
        filename = operator + '.zip'
        url = 'http://travelinedatahosting.basemap.co.uk/data/first/' + filename
        modified = download_if_modified(os.path.join(settings.DATA_DIR, filename), url)

        if modified:
            command.operators = operators
            command.region_id = region_id
            command.service_descriptions = {}
            command.service_codes = set()
            command.calendar_cache = {}

            command.source, created = DataSource.objects.get_or_create({'name': operator}, url=url)
            command.source.datetime = timezone.now()

            handle_file(command, filename)

            clean_up(operators.values(), [command.source])

class Command(BaseCommand):
    @staticmethod
    def add_arguments(parser):
        parser.add_argument('api_key', nargs=1, type=str)

    def handle(self, api_key, **options):

        bus_open_data(api_key)

        first()
<commit_msg>flake8<commit_after>"""Import timetable data "fresh from the cow"
"""
import os
from requests import Session
from ciso8601 import parse_datetime
from django.core.management.base import BaseCommand
from django.conf import settings
from django.utils import timezone
from busstops.models import DataSource, Service
from .import_gtfs import download_if_modified
from .import_transxchange import Command as TransXChangeCommand
from .import_passenger import handle_file
from ...models import Route, Calendar

def clean_up(operators, sources):
    Route.objects.filter(service__operator__in=operators).exclude(source__in=sources).delete()
    Service.objects.filter(operator__in=operators, current=True, route=None).update(current=False)
    Calendar.objects.filter(trip=None).delete()

def get_command():
    command = TransXChangeCommand()
    command.undefined_holidays = set()
    command.notes = {}
    command.corrections = {}

    return command

def bus_open_data(api_key):
    session = Session()
    command = get_command()

    for operator_id, region_id, operators in settings.BOD_OPERATORS:
        command.operators = operators
        command.region_id = region_id
        command.service_descriptions = {}
        command.service_codes = set()
        command.calendar_cache = {}

        sources = []

        url = 'https://data.bus-data.dft.gov.uk/api/v1/dataset/'
        params = {
            'api_key': api_key,
            'noc': operator_id,
            'status': ['published', 'expiring']
        }

        while url:
            response = session.get(url, params=params)
            json = response.json()

            for result in json['results']:
                filename = result['name']
                url = result['url']
                path = os.path.join(settings.DATA_DIR, filename)

                modified = parse_datetime(result['modified'])

                command.source, created = DataSource.objects.get_or_create({'name': filename}, url=url)

                if command.source.datetime != modified:
                    print(response.url, filename)
                    command.source.datetime = modified
                    download_if_modified(path, url)
                    handle_file(command, filename)

                    if not created:
                        command.source.name = filename
                    command.source.save(update_fields=['name', 'datetime'])

                sources.append(command.source)

            url = json['next']
            params = None

        clean_up(operators.values(), sources)

def first():
    command = get_command()

    for operator, region_id, operators in settings.FIRST_OPERATORS:
        filename = operator + '.zip'
        url = 'http://travelinedatahosting.basemap.co.uk/data/first/' + filename
        modified = download_if_modified(os.path.join(settings.DATA_DIR, filename), url)

        if modified:
            command.operators = operators
            command.region_id = region_id
            command.service_descriptions = {}
            command.service_codes = set()
            command.calendar_cache = {}

            command.source, created = DataSource.objects.get_or_create({'name': operator}, url=url)
            command.source.datetime = timezone.now()

            handle_file(command, filename)

            clean_up(operators.values(), [command.source])

class Command(BaseCommand):
    @staticmethod
    def add_arguments(parser):
        parser.add_argument('api_key', nargs=1, type=str)

    def handle(self, api_key, **options):

        bus_open_data(api_key)

        first()
<|endoftext|>


<commit_before>"""
byceps.services.board.last_view_service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2019 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

from datetime import datetime
from typing import Optional

from ...database import db
from ...typing import UserID

from .models.last_category_view import LastCategoryView
from .models.last_topic_view import LastTopicView
from .models.topic import Topic as DbTopic
from . import topic_query_service
from .transfer.models import CategoryID, CategoryWithLastUpdate, TopicID

# -------------------------------------------------------------------- #
# categories

def contains_category_unseen_postings(category: CategoryWithLastUpdate,
                                      user_id: UserID
                                     ) -> bool:
    """Return `True` if the category contains postings created after the
    last time the user viewed it.
    """
    if category.last_posting_updated_at is None:
        return False

    last_view = find_last_category_view(user_id, category.id)

    if last_view is None:
        return True

    return category.last_posting_updated_at > last_view.occurred_at

def find_last_category_view(user_id: UserID, category_id: CategoryID
                           ) -> Optional[LastCategoryView]:
    """Return the user's last view of the category, or `None` if not found."""
    return LastCategoryView.query \
        .filter_by(user_id=user_id, category_id=category_id) \
        .first()

def mark_category_as_just_viewed(category_id: CategoryID, user_id: UserID
                                ) -> None:
    """Mark the category as last viewed by the user (if logged in) at
    the current time.
    """
    now = datetime.now()

    last_view = find_last_category_view(user_id, category_id)

    if last_view is not None:
        last_view.occurred_at = now
    else:
        last_view = LastCategoryView(user_id, category_id, now)
        db.session.add(last_view)

    db.session.commit()

# -------------------------------------------------------------------- #
# topics

def contains_topic_unseen_postings(topic: DbTopic, user_id: UserID) -> bool:
    """Return `True` if the topic contains postings created after the
    last time the user viewed it.
    """
    last_viewed_at = find_topic_last_viewed_at(topic.id, user_id)

    return last_viewed_at is None \
        or topic.last_updated_at > last_viewed_at

def find_last_topic_view(user_id: UserID, topic_id: TopicID
                        ) -> Optional[LastTopicView]:
    """Return the user's last view of the topic, or `None` if not found."""
    return LastTopicView.query \
        .filter_by(user_id=user_id, topic_id=topic_id) \
        .first()

def find_topic_last_viewed_at(topic_id: TopicID, user_id: UserID
                             ) -> Optional[datetime]:
    """Return the time the topic was last viewed by the user (or
    nothing, if it hasn't been viewed by the user yet).
    """
    last_view = find_last_topic_view(user_id, topic_id)
    return last_view.occurred_at if (last_view is not None) else None

def mark_topic_as_just_viewed(topic_id: TopicID, user_id: UserID) -> None:
    """Mark the topic as last viewed by the user (if logged in) at the
    current time.
    """
    now = datetime.now()

    last_view = find_last_topic_view(user_id, topic_id)

    if last_view is not None:
        last_view.occurred_at = now
    else:
        last_view = LastTopicView(user_id, topic_id, now)
        db.session.add(last_view)

    db.session.commit()

# -------------------------------------------------------------------- #

def mark_all_topics_in_category_as_viewed(category_id: CategoryID,
                                          user_id: UserID) -> None:
    """Mark all topics in the category as viewed."""
    topic_ids = topic_query_service.get_all_topic_ids_in_category(category_id)

    if not topic_ids:
        return

    now = datetime.now()

    # Fetch existing last views.
    last_views = LastTopicView.query \
        .filter_by(user_id=user_id) \
        .filter(LastTopicView.topic_id.in_(topic_ids)) \
        .all()

    # Update existing last views.
    for last_view in last_views:
        last_view.occurred_at = now

    # Create last views for remaining topics.
    last_view_topic_ids = {last_view.topic_id for last_view in last_views}
    remaining_topic_ids = topic_ids - last_view_topic_ids
    for topic_id in remaining_topic_ids:
        last_view = LastTopicView(user_id, topic_id, now)
        db.session.add(last_view)

    db.session.commit()
<commit_msg>Use UPSERTs to mark board categories and topics as viewed<commit_after>"""
byceps.services.board.last_view_service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2019 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

from datetime import datetime
from typing import Optional

from sqlalchemy.dialects.postgresql import insert

from ...database import db
from ...typing import UserID

from .models.last_category_view import LastCategoryView
from .models.last_topic_view import LastTopicView
from .models.topic import Topic as DbTopic
from . import topic_query_service
from .transfer.models import CategoryID, CategoryWithLastUpdate, TopicID

# -------------------------------------------------------------------- #
# categories

def contains_category_unseen_postings(category: CategoryWithLastUpdate,
                                      user_id: UserID
                                     ) -> bool:
    """Return `True` if the category contains postings created after the
    last time the user viewed it.
    """
    if category.last_posting_updated_at is None:
        return False

    last_view = find_last_category_view(user_id, category.id)

    if last_view is None:
        return True

    return category.last_posting_updated_at > last_view.occurred_at

def find_last_category_view(user_id: UserID, category_id: CategoryID
                           ) -> Optional[LastCategoryView]:
    """Return the user's last view of the category, or `None` if not found."""
    return LastCategoryView.query \
        .filter_by(user_id=user_id, category_id=category_id) \
        .first()

def mark_category_as_just_viewed(category_id: CategoryID, user_id: UserID
                                ) -> None:
    """Mark the category as last viewed by the user (if logged in) at
    the current time.
    """
    table = LastCategoryView.__table__
    now = datetime.now()

    # UPSERT
    query = insert(table) \
        .values(
            user_id=user_id,
            category_id=category_id,
            occurred_at=now,
        ) \
        .on_conflict_do_update(
            constraint=table.primary_key,
            set_={'occurred_at': now})

    db.session.execute(query)
    db.session.commit()

# -------------------------------------------------------------------- #
# topics

def contains_topic_unseen_postings(topic: DbTopic, user_id: UserID) -> bool:
    """Return `True` if the topic contains postings created after the
    last time the user viewed it.
    """
    last_viewed_at = find_topic_last_viewed_at(topic.id, user_id)

    return last_viewed_at is None \
        or topic.last_updated_at > last_viewed_at

def find_last_topic_view(user_id: UserID, topic_id: TopicID
                        ) -> Optional[LastTopicView]:
    """Return the user's last view of the topic, or `None` if not found."""
    return LastTopicView.query \
        .filter_by(user_id=user_id, topic_id=topic_id) \
        .first()

def find_topic_last_viewed_at(topic_id: TopicID, user_id: UserID
                             ) -> Optional[datetime]:
    """Return the time the topic was last viewed by the user (or
    nothing, if it hasn't been viewed by the user yet).
    """
    last_view = find_last_topic_view(user_id, topic_id)
    return last_view.occurred_at if (last_view is not None) else None

def mark_topic_as_just_viewed(topic_id: TopicID, user_id: UserID) -> None:
    """Mark the topic as last viewed by the user (if logged in) at the
    current time.
    """
    table = LastTopicView.__table__
    now = datetime.now()

    # UPSERT
    query = insert(table) \
        .values(
            user_id=user_id,
            topic_id=topic_id,
            occurred_at=now,
        ) \
        .on_conflict_do_update(
            constraint=table.primary_key,
            set_={'occurred_at': now})

    db.session.execute(query)
    db.session.commit()

def mark_all_topics_in_category_as_viewed(category_id: CategoryID,
                                          user_id: UserID) -> None:
    """Mark all topics in the category as viewed."""
    topic_ids = topic_query_service.get_all_topic_ids_in_category(category_id)

    if not topic_ids:
        return

    table = LastTopicView.__table__
    now = datetime.now()

    for topic_id in topic_ids:
        # UPSERT
        query = insert(table) \
            .values(
                user_id=user_id,
                topic_id=topic_id,
                occurred_at=now,
            ) \
            .on_conflict_do_update(
                constraint=table.primary_key,
                set_={'occurred_at': now})

        db.session.execute(query)

    db.session.commit()
<|endoftext|>