<commit_before># Copyright (c) 2016 Intracom S.A. Telecom Solutions. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v1.0 which accompanies this distribution,
# and is available at http://www.eclipse.org/legal/epl-v10.html

""" Controller Class- All controller-related functionality is here"""

import json
import logging
import stress_test.report_gen
import stress_test.report_spec_templates
import stress_test.test_run
import sys
import time

class TestType:

    def __init__(self, args):
        """
        """
        self.test_type = args.test_type
        self.total_samples = None
        self.test_report_template = \
            stress_test.report_spec_templates.TestReport(self.test_type,
                                                         args.json_config)
        print('***************args.bypass_test***************')
        print(args.bypass_test)
        time.sleep(100)
        exit()

    def load_test_conf(self, args):
        """ Loading test configuration for NSTAT experiment. Parsing
        configuration options from JSON input file
        :param args: ArgumentParser object containing user specified
        parameters (i.e test type, controller base directory, generator base
        directory) when running NSTAT
        :returns: json_conf:
        :rtype: json_conf:  python object resulting from a deserialized file
        like object containing a json document
        :type args: ArgumentParser object
        """

        with open(args.json_config) as conf_file:
            json_conf = json.load(conf_file)
        return json_conf

    def set_test_log_level(self, args):
        """Setting log level for NSTAT experiment
        :param args: ArgumentParser object containing user specified
        parameters (i.e test type, controller base directory, generator base
        directory) when running NSTAT
        :type args: ArgumentParser object
        """
        logging_format = '[%(asctime)s %(levelname)7s ] %(message)s'
        if args.logging_level == 'INFO':
            logging.basicConfig(level=logging.INFO, stream=sys.stdout,
                                format=logging_format)
        elif args.logging_level == 'ERROR':
            logging.basicConfig(level=logging.ERROR, stream=sys.stdout,
                                format=logging_format)
        else:
            logging.basicConfig(level=logging.DEBUG, stream=sys.stdout,
                                format=logging_format)

        if args.log_file:
            open(args.log_file, 'a').close()
            file_logging_handler = logging.FileHandler(filename=args.log_file,
                                                       mode='w')
            if args.logging_level == 'INFO':
                file_logging_handler.setLevel(level=logging.INFO)
            elif args.logging_level == 'ERROR':
                file_logging_handler.setLevel(level=logging.ERROR)
            else:
                file_logging_handler.setLevel(level=logging.DEBUG)

    def test_selector(self, args):
        """
        """
        self.set_test_log_level(args)
        json_conf = self.load_test_conf(args)
        nstat_test_type_run = args.test_type + '_' + \
            json_conf['sb_emulator_name'].lower()

        # Create instance of TestRun and initialize controller
        nstat_test_run = stress_test.test_run.TestRun(args, json_conf,
                                                      nstat_test_type_run)

        # Run the test
        if nstat_test_type_run == 'sb_active_scalability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_scalability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_scalability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_active_stability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test:{0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_stability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_stability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_active_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_scalability_multinet(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_idle_scalability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_scalability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_scalability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_idle_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_scalability_multinet(
                        args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    args, json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))
        elif nstat_test_type_run == 'sb_idle_stability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_stability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_stability_multinet(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'nb_active_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.nb_active_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.nb_active_scalability_multinet_run(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))
        else:
            logging.error('[nstat_orchestrator] not valid test configuration')
            exit(0)
<commit_msg>Do not create Test_run object if bypass_test argument is True<commit_after># Copyright (c) 2016 Intracom S.A. Telecom Solutions. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v1.0 which accompanies this distribution,
# and is available at http://www.eclipse.org/legal/epl-v10.html

""" Controller Class- All controller-related functionality is here"""

import json
import logging
import stress_test.report_gen
import stress_test.report_spec_templates
import stress_test.test_run
import sys
import time

class TestType:

    def __init__(self, args):
        """
        """
        self.test_type = args.test_type
        self.total_samples = None
        self.test_report_template = \
            stress_test.report_spec_templates.TestReport(self.test_type,
                                                         args.json_config)
        print('***************args.bypass_test***************')
        print(args.bypass_test)
        time.sleep(100)
        exit()

    def load_test_conf(self, args):
        """ Loading test configuration for NSTAT experiment. Parsing
        configuration options from JSON input file
        :param args: ArgumentParser object containing user specified
        parameters (i.e test type, controller base directory, generator base
        directory) when running NSTAT
        :returns: json_conf:
        :rtype: json_conf:  python object resulting from a deserialized file
        like object containing a json document
        :type args: ArgumentParser object
        """

        with open(args.json_config) as conf_file:
            json_conf = json.load(conf_file)
        return json_conf

    def set_test_log_level(self, args):
        """Setting log level for NSTAT experiment
        :param args: ArgumentParser object containing user specified
        parameters (i.e test type, controller base directory, generator base
        directory) when running NSTAT
        :type args: ArgumentParser object
        """
        logging_format = '[%(asctime)s %(levelname)7s ] %(message)s'
        if args.logging_level == 'INFO':
            logging.basicConfig(level=logging.INFO, stream=sys.stdout,
                                format=logging_format)
        elif args.logging_level == 'ERROR':
            logging.basicConfig(level=logging.ERROR, stream=sys.stdout,
                                format=logging_format)
        else:
            logging.basicConfig(level=logging.DEBUG, stream=sys.stdout,
                                format=logging_format)

        if args.log_file:
            open(args.log_file, 'a').close()
            file_logging_handler = logging.FileHandler(filename=args.log_file,
                                                       mode='w')
            if args.logging_level == 'INFO':
                file_logging_handler.setLevel(level=logging.INFO)
            elif args.logging_level == 'ERROR':
                file_logging_handler.setLevel(level=logging.ERROR)
            else:
                file_logging_handler.setLevel(level=logging.DEBUG)

    def test_selector(self, args):
        """
        """
        self.set_test_log_level(args)
        json_conf = self.load_test_conf(args)
        nstat_test_type_run = args.test_type + '_' + \
            json_conf['sb_emulator_name'].lower()

        # Create instance of TestRun and initialize controller
        if not args.bypass_test:
            nstat_test_run = stress_test.test_run.TestRun(args, json_conf,
                                                          nstat_test_type_run)

        # Run the test
        if nstat_test_type_run == 'sb_active_scalability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_scalability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_scalability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_active_stability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test:{0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_stability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_stability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_active_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_active_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_active_scalability_multinet(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_idle_scalability_mtcbench':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_scalability_mtcbench_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_scalability_mtcbench(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'sb_idle_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_scalability_multinet(
                        args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    args, json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))
        elif nstat_test_type_run == 'sb_idle_stability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.sb_idle_stability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.sb_idle_stability_multinet(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))

        elif nstat_test_type_run == 'nb_active_scalability_multinet':
            if not args.bypass_test:
                logging.info('[nstat_orchestrator] running test: {0}'.
                             format(nstat_test_type_run))
                self.total_samples = \
                    nstat_test_run.nb_active_scalability_multinet_run(
                        json_conf,
                        args.json_output,
                        args.output_dir)
            try:
                logging.info('[{0}] Generating results report.'.
                             format(self.test_type))
                report_spec = \
                    self.test_report_template.nb_active_scalability_multinet_run(
                        self.args.json_output)
                report_gen = stress_test.report_gen.ReportGen(
                    self.args, self.json_conf, report_spec, self.total_samples)
                report_gen.results_report()
            except:
                logging.error('[{0}] Fail to generate test report.'.
                              format(self.test_type))
        else:
            logging.error('[nstat_orchestrator] not valid test configuration')
            exit(0)
<|endoftext|>


<commit_before>import os
import uuid

import logging
import time
import calendar
import tempfile
import re
from threading import Thread
import subprocess
import requests
import json
import shutil

import fs_tracker
import util

logging.basicConfig()

class FirebaseArtifactStore(object):

    def __init__(self, pyrebase_app, auth, measure_timestamp_diff=True):
        self.app = pyrebase_app
        self.auth = auth
        self.logger = logging.getLogger('FirebaseArtifactStore')
        self.logger.setLevel(10)

        self.timestamp_shift = 0

        if measure_timestamp_diff:
            max_diff = 60

            tmpfile = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()) + '.txt')
            with open(tmpfile, 'w') as f:
                f.write('timestamp_diff_test')
            key = 'tests/' + str(uuid.uuid4())
            self._upload_file(key, tmpfile)
            remote_timestamp = self._get_file_timestamp(key)

            if remote_timestamp is not None:

                now_remote_diff = time.time() - remote_timestamp
                self._delete_file(key)
                os.remove(tmpfile)

                assert -max_diff < now_remote_diff and \
                    now_remote_diff < max_diff, \
                    "Timestamp difference is more than 60 seconds. " + \
                    "You'll need to adjust local clock for caching " + \
                    "to work correctly"

                if now_remote_diff < 0:
                    self.timestamp_shift = -now_remote_diff

    def put_artifact(
            self,
            artifact,
            local_path=None,
            cache=True,
            background=False):
        if local_path is None:
            local_path = artifact['local']

        key = artifact.get('key')
        if os.path.exists(local_path):
            tar_filename = os.path.join(tempfile.gettempdir(),
                                        str(uuid.uuid4()))

            local_path = re.sub('/\Z', '', local_path)
            local_nameonly = re.sub('.*/', '', local_path)
            local_basepath = re.sub('/[^/]*\Z', '', local_path)

            if cache and key:
                cache_dir = fs_tracker.get_artifact_cache(key)
                if cache_dir != local_path:
                    self.logger.debug(
                        "Copying local path {} to cache {}"
                        .format(local_path, cache_dir))

                    if os.path.exists(cache_dir) and os.path.isdir(cache_dir):
                        shutil.rmtree(cache_dir)

                    subprocess.call(['cp', '-pR', local_path, cache_dir])

            self.logger.debug(
                ("Tarring and uploading directrory. " +
                 "tar_filename = {}, " +
                 "local_path = {}, " +
                 "key = {}").format(
                    tar_filename,
                    local_path,
                    key))

            tarcmd = 'tar -czf {} -C {} {}'.format(
                tar_filename,
                local_basepath,
                local_nameonly)

            self.logger.debug("Tar cmd = {}".format(tarcmd))

            subprocess.call(['/bin/bash', '-c', tarcmd])

            if key is None:
                key = 'blobstore/' + util.sha256_checksum(tar_filename) \
                      + '.tgz'

            def finish_upload():
                self._upload_file(key, tar_filename)

                os.remove(tar_filename)

            t = Thread(target=finish_upload)
            t.start()

            if background:
                return (key, t)
            else:
                t.join()
                return key
        else:
            self.logger.debug(("Local path {} does not exist. " +
                               "Not uploading anything.").format(local_path))

    def get_artifact(
            self,
            artifact,
            local_path=None,
            only_newer=True,
            background=False):

        key = artifact['key']

        if local_path is None:
            if 'local' in artifact.keys() and \
                    os.path.exists(artifact['local']):
                local_path = artifact['local']
            else:
                if artifact['mutable']:
                    local_path = fs_tracker.get_artifact_cache(key)
                else:
                    local_path = fs_tracker.get_blob_cache(key)

        local_path = re.sub('\/\Z', '', local_path)
        local_basepath = os.path.dirname(local_path)

        self.logger.debug("Downloading dir {} to local path {} from storage..."
                          .format(key, local_path))

        if only_newer and os.path.exists(local_path):
            self.logger.debug(
                'Comparing date of the artifact in storage with local')
            storage_time = self._get_file_timestamp(key)
            local_time = os.path.getmtime(local_path)
            if storage_time is None:
                self.logger.info(
                    "Unable to get storage timestamp, storage is either " +
                    "corrupted and has not finished uploading")
                return local_path

            if local_time > storage_time - self.timestamp_shift:
                self.logger.info(
                    "Local path is younger than stored, skipping the download")
                return local_path

        tar_filename = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
        self.logger.debug("tar_filename = {} ".format(tar_filename))

        def finish_download():
            self._download_file(key, tar_filename)
            if os.path.exists(tar_filename):
                # first, figure out if the tar file has a base path of .
                # or not
                self.logger.debug("Untarring {}".format(tar_filename))
                listtar, _ = subprocess.Popen(['tar', '-tzf', tar_filename],
                                              stdout=subprocess.PIPE,
                                              stderr=subprocess.STDOUT
                                              ).communicate()
                listtar = listtar.strip().split('\n')
                self.logger.debug('List of files in the tar: ' + str(listtar))
                if listtar[0].startswith('./'):
                    # Files are archived into tar from .; adjust path
                    # accordingly
                    basepath = local_path
                else:
                    basepath = local_basepath

                tarp = subprocess.Popen([
                    '/bin/bash',
                    '-c',
                    ('mkdir -p {} &&' +
                     'tar -xzf {} -C {} --keep-newer-files')
                    .format(basepath, tar_filename, basepath)],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT)
                tarout, _ = tarp.communicate()
                if tarp.returncode != 0:
                    self.logger.error('Tar had a non-zero return code!')
                    self.logger.error('tar output: \n ' + tarout)

                if len(listtar) == 1:
                    actual_path = os.path.join(basepath, listtar[0])
                    self.logger.info(
                        'Renaming {} into {}'.format(
                            actual_path, local_path))
                    os.rename(actual_path, local_path)
                os.remove(tar_filename)
            else:
                self.logger.error(
                    'file {} download failed'.format(tar_filename))

        t = Thread(target=finish_download)
        t.start()
        if background:
            return (local_path, t)
        else:
            t.join()
            return local_path

    def get_artifact_url(self, artifact):
        if 'key' in artifact.keys():
            return self._get_file_url(artifact['key'])
        return None

    def delete_artifact(self, artifact):
        if 'key' in artifact.keys():
            self._delete_file(artifact['key'])

    def _upload_file(self, key, local_file_path):
        try:
            storageobj = self.app.storage().child(key)
            if self.auth:
                storageobj.put(local_file_path,
                               self.auth.get_token(),
                               self.auth.get_user_id())
            else:
                storageobj.put(local_file_path)
        except Exception as err:
            self.logger.error(("Uploading file {} with key {} into storage " +
                               "raised an exception: {}")
                              .format(local_file_path, key, err))

    def _download_file(self, key, local_file_path):
        self.logger.debug("Downloading file at key {} to local path {}..."
                          .format(key, local_file_path))
        try:
            storageobj = self.app.storage().child(key)

            if self.auth:
                # pyrebase download does not work with files that require
                # authentication...
                # Need to rewrite
                # storageobj.download(local_file_path, self.auth.get_token())

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
                escaped_key = key.replace('/', '%2f')
                url = "{}/o/{}?alt=media".format(
                    self.app.storage().storage_bucket,
                    escaped_key)

                response = requests.get(url, stream=True, headers=headers)
                if response.status_code == 200:
                    with open(local_file_path, 'wb') as f:
                        for chunk in response:
                            f.write(chunk)
                else:
                    raise ValueError("Response error with code {}"
                                     .format(response.status_code))
            else:
                storageobj.download(local_file_path)
            self.logger.debug("Done")
        except Exception as err:
            self.logger.error(
                ("Downloading file {} to local path {} from storage " +
                 "raised an exception: {}") .format(
                    key,
                    local_file_path,
                    err))

    def _delete_file(self, key):
        self.logger.debug("Deleting file at key {}".format(key))
        try:
            if self.auth:

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
            else:
                headers = {}

            escaped_key = key.replace('/', '%2f')
            url = "{}/o/{}?alt=media".format(
                self.app.storage().storage_bucket,
                escaped_key)

            response = requests.delete(url, headers=headers)
            if response.status_code != 204:
                raise ValueError("Response error with code {}, text {}"
                                 .format(response.status_code, response.text))

            self.logger.debug("Done")
        except Exception as err:
            self.logger.error(
                ("Deleting file {} from storage " +
                 "raised an exception: {}") .format(key, err))

    def _get_file_url(self, key):
        self.logger.debug("Getting a download url for a file at key {}"
                          .format(key))

        response_dict, url = self._get_file_meta(key)
        if response_dict is None:
            self.logger.debug("Getting file metainfo failed")
            return None

        self.logger.debug("Done")
        return url + '?alt=media&token=' \
            + response_dict['downloadTokens']

    def _get_file_timestamp(self, key):
        response, _ = self._get_file_meta(key)
        if response is not None and 'updated' in response.keys():
            timestamp = calendar.timegm(
                time.strptime(
                    response['updated'],
                    "%Y-%m-%dT%H:%M:%S.%fZ"))
            return timestamp
        else:
            return None

    def _get_file_meta(self, key):
        self.logger.debug("Getting metainformation for a file at key {}"
                          .format(key))
        try:
            if self.auth:
                # pyrebase download does not work with files that require
                # authentication...
                # Need to rewrite
                # storageobj.download(local_file_path, self.auth.get_token())

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
            else:
                headers = {}

            escaped_key = key.replace('/', '%2f')
            url = "{}/o/{}".format(
                self.app.storage().storage_bucket,
                escaped_key)

            response = requests.get(url, headers=headers)
            if response.status_code != 200:
                raise ValueError("Response error with code {}"
                                 .format(response.status_code))

            return (json.loads(response.content), url)

        except Exception as err:
            self.logger.error(
                ("Getting metainfo of file {} " +
                 "raised an exception: {}") .format(key, err))
            return (None, None)
<commit_msg>piping output of cp command in artifact_store<commit_after>import os
import uuid

import logging
import time
import calendar
import tempfile
import re
from threading import Thread
import subprocess
import requests
import json
import shutil

import fs_tracker
import util

logging.basicConfig()

class FirebaseArtifactStore(object):

    def __init__(self, pyrebase_app, auth, measure_timestamp_diff=True):
        self.app = pyrebase_app
        self.auth = auth
        self.logger = logging.getLogger('FirebaseArtifactStore')
        self.logger.setLevel(10)

        self.timestamp_shift = 0

        if measure_timestamp_diff:
            max_diff = 60

            tmpfile = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()) + '.txt')
            with open(tmpfile, 'w') as f:
                f.write('timestamp_diff_test')
            key = 'tests/' + str(uuid.uuid4())
            self._upload_file(key, tmpfile)
            remote_timestamp = self._get_file_timestamp(key)

            if remote_timestamp is not None:

                now_remote_diff = time.time() - remote_timestamp
                self._delete_file(key)
                os.remove(tmpfile)

                assert -max_diff < now_remote_diff and \
                    now_remote_diff < max_diff, \
                    "Timestamp difference is more than 60 seconds. " + \
                    "You'll need to adjust local clock for caching " + \
                    "to work correctly"

                if now_remote_diff < 0:
                    self.timestamp_shift = -now_remote_diff

    def put_artifact(
            self,
            artifact,
            local_path=None,
            cache=True,
            background=False):
        if local_path is None:
            local_path = artifact['local']

        key = artifact.get('key')
        if os.path.exists(local_path):
            tar_filename = os.path.join(tempfile.gettempdir(),
                                        str(uuid.uuid4()))

            local_path = re.sub('/\Z', '', local_path)
            local_nameonly = re.sub('.*/', '', local_path)
            local_basepath = re.sub('/[^/]*\Z', '', local_path)

            if cache and key:
                cache_dir = fs_tracker.get_artifact_cache(key)
                if cache_dir != local_path:
                    self.logger.debug(
                        "Copying local path {} to cache {}"
                        .format(local_path, cache_dir))

                    if os.path.exists(cache_dir) and os.path.isdir(cache_dir):
                        shutil.rmtree(cache_dir)

                    pcp = subprocess.Popen(
                            ['cp', '-pR', local_path, cache_dir],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
                    cpout, _ = pcp.communicate()
                    if pcp.returncode != 0:
                        self.logger.info('cp returned non-zero exit code. Output:')
                        self.logger.info(cpout)

            self.logger.debug(
                ("Tarring and uploading directrory. " +
                 "tar_filename = {}, " +
                 "local_path = {}, " +
                 "key = {}").format(
                    tar_filename,
                    local_path,
                    key))

            tarcmd = 'tar -czf {} -C {} {}'.format(
                tar_filename,
                local_basepath,
                local_nameonly)

            self.logger.debug("Tar cmd = {}".format(tarcmd))

            subprocess.call(['/bin/bash', '-c', tarcmd])

            if key is None:
                key = 'blobstore/' + util.sha256_checksum(tar_filename) \
                      + '.tgz'

            def finish_upload():
                self._upload_file(key, tar_filename)

                os.remove(tar_filename)

            t = Thread(target=finish_upload)
            t.start()

            if background:
                return (key, t)
            else:
                t.join()
                return key
        else:
            self.logger.debug(("Local path {} does not exist. " +
                               "Not uploading anything.").format(local_path))

    def get_artifact(
            self,
            artifact,
            local_path=None,
            only_newer=True,
            background=False):

        key = artifact['key']

        if local_path is None:
            if 'local' in artifact.keys() and \
                    os.path.exists(artifact['local']):
                local_path = artifact['local']
            else:
                if artifact['mutable']:
                    local_path = fs_tracker.get_artifact_cache(key)
                else:
                    local_path = fs_tracker.get_blob_cache(key)

        local_path = re.sub('\/\Z', '', local_path)
        local_basepath = os.path.dirname(local_path)

        self.logger.debug("Downloading dir {} to local path {} from storage..."
                          .format(key, local_path))

        if only_newer and os.path.exists(local_path):
            self.logger.debug(
                'Comparing date of the artifact in storage with local')
            storage_time = self._get_file_timestamp(key)
            local_time = os.path.getmtime(local_path)
            if storage_time is None:
                self.logger.info(
                    "Unable to get storage timestamp, storage is either " +
                    "corrupted and has not finished uploading")
                return local_path

            if local_time > storage_time - self.timestamp_shift:
                self.logger.info(
                    "Local path is younger than stored, skipping the download")
                return local_path

        tar_filename = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
        self.logger.debug("tar_filename = {} ".format(tar_filename))

        def finish_download():
            self._download_file(key, tar_filename)
            if os.path.exists(tar_filename):
                # first, figure out if the tar file has a base path of .
                # or not
                self.logger.debug("Untarring {}".format(tar_filename))
                listtar, _ = subprocess.Popen(['tar', '-tzf', tar_filename],
                                              stdout=subprocess.PIPE,
                                              stderr=subprocess.STDOUT
                                              ).communicate()
                listtar = listtar.strip().split('\n')
                self.logger.debug('List of files in the tar: ' + str(listtar))
                if listtar[0].startswith('./'):
                    # Files are archived into tar from .; adjust path
                    # accordingly
                    basepath = local_path
                else:
                    basepath = local_basepath

                tarp = subprocess.Popen([
                    '/bin/bash',
                    '-c',
                    ('mkdir -p {} &&' +
                     'tar -xzf {} -C {} --keep-newer-files')
                    .format(basepath, tar_filename, basepath)],
                    stdout=subprocess.PIPE)

                tarout, _ = tarp.communicate()
                if tarp.returncode != 0:
                    self.logger.info('tar had a non-zero return code!')
                    self.logger.info('tar output: \n ' + tarout)

                if len(listtar) == 1:
                    actual_path = os.path.join(basepath, listtar[0])
                    self.logger.info(
                        'Renaming {} into {}'.format(
                            actual_path, local_path))
                    os.rename(actual_path, local_path)
                os.remove(tar_filename)
            else:
                self.logger.error(
                    'file {} download failed'.format(tar_filename))

        t = Thread(target=finish_download)
        t.start()
        if background:
            return (local_path, t)
        else:
            t.join()
            return local_path

    def get_artifact_url(self, artifact):
        if 'key' in artifact.keys():
            return self._get_file_url(artifact['key'])
        return None

    def delete_artifact(self, artifact):
        if 'key' in artifact.keys():
            self._delete_file(artifact['key'])

    def _upload_file(self, key, local_file_path):
        try:
            storageobj = self.app.storage().child(key)
            if self.auth:
                storageobj.put(local_file_path,
                               self.auth.get_token(),
                               self.auth.get_user_id())
            else:
                storageobj.put(local_file_path)
        except Exception as err:
            self.logger.error(("Uploading file {} with key {} into storage " +
                               "raised an exception: {}")
                              .format(local_file_path, key, err))

    def _download_file(self, key, local_file_path):
        self.logger.debug("Downloading file at key {} to local path {}..."
                          .format(key, local_file_path))
        try:
            storageobj = self.app.storage().child(key)

            if self.auth:
                # pyrebase download does not work with files that require
                # authentication...
                # Need to rewrite
                # storageobj.download(local_file_path, self.auth.get_token())

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
                escaped_key = key.replace('/', '%2f')
                url = "{}/o/{}?alt=media".format(
                    self.app.storage().storage_bucket,
                    escaped_key)

                response = requests.get(url, stream=True, headers=headers)
                if response.status_code == 200:
                    with open(local_file_path, 'wb') as f:
                        for chunk in response:
                            f.write(chunk)
                else:
                    raise ValueError("Response error with code {}"
                                     .format(response.status_code))
            else:
                storageobj.download(local_file_path)
            self.logger.debug("Done")
        except Exception as err:
            self.logger.error(
                ("Downloading file {} to local path {} from storage " +
                 "raised an exception: {}") .format(
                    key,
                    local_file_path,
                    err))

    def _delete_file(self, key):
        self.logger.debug("Deleting file at key {}".format(key))
        try:
            if self.auth:

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
            else:
                headers = {}

            escaped_key = key.replace('/', '%2f')
            url = "{}/o/{}?alt=media".format(
                self.app.storage().storage_bucket,
                escaped_key)

            response = requests.delete(url, headers=headers)
            if response.status_code != 204:
                raise ValueError("Response error with code {}, text {}"
                                 .format(response.status_code, response.text))

            self.logger.debug("Done")
        except Exception as err:
            self.logger.error(
                ("Deleting file {} from storage " +
                 "raised an exception: {}") .format(key, err))

    def _get_file_url(self, key):
        self.logger.debug("Getting a download url for a file at key {}"
                          .format(key))

        response_dict, url = self._get_file_meta(key)
        if response_dict is None:
            self.logger.debug("Getting file metainfo failed")
            return None

        self.logger.debug("Done")
        return url + '?alt=media&token=' \
            + response_dict['downloadTokens']

    def _get_file_timestamp(self, key):
        response, _ = self._get_file_meta(key)
        if response is not None and 'updated' in response.keys():
            timestamp = calendar.timegm(
                time.strptime(
                    response['updated'],
                    "%Y-%m-%dT%H:%M:%S.%fZ"))
            return timestamp
        else:
            return None

    def _get_file_meta(self, key):
        self.logger.debug("Getting metainformation for a file at key {}"
                          .format(key))
        try:
            if self.auth:
                # pyrebase download does not work with files that require
                # authentication...
                # Need to rewrite
                # storageobj.download(local_file_path, self.auth.get_token())

                headers = {"Authorization": "Firebase " +
                           self.auth.get_token()}
            else:
                headers = {}

            escaped_key = key.replace('/', '%2f')
            url = "{}/o/{}".format(
                self.app.storage().storage_bucket,
                escaped_key)

            response = requests.get(url, headers=headers)
            if response.status_code != 200:
                raise ValueError("Response error with code {}"
                                 .format(response.status_code))

            return (json.loads(response.content), url)

        except Exception as err:
            self.logger.error(
                ("Getting metainfo of file {} " +
                 "raised an exception: {}") .format(key, err))
            return (None, None)
<|endoftext|>


<commit_before># Copyright 2017 Canonical, Ltd.
#
# 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 (at your option) 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 logging

from urwid import (
    AttrMap,
    delegate_to_widget_mixin,
    emit_signal,
    MetaSignals,
    Text,
    WidgetDecoration,
    WidgetDisable,
    WidgetWrap,
    )

from subiquitycore.ui.buttons import cancel_btn, done_btn
from subiquitycore.ui.container import Columns, Pile
from subiquitycore.ui.interactive import (
    PasswordEditor,
    IntegerEditor,
    StringEditor,
    )
from subiquitycore.ui.utils import button_pile, Color

log = logging.getLogger("subiquitycore.ui.form")

class Toggleable(delegate_to_widget_mixin('_original_widget'), WidgetDecoration):

    def __init__(self, original):
        if not isinstance(original, AttrMap):
            raise RuntimeError("Toggleable must be passed an AttrMap, not %s", original)
        self.original = original
        self.enabled = False
        self.enable()

    def enable(self):
        if not self.enabled:
            self.original_widget = self.original
            self.enabled = True

    def disable(self):
        if self.enabled:
            self.original_widget = WidgetDisable(Color.info_minor(self.original.original_widget))
            self.enabled = False

class _Validator(WidgetWrap):

    def __init__(self, field, w):
        self.field = field
        super().__init__(w)

    def lost_focus(self):
        self.field.validate()

class FormField(object):

    next_index = 0

    def __init__(self, caption=None, cleaner=None, validator=None, help=None):
        self.caption = caption
        self.cleaner = cleaner
        self.validator = validator
        self.help = help
        self.index = FormField.next_index
        FormField.next_index += 1

    def _make_widget(self, form):
        raise NotImplementedError(self._make_widget)

    def bind(self, form):
        widget = self._make_widget(form)
        return BoundFormField(self, form, widget)

    def clean(self, value):
        if self.cleaner is not None:
            return self.cleaner(value)
        else:
            return value

    def validate(self, value):
        pass

class BoundFormField(object):

    def __init__(self, field, form, widget):
        self.field = field
        self.form = form
        self.in_error = False
        self._help = None
        self._caption = None
        self.pile = None
        self._enabled = True
        self.showing_extra = False
        self.widget = widget

    def clean(self, value):
        value = self.field.clean(value)
        cleaner = getattr(self.form, "clean_" + self.field.name, None)
        if cleaner is not None:
            value = cleaner(value)
        return value

    def _validate(self):
        if not self._enabled:
            return
        try:
            v = self.value
        except ValueError as e:
            return str(e)
        if self.field.validator is not None:
            r = self.field.validator(v)
            if r is not None:
                return r
        validator = getattr(self.form, "validate_" + self.field.name, None)
        if validator is not None:
            return validator()

    def validate(self):
        # cleaning/validation can call show_extra to add an
        # informative message. We record this by having show_extra to
        # set showing_extra so we don't immediately replace this
        # message with the widget's help in the case that validation
        # succeeds.
        self.showing_extra = False
        r = self._validate()
        if r is None:
            self.in_error = False
            if not self.showing_extra:
                self.help_text.set_text(self.help)
        else:
            self.in_error = True
            self.show_extra(('info_error', r))
        self.form.validated()

    def show_extra(self, extra_markup):
        self.showing_extra = True
        self.help_text.set_text(extra_markup)

    @property
    def value(self):
        return self.clean(self.widget.value)

    @value.setter
    def value(self, val):
        self.widget.value = val

    @property
    def help(self):
        if self._help is not None:
            return self._help
        elif self.field.help is not None:
            return self.field.help
        else:
            return ""

    @help.setter
    def help(self, val):
        self._help = val
        if self.pile is not None:
            self.pile[1].set_text(val)

    @property
    def caption(self):
        if self._caption is not None:
            return self._caption
        else:
            return self.field.caption

    @caption.setter
    def caption(self, val):
        self._caption = val

    def _cols(self):
        text = Text(self.caption, align="right")
        if self._enabled:
            input = Color.string_input(_Validator(self, self.widget))
        else:
            input = self.widget
        cols = [
                    (self._longest_caption, text),
                    input
                ]
        cols = Columns(cols, dividechars=2)
        if self._enabled:
            return cols
        else:
            return WidgetDisable(Color.info_minor(cols))

    def as_row(self, view, longest_caption):
        if self.pile is not None:
            raise RuntimeError("do not call as_row more than once!")
        self.parent_view = view
        self._longest_caption = longest_caption
        self.help_text = Text(self.help, align="center")
        cols = [
                    (self._longest_caption, Text("")),
                    self.help_text,
                ]
        self.pile = Pile([self._cols(), Columns(cols, dividechars=2)])
        return self.pile

    @property
    def enabled(self):
        return self._enabled

    @enabled.setter
    def enabled(self, val):
        if val != self._enabled:
            self._enabled = val
            if self.pile is not None:
                self.pile.contents[0] = (self._cols(), self.pile.contents[0][1])

def simple_field(widget_maker):
    class Field(FormField):
        def _make_widget(self, form):
            return widget_maker()
    return Field

StringField = simple_field(StringEditor)
PasswordField = simple_field(PasswordEditor)
IntegerField = simple_field(IntegerEditor)

class MetaForm(MetaSignals):

    def __init__(self, name, bases, attrs):
        super().__init__(name, bases, attrs)
        _unbound_fields = []
        for k, v in attrs.items():
            if isinstance(v, FormField):
                v.name = k
                if v.caption is None:
                    v.caption = k + ":"
                _unbound_fields.append(v)
        _unbound_fields.sort(key=lambda f:f.index)
        self._unbound_fields = _unbound_fields

class Form(object, metaclass=MetaForm):

    signals = ['submit', 'cancel']

    ok_label = _("Done")

    def __init__(self, initial={}):
        self.done_btn = Toggleable(done_btn(label=self.ok_label, on_press=self._click_done))
        self.cancel_btn = Toggleable(cancel_btn(on_press=self._click_cancel))
        self.buttons = button_pile([self.done_btn, self.cancel_btn])
        self._fields = []
        for field in self._unbound_fields:
            bf = field.bind(self)
            setattr(self, bf.field.name, bf)
            self._fields.append(bf)
            if field.name in initial:
                bf.value = initial[field.name]

    def _click_done(self, sender):
        emit_signal(self, 'submit', self)

    def _click_cancel(self, sender):
        emit_signal(self, 'cancel', self)

    def remove_field(self, field_name):
        new_fields = []
        for bf in self._fields:
            if bf.field.name != field_name:
                new_fields.append(bf)
        self._fields[:] = new_fields

    @property
    def longest_caption(self):
        longest_caption = 0
        for field in self._fields:
            longest_caption = max(longest_caption, len(field.caption))
        return longest_caption

    def as_rows(self, view):
        longest_caption = self.longest_caption
        rows = []
        for field in self._fields:
            rows.append(field.as_row(view, longest_caption))
        return Pile(rows)

    def validated(self):
        in_error = False
        for f in self._fields:
            if f.in_error:
                in_error = True
                break
        if in_error:
            self.buttons.base_widget.contents[0][0].disable()
            self.buttons.base_widget.focus_position = 1
        else:
            self.buttons.base_widget.contents[0][0].enable()

    def as_data(self):
        data = {}
        for field in self._fields:
            data[field.field.name] = field.value
        return data
<commit_msg>simplify things a bit by removing the ability to pass validator= and cleaner= to form fields<commit_after># Copyright 2017 Canonical, Ltd.
#
# 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 (at your option) 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 logging

from urwid import (
    AttrMap,
    delegate_to_widget_mixin,
    emit_signal,
    MetaSignals,
    Text,
    WidgetDecoration,
    WidgetDisable,
    WidgetWrap,
    )

from subiquitycore.ui.buttons import cancel_btn, done_btn
from subiquitycore.ui.container import Columns, Pile
from subiquitycore.ui.interactive import (
    PasswordEditor,
    IntegerEditor,
    StringEditor,
    )
from subiquitycore.ui.utils import button_pile, Color

log = logging.getLogger("subiquitycore.ui.form")

class Toggleable(delegate_to_widget_mixin('_original_widget'), WidgetDecoration):

    def __init__(self, original):
        if not isinstance(original, AttrMap):
            raise RuntimeError("Toggleable must be passed an AttrMap, not %s", original)
        self.original = original
        self.enabled = False
        self.enable()

    def enable(self):
        if not self.enabled:
            self.original_widget = self.original
            self.enabled = True

    def disable(self):
        if self.enabled:
            self.original_widget = WidgetDisable(Color.info_minor(self.original.original_widget))
            self.enabled = False

class _Validator(WidgetWrap):

    def __init__(self, field, w):
        self.field = field
        super().__init__(w)

    def lost_focus(self):
        self.field.validate()

class FormField(object):

    next_index = 0

    def __init__(self, caption=None, help=None):
        self.caption = caption
        self.help = help
        self.index = FormField.next_index
        FormField.next_index += 1

    def _make_widget(self, form):
        raise NotImplementedError(self._make_widget)

    def bind(self, form):
        widget = self._make_widget(form)
        return BoundFormField(self, form, widget)

class BoundFormField(object):

    def __init__(self, field, form, widget):
        self.field = field
        self.form = form
        self.in_error = False
        self._help = None
        self._caption = None
        self.pile = None
        self._enabled = True
        self.showing_extra = False
        self.widget = widget

    def clean(self, value):
        cleaner = getattr(self.form, "clean_" + self.field.name, None)
        if cleaner is not None:
            value = cleaner(value)
        return value

    def _validate(self):
        if not self._enabled:
            return
        try:
            self.value
        except ValueError as e:
            return str(e)
        validator = getattr(self.form, "validate_" + self.field.name, None)
        if validator is not None:
            return validator()

    def validate(self):
        # cleaning/validation can call show_extra to add an
        # informative message. We record this by having show_extra to
        # set showing_extra so we don't immediately replace this
        # message with the widget's help in the case that validation
        # succeeds.
        self.showing_extra = False
        r = self._validate()
        if r is None:
            self.in_error = False
            if not self.showing_extra:
                self.help_text.set_text(self.help)
        else:
            self.in_error = True
            self.show_extra(('info_error', r))
        self.form.validated()

    def show_extra(self, extra_markup):
        self.showing_extra = True
        self.help_text.set_text(extra_markup)

    @property
    def value(self):
        return self.clean(self.widget.value)

    @value.setter
    def value(self, val):
        self.widget.value = val

    @property
    def help(self):
        if self._help is not None:
            return self._help
        elif self.field.help is not None:
            return self.field.help
        else:
            return ""

    @help.setter
    def help(self, val):
        self._help = val
        if self.pile is not None:
            self.pile[1].set_text(val)

    @property
    def caption(self):
        if self._caption is not None:
            return self._caption
        else:
            return self.field.caption

    @caption.setter
    def caption(self, val):
        self._caption = val

    def _cols(self):
        text = Text(self.caption, align="right")
        if self._enabled:
            input = Color.string_input(_Validator(self, self.widget))
        else:
            input = self.widget
        cols = [
                    (self._longest_caption, text),
                    input
                ]
        cols = Columns(cols, dividechars=2)
        if self._enabled:
            return cols
        else:
            return WidgetDisable(Color.info_minor(cols))

    def as_row(self, view, longest_caption):
        if self.pile is not None:
            raise RuntimeError("do not call as_row more than once!")
        self.parent_view = view
        self._longest_caption = longest_caption
        self.help_text = Text(self.help, align="center")
        cols = [
                    (self._longest_caption, Text("")),
                    self.help_text,
                ]
        self.pile = Pile([self._cols(), Columns(cols, dividechars=2)])
        return self.pile

    @property
    def enabled(self):
        return self._enabled

    @enabled.setter
    def enabled(self, val):
        if val != self._enabled:
            self._enabled = val
            if self.pile is not None:
                self.pile.contents[0] = (self._cols(), self.pile.contents[0][1])

def simple_field(widget_maker):
    class Field(FormField):
        def _make_widget(self, form):
            return widget_maker()
    return Field

StringField = simple_field(StringEditor)
PasswordField = simple_field(PasswordEditor)
IntegerField = simple_field(IntegerEditor)

class MetaForm(MetaSignals):

    def __init__(self, name, bases, attrs):
        super().__init__(name, bases, attrs)
        _unbound_fields = []
        for k, v in attrs.items():
            if isinstance(v, FormField):
                v.name = k
                if v.caption is None:
                    v.caption = k + ":"
                _unbound_fields.append(v)
        _unbound_fields.sort(key=lambda f:f.index)
        self._unbound_fields = _unbound_fields

class Form(object, metaclass=MetaForm):

    signals = ['submit', 'cancel']

    ok_label = _("Done")

    def __init__(self, initial={}):
        self.done_btn = Toggleable(done_btn(label=self.ok_label, on_press=self._click_done))
        self.cancel_btn = Toggleable(cancel_btn(on_press=self._click_cancel))
        self.buttons = button_pile([self.done_btn, self.cancel_btn])
        self._fields = []
        for field in self._unbound_fields:
            bf = field.bind(self)
            setattr(self, bf.field.name, bf)
            self._fields.append(bf)
            if field.name in initial:
                bf.value = initial[field.name]

    def _click_done(self, sender):
        emit_signal(self, 'submit', self)

    def _click_cancel(self, sender):
        emit_signal(self, 'cancel', self)

    def remove_field(self, field_name):
        new_fields = []
        for bf in self._fields:
            if bf.field.name != field_name:
                new_fields.append(bf)
        self._fields[:] = new_fields

    @property
    def longest_caption(self):
        longest_caption = 0
        for field in self._fields:
            longest_caption = max(longest_caption, len(field.caption))
        return longest_caption

    def as_rows(self, view):
        longest_caption = self.longest_caption
        rows = []
        for field in self._fields:
            rows.append(field.as_row(view, longest_caption))
        return Pile(rows)

    def validated(self):
        in_error = False
        for f in self._fields:
            if f.in_error:
                in_error = True
                break
        if in_error:
            self.buttons.base_widget.contents[0][0].disable()
            self.buttons.base_widget.focus_position = 1
        else:
            self.buttons.base_widget.contents[0][0].enable()

    def as_data(self):
        data = {}
        for field in self._fields:
            data[field.field.name] = field.value
        return data
<|endoftext|>


<commit_before>import random
import functions

class Generate():
    """ index each cell in a nine by nine sudoku game
    """
    def __init__(self, column):
        self.column = column
        self.row = 0
        self.quadrant = 0
        self.numbers = set(range(1,10))
        self.value = 0

    def integer(self):
    ### set an integer to each cell and update the remaining options to zero

        if self.numbers == set():
            return 81
        self.value = random.choice(list(self.numbers))
        self.numbers = {0}
        return self.value

    def up_column(self, integer, column):
    ### update the set of possible choices on a column

        if self.column == column:
            if {integer} <= self.numbers:
                self.numbers.remove(integer)

    def up_row(self, integer, row):
    ### update the set of possible choices on a row
        if self.row == row:
            if {integer} <= self.numbers:
                self.numbers.remove(integer)

    def up_quadrant(self, integer, quadrant):
    ### update the set of possible choices on a quadrant
        if self.quadrant == quadrant:
            if {integer} <= self.numbers:
                self.numbers.remove(integer)

    def reset(self):
    ### reset the set of possible choices
        self.value = 0
        self.numbers = set(range(1,10))

def gen(mega, columns_index, rows_index):
    ### assign indexes to cells

    count = 0
    for i in range(0,81,9):
        for j in range(i,i+9):
            label = functions.bound(columns_index, j)
            mega[j] = Generate(columns_index[label])
            mega[j].row = rows_index[count]
            if (label < 3) and (count < 3):
                mega[j].quadrant = 'A'
            elif (label < 6) and (count < 3):
                mega[j].quadrant = 'B'
            elif (label < 9) and (count < 3):
                mega[j].quadrant = 'C'
            elif (label < 3) and (count < 6):
                mega[j].quadrant = 'D'
            elif (label < 6) and (count < 6):
                mega[j].quadrant = 'E'
            elif (label < 9) and (count < 6):
                mega[j].quadrant = 'F'
            elif (label < 3) and (count < 9):
                mega[j].quadrant = 'G'
            elif (label < 6) and (count < 9):
                mega[j].quadrant = 'H'
            elif (label < 9) and (count < 9): # dont need that extra condition
                mega[j].quadrant = 'I'

        count += 1

def operations(mega):
    """ choose an integer and update row, column , and quadrant"""

    count = 0
    i = 0
    while i < 81:

        value = mega[i].integer()
        if value == 81:
#            print(count)
            count += 1
            for j in range(81):
                mega[j].reset()
            i = 0
            continue

        row_integer = mega[i].row
        column_integer = mega[i].column
        quadrant_integer = mega[i].quadrant

        for j in range(81):
            mega[j].up_row(value, row_integer)
        for j in range(81):
            mega[j].up_column(value, column_integer)
#        for j in range(81):
#            mega[j].up_quadrant(value, column_integer)

        i += 1

def display(mega):
    """ print the nine by nine table"""

    for i in range(0,81,9):
        for j in range(i,i+9):
            print('{}{}{}{}{} '.format(mega[j].value,mega[j].quadrant,mega[j].column,mega[j].row, mega[i].numbers), end="")
        print("\n")

def main():

    mega = [0]*81
    columns_index = ['A','B','C','D','E','F','G','H','I']
    rows_index = ['a','b','c','d','e','f','g','h','i']

    gen(mega, columns_index, rows_index)
    operations(mega)
    display(mega)

main()
<commit_msg>Update existing file<commit_after>import random

def bound(seq, index):

    divisor = len(seq)
    infinite  = index % divisor
    return infinite

class Generate():
    """ index each cell in a nine by nine sudoku game
    """
    def __init__(self, column):
        self.column = column
        self.row = 0
        self.quadrant = 0
        self.numbers = set(range(1,10))
        self.value = 0
        self.chosen = {0}

    def up_column(self, integer, column):
    ### update the set of possible choices on a column

        if self.column == column:
            if {integer} <= self.numbers:
                self.numbers.remove(integer)

    def up_row(self, integer, row):
    ### update the set of possible choices on a row
        if self.row == row:
            if {integer} <= self.numbers:
                self.numbers.remove(integer)

def gen(mega, columns_index, rows_index):
    ### assign indexes to cells

    count = 0
    for i in range(0,54,9):
        for j in range(i,i+9):
            label = bound(columns_index, j)
            mega[j] = Generate(columns_index[label])
            mega[j].row = rows_index[count]
            if (label < 3) and (count < 3):
                mega[j].quadrant = 'A'
            elif (label < 6) and (count < 3):
                mega[j].quadrant = 'B'
            elif (label < 9) and (count < 3):
                mega[j].quadrant = 'C'
            elif (label < 3) and (count < 6):
                mega[j].quadrant = 'D'
            elif (label < 6) and (count < 6):
                mega[j].quadrant = 'E'
            elif (label < 9) and (count < 6):
                mega[j].quadrant = 'F'
            elif (label < 3) and (count < 9):
                mega[j].quadrant = 'G'
            elif (label < 6) and (count < 9):
                mega[j].quadrant = 'H'
            elif (label < 9) and (count < 9): # dont need that extra condition
                mega[j].quadrant = 'I'

        count += 1

def operations(mega, i):
    """ choose an integer and update row, column , and quadrant"""

    if i < 54:

        complete = {1,2,3,4,5,6,7,8,9}
        current = {0}
        row_integer = mega[i].row
        column_integer = mega[i].column
        quadrant_integer = mega[i].quadrant

        for k in range(54):
            if mega[k].row == row_integer or mega[k].column == column_integer or mega[k].quadrant == quadrant_integer:
                current.add(mega[k].value)
        mega[i].numbers = complete - (current | mega[i].chosen)

        if  mega[i].numbers != set():

            mega[i].value = random.choice(list(mega[i].numbers))
            value = mega[i].value
            mega[i].chosen.add(value)

            i += 1
            operations(mega,i)

        else:

            i -= 2
            for j in range(i+1,54):
                mega[j].chosen = {0}
                mega[j].value = 0

            operations(mega, i)
    else:
        return

def display(mega):
    """ print the nine by nine table"""

    for i in range(0,54,9):
        for j in range(i,i+9):
#            print('{}{}{}{} '.format(mega[j].value,mega[j].quadrant,mega[j].column,mega[j].row), end="")
            print(mega[j].value, end= "     ")
        print("\n")

def main():

    mega = [0]*54
    columns_index = ['A','B','C','D','E','F','G','H','I']
    rows_index = ['a','b','c','d','e','f','g','h','i']

    gen(mega, columns_index, rows_index)
    i = 0
    operations(mega,i)
    display(mega)

main()

<|endoftext|>


<commit_before>import re

import gobject
import gtk
import goocanvas
import rsvg

from sugar.util import GObjectSingletonMeta
from sugar.canvas.IconColor import IconColor

_ICON_SIZE = 48

class _IconCache:
	def __init__(self):
		self._icons = {}

	def _create_icon(self, name, color):
		theme = gtk.icon_theme_get_default()
		info = theme.lookup_icon(name, _ICON_SIZE, 0)
		icon_file = open(info.get_filename(), 'r')
		data = icon_file.read()
		icon_file.close()

		if color != None:
			fill = color.get_fill_color()
			stroke = color.get_stroke_color()

			style = '.fill {fill:%s;stroke:%s;}' % (fill, fill)
			data = re.sub('\.fill \{.*\}', style, data)

			style = '.shape {stroke:%s;fill:%s;}' % (stroke, stroke)
			data = re.sub('\.shape \{.*\}', style, data)

			style = '.shape-and-fill {fill:%s; stroke:%s;}' % (fill, stroke)
			data = re.sub('\.shape-and-fill \{.*\}', style, data)

		return data

	def get_icon(self, name, color):
		key = (name, color.get_fill_color())
		if self._icons.has_key(key):
			icon = self._icons[key]
		else:
			icon = self._create_icon(name, color)
			self._icons[key] = icon
		return icon

class IconView(goocanvas.ItemViewSimple, goocanvas.ItemView):
	__gtype_name__ = 'IconView'

	_icon_cache = _IconCache()

	def __init__(self, canvas_view, parent_view, item):
		goocanvas.ItemViewSimple.__init__(self)
		self.parent_view = parent_view
		self.canvas_view = canvas_view
		self.item = item

	def do_get_item_view_at(self, x, y, cr, is_pointer_event, parent_is_visible):
		result = self

		cr.save()

#		if self.item.transform != None:
#			cr.transform(self.item.transform)
#		if self.transform != None:
#			cr.transform(self.transform)

		[user_x, user_y] = cr.device_to_user(x, y)
		if user_x < self.item.x or \
		   user_x > self.item.x + self.item.size or \
		   user_y < self.item.y or \
		   user_y > self.item.y + self.item.size:
			result = None

		cr.restore()

		return result

	def do_update(self, entire_tree, cr):
		if entire_tree or self.flags & goocanvas.ITEM_VIEW_NEED_UPDATE:
			self.flags &= ~goocanvas.ITEM_VIEW_NEED_UPDATE

			cr.save()

#			if self.item.transform != None:
#				cr.transform(self.item.transform)
#			if self.transform != None:
#				cr.transform(self.transform)

			bounds = goocanvas.Bounds()
			bounds.x1 = self.item.x
			bounds.y1 = self.item.y
			bounds.x2 = self.item.x + self.item.size
			bounds.y2 = self.item.y + self.item.size
			self.item.user_bounds_to_device(cr, bounds)
			self.bounds = bounds

			cr.restore()

		return self.bounds

	def do_paint(self, cr, bounds, scale):
		if self.item.color == None:
			theme = gtk.icon_theme_get_default()
			info = theme.lookup_icon(self.item.icon_name, self.item.size, 0)
			handle = rsvg.Handle(file=info.get_filename())
		else:
			icon = IconView._icon_cache.get_icon(self.item.icon_name,
												 self.item.color)
			handle = rsvg.Handle(data=icon)

		cr.save()

#		if self.item.transform != None:
#			cr.transform(self.item.transform)
#		if self.transform != None:
#			cr.transform(self.transform)

		cr.translate(self.item.x, self.item.y)
		scale_factor = float(self.item.size) / float(_ICON_SIZE)
		cr.scale(scale_factor, scale_factor)

		handle.render_cairo(cr)

		cr.restore()

		return self.bounds

class IconItem(goocanvas.ItemSimple, goocanvas.Item):
	__gsignals__ = {
		'clicked': (gobject.SIGNAL_RUN_FIRST,
					gobject.TYPE_NONE, ([])),
	}

	__gproperties__ = {
		'x'        : (float, None, None, -10e6, 10e6, 0,
					  gobject.PARAM_READWRITE),
		'y'        : (float, None, None, -10e6, 10e6, 0,
					  gobject.PARAM_READWRITE),
		'icon-name': (str, None, None, None,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE),
		'color'    : (object, None, None,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE),
		'size'     : (int, None, None,
					  0, 1024, 24,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE)
	}

	def __init__(self, **kwargs):
		self.x = 0.0
		self.y = 0.0

		goocanvas.ItemSimple.__init__(self, **kwargs)

	def do_set_property(self, pspec, value):
		if pspec.name == 'icon-name':
			self.icon_name = value
		elif pspec.name == 'color':
			self.color = value
		elif pspec.name == 'size':
			self.size = value
		elif pspec.name == 'x':
			self.x = value
		elif pspec.name == 'y':
			self.y = value

	def do_get_property(self, pspec):
		if pspec.name == 'x':
			return self.x
		elif pspec.name == 'y':
			return self.y
		elif pspec.name == 'size':
			return self.size
		elif pspec.name == 'icon-name':
			return self.icon_name
		elif pspec.name == 'color':
			return self.color

	def do_create_view(self, canvas, parent_view):
		view = IconView(canvas, parent_view, self)
		view.connect('button-press-event', self.__button_press_cb)
		return view

	def __button_press_cb(self, view, target, event):
		self.emit('clicked')
<commit_msg>Handle transformations<commit_after>import re

import gobject
import gtk
import goocanvas
import rsvg

from sugar.util import GObjectSingletonMeta
from sugar.canvas.IconColor import IconColor

_ICON_SIZE = 48

class _IconCache:
	def __init__(self):
		self._icons = {}

	def _create_icon(self, name, color):
		theme = gtk.icon_theme_get_default()
		info = theme.lookup_icon(name, _ICON_SIZE, 0)
		icon_file = open(info.get_filename(), 'r')
		data = icon_file.read()
		icon_file.close()

		if color != None:
			fill = color.get_fill_color()
			stroke = color.get_stroke_color()

			style = '.fill {fill:%s;stroke:%s;}' % (fill, fill)
			data = re.sub('\.fill \{.*\}', style, data)

			style = '.shape {stroke:%s;fill:%s;}' % (stroke, stroke)
			data = re.sub('\.shape \{.*\}', style, data)

			style = '.shape-and-fill {fill:%s; stroke:%s;}' % (fill, stroke)
			data = re.sub('\.shape-and-fill \{.*\}', style, data)

		return data

	def get_icon(self, name, color):
		key = (name, color.get_fill_color())
		if self._icons.has_key(key):
			icon = self._icons[key]
		else:
			icon = self._create_icon(name, color)
			self._icons[key] = icon
		return icon

class IconView(goocanvas.ItemViewSimple, goocanvas.ItemView):
	__gtype_name__ = 'IconView'

	_icon_cache = _IconCache()

	def __init__(self, canvas_view, parent_view, item):
		goocanvas.ItemViewSimple.__init__(self)
		self.parent_view = parent_view
		self.canvas_view = canvas_view
		self.item = item

	def do_get_item_view_at(self, x, y, cr, is_pointer_event, parent_is_visible):
		result = self

		cr.save()

		if self.item.transform != None:
			cr.transform(self.item.transform)
		if self.transform != None:
			cr.transform(self.transform)

		[user_x, user_y] = cr.device_to_user(x, y)
		if user_x < self.item.x or \
		   user_x > self.item.x + self.item.size or \
		   user_y < self.item.y or \
		   user_y > self.item.y + self.item.size:
			result = None

		cr.restore()

		return result

	def do_update(self, entire_tree, cr):
		if entire_tree or self.flags & goocanvas.ITEM_VIEW_NEED_UPDATE:
			self.flags &= ~goocanvas.ITEM_VIEW_NEED_UPDATE

			cr.save()

			if self.item.transform != None:
				cr.transform(self.item.transform)
			if self.transform != None:
				cr.transform(self.transform)

			bounds = goocanvas.Bounds()
			bounds.x1 = self.item.x
			bounds.y1 = self.item.y
			bounds.x2 = self.item.x + self.item.size
			bounds.y2 = self.item.y + self.item.size
			self.item.user_bounds_to_device(cr, bounds)
			self.bounds = bounds

			cr.restore()

		return self.bounds

	def do_paint(self, cr, bounds, scale):
		if self.item.color == None:
			theme = gtk.icon_theme_get_default()
			info = theme.lookup_icon(self.item.icon_name, self.item.size, 0)
			handle = rsvg.Handle(file=info.get_filename())
		else:
			icon = IconView._icon_cache.get_icon(self.item.icon_name,
												 self.item.color)
			handle = rsvg.Handle(data=icon)

		cr.save()

		if self.item.transform != None:
			cr.transform(self.item.transform)
		if self.transform != None:
			cr.transform(self.transform)

		cr.translate(self.item.x, self.item.y)
		scale_factor = float(self.item.size) / float(_ICON_SIZE)
		cr.scale(scale_factor, scale_factor)

		handle.render_cairo(cr)

		cr.restore()

		return self.bounds

class IconItem(goocanvas.ItemSimple, goocanvas.Item):
	__gsignals__ = {
		'clicked': (gobject.SIGNAL_RUN_FIRST,
					gobject.TYPE_NONE, ([])),
	}

	__gproperties__ = {
		'x'        : (float, None, None, -10e6, 10e6, 0,
					  gobject.PARAM_READWRITE),
		'y'        : (float, None, None, -10e6, 10e6, 0,
					  gobject.PARAM_READWRITE),
		'icon-name': (str, None, None, None,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE),
		'color'    : (object, None, None,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE),
		'size'     : (int, None, None,
					  0, 1024, 24,
					  gobject.PARAM_CONSTRUCT_ONLY |
					  gobject.PARAM_READWRITE)
	}

	def __init__(self, **kwargs):
		self.x = 0.0
		self.y = 0.0

		goocanvas.ItemSimple.__init__(self, **kwargs)

	def do_set_property(self, pspec, value):
		if pspec.name == 'icon-name':
			self.icon_name = value
		elif pspec.name == 'color':
			self.color = value
		elif pspec.name == 'size':
			self.size = value
		elif pspec.name == 'x':
			self.x = value
		elif pspec.name == 'y':
			self.y = value

	def do_get_property(self, pspec):
		if pspec.name == 'x':
			return self.x
		elif pspec.name == 'y':
			return self.y
		elif pspec.name == 'size':
			return self.size
		elif pspec.name == 'icon-name':
			return self.icon_name
		elif pspec.name == 'color':
			return self.color

	def do_create_view(self, canvas, parent_view):
		view = IconView(canvas, parent_view, self)
		view.connect('button-press-event', self.__button_press_cb)
		return view

	def __button_press_cb(self, view, target, event):
		self.emit('clicked')
<|endoftext|>


<commit_before>__version_info__ = {
    'major': 0,
    'minor': 5,
    'micro': 0,
    'releaselevel': 'beta',
    'serial': 1
}

def get_version():
    vers = ["%(major)i.%(minor)i" % __version_info__, ]

    if __version_info__['micro']:
        vers.append(".%(micro)i" % __version_info__)
    if __version_info__['releaselevel'] != 'final':
        vers.append('%(releaselevel)s%(serial)i' % __version_info__)
    return ''.join(vers)

__version__ = get_version()

class AlreadyRegistered(Exception):
    """
    An attempt was made to register a model more than once.
    """
    pass

registry = []

def register(model, tag_descriptor_attr='supertags',
             tagged_item_manager_attr='supertagged'):
    """
    Sets the given model class up for working with supertags.
    """

    from supertagging.managers import ModelTaggedItemManager, TagDescriptor

    if model in registry:
        raise AlreadyRegistered("The model '%s' has already been "
            "registered." % model._meta.object_name)
    if hasattr(model, tag_descriptor_attr):
        raise AttributeError("'%s' already has an attribute '%s'. You must "
            "provide a custom tag_descriptor_attr to register." % (
                model._meta.object_name,
                tag_descriptor_attr,
            )
        )
    if hasattr(model, tagged_item_manager_attr):
        raise AttributeError("'%s' already has an attribute '%s'. You must "
            "provide a custom tagged_item_manager_attr to register." % (
                model._meta.object_name,
                tagged_item_manager_attr,
            )
        )

    # Add tag descriptor
    setattr(model, tag_descriptor_attr, TagDescriptor())

    # Add custom manager
    ModelTaggedItemManager().contribute_to_class(model, tagged_item_manager_attr)

    # Finally register in registry
    registry.append(model)<commit_msg>Added a GenericRelation called supertaggeditem_set, in case there was a need for it.<commit_after>__version_info__ = {
    'major': 0,
    'minor': 5,
    'micro': 0,
    'releaselevel': 'beta',
    'serial': 1
}

def get_version():
    vers = ["%(major)i.%(minor)i" % __version_info__, ]

    if __version_info__['micro']:
        vers.append(".%(micro)i" % __version_info__)
    if __version_info__['releaselevel'] != 'final':
        vers.append('%(releaselevel)s%(serial)i' % __version_info__)
    return ''.join(vers)

__version__ = get_version()

class AlreadyRegistered(Exception):
    """
    An attempt was made to register a model more than once.
    """
    pass

registry = []

def register(model, tag_descriptor_attr='supertags',
             tagged_item_manager_attr='supertagged'):
    """
    Sets the given model class up for working with supertags.
    """

    from supertagging.managers import ModelTaggedItemManager, TagDescriptor
    from supertagging.models import SuperTaggedItem
    from django.contrib.contenttypes.generic import GenericRelation

    if model in registry:
        raise AlreadyRegistered("The model '%s' has already been "
            "registered." % model._meta.object_name)
    if hasattr(model, tag_descriptor_attr):
        raise AttributeError("'%s' already has an attribute '%s'. You must "
            "provide a custom tag_descriptor_attr to register." % (
                model._meta.object_name,
                tag_descriptor_attr,
            )
        )
    if hasattr(model, tagged_item_manager_attr):
        raise AttributeError("'%s' already has an attribute '%s'. You must "
            "provide a custom tagged_item_manager_attr to register." % (
                model._meta.object_name,
                tagged_item_manager_attr,
            )
        )

    # Add tag descriptor
    setattr(model, tag_descriptor_attr, TagDescriptor())

    # Add custom manager
    ModelTaggedItemManager().contribute_to_class(model, tagged_item_manager_attr)

    # Add a reverse generic relationship to supertaggeditems
    GenericRelation(SuperTaggedItem).contribute_to_class(model, 'supertaggeditem_set')

    # Finally register in registry
    registry.append(model)<|endoftext|>


<commit_before># Copyright (C) 2016 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import flask

from ..main import app

@app.route('/')
def homepage():
    """Home page

    """
    flask.flash('This Web app is still work in progress, use at your own risk',
                'warning')
    return flask.render_template('home.html')

@app.route('/about/')
def about():
    return flask.render_template('about.html')
<commit_msg>Fix rendering on main page<commit_after># Copyright (C) 2016 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import flask

from ..main import app
from flask.ext.api.decorators import set_renderers
from flask.ext.api.renderers import HTMLRenderer

@app.route('/')
@set_renderers(HTMLRenderer)
def homepage():
    """Home page

    """
    flask.flash('This Web app is still work in progress, use at your own risk',
                'warning')
    return flask.render_template('home.html')

@app.route('/about/')
@set_renderers(HTMLRenderer)
def about():
    return flask.render_template('about.html')
<|endoftext|>