<commit_before>'''
Top level interface into Riffle library. The crust sits atop the mantle, which sits atop the core.
The mantle is imported here as pymantle. It doesn't add functionality on top of the core, just
translates between coreRiffle and python as needed.

Fixing the osx python version:
    https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Common-Issues.md#python-segmentation-fault-11-on-import-some_python_module
'''

import os
import json

from greenlet import greenlet

import pymantle
from riffle import cumin, utils

class Domain(object):

    def __init__(self, name, superdomain=None):
        self.name = name

        if superdomain is None:
            self.mantleDomain = pymantle.NewDomain(name)
            self.app = App()
        else:
            self.mantleDomain = superdomain.mantleDomain.Subdomain(name)
            self.app = superdomain.app

    def join(self):
        # TODO: convert the "control plane" to use deferreds using Twisted style callbacks
        cb, eb = utils.newID(2)
        self.app.control[cb] = self.onJoin
        self.mantleDomain.Join(cb, eb)

        self.app.mainGreenlet = greenlet(self.app.handle)
        self.app.mainGreenlet.switch(self.mantleDomain)

    def leave(self):
        # TODO: Emit a deferred here (?)
        self.mantleDomain.Leave()

    def onJoin(self):
        pymantle.Info("Default onJoin")

    def onLeave(self):
        pymantle.Info("Default onLeave")

    def subscribe(self, endpoint, handler):
        return self._setHandler(endpoint, handler, self.mantleDomain.Subscribe, False)

    def register(self, endpoint, handler):
        return self._setHandler(endpoint, handler, self.mantleDomain.Register, True)

    def publish(self, endpoint, *args):
        return self._invoke(endpoint, args, self.mantleDomain.Publish, False)

    def call(self, endpoint, *args):
        return self._invoke(endpoint, args, self.mantleDomain.Call, True)

    def _setHandler(self, endpoint, handler, coreFunction, doesReturn):
        '''
        Register or Subscribe. Invokes targetFunction for the given endpoint and handler.

        :param coreFunction: the intended core function, either Subscribe or Register
        :param doesReturn: True if this handler can return a value (is a registration)
        '''

        d, handlerId = Deferred(), utils.newID()
        self.app.deferreds[d.cb], self.app.deferreds[d.eb] = d, d
        self.app.handlers[handlerId] = handler, doesReturn

        coreFunction(endpoint, d.cb, d.eb, handlerId, cumin.reflect(handler))
        return d

    def _invoke(self, endpoint, args, coreFunction, doesReturn):
        '''
        Publish or Call. Invokes targetFunction for the given endpoint and handler.

        :param coreFunction: the intended core function, either Subscribe or Register
        :param doesReturn: True if this handler can receive results (is a call)
        '''

        d = Deferred()
        d.canReturn = doesReturn
        d.mantleDomain = self.mantleDomain
        self.app.deferreds[d.cb], self.app.deferreds[d.eb] = d, d

        coreFunction(endpoint, d.cb, d.eb, cumin.marshall(args))
        return d

class Deferred(object):

    def __init__(self):
        self.cb, self.eb = utils.newID(2)
        self.green = None

        # Boy does it hurt me to put this here. canReturn needs to have access to the domain
        # for call returns
        self.mantleDomain = None

        # True if this deferred should inform the core of its types once set
        # Only true for calls
        self.canReturn = False

    def wait(self, *types):
        ''' Wait until the results of this invocation are resolved '''

        # if canReturn then this is a call. We need to retroactively inform the core of our types
        if self.canReturn:
            self.mantleDomain.CallExpects(self.cb, cumin.prepareSchema(types))

        # Get our current greenlet. If we're not running in a greenlet, someone screwed up bad
        self.green = greenlet.getcurrent()

        # Switch back to the parent greenlet: block until the parent has time to resolve us
        results = self.green.parent.switch(self)

        if isinstance(results, Exception):
            raise results

        r = cumin.unmarshall(results, types)

        # Actually, this cant happen. A return from a function should always come back as
        # a list. Unless you mean a return from... any other thing. In which case, bleh.
        if r is None:
            return r

        return r[0] if len(r) == 1 else r

    def setCallback(self, handler):
        ''' Traditional Twisted style callbacks '''
        pass

class App(object):

    def __init__(self):
        # self.handlers contains a tuple of (function, bool), where the bool is True if the
        # function can return (i.e. is a registration)
        self.deferreds, self.handlers, self.control = {}, {}, {}
        self.mainGreenlet = None

    def handle(self, domain):
        ''' Open connection with the core and begin handling callbacks '''

        while True:
            i, args = json.loads(domain.Receive())
            args = [] if args is None else args

            # When the channel that feeds the Receive function closes, 0 is returned
            # This is currently automagic and happened to work on accident, but consider
            # a more explicit close
            if i == 0:
                break

            if i in self.deferreds:
                d = self.deferreds[i]
                del self.deferreds[d.cb]
                del self.deferreds[d.eb]

                # Special Python case-- if this is an errback construct an excepction
                if i == d.eb:
                    args = utils.Error(args[0])

                # Deferreds are always emitted by async methods. If the user called .wait()
                # then the deferred instantiates a greenlet as .green. Resume that greenlet.
                # If that greenlet emits another deferred the user has called .wait() again.
                if d.green is not None:
                    d = d.green.switch(args)
                    if d is not None:
                        self.deferreds[d.cb], self.deferreds[d.eb] = d, d

            elif i in self.handlers:
                handler, canReturn = self.handlers[i]

                if canReturn:
                    resultID = args.pop(0)

                # Consolidated handlers into one
                try:
                    ret = handler(*args)
                except Exception as error:
                    # TODO: Differentiate Riffle errors vs. other exceptions,
                    # maybe reraise other exceptions.
                    #
                    # I hard-coded "wamp.error.internal_error" for now because
                    # we cannot tell the cause of the error yet.
                    if canReturn:
                        domain.YieldError(resultID, "Exception in handler", json.dumps([str(error)]))
                    else:
                        print "An exception occured: ", error
                else:
                    if canReturn:
                        ret = [] if ret is None else ret
                        if not isinstance(ret, (list, tuple)):
                            ret = [ret]

                        domain.Yield(resultID, json.dumps(ret))

            # Control messages. These should really just be deferreds, but not implemented yet
            if i in self.control:
                d = greenlet(self.control[i]).switch(*args)

                # If user code called .wait(), this deferred is emitted, waiting on the results of some operation
                if d is not None:
                    self.deferreds[d.cb], self.deferreds[d.eb] = d, d
<commit_msg>Fixed another error type.<commit_after>'''
Top level interface into Riffle library. The crust sits atop the mantle, which sits atop the core.
The mantle is imported here as pymantle. It doesn't add functionality on top of the core, just
translates between coreRiffle and python as needed.

Fixing the osx python version:
    https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Common-Issues.md#python-segmentation-fault-11-on-import-some_python_module
'''

import os
import json

from greenlet import greenlet

import pymantle
from riffle import cumin, utils

class Domain(object):

    def __init__(self, name, superdomain=None):
        self.name = name

        if superdomain is None:
            self.mantleDomain = pymantle.NewDomain(name)
            self.app = App()
        else:
            self.mantleDomain = superdomain.mantleDomain.Subdomain(name)
            self.app = superdomain.app

    def join(self):
        # TODO: convert the "control plane" to use deferreds using Twisted style callbacks
        cb, eb = utils.newID(2)
        self.app.control[cb] = self.onJoin
        self.mantleDomain.Join(cb, eb)

        self.app.mainGreenlet = greenlet(self.app.handle)
        self.app.mainGreenlet.switch(self.mantleDomain)

    def leave(self):
        # TODO: Emit a deferred here (?)
        self.mantleDomain.Leave()

    def onJoin(self):
        pymantle.Info("Default onJoin")

    def onLeave(self):
        pymantle.Info("Default onLeave")

    def subscribe(self, endpoint, handler):
        return self._setHandler(endpoint, handler, self.mantleDomain.Subscribe, False)

    def register(self, endpoint, handler):
        return self._setHandler(endpoint, handler, self.mantleDomain.Register, True)

    def publish(self, endpoint, *args):
        return self._invoke(endpoint, args, self.mantleDomain.Publish, False)

    def call(self, endpoint, *args):
        return self._invoke(endpoint, args, self.mantleDomain.Call, True)

    def _setHandler(self, endpoint, handler, coreFunction, doesReturn):
        '''
        Register or Subscribe. Invokes targetFunction for the given endpoint and handler.

        :param coreFunction: the intended core function, either Subscribe or Register
        :param doesReturn: True if this handler can return a value (is a registration)
        '''

        d, handlerId = Deferred(), utils.newID()
        self.app.deferreds[d.cb], self.app.deferreds[d.eb] = d, d
        self.app.handlers[handlerId] = handler, doesReturn

        coreFunction(endpoint, d.cb, d.eb, handlerId, cumin.reflect(handler))
        return d

    def _invoke(self, endpoint, args, coreFunction, doesReturn):
        '''
        Publish or Call. Invokes targetFunction for the given endpoint and handler.

        :param coreFunction: the intended core function, either Subscribe or Register
        :param doesReturn: True if this handler can receive results (is a call)
        '''

        d = Deferred()
        d.canReturn = doesReturn
        d.mantleDomain = self.mantleDomain
        self.app.deferreds[d.cb], self.app.deferreds[d.eb] = d, d

        coreFunction(endpoint, d.cb, d.eb, cumin.marshall(args))
        return d

class Deferred(object):

    def __init__(self):
        self.cb, self.eb = utils.newID(2)
        self.green = None

        # Boy does it hurt me to put this here. canReturn needs to have access to the domain
        # for call returns
        self.mantleDomain = None

        # True if this deferred should inform the core of its types once set
        # Only true for calls
        self.canReturn = False

    def wait(self, *types):
        ''' Wait until the results of this invocation are resolved '''

        # if canReturn then this is a call. We need to retroactively inform the core of our types
        if self.canReturn:
            self.mantleDomain.CallExpects(self.cb, cumin.prepareSchema(types))

        # Get our current greenlet. If we're not running in a greenlet, someone screwed up bad
        self.green = greenlet.getcurrent()

        # Switch back to the parent greenlet: block until the parent has time to resolve us
        results = self.green.parent.switch(self)

        if isinstance(results, Exception):
            raise results

        r = cumin.unmarshall(results, types)

        # Actually, this cant happen. A return from a function should always come back as
        # a list. Unless you mean a return from... any other thing. In which case, bleh.
        if r is None:
            return r

        return r[0] if len(r) == 1 else r

    def setCallback(self, handler):
        ''' Traditional Twisted style callbacks '''
        pass

class App(object):

    def __init__(self):
        # self.handlers contains a tuple of (function, bool), where the bool is True if the
        # function can return (i.e. is a registration)
        self.deferreds, self.handlers, self.control = {}, {}, {}
        self.mainGreenlet = None

    def handle(self, domain):
        ''' Open connection with the core and begin handling callbacks '''

        while True:
            i, args = json.loads(domain.Receive())
            args = [] if args is None else args

            # When the channel that feeds the Receive function closes, 0 is returned
            # This is currently automagic and happened to work on accident, but consider
            # a more explicit close
            if i == 0:
                break

            if i in self.deferreds:
                d = self.deferreds[i]
                del self.deferreds[d.cb]
                del self.deferreds[d.eb]

                # Special Python case-- if this is an errback construct an excepction
                if i == d.eb:
                    args = utils.Error(args[0])

                # Deferreds are always emitted by async methods. If the user called .wait()
                # then the deferred instantiates a greenlet as .green. Resume that greenlet.
                # If that greenlet emits another deferred the user has called .wait() again.
                if d.green is not None:
                    d = d.green.switch(args)
                    if d is not None:
                        self.deferreds[d.cb], self.deferreds[d.eb] = d, d

            elif i in self.handlers:
                handler, canReturn = self.handlers[i]

                if canReturn:
                    resultID = args.pop(0)

                # Consolidated handlers into one
                try:
                    ret = handler(*args)
                except Exception as error:
                    # TODO: Differentiate Riffle errors vs. other exceptions,
                    # maybe reraise other exceptions.
                    if canReturn:
                        domain.YieldError(resultID, "wamp.error.runtime_error", json.dumps([str(error)]))
                    else:
                        print "An exception occured: ", error
                else:
                    if canReturn:
                        ret = [] if ret is None else ret
                        if not isinstance(ret, (list, tuple)):
                            ret = [ret]

                        domain.Yield(resultID, json.dumps(ret))

            # Control messages. These should really just be deferreds, but not implemented yet
            if i in self.control:
                d = greenlet(self.control[i]).switch(*args)

                # If user code called .wait(), this deferred is emitted, waiting on the results of some operation
                if d is not None:
                    self.deferreds[d.cb], self.deferreds[d.eb] = d, d
<|endoftext|>


<commit_before>import unittest
from robot.output.logger import Logger
from robot.output.readers import ExecutionErrors
import resources
from robot.common.model import BaseTestSuite
import robot.output
from robot.serializing.testoutput import Reporter
import robot.serializing.testoutput

def set_serialize_log_mock():
    results = {'log_path':None}
    def serialize_log(test_output_datamodel, log_path, title=None):
        results['log_path'] = log_path
        results['title'] = title
    robot.serializing.testoutput.serialize_log = serialize_log
    return results

def set_serialize_report_mock():
    results = {'report_path':None}
    def serialize_report(test_output_datamodel, report_path, title=None, background=None, logpath=None):
        results['report_path'] = report_path
        results['title'] = title
        results['background'] = background
        results['logpath'] = logpath
    robot.serializing.testoutput.serialize_report = serialize_report
    return results

def set_process_outputs_mock():
    results = {'paths':None}
    def process_outputs(paths, settings):
        results['paths'] = paths
        results['settings'] = settings
        suite = BaseTestSuite('Suite')
        suite.starttime = 7
        suite.endtime = 42
        return suite, ExecutionErrors(None)
    robot.serializing.testoutput.process_outputs = process_outputs
    return results

class TestReporting(unittest.TestCase):

    def setUp(self):
        self._reporter = Reporter()
        self._settings = {
            'Report': 'NONE',
            'Log': 'NONE',
            'XUnitFile': 'NONE',
            'Output': 'NONE',
            'LogTitle': None,
            'ReportTitle': None,
            'ReportBackground': None,
            'SuiteStatLevel': None,
            'TagStatInclude': None,
            'TagStatExclude': None,
            'TagStatCombine': None,
            'TagDoc': None,
            'TagStatLink': None,
            'SetTag': None,
            'SuiteNames': None,
            'TestNames': None,
            'Include': None,
            'Exclude': None,
            'StartTime': 0,
            'Name': None,
            'Doc': None,
            'Metadata': {},
            'Critical': None,
            'NonCritical': None,
            'NoStatusRC': None,
            'EndTime': 0,
            'LogLevel': 'INFO'
        }
        self._original_logger = robot.serializing.testoutput.LOGGER
        robot.serializing.testoutput.LOGGER = Logger()
        robot.serializing.testoutput.LOGGER.disable_automatic_console_logger()
        self._log_results = set_serialize_log_mock()
        self._report_results = set_serialize_report_mock()
        #self._process_outputs_results = set_process_outputs_mock()

    def tearDown(self):
        robot.serializing.testoutput.LOGGER = self._original_logger

    def test_generate_report_and_log(self):
        self._settings['Log'] = 'log.html'
        self._settings['Report'] = 'report.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_expected_log('log.html')
        self._assert_expected_report('report.html')
        self._assert_log_link_in_report('log.html')

    def test_no_generation(self):
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_no_log()
        self._assert_no_report()

    def test_only_log(self):
        self._settings['Log'] = 'only-log.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_expected_log('only-log.html')
        self._assert_no_report()

    def test_only_report(self):
        self._settings['Report'] = 'reports-only.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_no_log()
        self._assert_expected_report('reports-only.html')
        self._assert_no_log_links_in_report()

    def test_multiple_outputs(self):
        self._settings['Log'] = 'log.html'
        self._settings['Report'] = 'report.html'
        self._reporter.execute_rebot(self._settings, *[resources.GOLDEN_OUTPUT, resources.GOLDEN_OUTPUT2])
        self._assert_expected_log('log.html')
        self._assert_expected_report('report.html')

    def _assert_expected_log(self, expected_file_name):
        self.assertEquals(self._log_results['log_path'], expected_file_name)

    def _assert_expected_report(self, expected_file_name):
        self.assertEquals(self._report_results['report_path'], expected_file_name)

    def _assert_log_link_in_report(self, expected_log_link):
        self.assertEquals(self._report_results['logpath'], expected_log_link)

    def _assert_no_log_links_in_report(self):
        self._assert_log_link_in_report(None)

    def _assert_no_log(self):
        self._assert_expected_log(None)

    def _assert_no_report(self):
        self._assert_expected_report(None)

if __name__ == '__main__':
    unittest.main()
<commit_msg>test fix after adding new setting<commit_after>import unittest
from robot.output.logger import Logger
from robot.output.readers import ExecutionErrors
import resources
from robot.common.model import BaseTestSuite
import robot.output
from robot.serializing.testoutput import Reporter
import robot.serializing.testoutput

def set_serialize_log_mock():
    results = {'log_path':None}
    def serialize_log(test_output_datamodel, log_path, title=None):
        results['log_path'] = log_path
        results['title'] = title
    robot.serializing.testoutput.serialize_log = serialize_log
    return results

def set_serialize_report_mock():
    results = {'report_path':None}
    def serialize_report(test_output_datamodel, report_path, title=None, background=None, logpath=None):
        results['report_path'] = report_path
        results['title'] = title
        results['background'] = background
        results['logpath'] = logpath
    robot.serializing.testoutput.serialize_report = serialize_report
    return results

def set_process_outputs_mock():
    results = {'paths':None}
    def process_outputs(paths, settings):
        results['paths'] = paths
        results['settings'] = settings
        suite = BaseTestSuite('Suite')
        suite.starttime = 7
        suite.endtime = 42
        return suite, ExecutionErrors(None)
    robot.serializing.testoutput.process_outputs = process_outputs
    return results

class TestReporting(unittest.TestCase):

    def setUp(self):
        self._reporter = Reporter()
        self._settings = {
            'Report': 'NONE',
            'Log': 'NONE',
            'XUnitFile': 'NONE',
            'Output': 'NONE',
            'LogTitle': None,
            'ReportTitle': None,
            'ReportBackground': None,
            'SuiteStatLevel': None,
            'TagStatInclude': None,
            'TagStatExclude': None,
            'TagStatCombine': None,
            'TagDoc': None,
            'TagStatLink': None,
            'SetTag': None,
            'SuiteNames': None,
            'TestNames': None,
            'Include': None,
            'Exclude': None,
            'StartTime': 0,
            'Name': None,
            'Doc': None,
            'Metadata': {},
            'Critical': None,
            'NonCritical': None,
            'NoStatusRC': None,
            'RunEmptySuite': False,
            'EndTime': 0,
            'LogLevel': 'INFO'
        }
        self._original_logger = robot.serializing.testoutput.LOGGER
        robot.serializing.testoutput.LOGGER = Logger()
        robot.serializing.testoutput.LOGGER.disable_automatic_console_logger()
        self._log_results = set_serialize_log_mock()
        self._report_results = set_serialize_report_mock()
        #self._process_outputs_results = set_process_outputs_mock()

    def tearDown(self):
        robot.serializing.testoutput.LOGGER = self._original_logger

    def test_generate_report_and_log(self):
        self._settings['Log'] = 'log.html'
        self._settings['Report'] = 'report.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_expected_log('log.html')
        self._assert_expected_report('report.html')
        self._assert_log_link_in_report('log.html')

    def test_no_generation(self):
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_no_log()
        self._assert_no_report()

    def test_only_log(self):
        self._settings['Log'] = 'only-log.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_expected_log('only-log.html')
        self._assert_no_report()

    def test_only_report(self):
        self._settings['Report'] = 'reports-only.html'
        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
        self._assert_no_log()
        self._assert_expected_report('reports-only.html')
        self._assert_no_log_links_in_report()

    def test_multiple_outputs(self):
        self._settings['Log'] = 'log.html'
        self._settings['Report'] = 'report.html'
        self._reporter.execute_rebot(self._settings, *[resources.GOLDEN_OUTPUT, resources.GOLDEN_OUTPUT2])
        self._assert_expected_log('log.html')
        self._assert_expected_report('report.html')

    def _assert_expected_log(self, expected_file_name):
        self.assertEquals(self._log_results['log_path'], expected_file_name)

    def _assert_expected_report(self, expected_file_name):
        self.assertEquals(self._report_results['report_path'], expected_file_name)

    def _assert_log_link_in_report(self, expected_log_link):
        self.assertEquals(self._report_results['logpath'], expected_log_link)

    def _assert_no_log_links_in_report(self):
        self._assert_log_link_in_report(None)

    def _assert_no_log(self):
        self._assert_expected_log(None)

    def _assert_no_report(self):
        self._assert_expected_report(None)

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


<commit_before>from django.test import TestCase

# Import Some Django models that we use

from django.contrib.auth.models import User, Group

# Import some models

from .models import ActivityGenerator
from .models import AssessmentResource
from .models import AssessmentResourceType
from .models import AssessmentStaff
from .models import Campus
from .models import ExternalExaminer
from .models import Staff
from .models import Task
from .models import Activity
from .models import TaskCompletion
from .models import Module
from .models import ModuleSize
from .models import ModuleStaff
from .models import Programme
from .models import ExamTracker
from .models import CourseworkTracker
from .models import Project
from .models import ProjectStaff
from .models import WorkPackage

class AssessmentResourceTestCase(TestCase):
    def setUp(self):
        # Create a workpackage
        package = WorkPackage.objects.create(name="test", startdate="2017-09-01", enddate="2018-08-31")

        # Create a campus
        campus = Campus.objects.create(name="campus")

        # Create a Module Size
        modulesize = ModuleSize.objects.create(text="50", admin_scaling=1.0, assessment_scaling=1.0)

        # Create some Users
        user_aA = User.objects.create(username="academicA")
        user_aB = User.objects.create(username="academicB")
        user_aC = User.objects.create(username="academicC")
        user_aD = User.objects.create(username="academicD")
        user_aE = User.objects.create(username="academicE")

        user_eA = User.objects.create(username="externalA")
        user_eB = User.objects.create(username="externalB")
        user_eC = User.objects.create(username="externalC")

        # Create linked Staff and ExternalExaminers
        coordinator = Staff.objects.create(user=user_aA)
        team_member = Staff.objects.create(user=user_aB)
        resource_owner = Staff.objects.create(user=user_aC)
        moderator = Staff.objects.create(user=user_aD)
        other_staff = Staff.objects.create(user=user_aE)

        lead_examiner = ExternalExaminer.objects.create(user=user_eA)
        associated_examiner = ExternalExaminer.objects.create(user=user_eB)
        other_examiner = ExternalExaminer.objects.create(user=user_eC)

        # Create some programmes
        lead_programme = Programme.objects.create(
                programme_code="123",
                programme_name="BSc Breaking Things",
                package=package)

        lead_programme.examiners.add(lead_examiner)
        lead_programme.save()

        other_programme = Programme.objects.create(
                programme_code="456",
                programme_name="MSc Breaking Things",
                package=package)

        other_programme.examiners.add(associated_examiner)
        other_programme.save()

        # Create a module with staffA as coordinator and staff
        module = Module.objects.create(module_code="ABC101",
                module_name="Breaking Things",
                package=package,
                coordinator=coordinator,
                lead_programme=lead_programme,
                campus=campus,
                size=modulesize)

        module.moderators.add(moderator)
        module.programmes.add(lead_programme)
        module.programmes.add(other_programme)
        module.save()

        # and staffB on teaching team
        ModuleStaff.objects.create(
                module=module,
                staff=team_member,
                contact_proportion=50,
                admin_proportion=50,
                assessment_proportion=50,
                package=package)

        # Create an AssessmentResourceType
        resource_type = AssessmentResourceType.objects.create(name="exam")

        # Create a resource, with staffC as an owner
        resource = AssessmentResource.objects.create(
                name="test",
                module=module,
                owner=resource_owner,
                resource_type=resource_type)

    def test_resource_coordinator_permissions(self):

        coordinator = Staff.objects.get(user__username="academicA")
        resource = AssessmentResource.objects.get(name="test")

        # Coordinators should be able to download
        self.assertEqual(resource.is_downloadable_by(coordinator), True)
        self.assertEqual(resource.is_downloadable_by_staff(coordinator), True)
        self.assertEqual(resource.is_downloadable_by_external(coordinator), False)

    def test_resource_team_permissions(self):

        team_member = Staff.objects.get(user__username="academicB")
        resource = AssessmentResource.objects.get(name="test")

        # Team members should be able to download
        self.assertEqual(resource.is_downloadable_by(team_member), True)
        self.assertEqual(resource.is_downloadable_by_staff(team_member), True)
        self.assertEqual(resource.is_downloadable_by_external(team_member), False)

    def test_resource_moderator_permissions(self):

        moderator = Staff.objects.get(user__username="academicD")
        resource = AssessmentResource.objects.get(name="test")

        # Moderators should be able to download
        self.assertEqual(resource.is_downloadable_by(moderator), True)
        self.assertEqual(resource.is_downloadable_by_staff(moderator), True)
        self.assertEqual(resource.is_downloadable_by_external(moderator), False)

    def test_resource_owner_permissions(self):

        owner = Staff.objects.get(user__username="academicC")
        resource = AssessmentResource.objects.get(name="test")

        # Owners should be able to download
        self.assertEqual(resource.is_downloadable_by(owner), True)
        self.assertEqual(resource.is_downloadable_by_staff(owner), True)
        self.assertEqual(resource.is_downloadable_by_external(owner), False)

    def test_resource_other_staff_permissions(self):

        other = Staff.objects.get(user__username="academicE")
        resource = AssessmentResource.objects.get(name="test")

        # Others should NOT be able to download
        self.assertEqual(resource.is_downloadable_by(other), False)
        self.assertEqual(resource.is_downloadable_by_staff(other), False)
        self.assertEqual(resource.is_downloadable_by_external(other), False)

    def test_resource_lead_examiner_permissions(self):

        lead_examiner = ExternalExaminer.objects.get(user__username="externalA")
        resource = AssessmentResource.objects.get(name="test")

        # Lead Examiners should be able to download
        self.assertEqual(resource.is_downloadable_by(lead_examiner), True)
        self.assertEqual(resource.is_downloadable_by_staff(lead_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(lead_examiner), True)

    def test_resource_associate_examiner_permissions(self):

        associate_examiner = ExternalExaminer.objects.get(user__username="externalB")
        resource = AssessmentResource.objects.get(name="test")

        # Lead Examiners should be able to download
        self.assertEqual(resource.is_downloadable_by(associate_examiner), True)
        self.assertEqual(resource.is_downloadable_by_staff(associate_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(associate_examiner), True)

    def test_resource_other_examiner_permissions(self):

        other_examiner = ExternalExaminer.objects.get(user__username="externalC")
        resource = AssessmentResource.objects.get(name="test")

        # Other Examiners should not be able to download
        self.assertEqual(resource.is_downloadable_by(other_examiner), False)
        self.assertEqual(resource.is_downloadable_by_staff(other_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(other_examiner), False)

<commit_msg>Check assessment staff can download<commit_after>from django.test import TestCase

# Import Some Django models that we use

from django.contrib.auth.models import User, Group

# Import some models

from .models import ActivityGenerator
from .models import AssessmentResource
from .models import AssessmentResourceType
from .models import AssessmentStaff
from .models import Campus
from .models import ExternalExaminer
from .models import Staff
from .models import Task
from .models import Activity
from .models import TaskCompletion
from .models import Module
from .models import ModuleSize
from .models import ModuleStaff
from .models import Programme
from .models import ExamTracker
from .models import CourseworkTracker
from .models import Project
from .models import ProjectStaff
from .models import WorkPackage

class AssessmentResourceTestCase(TestCase):
    def setUp(self):
        # Create a workpackage
        package = WorkPackage.objects.create(name="test", startdate="2017-09-01", enddate="2018-08-31")

        # Create a campus
        campus = Campus.objects.create(name="campus")

        # Create a Module Size
        modulesize = ModuleSize.objects.create(text="50", admin_scaling=1.0, assessment_scaling=1.0)

        # Create some Users
        user_aA = User.objects.create(username="academicA")
        user_aB = User.objects.create(username="academicB")
        user_aC = User.objects.create(username="academicC")
        user_aD = User.objects.create(username="academicD")
        user_aE = User.objects.create(username="academicE")

        user_aF = User.objects.create(username="assessmentstaffA")

        user_eA = User.objects.create(username="externalA")
        user_eB = User.objects.create(username="externalB")
        user_eC = User.objects.create(username="externalC")

        # Create linked Staff and ExternalExaminers
        coordinator = Staff.objects.create(user=user_aA)
        team_member = Staff.objects.create(user=user_aB)
        resource_owner = Staff.objects.create(user=user_aC)
        moderator = Staff.objects.create(user=user_aD)
        other_staff = Staff.objects.create(user=user_aE)

        assessment_staff = Staff.objects.create(user=user_aF)

        lead_examiner = ExternalExaminer.objects.create(user=user_eA)
        associated_examiner = ExternalExaminer.objects.create(user=user_eB)
        other_examiner = ExternalExaminer.objects.create(user=user_eC)

        # Add the user to AssessmentStaff
        AssessmentStaff.objects.create(staff=assessment_staff, package=package)

        # Create some programmes
        lead_programme = Programme.objects.create(
                programme_code="123",
                programme_name="BSc Breaking Things",
                package=package)

        lead_programme.examiners.add(lead_examiner)
        lead_programme.save()

        other_programme = Programme.objects.create(
                programme_code="456",
                programme_name="MSc Breaking Things",
                package=package)

        other_programme.examiners.add(associated_examiner)
        other_programme.save()

        # Create a module with staffA as coordinator and staff
        module = Module.objects.create(module_code="ABC101",
                module_name="Breaking Things",
                package=package,
                coordinator=coordinator,
                lead_programme=lead_programme,
                campus=campus,
                size=modulesize)

        module.moderators.add(moderator)
        module.programmes.add(lead_programme)
        module.programmes.add(other_programme)
        module.save()

        # and staffB on teaching team
        ModuleStaff.objects.create(
                module=module,
                staff=team_member,
                contact_proportion=50,
                admin_proportion=50,
                assessment_proportion=50,
                package=package)

        # Create an AssessmentResourceType
        resource_type = AssessmentResourceType.objects.create(name="exam")

        # Create a resource, with staffC as an owner
        resource = AssessmentResource.objects.create(
                name="test",
                module=module,
                owner=resource_owner,
                resource_type=resource_type)

    def test_resource_coordinator_permissions(self):

        coordinator = Staff.objects.get(user__username="academicA")
        resource = AssessmentResource.objects.get(name="test")

        # Coordinators should be able to download
        self.assertEqual(resource.is_downloadable_by(coordinator), True)
        self.assertEqual(resource.is_downloadable_by_staff(coordinator), True)
        self.assertEqual(resource.is_downloadable_by_external(coordinator), False)

    def test_resource_team_permissions(self):

        team_member = Staff.objects.get(user__username="academicB")
        resource = AssessmentResource.objects.get(name="test")

        # Team members should be able to download
        self.assertEqual(resource.is_downloadable_by(team_member), True)
        self.assertEqual(resource.is_downloadable_by_staff(team_member), True)
        self.assertEqual(resource.is_downloadable_by_external(team_member), False)

    def test_resource_moderator_permissions(self):

        moderator = Staff.objects.get(user__username="academicD")
        resource = AssessmentResource.objects.get(name="test")

        # Moderators should be able to download
        self.assertEqual(resource.is_downloadable_by(moderator), True)
        self.assertEqual(resource.is_downloadable_by_staff(moderator), True)
        self.assertEqual(resource.is_downloadable_by_external(moderator), False)

    def test_resource_owner_permissions(self):

        owner = Staff.objects.get(user__username="academicC")
        resource = AssessmentResource.objects.get(name="test")

        # Owners should be able to download
        self.assertEqual(resource.is_downloadable_by(owner), True)
        self.assertEqual(resource.is_downloadable_by_staff(owner), True)
        self.assertEqual(resource.is_downloadable_by_external(owner), False)

    def test_resource_assessment_staff_permissions(self):

        assessment_staff = Staff.objects.get(user__username="assessmentstaffA")
        resource = AssessmentResource.objects.get(name="test")

        # Assessment staff should be able to download
        self.assertEqual(resource.is_downloadable_by(assessment_staff), True)
        self.assertEqual(resource.is_downloadable_by_staff(assessment_staff), True)
        self.assertEqual(resource.is_downloadable_by_external(assessment_staff), False)

    def test_resource_other_staff_permissions(self):

        other = Staff.objects.get(user__username="academicE")
        resource = AssessmentResource.objects.get(name="test")

        # Others should NOT be able to download
        self.assertEqual(resource.is_downloadable_by(other), False)
        self.assertEqual(resource.is_downloadable_by_staff(other), False)
        self.assertEqual(resource.is_downloadable_by_external(other), False)

    def test_resource_lead_examiner_permissions(self):

        lead_examiner = ExternalExaminer.objects.get(user__username="externalA")
        resource = AssessmentResource.objects.get(name="test")

        # Lead Examiners should be able to download
        self.assertEqual(resource.is_downloadable_by(lead_examiner), True)
        self.assertEqual(resource.is_downloadable_by_staff(lead_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(lead_examiner), True)

    def test_resource_associate_examiner_permissions(self):

        associate_examiner = ExternalExaminer.objects.get(user__username="externalB")
        resource = AssessmentResource.objects.get(name="test")

        # Lead Examiners should be able to download
        self.assertEqual(resource.is_downloadable_by(associate_examiner), True)
        self.assertEqual(resource.is_downloadable_by_staff(associate_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(associate_examiner), True)

    def test_resource_other_examiner_permissions(self):

        other_examiner = ExternalExaminer.objects.get(user__username="externalC")
        resource = AssessmentResource.objects.get(name="test")

        # Other Examiners should not be able to download
        self.assertEqual(resource.is_downloadable_by(other_examiner), False)
        self.assertEqual(resource.is_downloadable_by_staff(other_examiner), False)
        self.assertEqual(resource.is_downloadable_by_external(other_examiner), False)

<|endoftext|>


<commit_before>import logging
import os

class JobProvider:
    def __init__(self):
        pass

    def done(self):
        raise NotImplementedError

    def obtain(self):
        raise NotImplementedError

    def release(self, id, return_code, output, task):
        raise NotImplementedError

    def work_left(self):
        raise NotImplementedError

class SimpleJobProvider(JobProvider):
    def __init__(self, config):
        self.__workdir = config.get('workdir', os.getcwd())
        self.__stageoutdir = config.get('stageout location', os.getcwd())
        self.__cmd = config.get('cmd')
        self.__max = config.get('max')
        self.__inputs = config.get('inputs', [])
        self.__outputs = config.get('outputs', [])
        self.__done = 0
        self.__running = 0
        self.__id = 0

        if not os.path.exists(self.__stageoutdir):
            os.makedirs(self.__stageoutdir)

    def done(self):
        return self.__done == self.__max

    def obtain(self, num=1, bijective=False):
        if bijective:
            raise NotImplementedError

        tasks = []

        for i in range(num):
            if self.__id < self.__max:
                self.__running += 1
                self.__id += 1

                inputs = [(x, x) for x in self.__inputs]
                outputs = [(os.path.join(self.__stageoutdir, x.replace(x, '%s_%s' % (self.__id, x))), x) for x in self.__outputs]

                logging.info("creating {0}".format(self.__id))
                tasks.append((str(self.__id), self.__cmd, inputs, outputs))
            else:
                break

        return tasks

    def release(self, tasks):
        for task in tasks:
            self.__running -= 1
            if task.return_status == 0:
                self.__done += 1
            logging.info("job {0} returned with return code {1} [{2} jobs finished / {3} total ]".format(id, task.return_status, self.__done, self.__max))

    def work_left(self):
        return self.__max - self.__done
<commit_msg>Return output from simple jobs, if available.<commit_after>import logging
import os

class JobProvider:
    def __init__(self):
        pass

    def done(self):
        raise NotImplementedError

    def obtain(self):
        raise NotImplementedError

    def release(self, id, return_code, output, task):
        raise NotImplementedError

    def work_left(self):
        raise NotImplementedError

class SimpleJobProvider(JobProvider):
    def __init__(self, config):
        self.__workdir = config.get('workdir', os.getcwd())
        self.__stageoutdir = config.get('stageout location', os.getcwd())
        self.__cmd = config.get('cmd')
        self.__max = config.get('max')
        self.__inputs = config.get('inputs', [])
        self.__outputs = config.get('outputs', [])
        self.__done = 0
        self.__running = 0
        self.__id = 0

        if not os.path.exists(self.__stageoutdir):
            os.makedirs(self.__stageoutdir)

    def done(self):
        return self.__done == self.__max

    def obtain(self, num=1, bijective=False):
        if bijective:
            raise NotImplementedError

        tasks = []

        for i in range(num):
            if self.__id < self.__max:
                self.__running += 1
                self.__id += 1

                inputs = [(x, x) for x in self.__inputs]
                outputs = [(os.path.join(self.__stageoutdir, x.replace(x, '%s_%s' % (self.__id, x))), x) for x in self.__outputs]

                logging.info("creating {0}".format(self.__id))
                tasks.append((str(self.__id), self.__cmd, inputs, outputs))
            else:
                break

        return tasks

    def release(self, tasks):
        for task in tasks:
            self.__running -= 1
            if task.return_status == 0:
                self.__done += 1
            logging.info("job {0} returned with return code {1} [{2} jobs finished / {3} total ]".format(task.tag, task.return_status, self.__done, self.__max))

            if task.output:
                f = gzip.open(os.path.join(self.__stageoutdir, task.tag+'_job.log.gz'), 'wb')
                f.write(task.output)
                f.close()

    def work_left(self):
        return self.__max - self.__done
<|endoftext|>


<commit_before>import time, shelve
arguments = ["self", "info", "args"]
minlevel = 1
helpstring = "points [nick] [+/-number] or points [+/-]list or points -flush"

def main(connection, info, args) :
    points = shelve.open("points.db", writeback=True)
    if not points.has_key("users") :
        points["users"] = {}
        points.sync()
        points["userlist"] = []
        points.sync()
    if info["sender"].lower() not in points["users"].keys() :
        points["users"][info["sender"].lower()] = {"points":0, "outward":{}}
        points.sync()
        if len(args) == 3 :
            points["users"][info["sender"].lower()]["outward"][args[1].lower()] = 0
            points.sync()
    if len(args) > 1 :

        if not args[1].startswith("-") and not args[1].startswith("+") :
            if args[1].lower() not in points["users"].keys() :
                points["users"][args[1].lower()] = {"points":0, "outward":{}}
                points.sync()
            if len(args) == 3 :
                if args[1].lower() not in points["users"][info["sender"].lower()]["outward"] :
                    points["users"][info["sender"].lower()]["outward"][args[1].lower()] = 0
                if args[1].lower() != info["sender"].lower() and points["users"][info["sender"].lower()]["outward"][args[1].lower()] < time.time() :
                    sendpoints = int(args[2])
                    if sendpoints != 0 :
                        if sendpoints < -2 :
                            sendpoints = -2
                        elif sendpoints > 2 :
                            sendpoints = 2
                        points["users"][args[1].lower()]["points"] += sendpoints
                        points.sync()
                        givetake = ["given", "to"]
                        if sendpoints < 0 :
                            points["users"][info["sender"].lower()]["points"] -= 5
                            points.sync()
                            givetake = ["taken", "from"]
                            connection.ircsend(info["channel"], "%s: You have lost 5 points in the proccess of taking points from others" % (info["sender"]))
                        points["users"][info["sender"].lower()]["outward"][args[1].lower()] = time.time() + (60*60*24)
                        points.sync()
                        connection.ircsend(info["channel"], "%s: You have %s %s point(s) %s %s" % (info["sender"], givetake[0], str(abs(sendpoints)), givetake[1], args[1]))
                        if args[1].lower() not in points["userlist"] : points["userlist"].append(args[1].lower())
                    else :
                        connection.ircsend(info["channel"], "%s: %s has %s point(s)." % (info["sender"], args[1], str(points["users"][args[1].lower()]["points"])))

                else : connection.ircsend(info["channel"], "%s: Sorry, but you can't give/take point(s) away from that person (at this time)." % (info["sender"]))
            if len(args) == 2 :
                connection.ircsend(info["channel"], "%s: %s has %s point(s)." % (info["sender"], args[1], str(points["users"][args[1].lower()]["points"])))
        else :
            if args[1] == "-reset" :
                points["user"][info["sender"].lower()]["points"] = 0
                points.sync()
                connection.ircsend(info["channel"], "%s: Your points have been reset to 0" % (info["sender"]))
            elif args[1].endswith("list") :
                pointslist = [points["users"][x]["points"] for x in points["userlist"]]
                pointslist.sort()
                userlist = points["userlist"]
                if args[1].startswith("+") :
                    pointslist.reverse()
                    userlist.reverse()
                pointslist = pointslist[:5]
                finalistlist = userlist
                winnerlist = []
                for point in pointslist :
                    winner = [x for x in finalistlist if points["users"][x]["points"] == point and x not in winnerlist][0]
                    winnerlist.append(winner)
                connection.ircsend(info["channel"], "\n".join(["%s %s with %s point(s)" % (str(winnerlist.index(winner) + 1), winner, points["users"][winner]["points"]) for winner in winnerlist]))

    else :
        connection.ircsend(info["channel"], "%s: You have %s point(s)." % (info["sender"], str(points["users"][info["sender"].lower()]["points"])))
    points.close()
<commit_msg>Added features to ignore users, limit number of point givings per day, and comparisons to points.py<commit_after>import time, shelve
arguments = ["self", "info", "args"]
minlevel = 1
helpstring = "points [nick] [+/-number] or points [+/-]list or points -flush"
maxperday = 2
def main(connection, info, args) :
    points = shelve.open("points.db", writeback=True)
    if not points.has_key("users") :
        points["users"] = {}
        points.sync()
        points["userlist"] = []
        points.sync()
    if not points.has_key("ignored") :
        points["ignored"] = {"nicks":[], "hostnames":[]}
        points.sync()
    if info["sender"].lower() not in points["users"].keys() :
        points["users"][info["sender"].lower()] = {"points":0, "outward":{}}
        points.sync()
        if len(args) == 3 :
            points["users"][info["sender"].lower()]["outward"][args[1].lower()] = 0
            points.sync()
    if len(args) > 1 :

        if not args[1].startswith("-") and not args[1].startswith("+") :
            if args[1].lower() not in points["users"].keys() :
                points["users"][args[1].lower()] = {"points":0, "outward":{}}
                points.sync()
            if len(args) == 3 :
                if args[1].lower() not in points["users"][info["sender"].lower()]["outward"] :
                    points["users"][info["sender"].lower()]["outward"][args[1].lower()] = 0
                if args[1].lower() != info["sender"].lower() and points["users"][info["sender"].lower()]["outward"][args[1].lower()] < time.time() and verify(info["sender"].lower(), points) and info["sender"].lower() not in points["ignored"]["nicks"] and info["hostname"] not in points["ignored"]["hostnames"]:
                    sendpoints = int(args[2])
                    if sendpoints != 0 :
                        if sendpoints < -2 :
                            sendpoints = -2
                        elif sendpoints > 2 :
                            sendpoints = 2
                        points["users"][args[1].lower()]["points"] += sendpoints
                        points.sync()
                        givetake = ["given", "to"]
                        if sendpoints < 0 :
                            points["users"][info["sender"].lower()]["points"] -= 5
                            points.sync()
                            givetake = ["taken", "from"]
                            connection.ircsend(info["channel"], "%s: You have lost 5 points in the proccess of taking points from others" % (info["sender"]))
                        points["users"][info["sender"].lower()]["outward"][args[1].lower()] = time.time() + (60*60*24)
                        points.sync()
                        connection.ircsend(info["channel"], "%s: You have %s %s point(s) %s %s" % (info["sender"], givetake[0], str(abs(sendpoints)), givetake[1], args[1]))
                        if args[1].lower() not in points["userlist"] : points["userlist"].append(args[1].lower())
                    else :
                        connection.ircsend(info["channel"], "%s: %s has %s point(s)." % (info["sender"], args[1], str(points["users"][args[1].lower()]["points"])))

                else : connection.ircsend(info["channel"], "%s: Sorry, but you can't give/take point(s) away from that person (at this time)." % (info["sender"]))
            if len(args) == 2 :
                connection.ircsend(info["channel"], "%s: %s has %s point(s)." % (info["sender"], args[1], str(points["users"][args[1].lower()]["points"])))
        else :
            if args[1] == "-reset" :
                points["user"][info["sender"].lower()]["points"] = 0
                points.sync()
                connection.ircsend(info["channel"], "%s: Your points have been reset to 0" % (info["sender"]))
            elif args[1].endswith("list") :
                pointslist = [points["users"][x]["points"] for x in points["userlist"]]
                pointslist.sort()
                userlist = points["userlist"]
                if args[1].startswith("+") :
                    pointslist.reverse()
                    userlist.reverse()
                pointslist = pointslist[:int(args[2])]
                finalistlist = userlist
                winnerlist = []
                for point in pointslist :
                    winner = [x for x in finalistlist if points["users"][x]["points"] == point and x not in winnerlist][0]
                    winnerlist.append(winner)
                connection.ircsend(info["channel"], "\n".join(["%s %s with %s point(s)" % (str(winnerlist.index(winner) + 1), winner, points["users"][winner]["points"]) for winner in winnerlist]))
            elif args[1].endswith("compare") :
                if args[2].lower() not in points["users"].keys() :
                    points["users"][args[2].lower()] = {"points":0, "outward":{}}
                    points.sync()
                if args[3].lower() not in points["users"].keys() :
                    points["users"][args[3].lower()] = {"points":0, "outward":{}}
                    points.sync()
                connection.ircsend(info["channel"], "%s has % point(s) while %s has %s point(s).  This means %s" (args[2].lower(), str(points["users"][args[2].lower()]["points"]), args[3].lower(), str(points["users"][args[3].lower()]["points"]), compare(points, args[2].lower(), args[3].lower())))
            elif args[1].endswith("ignore") :
                if args[2].lower() not in points["ignored"]["nicks"] :
                    points["ignored"]["nicks"].append(args[2].lower())
                    points.sync()
                if args[2] in connection.nicks.keys() :
                    if connection.nicks[args[2]] not in points["ignored"]["hostnames"] :
                        points["ignored"]["hostnames"].append(connection.nicks[args[2]])
                        points.sync()
                connection.ircsend(info["channel"], "%s will now be ignored" % (args[2]))
    else :
        connection.ircsend(info["channel"], "%s: You have %s point(s)." % (info["sender"], str(points["users"][info["sender"].lower()]["points"])))
    points.close()

def verify(sender) :
    history = [points["users"][sender]["outward"][x] for x in points["users"][sender]["outward"].keys()]
    history.sort()
    if len(history) in [0, 1] :
        return True
    elif time.time() >  history[-2] : return True
    else : return False

def compare(points, user1, user2) :
    if points["users"][user1]["points"] == points["users"][user2]["points"] :
        return "it is a tie"
    elif points["users"][user1]["points"] > points["users"][user2]["points"] :
        return "%s has %s more points over %s" % (user1, user2, str(points["users"][user1]["points"] - points["users"][user2]["points"]))
    elif points["users"][user1]["points"] > points["users"][user2]["points"] :
        return "%s has %s more points over %s" % (user2, user1, str(points["users"][user2]["points"] - points["users"][user1]["points"]))

<|endoftext|>


<commit_before>from django.conf.urls import include, url
from rest_framework import routers
import rest_framework.authtoken.views as drf_views

from workflows.api import views

router = routers.DefaultRouter()

router.register(r'inputs', views.InputViewSet, base_name='input')
router.register(r'abstract_inputs', views.AbstractInputViewSet, base_name='abstractinput')
router.register(r'abstract_outputs', views.AbstractOutputViewSet, base_name='abstractoutput')
router.register(r'outputs', views.OutputViewSet, base_name='output')
router.register(r'users', views.UserViewSet, base_name='user')
router.register(r'streams', views.StreamViewSet, base_name='stream')
router.register(r'workflows', views.WorkflowViewSet, base_name='workflow')
router.register(r'widgets', views.WidgetViewSet, base_name='widget')
router.register(r'connections', views.ConnectionViewSet, base_name='connection')
router.register(r'inputs', views.InputViewSet, base_name='input')
router.register(r'outputs', views.OutputViewSet, base_name='output')
router.register(r'widget-library', views.CategoryViewSet, base_name='widget-library')

urlpatterns = [
   url(r'^widget-library/$', views.widget_library, name='widget-library'),
   # url(r'^register/$', views.user_register, name='api-user-register'),
   # url(r'^login/$', views.user_login, name='api-user-login'),
   # url(r'^logout/$', views.user_logout, name='api-user-logout'),
   url(r'^recommender-model/$', views.recommender_model, name='recommender-model'),
   url(r'^auth/users/create/$', views.user_register, name='user-create'),

   url(r'^', include(router.urls)),
   # url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
   # url(r'^api-token-auth/', drf_views.obtain_auth_token),
   url(r'^auth/', include('djoser.urls')),
   url(r'^auth/', include('djoser.urls.authtoken')),

]
<commit_msg>Add sentry debug url<commit_after>from django.conf.urls import include, url
from rest_framework import routers
import rest_framework.authtoken.views as drf_views

from workflows.api import views

router = routers.DefaultRouter()

def trigger_error(request):
    division_by_zero = 1 / 0

router.register(r'inputs', views.InputViewSet, base_name='input')
router.register(r'abstract_inputs', views.AbstractInputViewSet, base_name='abstractinput')
router.register(r'abstract_outputs', views.AbstractOutputViewSet, base_name='abstractoutput')
router.register(r'outputs', views.OutputViewSet, base_name='output')
router.register(r'users', views.UserViewSet, base_name='user')
router.register(r'streams', views.StreamViewSet, base_name='stream')
router.register(r'workflows', views.WorkflowViewSet, base_name='workflow')
router.register(r'widgets', views.WidgetViewSet, base_name='widget')
router.register(r'connections', views.ConnectionViewSet, base_name='connection')
router.register(r'inputs', views.InputViewSet, base_name='input')
router.register(r'outputs', views.OutputViewSet, base_name='output')
router.register(r'widget-library', views.CategoryViewSet, base_name='widget-library')

urlpatterns = [
   url(r'^widget-library/$', views.widget_library, name='widget-library'),
   # url(r'^register/$', views.user_register, name='api-user-register'),
   # url(r'^login/$', views.user_login, name='api-user-login'),
   # url(r'^logout/$', views.user_logout, name='api-user-logout'),
   url(r'^recommender-model/$', views.recommender_model, name='recommender-model'),
   url(r'^auth/users/create/$', views.user_register, name='user-create'),

   url(r'^', include(router.urls)),
   # url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
   # url(r'^api-token-auth/', drf_views.obtain_auth_token),
   url(r'^auth/', include('djoser.urls')),
   url(r'^auth/', include('djoser.urls.authtoken')),
   url(r'^sentry-error/', trigger_error),

]
<|endoftext|>


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

from __future__ import division, print_function

"""
This script generates a file to use for building authorea papers, and then runs
latex on them.

Requires python >= 2.6 (3.x should work, too)

The key assumptions are:
* ``layout.md`` exists in the article (I think it always does in Authorea)
* ``preamble.tex`` and/or``header.tex``, ``title.tex``, and
  ``bibliobgraphy/biblio.bib`` exist (I think these are created normally in a
    new Authorea article)
* ``posttitle.tex`` exists.  This one you'll have to create, containing
  everything you want after the title but before the beginning of the document.
  If it doesn't exist that's ok too, it'll just be ignored.
* Figures are in the ``figures`` directory (where authorea puts them), and in
  the correct place in the ``layout.md`` file.  A file named 'latexfigopts.json'
  can specify a dictionary for additional figure options (see the
  `FIGURE_DEFAULTS` below for what options that can be replaced).

"""

import os
import subprocess

#lots of dobule-{}'s are here because we use it as a formatting template below
MAIN_TEMPLATE = r"""
{preamblein}

{headerin}

\begin{{document}}

{titlecontent}

{sectioninputs}

\bibliography{{{bibloc}}}{{}}

\end{{document}}
"""

FIGURE_TEMPLATE = r"""
\begin{<figure_env>}[<placement>]
\begin{center}
\includegraphics[width=<width>]{<figfn>}
<caption>
\end{center}
\end{<figure_env>}
""".replace('{', '{{').replace('}', '}}').replace('<', '{').replace('>', '}')

FIGURE_DEFAULTS = {'placement': '', 'width': '1\columnwidth', 'figure_env': 'figure'}

def get_input_string(filename, localdir, quotepath=True):
    if filename.endswith('.tex'):
        filename = filename[:-4]
    if quotepath:
        quote_chr = '"'
    else:
        quote_chr = ''
    return r'\input{' + quote_chr + os.path.join(localdir, filename) + quote_chr + '}'

def get_figure_string(filename, localdir):
    import json

    figdir, figfn = os.path.split(filename)
    figdir = os.path.join(localdir, figdir)

    figfnbase = os.path.splitext(figfn)[0]
    figfn = os.path.join(figdir, figfn)
    pdffn = os.path.join(figdir, figfnbase + '.pdf')
    epsfn = os.path.join(figdir, figfnbase + '.eps')

    if not os.path.exists(pdffn):
        pdffn = None
    if not os.path.exists(epsfn):
        epsfn = None

    if pdffn or epsfn:
        figfn = os.path.join(figdir, figfnbase)

    capfn = os.path.join(figdir, 'caption.tex')
    if os.path.exists(capfn):
        caption = r'\caption{ \protect\input{' + capfn + '}}'
    else:
        caption = ''

    optsfn = os.path.join(figdir, 'latexfigopts.json')
    figopts = FIGURE_DEFAULTS.copy()
    if os.path.exists(optsfn):
        with open(optsfn) as f:
            optsjson = json.load(f)
        if not isinstance(optsjson, dict):
            raise ValueError('File "{0}" does not have a top-level dict'.format(optsfn))
        for k in FIGURE_DEFAULTS.keys():
            v = optsjson.pop(k, None)
            if v is not None:
                figopts[k] = v
        if len(optsjson) != 0:
            raise ValueError('Entries in "{0}" that were not understood: {1}'.format(optsfn, optsjson))

    figopts.update(locals())
    return FIGURE_TEMPLATE.format(**figopts)

# if builddir and local dir are the same set the paths used for input/include in
# the output tex file to be relative paths (this helps if sharing the created
# tex file with collaborator who don't use this script) otherwise use absolute
# paths. Alternatively, use the user defined option for whether relative or
# absolute paths are used.
def get_in_path(localdir, builddir, pathtype=None):
    """
    Figure out the path for a file in ``localdir`` relative to ``builddir``, or
    just use an absolute path dependeing on whether ``pathtype`` is 'rel' or
    'abs'. Or if it is None, pick relative only if ``localdir`` is the same as
    ``builddir``.
    """
    if pathtype == 'rel':
        return os.path.relpath(localdir, builddir)
    elif pathtype == 'abs':
        return os.path.abspath(localdir)
    elif pathtype is None:
        if builddir == localdir:
            return os.path.relpath(localdir, builddir)
        else:
            return os.path.abspath(localdir)
    else:
        raise ValueError('Invalid pathtype: "{0}"'.format(pathtype))

def build_authorea_latex(localdir, builddir, latex_exec, bibtex_exec, outname,
                         usetitle, dobibtex, npostbibcalls, openwith, titleinput,
                         dobuild, pathtype):
    if not os.path.exists(builddir):
        os.mkdir(builddir)

    if not os.path.isdir(builddir):
        raise IOError('Requested build directory {0} is a file, not a '
                      'directory'.format(builddir))

    # generate the main tex file as a string
    if os.path.exists(os.path.join(localdir, 'preamble.tex')):
        preamblein = get_input_string('preamble', get_in_path(localdir, builddir, pathtype))
    else:
        preamblein = ''
    if os.path.exists(os.path.join(localdir, 'header.tex')):
        headerin = get_input_string('header', get_in_path(localdir, builddir, pathtype))
    else:
        headerin = ''

    if not headerin and not preamblein:
        print("Neither preable nor header found!  Proceeding, but that's rather weird")

    bibloc = os.path.join(get_in_path(localdir, builddir, pathtype), 'bibliography', 'biblio')

    titlecontent = []
    if usetitle:
        if titleinput:
            titlestr = get_input_string('title', get_in_path(localdir, builddir, pathtype))
        else:
            with open(os.path.join(get_in_path(localdir, builddir, 'abs'), 'title.tex')) as f:
                titlestr = f.read()
        titlecontent.append(r'\title{' + titlestr + '}')

    sectioninputs = []
    with open(os.path.join(localdir, 'layout.md')) as f:
        for l in f:
            ls = l.strip()
            if ls == '':
                pass
            elif ls in ('posttitle.tex', 'title.tex', 'preamble.tex', 'header.tex'):
                pass # skip any that have been processed above
            elif ls in ('abstract.tex'):
                # add abstract to title content
                titlecontent.append(r'\begin{abstract}' + get_input_string('abstract', get_in_path(localdir, builddir, pathtype))  + '\end{abstract}')
            elif ls.endswith('.html') or ls.endswith('.htm'):
                pass  # html files aren't latex-able
            elif ls.startswith('figures'):
                sectioninputs.append(get_figure_string(ls, get_in_path(localdir, builddir, pathtype)))
            else:
                sectioninputs.append(get_input_string(ls, get_in_path(localdir, builddir, pathtype)))
    sectioninputs = '\n'.join(sectioninputs)

    if os.path.exists(os.path.join(get_in_path(localdir, builddir, pathtype), 'posttitle.tex')):
        titlecontent.append(get_input_string('posttitle', get_in_path(localdir, builddir, pathtype)))
    titlecontent = '\n'.join(titlecontent)

    maintexstr = MAIN_TEMPLATE.format(**locals())

    #now save that string out as a file
    outname = outname if outname.endswith('.tex') else (outname + '.tex')
    outtexpath = os.path.join(builddir, outname)
    with open(outtexpath, 'w') as f:
        f.write(maintexstr)

    if outname.endswith('.tex'):
        outname = outname[:-4]

    if dobuild:
        #now actually run latex/bibtex
        args = [latex_exec, outname + '.tex']
        print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
        subprocess.check_call(args, cwd=builddir)
        if dobibtex:
            args = [bibtex_exec, outname]
            print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
            subprocess.check_call(args, cwd=builddir)
        for _ in range(npostbibcalls):
            args = [latex_exec, outname + '.tex']
            print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
            subprocess.check_call(args, cwd=builddir)

        #launch the result if necessary
        resultfn = outtexpath[:-4] + ('.pdf' if 'pdf' in latex_exec else '.dvi')
        if openwith:
            args = openwith.split(' ')
            args.append(resultfn)
            print('\nLaunching as:' + str(args), '\n')
            subprocess.check_call(args)
        else:
            msg = '\nBuild completed.  You can see the result in "{0}": "{1}"'
            print(msg.format(builddir, resultfn), '\n')
    else:
        msg = 'Preprocessing done but skipping build.  Main file:"{0}.tex"'
        print(msg.format(os.path.join(builddir, outname)))

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser(description='Local builder for authorea papers.')

    parser.add_argument('localdir', nargs='?', default='.',
                        help='The directory to actually search for the authorea'
                             ' files in. Default to the current directory.')
    parser.add_argument('--build-dir', '-d', default='authorea_build',
                        help='the directory to build the paper in')
    parser.add_argument('--latex', '-l', default='pdflatex',
                        help='The executable to use for the latex build step.')
    parser.add_argument('--bibtex', '-b', default='bibtex',
                        help='The executable to use for the bibtex build step.')
    parser.add_argument('--filename', '-f', default='authorea_paper',
                        help='The name to use for the output tex file.')
    parser.add_argument('--no-bibtex', action='store_false', dest='usebibtex',
                        help='Provide this to not run bibtex.')
    parser.add_argument('--no-title', action='store_false', dest='usetitle',
                        help='Provide this to skip the title command.')
    parser.add_argument('--title-input', action='store_true', dest='titleinput',
                        help='Provide this to have the title included via the '
                             '\\input command instead of directly in the '
                             'generated tex file. This is useful because \\input'
                             'sometimes prevents the title from being cased '
                             'correctly.', default=False)
    parser.add_argument('--n-runs-after-bibtex', '-n', type=int, default=3,
                        help='The number of times to call latex after bibtex.')
    parser.add_argument('--open-with', '-o', default=None,
                        help='An executable to launch the output file with. '
                             'Default is to not do anything with it.')
    parser.add_argument('--no-build', action='store_false', dest='dobuild',
                        help='Only do preprocessing and skip all the build/open steps')
    parser.add_argument('--relative-links', action='store_true', dest='rellinks',
                        help='Always make links (to input files and figures) within '
                             'the .tex file relative. Default is to do this if '
                             'localdir and buildir are the same.')
    parser.add_argument('--absolute-links', action='store_true', dest='abslinks',
                        help='Always make links (to input files and figures) within '
                             'the .tex file absolute. Default is to do this if '
                             'localdir and buildir are different.')

    args = parser.parse_args()

    pathtype = None
    if args.rellinks and args.abslinks:
        raise IOError('You must supply either "--relative-links" OR "--absolute-links".')
    else:
        if args.rellinks:
            pathtype = 'rel'
        if args.abslinks:
            pathtype = 'abs'

    build_authorea_latex(args.localdir, args.build_dir, args.latex, args.bibtex,
                         args.filename, args.usetitle, args.usebibtex,
                         args.n_runs_after_bibtex, args.open_with,
                         args.titleinput, args.dobuild, pathtype)
<commit_msg>A fix to inclusion of posttitle.tex (also moving it before abstract inclusion)<commit_after>#!/usr/bin/env python

from __future__ import division, print_function

"""
This script generates a file to use for building authorea papers, and then runs
latex on them.

Requires python >= 2.6 (3.x should work, too)

The key assumptions are:
* ``layout.md`` exists in the article (I think it always does in Authorea)
* ``preamble.tex`` and/or``header.tex``, ``title.tex``, and
  ``bibliobgraphy/biblio.bib`` exist (I think these are created normally in a
    new Authorea article)
* ``posttitle.tex`` exists.  This one you'll have to create, containing
  everything you want after the title but before the beginning of the document.
  If it doesn't exist that's ok too, it'll just be ignored.
* Figures are in the ``figures`` directory (where authorea puts them), and in
  the correct place in the ``layout.md`` file.  A file named 'latexfigopts.json'
  can specify a dictionary for additional figure options (see the
  `FIGURE_DEFAULTS` below for what options that can be replaced).

"""

import os
import subprocess

#lots of dobule-{}'s are here because we use it as a formatting template below
MAIN_TEMPLATE = r"""
{preamblein}

{headerin}

\begin{{document}}

{titlecontent}

{sectioninputs}

\bibliography{{{bibloc}}}{{}}

\end{{document}}
"""

FIGURE_TEMPLATE = r"""
\begin{<figure_env>}[<placement>]
\begin{center}
\includegraphics[width=<width>]{<figfn>}
<caption>
\end{center}
\end{<figure_env>}
""".replace('{', '{{').replace('}', '}}').replace('<', '{').replace('>', '}')

FIGURE_DEFAULTS = {'placement': '', 'width': '1\columnwidth', 'figure_env': 'figure'}

def get_input_string(filename, localdir, quotepath=True):
    if filename.endswith('.tex'):
        filename = filename[:-4]
    if quotepath:
        quote_chr = '"'
    else:
        quote_chr = ''
    return r'\input{' + quote_chr + os.path.join(localdir, filename) + quote_chr + '}'

def get_figure_string(filename, localdir):
    import json

    figdir, figfn = os.path.split(filename)
    figdir = os.path.join(localdir, figdir)

    figfnbase = os.path.splitext(figfn)[0]
    figfn = os.path.join(figdir, figfn)
    pdffn = os.path.join(figdir, figfnbase + '.pdf')
    epsfn = os.path.join(figdir, figfnbase + '.eps')

    if not os.path.exists(pdffn):
        pdffn = None
    if not os.path.exists(epsfn):
        epsfn = None

    if pdffn or epsfn:
        figfn = os.path.join(figdir, figfnbase)

    capfn = os.path.join(figdir, 'caption.tex')
    if os.path.exists(capfn):
        caption = r'\caption{ \protect\input{' + capfn + '}}'
    else:
        caption = ''

    optsfn = os.path.join(figdir, 'latexfigopts.json')
    figopts = FIGURE_DEFAULTS.copy()
    if os.path.exists(optsfn):
        with open(optsfn) as f:
            optsjson = json.load(f)
        if not isinstance(optsjson, dict):
            raise ValueError('File "{0}" does not have a top-level dict'.format(optsfn))
        for k in FIGURE_DEFAULTS.keys():
            v = optsjson.pop(k, None)
            if v is not None:
                figopts[k] = v
        if len(optsjson) != 0:
            raise ValueError('Entries in "{0}" that were not understood: {1}'.format(optsfn, optsjson))

    figopts.update(locals())
    return FIGURE_TEMPLATE.format(**figopts)

# if builddir and local dir are the same set the paths used for input/include in
# the output tex file to be relative paths (this helps if sharing the created
# tex file with collaborator who don't use this script) otherwise use absolute
# paths. Alternatively, use the user defined option for whether relative or
# absolute paths are used.
def get_in_path(localdir, builddir, pathtype=None):
    """
    Figure out the path for a file in ``localdir`` relative to ``builddir``, or
    just use an absolute path dependeing on whether ``pathtype`` is 'rel' or
    'abs'. Or if it is None, pick relative only if ``localdir`` is the same as
    ``builddir``.
    """
    if pathtype == 'rel':
        return os.path.relpath(localdir, builddir)
    elif pathtype == 'abs':
        return os.path.abspath(localdir)
    elif pathtype is None:
        if builddir == localdir:
            return os.path.relpath(localdir, builddir)
        else:
            return os.path.abspath(localdir)
    else:
        raise ValueError('Invalid pathtype: "{0}"'.format(pathtype))

def build_authorea_latex(localdir, builddir, latex_exec, bibtex_exec, outname,
                         usetitle, dobibtex, npostbibcalls, openwith, titleinput,
                         dobuild, pathtype):
    if not os.path.exists(builddir):
        os.mkdir(builddir)

    if not os.path.isdir(builddir):
        raise IOError('Requested build directory {0} is a file, not a '
                      'directory'.format(builddir))

    # generate the main tex file as a string
    if os.path.exists(os.path.join(localdir, 'preamble.tex')):
        preamblein = get_input_string('preamble', get_in_path(localdir, builddir, pathtype))
    else:
        preamblein = ''
    if os.path.exists(os.path.join(localdir, 'header.tex')):
        headerin = get_input_string('header', get_in_path(localdir, builddir, pathtype))
    else:
        headerin = ''

    if not headerin and not preamblein:
        print("Neither preable nor header found!  Proceeding, but that's rather weird")

    bibloc = os.path.join(get_in_path(localdir, builddir, pathtype), 'bibliography', 'biblio')

    titlecontent = []
    if usetitle:
        if titleinput:
            titlestr = get_input_string('title', get_in_path(localdir, builddir, pathtype))
        else:
            with open(os.path.join(get_in_path(localdir, builddir, 'abs'), 'title.tex')) as f:
                titlestr = f.read()
        titlecontent.append(r'\title{' + titlestr + '}')

    sectioninputs = []
    with open(os.path.join(localdir, 'layout.md')) as f:
        for l in f:
            ls = l.strip()
            if ls == '':
                pass
            elif ls in ('posttitle.tex', 'title.tex', 'preamble.tex', 'header.tex'):
                pass # skip any that have been processed above
            elif ls in ('abstract.tex'):
                # add abstract to title content
                titlecontent.append(r'\begin{abstract}' + get_input_string('abstract', get_in_path(localdir, builddir, pathtype))  + '\end{abstract}')
            elif ls.endswith('.html') or ls.endswith('.htm'):
                pass  # html files aren't latex-able
            elif ls.startswith('figures'):
                sectioninputs.append(get_figure_string(ls, get_in_path(localdir, builddir, pathtype)))
            else:
                sectioninputs.append(get_input_string(ls, get_in_path(localdir, builddir, pathtype)))
    sectioninputs = '\n'.join(sectioninputs)

    if os.path.exists(os.path.join(localdir, 'posttitle.tex')):
        titlecontent.append(get_input_string('posttitle', get_in_path(localdir, builddir, pathtype)))
        # swap this to before the abstract
        if r'\begin{abstract}' in titlecontent[-2]: # check second to last value and swap position
            titlecontent[-1], titlecontent[-2] = titlecontent[-2], titlecontent[-1]
    titlecontent = '\n'.join(titlecontent)

    maintexstr = MAIN_TEMPLATE.format(**locals())

    #now save that string out as a file
    outname = outname if outname.endswith('.tex') else (outname + '.tex')
    outtexpath = os.path.join(builddir, outname)
    with open(outtexpath, 'w') as f:
        f.write(maintexstr)

    if outname.endswith('.tex'):
        outname = outname[:-4]

    if dobuild:
        #now actually run latex/bibtex
        args = [latex_exec, outname + '.tex']
        print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
        subprocess.check_call(args, cwd=builddir)
        if dobibtex:
            args = [bibtex_exec, outname]
            print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
            subprocess.check_call(args, cwd=builddir)
        for _ in range(npostbibcalls):
            args = [latex_exec, outname + '.tex']
            print('\n\RUNNING THIS COMMAND: "{0}"\n'.format(' '.join([latex_exec, outname + '.tex'])))
            subprocess.check_call(args, cwd=builddir)

        #launch the result if necessary
        resultfn = outtexpath[:-4] + ('.pdf' if 'pdf' in latex_exec else '.dvi')
        if openwith:
            args = openwith.split(' ')
            args.append(resultfn)
            print('\nLaunching as:' + str(args), '\n')
            subprocess.check_call(args)
        else:
            msg = '\nBuild completed.  You can see the result in "{0}": "{1}"'
            print(msg.format(builddir, resultfn), '\n')
    else:
        msg = 'Preprocessing done but skipping build.  Main file:"{0}.tex"'
        print(msg.format(os.path.join(builddir, outname)))

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser(description='Local builder for authorea papers.')

    parser.add_argument('localdir', nargs='?', default='.',
                        help='The directory to actually search for the authorea'
                             ' files in. Default to the current directory.')
    parser.add_argument('--build-dir', '-d', default='authorea_build',
                        help='the directory to build the paper in')
    parser.add_argument('--latex', '-l', default='pdflatex',
                        help='The executable to use for the latex build step.')
    parser.add_argument('--bibtex', '-b', default='bibtex',
                        help='The executable to use for the bibtex build step.')
    parser.add_argument('--filename', '-f', default='authorea_paper',
                        help='The name to use for the output tex file.')
    parser.add_argument('--no-bibtex', action='store_false', dest='usebibtex',
                        help='Provide this to not run bibtex.')
    parser.add_argument('--no-title', action='store_false', dest='usetitle',
                        help='Provide this to skip the title command.')
    parser.add_argument('--title-input', action='store_true', dest='titleinput',
                        help='Provide this to have the title included via the '
                             '\\input command instead of directly in the '
                             'generated tex file. This is useful because \\input'
                             'sometimes prevents the title from being cased '
                             'correctly.', default=False)
    parser.add_argument('--n-runs-after-bibtex', '-n', type=int, default=3,
                        help='The number of times to call latex after bibtex.')
    parser.add_argument('--open-with', '-o', default=None,
                        help='An executable to launch the output file with. '
                             'Default is to not do anything with it.')
    parser.add_argument('--no-build', action='store_false', dest='dobuild',
                        help='Only do preprocessing and skip all the build/open steps')
    parser.add_argument('--relative-links', action='store_true', dest='rellinks',
                        help='Always make links (to input files and figures) within '
                             'the .tex file relative. Default is to do this if '
                             'localdir and buildir are the same.')
    parser.add_argument('--absolute-links', action='store_true', dest='abslinks',
                        help='Always make links (to input files and figures) within '
                             'the .tex file absolute. Default is to do this if '
                             'localdir and buildir are different.')

    args = parser.parse_args()

    pathtype = None
    if args.rellinks and args.abslinks:
        raise IOError('You must supply either "--relative-links" OR "--absolute-links".')
    else:
        if args.rellinks:
            pathtype = 'rel'
        if args.abslinks:
            pathtype = 'abs'

    build_authorea_latex(args.localdir, args.build_dir, args.latex, args.bibtex,
                         args.filename, args.usetitle, args.usebibtex,
                         args.n_runs_after_bibtex, args.open_with,
                         args.titleinput, args.dobuild, pathtype)
<|endoftext|>