<commit_before>from datetime import datetime, timedelta
from devilry.apps.core.models import Assignment
from devilry.apps.core.testhelper import TestHelper

from .base import SubjectAdminSeleniumTestCase

class TestAssignment(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_doesnotexists(self):
        self.testhelper.create_user('normal')
        self.login('normal')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_shortcuts_render(self):
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1'],
                            assignments=['week2'])
        self._browseToAssignment(self.testhelper.duck1100_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertTrue('Manage students' in self.selenium.page_source)
        self.assertTrue('Manage deadlines' in self.selenium.page_source)

    #def test_notpublished(self):
        #self.testhelper.add(nodes='uni',
                            #subjects=['duck1100'],
                            #periods=['period1'],
                            #assignments=['week2:pub(2)'])
        #self._browseToAssignment(self.duck1100_period1_week2.id)
        #self.waitForText('Not published')

    #def test_published(self):
        #self.testhelper.add(nodes='uni',
                            #subjects=['duck1100'],
                            #periods=['period1:begins(-2)'],
                            #assignments=['week2'])
        #self.browseTo('/duck1100/period1/week2/')
        #self.waitForText('Published')

class TestEditPublishingTime(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1'])

        self.browseTo('/assignment/1/')
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime')

        editpublishingtime_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime')
        self.datefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_datefield input')
        self.timefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_timefield input')
        self.savebutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editpublishingtime_window = editpublishingtime_window

    def _set_datetime(self, date, time):
        self.datefield.clear()
        self.timefield.clear()
        self.datefield.send_keys(date)
        self.timefield.send_keys(time)

    def test_editpublishingtime(self):
        self.assertTrue('Published' in self.selenium.page_source)
        self.assertTrue('Choose a time when time when students will be able to start adding deliveries on the assignment' in self.selenium.page_source)
        yesterday = datetime.now() - timedelta(days=1)
        isoday_yesterday = yesterday.date().isoformat()
        self._set_datetime(isoday_yesterday, '12:00')
        self.savebutton.click()
        self.waitForText('Publishing time was {isoday_yesterday} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.duck1100_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), yesterday.date())

    def test_editpublishingtime_notpublished(self):
        tomorrow = datetime.now() + timedelta(days=1)
        isoday_tomorrow = tomorrow.date().isoformat()
        self._set_datetime(isoday_tomorrow, '12:00')
        self.savebutton.click()
        self.waitForText('Will be published {isoday_tomorrow} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.duck1100_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), tomorrow.date())

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editpublishingtime_window, '.x-tool-close')  # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editpublishingtime' in self.selenium.page_source)

    def test_editpublishingtime_errorhandling(self):
        self._set_datetime('2000-02-01', '12:00')
        self.savebutton.click()
        self.waitForText("The publishing time, 2000-02-01 12:00:00, is invalid")

class TestEditAnonymous(SubjectAdminSeleniumTestCase):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1'])

        self.week1 = self.testhelper.duck1100_period1_week1

        self.browseTo('/assignment/{0}/'.format(self.testhelper.duck1100_period1_week1.id))
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous')

        editanonymous_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous')
        self.anonymouscheckbox = editanonymous_window.find_element_by_css_selector('input[role="checkbox"]')
        self.savebutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editanonymous_window = editanonymous_window

    def test_editanonymous(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.assertTrue('Not anonymous' in self.selenium.page_source)
        self.assertTrue('Examiners and students can see each other and communicate.' in self.selenium.page_source)
        self.anonymouscheckbox.click()
        self.savebutton.click()
        self.waitForText('>Anonymous') # If this times out, is has not been updated
        self.waitForText('Examiners and students can not see each other and they can not communicate.') # If this times out, is has not been updated
        self.assertTrue(Assignment.objects.get(pk=self.week1.pk).anonymous)

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editanonymous_window, '.x-tool-close') # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editanonymous' in self.selenium.page_source)

    def test_editanonymous_nochange(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.savebutton.click()
        self.waitForText('Not anonymous') # If this times out, it has not been updated
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
<commit_msg>devilry_subjectadmin.test.assignment: Test as assignment admin.<commit_after>from datetime import datetime, timedelta
from devilry.apps.core.models import Assignment
from devilry.apps.core.testhelper import TestHelper

from .base import SubjectAdminSeleniumTestCase

class TestAssignment(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_doesnotexists(self):
        self.testhelper.create_user('normal')
        self.login('normal')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_shortcuts_render(self):
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1'],
                            assignments=['week2'])
        self._browseToAssignment(self.testhelper.duck1100_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertTrue('Manage students' in self.selenium.page_source)
        self.assertTrue('Manage deadlines' in self.selenium.page_source)

    def test_notpublished(self):
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.duck1100_period1_week2.id)
        self.waitForText('>Not published')

    def test_published(self):
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1:begins(-2):admin(week2admin)'],
                            assignments=['week2'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.duck1100_period1_week2.id)
        self.waitForText('>Published')

class TestEditPublishingTime(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.browseTo('/assignment/1/')
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime')

        editpublishingtime_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime')
        self.datefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_datefield input')
        self.timefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_timefield input')
        self.savebutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editpublishingtime_window = editpublishingtime_window

    def _set_datetime(self, date, time):
        self.datefield.clear()
        self.timefield.clear()
        self.datefield.send_keys(date)
        self.timefield.send_keys(time)

    def test_editpublishingtime(self):
        self.assertTrue('Published' in self.selenium.page_source)
        self.assertTrue('Choose a time when time when students will be able to start adding deliveries on the assignment' in self.selenium.page_source)
        yesterday = datetime.now() - timedelta(days=1)
        isoday_yesterday = yesterday.date().isoformat()
        self._set_datetime(isoday_yesterday, '12:00')
        self.savebutton.click()
        self.waitForText('Publishing time was {isoday_yesterday} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.duck1100_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), yesterday.date())

    def test_editpublishingtime_notpublished(self):
        tomorrow = datetime.now() + timedelta(days=1)
        isoday_tomorrow = tomorrow.date().isoformat()
        self._set_datetime(isoday_tomorrow, '12:00')
        self.savebutton.click()
        self.waitForText('Will be published {isoday_tomorrow} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.duck1100_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), tomorrow.date())

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editpublishingtime_window, '.x-tool-close')  # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editpublishingtime' in self.selenium.page_source)

    def test_editpublishingtime_errorhandling(self):
        self._set_datetime('2000-02-01', '12:00')
        self.savebutton.click()
        self.waitForText("The publishing time, 2000-02-01 12:00:00, is invalid")

class TestEditAnonymous(SubjectAdminSeleniumTestCase):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['duck1100'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.week1 = self.testhelper.duck1100_period1_week1

        self.browseTo('/assignment/{0}/'.format(self.testhelper.duck1100_period1_week1.id))
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous')

        editanonymous_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous')
        self.anonymouscheckbox = editanonymous_window.find_element_by_css_selector('input[role="checkbox"]')
        self.savebutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editanonymous_window = editanonymous_window

    def test_editanonymous(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.assertTrue('Not anonymous' in self.selenium.page_source)
        self.assertTrue('Examiners and students can see each other and communicate.' in self.selenium.page_source)
        self.anonymouscheckbox.click()
        self.savebutton.click()
        self.waitForText('>Anonymous') # If this times out, is has not been updated
        self.waitForText('Examiners and students can not see each other and they can not communicate.') # If this times out, is has not been updated
        self.assertTrue(Assignment.objects.get(pk=self.week1.pk).anonymous)

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editanonymous_window, '.x-tool-close') # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editanonymous' in self.selenium.page_source)

    def test_editanonymous_nochange(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.savebutton.click()
        self.waitForText('Not anonymous') # If this times out, it has not been updated
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
<|endoftext|>


<commit_before>Package ('gtk-quartz-engine', 'master',
	sources = [ 'git://github.com/mono/gtk-quartz-engine.git' ],
	override_properties = {
		'configure': './autogen.sh --prefix=%{prefix}'
	},
	revision = '8df9784d4572c69938f05da009bd3646e253ff9d'
)
<commit_msg>Bump gtk-quartz-engine. Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=15694<commit_after>Package ('gtk-quartz-engine', 'master',
	sources = [ 'git://github.com/mono/gtk-quartz-engine.git' ],
	override_properties = {
		'configure': './autogen.sh --prefix=%{prefix}'
	},
	revision = '8e0437c006eae316389fa89a670de6a7b56e9136'
)
<|endoftext|>


<commit_before>"""
In this module, we define the coordinate representation classes, which are
used to represent low-level cartesian, spherical, cylindrical, and other
coordinates.

All classes should define a ``to_cartesian`` method and a ``from_cartesian``
class method. By default, transformations are done via the cartesian system,
but classes that want to define a smarter transformation path can overload
the ``represent_as`` method.
"""

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import abc

import numpy as np
import astropy.units as u

from .angles import Angle, Longitude, Latitude
from .distances import Distance
from ..extern import six

# Suggestions to improve API
#
# - change PhysicistSphericalRepresentation to PhysicsSphericalRepresentation
# (implemented below).
#
# - add a rotated() method that can rotate the 3D coordinates

__all__ = ["CartesianRepresentation", "SphericalRepresentation",
           "UnitSphericalRepresentation", "PhysicsSphericalRepresentation",
           "CylindricalRepresentation"]

def broadcast_quantity(*args, **kwargs):
    """
    A Quantity-aware version of np.broadcast_arrays
    """
    new_arrays = np.broadcast_arrays(*args)
    new_quantities = []
    for i in range(len(new_arrays)):
        new_quantities.append(args[i].__class__(new_arrays[i], unit=args[i].unit, **kwargs))
    return tuple(new_quantities)

@six.add_metaclass(abc.ABCMeta)
class BaseRepresentation(object):
    """
    Base Representation object, for representing a point in a 3D coordinate system
    """

    def represent_as(self, other_class):
        if other_class == self.__class__:
            return self
        else:
            # The default is to convert via cartesian coordinates
            return other_class.from_cartesian(self.to_cartesian())

    @classmethod
    def from_representation(cls, representation):
        return representation.represent_as(cls)

    # Should be replaced by abstractclassmethod once we support only Python 3
    @abc.abstractmethod
    def from_cartesian(self):
        raise NotImplementedError()

    @abc.abstractmethod
    def to_cartesian(self):
        raise NotImplementedError()

class CartesianRepresentation(BaseRepresentation):
    """
    Representation of points in 3D cartesian coordinates.

    Parameters
    ----------
    x, y, z : `~astropy.units.Quantity` or float or `~numpy.ndarray`
        The x, y, and z coordinates of the point(s), which should either be
        `~astropy.units.Quantity` instances, or can be passed as
        float or `numpy.ndarray` provided that the ``unit`` parameter is
        specified. If ``x``, ``y``, and ``z`` have different shapes, they
        should be broadcastable.

    unit : `~astropy.units.Unit`, optional
        If ``x``, ``y``, or ``z`` are specified as float or
        ``numpy.ndarray``, then ``unit`` should be specified to indicate the
        units for these parameters. If ``x``, ``y``, or ``z`` are
        `~astropy.units.Quantity` instances, and ``unit`` is specified, they
        are converted to ``unit``.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, x, y=None, z=None, unit=None, copy=True):

        if y is None and z is None:
            x, y, z = x
        elif (y is None and z is not None) or (y is not None and z is None):
            raise ValueError("x, y, and z are required to instantiate CartesianRepresentation")

        if unit is not None:
            unit = u.Unit(unit)

        x = u.Quantity(x, unit=unit, copy=copy)
        y = u.Quantity(y, unit=unit, copy=copy)
        z = u.Quantity(z, unit=unit, copy=copy)

        if not (x.unit.physical_type == y.unit.physical_type == z.unit.physical_type):
            raise u.UnitsError("x, y, and z should have matching physical types")

        try:
            x, y, z = broadcast_quantity(x, y, z, copy=copy)
        except ValueError:
            raise ValueError("Input parameters x, y, and z cannot be broadcast")

        self._x = x
        self._y = y
        self._z = z

    @property
    def x(self):
        """
        The x componen of the point(s).
        """
        return self._x

    @property
    def y(self):
        """
        The y position of the point(s).
        """
        return self._y

    @property
    def z(self):
        """
        The z position of the point(s).
        """
        return self._z

    @property
    def xyz(self):
        return u.Quantity((self._x, self._y, self._z))

    @classmethod
    def from_cartesian(cls, other):
        return other

    def to_cartesian(self):
        return self

class SphericalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D spherical coordinates.

    Parameters
    ----------
    lon, lat : `~astropy.units.Quantity` or str
        The longitude and latitude of the point(s). The input values are
        passed to the `~astropy.coordinates.Longitude` and
        `~astropy.coordinates.Latitude` class respectively, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, lists of strings, and
        so on. `~astropy.coordinates.Longitude` instances can only be passed
        to ``lon``, and `~astropy.coordinates.Latitude` instances can only be
        passed to ``lat``.

    distance : `~astropy.units.Quantity`
        The distance to the point(s). If the distance is a length, it is
        passed to the :class:`~astropy.coordinates.Distance` class, otherwise
        it is passed to the :class:`~astropy.units.Quantity` class.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, lon, lat, distance, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        lon = Longitude(lon, copy=copy)
        lat = Latitude(lat, copy=copy)

        if isinstance(distance, u.Quantity) and distance.unit.physical_type == 'length':
            distance = Distance(distance, copy=copy)
        else:
            distance = u.Quantity(distance, copy=copy)

        try:
            lon, lat, distance = broadcast_quantity(lon, lat, distance, copy=copy)
        except ValueError:
            raise ValueError("Input parameters lon, lat, and distance cannot be broadcast")

        self._lon = lon
        self._lat = lat
        self._distance = distance

    @property
    def lon(self):
        """
        The longitude of the point(s).
        """
        return self._lon

    @property
    def lat(self):
        """
        The latitude of the point(s).
        """
        return self._lat

    @property
    def distance(self):
        """
        The distance from the origin to the point(s).
        """
        return self._distance

    def represent_as(self, other_class):
        # Take a short cut if the other clsss is a spherical representation
        if other_class is PhysicsSphericalRepresentation:
            return PhysicsSphericalRepresentation(phi=self.lon,
                                                  theta=90 * u.deg - self.lat,
                                                  distance=self.distance)
        else:
            return super(SphericalRepresentation, self).represent_as(other_class)

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        # We need to convert Distance to Quantity to allow negative values.
        if isinstance(self.distance, Distance):
            d = self.distance.view(u.Quantity)
        else:
            d = self.distance

        x = d * np.cos(self.lat) * np.cos(self.lon)
        y = d * np.cos(self.lat) * np.sin(self.lon)
        z = d * np.sin(self.lat)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)
        r = np.hypot(s, cart.z)

        lon = np.arctan2(cart.y, cart.x)
        lat = np.arctan2(cart.z, s)

        return SphericalRepresentation(lon=lon, lat=lat, distance=r)

class UnitSphericalRepresentation(BaseRepresentation):
    """
    Representation of points on a unit sphere

    Parameters
    ----------
    lon, lat : `~astropy.units.Quantity` or str
        The longitude and latitude of the point(s). The input values are
        passed to the `~astropy.coordinates.Longitude` and
        `~astropy.coordinates.Latitude` class respectively, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, lists of strings, and
        so on. `~astropy.coordinates.Longitude` instances can only be passed
        to ``lon``, and `~astropy.coordinates.Latitude` instances can only be
        passed to ``lat``.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, lon, lat, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        lon = Longitude(lon, copy=copy)
        lat = Latitude(lat, copy=copy)

        try:
            lon, lat = broadcast_quantity(lon, lat, copy=copy)
        except ValueError:
            raise ValueError("Input parameters lon and lat cannot be broadcast")

        self._lon = lon
        self._lat = lat

    @property
    def lon(self):
        """
        The longitude of the point(s).
        """
        return self._lon

    @property
    def lat(self):
        """
        The latitude of the point(s).
        """
        return self._lat

    # TODO: implement represent_as for efficient transformations

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        x = u.one * np.cos(self.lat) * np.cos(self.lon)
        y = u.one * np.cos(self.lat) * np.sin(self.lon)
        z = u.one * np.sin(self.lat)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)

        lon = np.arctan2(cart.y, cart.x)
        lat = np.arctan2(cart.z, s)

        return UnitSphericalRepresentation(lon=lon, lat=lat)

class PhysicsSphericalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D spherical coordinates (using the physics
    convention of using ``phi`` and ``theta`` for azimuth and inclination
    from the pole).

    Parameters
    ----------
    phi, theta : `~astropy.units.Quantity` or str
        The azimuth and inclination of the point(s). The input values are
        passed to the `~astropy.coordinates.Angle`, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, and lists of strings.

    distance : `~astropy.units.Quantity`
        The distance to the point(s). If the distance is a length, it is
        passed to the :class:`~astropy.coordinates.Distance` class, otherwise
        it is passed to the :class:`~astropy.units.Quantity` class.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, phi, theta, distance, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        phi = Angle(phi, copy=copy)
        theta = Angle(theta, copy=copy)

        if isinstance(distance, u.Quantity) and distance.unit.physical_type == 'length':
            distance = Distance(distance, copy=copy)
        else:
            distance = u.Quantity(distance, copy=copy)

        try:
            phi, theta, distance = broadcast_quantity(phi, theta, distance, copy=copy)
        except ValueError:
            raise ValueError("Input parameters phi, theta, and distance cannot be broadcast")

        self._phi = phi
        self._theta = theta
        self._distance = distance

    @property
    def phi(self):
        """
        The azimuth of the point(s).
        """
        return self._phi

    @property
    def theta(self):
        """
        The elevation of the point(s).
        """
        return self._theta

    @property
    def distance(self):
        """
        The distance from the origin to the point(s).
        """
        return self._distance

    def represent_as(self, other_class):
        # Take a short cut if the other clsss is a spherical representation
        if other_class is SphericalRepresentation:
            return SphericalRepresentation(lon=self.phi,
                                           lat=90 * u.deg - self.theta,
                                           distance=self.distance)
        else:
            return super(PhysicsSphericalRepresentation, self).represent_as(other_class)

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        # We need to convert Distance to Quantity to allow negative values.
        if isinstance(self.distance, Distance):
            d = self.distance.view(u.Quantity)
        else:
            d = self.distance

        x = d * np.sin(self.theta) * np.cos(self.phi)
        y = d * np.sin(self.theta) * np.sin(self.phi)
        z = d * np.cos(self.theta)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)
        r = np.hypot(s, cart.z)

        phi = np.arctan2(cart.y, cart.x)
        theta = np.arctan2(s, cart.z)

        return PhysicsSphericalRepresentation(phi=phi, theta=theta, distance=r)

class CylindricalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D cylindrical coordinates.

    Parameters
    ----------
    rho : `~astropy.units.Quantity`
        The distance from the z axis to the point(s).

    phi : `~astropy.units.Quantity` or str
        The azimuth of the point(s). The input is passed to the
        `~astropy.coordinates.Angle` class, so any valid input for that class
        is acceptable

    z : `~astropy.units.Quantity`
        The z coordinate(s) of the point(s)

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, rho, phi, z, copy=True):

        rho = u.Quantity(rho, copy=copy)
        phi = Angle(phi, copy=copy)
        z = u.Quantity(z, copy=copy)

        if not (rho.unit.physical_type == z.unit.physical_type):
            raise u.UnitsError("rho and z should have matching physical types")

        try:
            rho, phi, z = broadcast_quantity(rho, phi, z, copy=copy)
        except ValueError:
            raise ValueError("Input parameters rho, phi, and z cannot be broadcast")

        self._rho = rho
        self._phi = phi
        self._z = z

    @property
    def rho(self):
        """
        The distance of the point(s) from the z-axis.
        """
        return self._rho

    @property
    def phi(self):
        """
        The azimuth of the point(s).
        """
        return self._phi

    @property
    def z(self):
        """
        The height of the point(s).
        """
        return self._z

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to cylindrical polar
        coordinates.
        """

        rho = np.hypot(cart.x, cart.y)
        phi = np.arctan2(cart.y, cart.x)
        z = cart.z

        return CylindricalRepresentation(rho=rho, phi=phi, z=z)

    def to_cartesian(self):
        """
        Converts cylindrical polar coordinates to 3D rectangular cartesian
        coordinates.
        """
        x = self.rho * np.cos(self.phi)
        y = self.rho * np.sin(self.phi)
        z = self.z

        return CartesianRepresentation(x=x, y=y, z=z)
<commit_msg>Removed comment on changing API<commit_after>"""
In this module, we define the coordinate representation classes, which are
used to represent low-level cartesian, spherical, cylindrical, and other
coordinates.

All classes should define a ``to_cartesian`` method and a ``from_cartesian``
class method. By default, transformations are done via the cartesian system,
but classes that want to define a smarter transformation path can overload
the ``represent_as`` method.
"""

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import abc

import numpy as np
import astropy.units as u

from .angles import Angle, Longitude, Latitude
from .distances import Distance
from ..extern import six

__all__ = ["CartesianRepresentation", "SphericalRepresentation",
           "UnitSphericalRepresentation", "PhysicsSphericalRepresentation",
           "CylindricalRepresentation"]

def broadcast_quantity(*args, **kwargs):
    """
    A Quantity-aware version of np.broadcast_arrays
    """
    new_arrays = np.broadcast_arrays(*args)
    new_quantities = []
    for i in range(len(new_arrays)):
        new_quantities.append(args[i].__class__(new_arrays[i], unit=args[i].unit, **kwargs))
    return tuple(new_quantities)

@six.add_metaclass(abc.ABCMeta)
class BaseRepresentation(object):
    """
    Base Representation object, for representing a point in a 3D coordinate system
    """

    def represent_as(self, other_class):
        if other_class == self.__class__:
            return self
        else:
            # The default is to convert via cartesian coordinates
            return other_class.from_cartesian(self.to_cartesian())

    @classmethod
    def from_representation(cls, representation):
        return representation.represent_as(cls)

    # Should be replaced by abstractclassmethod once we support only Python 3
    @abc.abstractmethod
    def from_cartesian(self):
        raise NotImplementedError()

    @abc.abstractmethod
    def to_cartesian(self):
        raise NotImplementedError()

class CartesianRepresentation(BaseRepresentation):
    """
    Representation of points in 3D cartesian coordinates.

    Parameters
    ----------
    x, y, z : `~astropy.units.Quantity` or float or `~numpy.ndarray`
        The x, y, and z coordinates of the point(s), which should either be
        `~astropy.units.Quantity` instances, or can be passed as
        float or `numpy.ndarray` provided that the ``unit`` parameter is
        specified. If ``x``, ``y``, and ``z`` have different shapes, they
        should be broadcastable.

    unit : `~astropy.units.Unit`, optional
        If ``x``, ``y``, or ``z`` are specified as float or
        ``numpy.ndarray``, then ``unit`` should be specified to indicate the
        units for these parameters. If ``x``, ``y``, or ``z`` are
        `~astropy.units.Quantity` instances, and ``unit`` is specified, they
        are converted to ``unit``.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, x, y=None, z=None, unit=None, copy=True):

        if y is None and z is None:
            x, y, z = x
        elif (y is None and z is not None) or (y is not None and z is None):
            raise ValueError("x, y, and z are required to instantiate CartesianRepresentation")

        if unit is not None:
            unit = u.Unit(unit)

        x = u.Quantity(x, unit=unit, copy=copy)
        y = u.Quantity(y, unit=unit, copy=copy)
        z = u.Quantity(z, unit=unit, copy=copy)

        if not (x.unit.physical_type == y.unit.physical_type == z.unit.physical_type):
            raise u.UnitsError("x, y, and z should have matching physical types")

        try:
            x, y, z = broadcast_quantity(x, y, z, copy=copy)
        except ValueError:
            raise ValueError("Input parameters x, y, and z cannot be broadcast")

        self._x = x
        self._y = y
        self._z = z

    @property
    def x(self):
        """
        The x componen of the point(s).
        """
        return self._x

    @property
    def y(self):
        """
        The y position of the point(s).
        """
        return self._y

    @property
    def z(self):
        """
        The z position of the point(s).
        """
        return self._z

    @property
    def xyz(self):
        return u.Quantity((self._x, self._y, self._z))

    @classmethod
    def from_cartesian(cls, other):
        return other

    def to_cartesian(self):
        return self

class SphericalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D spherical coordinates.

    Parameters
    ----------
    lon, lat : `~astropy.units.Quantity` or str
        The longitude and latitude of the point(s). The input values are
        passed to the `~astropy.coordinates.Longitude` and
        `~astropy.coordinates.Latitude` class respectively, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, lists of strings, and
        so on. `~astropy.coordinates.Longitude` instances can only be passed
        to ``lon``, and `~astropy.coordinates.Latitude` instances can only be
        passed to ``lat``.

    distance : `~astropy.units.Quantity`
        The distance to the point(s). If the distance is a length, it is
        passed to the :class:`~astropy.coordinates.Distance` class, otherwise
        it is passed to the :class:`~astropy.units.Quantity` class.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, lon, lat, distance, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        lon = Longitude(lon, copy=copy)
        lat = Latitude(lat, copy=copy)

        if isinstance(distance, u.Quantity) and distance.unit.physical_type == 'length':
            distance = Distance(distance, copy=copy)
        else:
            distance = u.Quantity(distance, copy=copy)

        try:
            lon, lat, distance = broadcast_quantity(lon, lat, distance, copy=copy)
        except ValueError:
            raise ValueError("Input parameters lon, lat, and distance cannot be broadcast")

        self._lon = lon
        self._lat = lat
        self._distance = distance

    @property
    def lon(self):
        """
        The longitude of the point(s).
        """
        return self._lon

    @property
    def lat(self):
        """
        The latitude of the point(s).
        """
        return self._lat

    @property
    def distance(self):
        """
        The distance from the origin to the point(s).
        """
        return self._distance

    def represent_as(self, other_class):
        # Take a short cut if the other clsss is a spherical representation
        if other_class is PhysicsSphericalRepresentation:
            return PhysicsSphericalRepresentation(phi=self.lon,
                                                  theta=90 * u.deg - self.lat,
                                                  distance=self.distance)
        else:
            return super(SphericalRepresentation, self).represent_as(other_class)

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        # We need to convert Distance to Quantity to allow negative values.
        if isinstance(self.distance, Distance):
            d = self.distance.view(u.Quantity)
        else:
            d = self.distance

        x = d * np.cos(self.lat) * np.cos(self.lon)
        y = d * np.cos(self.lat) * np.sin(self.lon)
        z = d * np.sin(self.lat)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)
        r = np.hypot(s, cart.z)

        lon = np.arctan2(cart.y, cart.x)
        lat = np.arctan2(cart.z, s)

        return SphericalRepresentation(lon=lon, lat=lat, distance=r)

class UnitSphericalRepresentation(BaseRepresentation):
    """
    Representation of points on a unit sphere

    Parameters
    ----------
    lon, lat : `~astropy.units.Quantity` or str
        The longitude and latitude of the point(s). The input values are
        passed to the `~astropy.coordinates.Longitude` and
        `~astropy.coordinates.Latitude` class respectively, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, lists of strings, and
        so on. `~astropy.coordinates.Longitude` instances can only be passed
        to ``lon``, and `~astropy.coordinates.Latitude` instances can only be
        passed to ``lat``.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, lon, lat, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        lon = Longitude(lon, copy=copy)
        lat = Latitude(lat, copy=copy)

        try:
            lon, lat = broadcast_quantity(lon, lat, copy=copy)
        except ValueError:
            raise ValueError("Input parameters lon and lat cannot be broadcast")

        self._lon = lon
        self._lat = lat

    @property
    def lon(self):
        """
        The longitude of the point(s).
        """
        return self._lon

    @property
    def lat(self):
        """
        The latitude of the point(s).
        """
        return self._lat

    # TODO: implement represent_as for efficient transformations

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        x = u.one * np.cos(self.lat) * np.cos(self.lon)
        y = u.one * np.cos(self.lat) * np.sin(self.lon)
        z = u.one * np.sin(self.lat)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)

        lon = np.arctan2(cart.y, cart.x)
        lat = np.arctan2(cart.z, s)

        return UnitSphericalRepresentation(lon=lon, lat=lat)

class PhysicsSphericalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D spherical coordinates (using the physics
    convention of using ``phi`` and ``theta`` for azimuth and inclination
    from the pole).

    Parameters
    ----------
    phi, theta : `~astropy.units.Quantity` or str
        The azimuth and inclination of the point(s). The input values are
        passed to the `~astropy.coordinates.Angle`, so any valid
        input for these classes is acceptable. This includes
        `~astropy.units.Quantity` instances, strings, and lists of strings.

    distance : `~astropy.units.Quantity`
        The distance to the point(s). If the distance is a length, it is
        passed to the :class:`~astropy.coordinates.Distance` class, otherwise
        it is passed to the :class:`~astropy.units.Quantity` class.

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, phi, theta, distance, copy=True):

        # Let the Longitude and Latitude classes deal with e.g. parsing
        phi = Angle(phi, copy=copy)
        theta = Angle(theta, copy=copy)

        if isinstance(distance, u.Quantity) and distance.unit.physical_type == 'length':
            distance = Distance(distance, copy=copy)
        else:
            distance = u.Quantity(distance, copy=copy)

        try:
            phi, theta, distance = broadcast_quantity(phi, theta, distance, copy=copy)
        except ValueError:
            raise ValueError("Input parameters phi, theta, and distance cannot be broadcast")

        self._phi = phi
        self._theta = theta
        self._distance = distance

    @property
    def phi(self):
        """
        The azimuth of the point(s).
        """
        return self._phi

    @property
    def theta(self):
        """
        The elevation of the point(s).
        """
        return self._theta

    @property
    def distance(self):
        """
        The distance from the origin to the point(s).
        """
        return self._distance

    def represent_as(self, other_class):
        # Take a short cut if the other clsss is a spherical representation
        if other_class is SphericalRepresentation:
            return SphericalRepresentation(lon=self.phi,
                                           lat=90 * u.deg - self.theta,
                                           distance=self.distance)
        else:
            return super(PhysicsSphericalRepresentation, self).represent_as(other_class)

    def to_cartesian(self):
        """
        Converts spherical polar coordinates to 3D rectangular cartesian
        coordinates.
        """

        # We need to convert Distance to Quantity to allow negative values.
        if isinstance(self.distance, Distance):
            d = self.distance.view(u.Quantity)
        else:
            d = self.distance

        x = d * np.sin(self.theta) * np.cos(self.phi)
        y = d * np.sin(self.theta) * np.sin(self.phi)
        z = d * np.cos(self.theta)

        return CartesianRepresentation(x=x, y=y, z=z)

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to spherical polar
        coordinates.
        """

        s = np.hypot(cart.x, cart.y)
        r = np.hypot(s, cart.z)

        phi = np.arctan2(cart.y, cart.x)
        theta = np.arctan2(s, cart.z)

        return PhysicsSphericalRepresentation(phi=phi, theta=theta, distance=r)

class CylindricalRepresentation(BaseRepresentation):
    """
    Representation of points in 3D cylindrical coordinates.

    Parameters
    ----------
    rho : `~astropy.units.Quantity`
        The distance from the z axis to the point(s).

    phi : `~astropy.units.Quantity` or str
        The azimuth of the point(s). The input is passed to the
        `~astropy.coordinates.Angle` class, so any valid input for that class
        is acceptable

    z : `~astropy.units.Quantity`
        The z coordinate(s) of the point(s)

    copy : bool, optional
        If True arrays will be copied rather than referenced.
    """

    def __init__(self, rho, phi, z, copy=True):

        rho = u.Quantity(rho, copy=copy)
        phi = Angle(phi, copy=copy)
        z = u.Quantity(z, copy=copy)

        if not (rho.unit.physical_type == z.unit.physical_type):
            raise u.UnitsError("rho and z should have matching physical types")

        try:
            rho, phi, z = broadcast_quantity(rho, phi, z, copy=copy)
        except ValueError:
            raise ValueError("Input parameters rho, phi, and z cannot be broadcast")

        self._rho = rho
        self._phi = phi
        self._z = z

    @property
    def rho(self):
        """
        The distance of the point(s) from the z-axis.
        """
        return self._rho

    @property
    def phi(self):
        """
        The azimuth of the point(s).
        """
        return self._phi

    @property
    def z(self):
        """
        The height of the point(s).
        """
        return self._z

    @classmethod
    def from_cartesian(cls, cart):
        """
        Converts 3D rectangular cartesian coordinates to cylindrical polar
        coordinates.
        """

        rho = np.hypot(cart.x, cart.y)
        phi = np.arctan2(cart.y, cart.x)
        z = cart.z

        return CylindricalRepresentation(rho=rho, phi=phi, z=z)

    def to_cartesian(self):
        """
        Converts cylindrical polar coordinates to 3D rectangular cartesian
        coordinates.
        """
        x = self.rho * np.cos(self.phi)
        y = self.rho * np.sin(self.phi)
        z = self.z

        return CartesianRepresentation(x=x, y=y, z=z)
<|endoftext|>


<commit_before><commit_msg>Try to fix linux views, part I don't even know how many<commit_after><|endoftext|>


<commit_before>import sys
import unittest
import webbrowser

import pytest

from PyQt5 import QtGui, QtWidgets
from PyQt5.QtTest import QTest
from PyQt5 import QtCore
from unittest.mock import MagicMock

####### TEMP
import sas.qtgui.path_prepare
#######

# Local
#from sas.qtgui.Calculators.SldPanel import SldResult
from sas.qtgui.Calculators.SldPanel import SldPanel
from sas.qtgui.Calculators.SldPanel import neutronSldAlgorithm, xraySldAlgorithm
from sas.qtgui.Utilities.GuiUtils import FormulaValidator

if not QtWidgets.QApplication.instance():
    app = QtWidgets.QApplication(sys.argv)

class SldAlgorithmTest(unittest.TestCase):
    """ Test the periodictable wrapper """

    @pytest.mark.xfail(reason="2022-09 already broken")
    def testSldAlgorithm1(self):
        molecular_formula = "H2O"
        mass_density = 1.0
        wavelength = 6.0

        results = neutronSldAlgorithm( molecular_formula,
                                mass_density,
                                wavelength)
        #self.assertIsInstance(results, SldResult)
        self.assertAlmostEqual(results.neutron_length, 0.175463, 5)
        self.assertAlmostEqual(results.neutron_inc_xs, 5.365857, 5)
        self.assertAlmostEqual(results.neutron_abs_xs, 0.074224, 5)

    @pytest.mark.xfail(reason="2022-09 already broken")
    def testSldAlgorithm2(self):
        molecular_formula = "C29O[18]5+7NH[2]3"
        mass_density = 3.0
        wavelength = 666.0

        results = neutronSldAlgorithm( molecular_formula,
                                mass_density,
                                wavelength)
        #self.assertIsInstance(results, SldResult)
        self.assertAlmostEqual(results.neutron_length,   0.059402, 5)
        self.assertAlmostEqual(results.neutron_inc_xs,   0.145427, 5)
        self.assertAlmostEqual(results.neutron_abs_xs,  15.512215, 5)
        self.assertAlmostEqual(results.neutron_sld_real, 1.3352833e-05, 5)
        self.assertAlmostEqual(results.neutron_sld_imag, 1.1645807e-10, 5)

class SLDCalculatorTest(unittest.TestCase):
    '''Test the SLDCalculator'''
    def setUp(self):
        '''Create the SLDCalculator'''
        self.widget = SldPanel(None)

    def tearDown(self):
        '''Destroy the DensityCalculator'''
        self.widget.close()
        self.widget = None

    def testDefaults(self):
        '''Test the GUI in its default state'''
        self.assertIsInstance(self.widget, QtWidgets.QWidget)
        # temporarily commented out until FormulaValidator fixed for Qt5
        # self.assertEqual(self.widget.windowTitle(), "SLD Calculator")
        # self.assertIsInstance(self.widget.ui.editMolecularFormula.validator(), FormulaValidator)
        self.assertEqual(self.widget.ui.editMolecularFormula.styleSheet(), '')
        self.assertEqual(self.widget.model.columnCount(), 1)
        self.assertEqual(self.widget.model.rowCount(), 11)
        self.assertEqual(self.widget.sizePolicy().Policy(), QtWidgets.QSizePolicy.Fixed)

    def testSimpleEntry(self):
        ''' Default compound calculations '''

        self.widget.ui.editMassDensity.clear()
        self.widget.ui.editMassDensity.insert("1.0")
        # Send tab x3
        key = QtCore.Qt.Key_Tab
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QtWidgets.qApp.processEvents()
        QTest.qWait(100)

        # Assure the output fields are set
        self.assertEqual(self.widget.ui.editNeutronIncXs.text(), '5.62')

        # Change mass density
        self.widget.ui.editNeutronWavelength.clear()
        self.widget.ui.editNeutronWavelength.setText("666.0")

        # Send shift-tab to update the molar volume field
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QtWidgets.qApp.processEvents()
        QTest.qWait(100)

        # Assure the molar volume field got updated
        self.assertEqual(self.widget.ui.editNeutronAbsXs.text(), '8.24')

    def testComplexEntryAndReset(self):
        ''' User entered compound calculations and subsequent reset'''

        self.widget.ui.editMolecularFormula.clear()
        self.widget.ui.editMolecularFormula.insert("CaCO[18]3+6H2O")
        self.widget.ui.editMassDensity.insert("5.0")

        self.widget.show()
        # Send tab x2
        key = QtCore.Qt.Key_Tab
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)
        QTest.qWait(100)
        QTest.keyEvent(QTest.Press, self.widget, key, QtCore.Qt.NoModifier)

        # Assure the mass density field is set
        self.assertEqual(self.widget.ui.editNeutronIncXs.text(), '2.89')

    def testHelp(self):
        """ Assure help file is shown """
        self.widget.manager = QtWidgets.QWidget()
        self.widget.manager.showHelp = MagicMock()
        self.widget.displayHelp()
        self.assertTrue(self.widget.manager.showHelp.called_once())
        args = self.widget.manager.showHelp.call_args
        self.assertIn('sld_calculator_help.html', args[0][0])

if __name__ == "__main__":
    unittest.main()
<commit_msg>Change tests to asserts in SLDCalculatorTest.py<commit_after>import sys
import webbrowser

import pytest

from PyQt5 import QtGui, QtWidgets
from PyQt5.QtTest import QTest
from PyQt5 import QtCore
from unittest.mock import MagicMock

# Local
#from sas.qtgui.Calculators.SldPanel import SldResult
from sas.qtgui.Calculators.SldPanel import SldPanel
from sas.qtgui.Calculators.SldPanel import neutronSldAlgorithm, xraySldAlgorithm
from sas.qtgui.Utilities.GuiUtils import FormulaValidator

class SldAlgorithmTest:
    """ Test the periodictable wrapper """

    @pytest.mark.xfail(reason="2022-09 already broken")
    def testSldAlgorithm1(self):
        molecular_formula = "H2O"
        mass_density = 1.0
        wavelength = 6.0

        results = neutronSldAlgorithm( molecular_formula,
                                mass_density,
                                wavelength)
        #assert isinstance(results, SldResult)
        assert round(abs(results.neutron_length-0.175463), 5) == 0
        assert round(abs(results.neutron_inc_xs-5.365857), 5) == 0
        assert round(abs(results.neutron_abs_xs-0.074224), 5) == 0

    @pytest.mark.xfail(reason="2022-09 already broken")
    def testSldAlgorithm2(self):
        molecular_formula = "C29O[18]5+7NH[2]3"
        mass_density = 3.0
        wavelength = 666.0

        results = neutronSldAlgorithm( molecular_formula,
                                mass_density,
                                wavelength)
        #assert isinstance(results, SldResult)
        assert round(abs(results.neutron_length-0.059402), 5) == 0
        assert round(abs(results.neutron_inc_xs-0.145427), 5) == 0
        assert round(abs(results.neutron_abs_xs-15.512215), 5) == 0
        assert round(abs(results.neutron_sld_real-1.3352833e-05), 5) == 0
        assert round(abs(results.neutron_sld_imag-1.1645807e-10), 5) == 0

class SLDCalculatorTest:
    '''Test the SLDCalculator'''
    @pytest.fixture(autouse=True)
    def widget(self, qapp):
        """Create/Destroy the SLDCalculator"""
        w = SldPanel(None)

        yield w

        w.close()
        w = None

    def testDefaults(self, widget):
        '''Test the GUI in its default state'''
        assert isinstance(widget, QtWidgets.QWidget)
        # temporarily commented out until FormulaValidator fixed for Qt5
        # assert widget.windowTitle() == "SLD Calculator"
        # assert isinstance(widget.ui.editMolecularFormula.validator(), FormulaValidator)
        assert widget.ui.editMolecularFormula.styleSheet() == ''
        assert widget.model.columnCount() == 1
        assert widget.model.rowCount() == 11
        assert widget.sizePolicy().Policy() == QtWidgets.QSizePolicy.Fixed

    def testSimpleEntry(self, widget):
        ''' Default compound calculations '''

        widget.ui.editMassDensity.clear()
        widget.ui.editMassDensity.insert("1.0")
        # Send tab x3
        key = QtCore.Qt.Key_Tab
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QtWidgets.qApp.processEvents()
        QTest.qWait(100)

        # Assure the output fields are set
        assert widget.ui.editNeutronIncXs.text() == '5.62'

        # Change mass density
        widget.ui.editNeutronWavelength.clear()
        widget.ui.editNeutronWavelength.setText("666.0")

        # Send shift-tab to update the molar volume field
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QtWidgets.qApp.processEvents()
        QTest.qWait(100)

        # Assure the molar volume field got updated
        assert widget.ui.editNeutronAbsXs.text() == '8.24'

    def testComplexEntryAndReset(self, widget):
        ''' User entered compound calculations and subsequent reset'''

        widget.ui.editMolecularFormula.clear()
        widget.ui.editMolecularFormula.insert("CaCO[18]3+6H2O")
        widget.ui.editMassDensity.insert("5.0")

        widget.show()
        # Send tab x2
        key = QtCore.Qt.Key_Tab
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)
        QTest.qWait(100)
        QTest.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)

        # Assure the mass density field is set
        assert widget.ui.editNeutronIncXs.text() == '2.89'

    def testHelp(self, widget):
        """ Assure help file is shown """
        widget.manager = QtWidgets.QWidget()
        widget.manager.showHelp = MagicMock()
        widget.displayHelp()
        assert widget.manager.showHelp.called_once()
        args = widget.manager.showHelp.call_args
        assert 'sld_calculator_help.html' in args[0][0]
<|endoftext|>


<commit_before>"""

Views
Full text search views.
YaCy back-end connections.

"""
import math
import time
from datetime import date

import simplejson as json
import urllib3  # HTTP conncetions
import urllib # URL encoding
from django.conf import settings  # For the back-end connection settings
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template import Context, loader
from django.views.decorators.http import require_GET, require_http_methods

from ahmia.models import HiddenWebsite, HiddenWebsitePopularity

import re

@require_http_methods(["GET", "POST"])
def proxy(request):
    """Proxy connection to Elasticsearch"""
    full_url = request.get_full_path()
    http = urllib3.PoolManager()
    url = settings.PROXY_BASE_URL + full_url.replace("/elasticsearch/", "")
    content_type = {'Content-Type':request.META.get('CONTENT_TYPE')}
    response = http.request(request.method, url, headers=content_type, body=request.body)
    r_type = response.getheader('content-type')
    r_data = response.data
    r_status = response.status
    return HttpResponse(content=r_data, content_type=r_type, status=r_status)

@require_GET
def default(request):
    """The default page."""
    template = loader.get_template('index_tor.html')
    content = Context()
    return HttpResponse(template.render(content))

@require_GET
def i2p_search(request):
    """The default page."""
    template = loader.get_template('index_i2p.html')
    content = Context()
    return HttpResponse(template.render(content))

@require_GET
def results(request):
    """Search results page."""
    RESULTS_PER_PAGE = 50
    query_string = request.GET.get('q', '')
    page  = request.GET.get('page', '')
    search_time = ""
    if query_string:
        start = time.time()
        if "i2p" in request.path:
            search_results = query_object_elasticsearch(query_string, item_type="i2p")
        else:
            search_results = query_object_elasticsearch(query_string)
        end = time.time()
        search_time = end - start
        search_time = round(search_time, 2)
    else:
        # don't query anything
        return redirect('/')

    # fake paginate results
    if page:
        try:
            page = int(page) - 1
        except:
            page = 0
    else:
        page = 0

    total_search_results = len(search_results)

    # calculate offsets
    result_offset = page * RESULTS_PER_PAGE
    result_final  = result_offset + RESULTS_PER_PAGE

    max_pages = int(math.ceil(float(total_search_results) / RESULTS_PER_PAGE))
    if result_offset >= total_search_results:
        page = max_pages
        result_offset = max_pages * RESULTS_PER_PAGE
    if result_final > total_search_results:
        result_final = total_search_results

    # truncate results
    search_results = search_results[result_offset:result_final]

    if "i2p" in request.path:
        template = loader.get_template('i2p_results.html')
    else:
        template = loader.get_template('tor_results.html')

    content = Context({
        'page': page+1,
        'max_pages': max_pages,
        'result_begin': result_offset+1,
        'result_end': result_final,
        'total_search_results': total_search_results,
        'query_string' : query_string,
        'search_results': search_results,
        'search_time': search_time,
        'now': date.fromtimestamp(time.time())
        })
    return HttpResponse(template.render(content))

def query_object_elasticsearch(query_string, item_type="tor"):
    """Return a dict of Elasticsearch results."""
    # make an http request to elasticsearch
    #pool = urllib3.HTTPSConnectionPool(settings.ELASTICSEARCH_HOST,
    #        settings.ELASTICSEARCH_PORT,
    #        assert_fingerprint=settings.ELASTICSEARCH_TLS_FPRINT)
    #pool = urllib3.HTTPConnectionPool('127.0.0.1', '9200')
    # For testing, external Elasticsearch end-point
    pool = urllib3.HTTPSConnectionPool('ahmia.fi', '443')
    query_string = re.escape(query_string)
    query = urllib.quote_plus(query_string.encode('utf-8'))
    #endpoint = '/crawl/' + item_type + '/_search/?size=100&q=' + query
    # For testing, external Elasticsearch end-point
    endpoint = '/elasticsearch/crawl/' + item_type + '/_search'
    http_res = pool.request('GET', endpoint)
    res_json = http_res.data
    response = json.loads(res_json)
    results_obj = {}
    for element in response['hits']['hits']:
        element = element['_source']
        # Skip if there already is the most possible front page in the results
        if results_obj.has_key(element['domain']):
            if results_obj[element['domain']]['url'] == element['domain']:
                continue
        res = {
            'title': element['title'] or 'No title found',
            'description': element['text'] or 'No description found',
            'pub_date': element['timestamp'] or -1,
            'url': element['url'] or '',
            'domain': element['domain'] or ''
        }
        timestamp = time.strptime(element['timestamp'], '%Y-%m-%dT%H:%M:%S')
        res['date'] = date.fromtimestamp(time.mktime(timestamp))
        res['timestamp'] = int(time.mktime(timestamp))
        url = element['domain'] or ''
        if '.onion' in url:
            res['url_tor2web'] = url.replace('.onion', '.tor2web.org')
        res['url'] = url
        results_obj[res['domain']] = res
    return results_obj.values()

def add_result(answer, host, results):
    """Add new search result and get the stats about it."""
    if host:
        onion_id = host.replace(".onion", "")
        tor2web, backlinks, clicks = get_popularity(onion_id)
        if tor2web > 0 or backlinks > 0 or clicks > 0:
            results.append(Popularity(host, answer, tor2web, backlinks, clicks))
    else:
        results.append(Popularity(host, answer, 1, 1, 1))

def get_popularity(onion):
    """Calculate the popularity of an onion page."""
    try:
        hs = HiddenWebsite.objects.get(id=onion)
    except ObjectDoesNotExist:
        return 1, 1, 1
    if hs.banned:
        return 0, 0, 0
    try:
        pop = HiddenWebsitePopularity.objects.get(about=hs)
        clicks = pop.clicks
        public_backlinks = pop.public_backlinks
        tor2web = pop.tor2web
        return tor2web, public_backlinks, clicks
    except ObjectDoesNotExist:
        return 1, 1, 1

def sort_results(p_tuples):
    """Sort the results according to stats."""
    # Scaling the number of backlinks
    p_by_backlinks = sorted(p_tuples, key=lambda popularity: popularity.backlinks, reverse=True)
    for index, p_info in enumerate(p_by_backlinks):
        p_info.backlinks = 1 / (float(index) + 1)
    # Scaling the number of clicks
    p_by_clicks = sorted(p_by_backlinks, key=lambda popularity: popularity.clicks, reverse=True)
    for index, p_info in enumerate(p_by_clicks):
        p_info.clicks = 1 / (float(index) + 1)
    # Scaling the number of Tor2web
    p_by_tor2web = sorted(p_by_clicks, key=lambda popularity: popularity.tor2web, reverse=True)
    for index, p_info in enumerate(p_by_tor2web):
        p_info.tor2web = 1 / (float(index) + 1)
    p_by_sum = sorted(p_by_tor2web, key=lambda popularity: popularity.sum(), reverse=True)
    html_answer = ""
    for p_info in p_by_sum:
        html_answer = html_answer + p_info.content
    return html_answer

class Popularity(object):
    """Popularity by Tor2web visits, backlinks and clicks."""
    def __init__(self, url, content, tor2web, backlinks, clicks):
        self.url = url
        self.content = content
        self.tor2web = float(tor2web)
        self.backlinks = float(backlinks)
        self.clicks = float(clicks)
    def func(self):
        """Print the sum function."""
        print "2.0*%f + 3.0*%f + 1.0*%f" % self.tor2web, self.backlinks, self.clicks
    def sum(self):
        """Calculate the popularity."""
        #The model can be very simple (sum)
        #What are the proper coefficients?
        sum_function = 2.0*self.tor2web + 3.0*self.backlinks + 1.0*self.clicks
        return sum_function
    def __repr__(self):
        return repr((self.url, self.tor2web, self.backlinks, self.clicks, self.sum))
<commit_msg>Fixed Elastic issue when accessed externally<commit_after>"""

Views
Full text search views.
YaCy back-end connections.

"""
import math
import time
from datetime import date

import simplejson as json
import urllib3  # HTTP conncetions
import urllib # URL encoding
from django.conf import settings  # For the back-end connection settings
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template import Context, loader
from django.views.decorators.http import require_GET, require_http_methods

from ahmia.models import HiddenWebsite, HiddenWebsitePopularity

import re

@require_http_methods(["GET", "POST"])
def proxy(request):
    """Proxy connection to Elasticsearch"""
    full_url = request.get_full_path()
    http = urllib3.PoolManager()
    url = settings.PROXY_BASE_URL + full_url.replace("/elasticsearch/", "")
    content_type = {'Content-Type':request.META.get('CONTENT_TYPE')}
    response = http.request(request.method, url, headers=content_type, body=request.body)
    r_type = response.getheader('content-type')
    r_data = response.data
    r_status = response.status
    return HttpResponse(content=r_data, content_type=r_type, status=r_status)

@require_GET
def default(request):
    """The default page."""
    template = loader.get_template('index_tor.html')
    content = Context()
    return HttpResponse(template.render(content))

@require_GET
def i2p_search(request):
    """The default page."""
    template = loader.get_template('index_i2p.html')
    content = Context()
    return HttpResponse(template.render(content))

@require_GET
def results(request):
    """Search results page."""
    RESULTS_PER_PAGE = 50
    query_string = request.GET.get('q', '')
    page  = request.GET.get('page', '')
    search_time = ""
    if query_string:
        start = time.time()
        if "i2p" in request.path:
            search_results = query_object_elasticsearch(query_string, item_type="i2p")
        else:
            search_results = query_object_elasticsearch(query_string)
        end = time.time()
        search_time = end - start
        search_time = round(search_time, 2)
    else:
        # don't query anything
        return redirect('/')

    # fake paginate results
    if page:
        try:
            page = int(page) - 1
        except:
            page = 0
    else:
        page = 0

    total_search_results = len(search_results)

    # calculate offsets
    result_offset = page * RESULTS_PER_PAGE
    result_final  = result_offset + RESULTS_PER_PAGE

    max_pages = int(math.ceil(float(total_search_results) / RESULTS_PER_PAGE))
    if result_offset >= total_search_results:
        page = max_pages
        result_offset = max_pages * RESULTS_PER_PAGE
    if result_final > total_search_results:
        result_final = total_search_results

    # truncate results
    search_results = search_results[result_offset:result_final]

    if "i2p" in request.path:
        template = loader.get_template('i2p_results.html')
    else:
        template = loader.get_template('tor_results.html')

    content = Context({
        'page': page+1,
        'max_pages': max_pages,
        'result_begin': result_offset+1,
        'result_end': result_final,
        'total_search_results': total_search_results,
        'query_string' : query_string,
        'search_results': search_results,
        'search_time': search_time,
        'now': date.fromtimestamp(time.time())
        })
    return HttpResponse(template.render(content))

def query_object_elasticsearch(query_string, item_type="tor"):
    """Return a dict of Elasticsearch results."""
    # make an http request to elasticsearch
    #pool = urllib3.HTTPSConnectionPool(settings.ELASTICSEARCH_HOST,
    #        settings.ELASTICSEARCH_PORT,
    #        assert_fingerprint=settings.ELASTICSEARCH_TLS_FPRINT)
    pool = urllib3.HTTPConnectionPool('127.0.0.1', '9200')
    # For testing, external Elasticsearch end-point
    #pool = urllib3.HTTPSConnectionPool('ahmia.fi', '443')
    query_string = re.escape(query_string)
    query = urllib.quote_plus(query_string.encode('utf-8'))
    endpoint = '/crawl/' + item_type + '/_search/?size=100&q=' + query
    # For testing, external Elasticsearch end-point
    #endpoint = '/elasticsearch/crawl/' + item_type + '/_search/?size=100&q=' + query
    http_res = pool.request('GET', endpoint)
    res_json = http_res.data
    response = json.loads(res_json)
    results_obj = {}
    for element in response['hits']['hits']:
        element = element['_source']
        # Skip if there already is the most possible front page in the results
        if results_obj.has_key(element['domain']):
            if results_obj[element['domain']]['url'] == element['domain']:
                continue
        res = {
            'title': element['title'] or 'No title found',
            'description': element['text'] or 'No description found',
            'pub_date': element['timestamp'] or -1,
            'url': element['url'] or '',
            'domain': element['domain'] or ''
        }
        timestamp = time.strptime(element['timestamp'], '%Y-%m-%dT%H:%M:%S')
        res['date'] = date.fromtimestamp(time.mktime(timestamp))
        res['timestamp'] = int(time.mktime(timestamp))
        url = element['domain'] or ''
        if '.onion' in url:
            res['url_tor2web'] = url.replace('.onion', '.tor2web.org')
        res['url'] = url
        results_obj[res['domain']] = res
    return results_obj.values()

def add_result(answer, host, results):
    """Add new search result and get the stats about it."""
    if host:
        onion_id = host.replace(".onion", "")
        tor2web, backlinks, clicks = get_popularity(onion_id)
        if tor2web > 0 or backlinks > 0 or clicks > 0:
            results.append(Popularity(host, answer, tor2web, backlinks, clicks))
    else:
        results.append(Popularity(host, answer, 1, 1, 1))

def get_popularity(onion):
    """Calculate the popularity of an onion page."""
    try:
        hs = HiddenWebsite.objects.get(id=onion)
    except ObjectDoesNotExist:
        return 1, 1, 1
    if hs.banned:
        return 0, 0, 0
    try:
        pop = HiddenWebsitePopularity.objects.get(about=hs)
        clicks = pop.clicks
        public_backlinks = pop.public_backlinks
        tor2web = pop.tor2web
        return tor2web, public_backlinks, clicks
    except ObjectDoesNotExist:
        return 1, 1, 1

def sort_results(p_tuples):
    """Sort the results according to stats."""
    # Scaling the number of backlinks
    p_by_backlinks = sorted(p_tuples, key=lambda popularity: popularity.backlinks, reverse=True)
    for index, p_info in enumerate(p_by_backlinks):
        p_info.backlinks = 1 / (float(index) + 1)
    # Scaling the number of clicks
    p_by_clicks = sorted(p_by_backlinks, key=lambda popularity: popularity.clicks, reverse=True)
    for index, p_info in enumerate(p_by_clicks):
        p_info.clicks = 1 / (float(index) + 1)
    # Scaling the number of Tor2web
    p_by_tor2web = sorted(p_by_clicks, key=lambda popularity: popularity.tor2web, reverse=True)
    for index, p_info in enumerate(p_by_tor2web):
        p_info.tor2web = 1 / (float(index) + 1)
    p_by_sum = sorted(p_by_tor2web, key=lambda popularity: popularity.sum(), reverse=True)
    html_answer = ""
    for p_info in p_by_sum:
        html_answer = html_answer + p_info.content
    return html_answer

class Popularity(object):
    """Popularity by Tor2web visits, backlinks and clicks."""
    def __init__(self, url, content, tor2web, backlinks, clicks):
        self.url = url
        self.content = content
        self.tor2web = float(tor2web)
        self.backlinks = float(backlinks)
        self.clicks = float(clicks)
    def func(self):
        """Print the sum function."""
        print "2.0*%f + 3.0*%f + 1.0*%f" % self.tor2web, self.backlinks, self.clicks
    def sum(self):
        """Calculate the popularity."""
        #The model can be very simple (sum)
        #What are the proper coefficients?
        sum_function = 2.0*self.tor2web + 3.0*self.backlinks + 1.0*self.clicks
        return sum_function
    def __repr__(self):
        return repr((self.url, self.tor2web, self.backlinks, self.clicks, self.sum))
<|endoftext|>


<commit_before>import datetime
import numpy as np

def datetime2jd(dt):
    return (367.0 * dt.year
            - np.floor((7 * (dt.year + np.floor((dt.month + 9) / 12.0))) * 0.25)
            + np.floor(275 * dt.month / 9.0)
            + dt.day + 1721013.5
            + ((dt.second / 60.0 + dt.minute) / 60.0 + dt.hour) / 24.0)
<commit_msg>Add non-datetime functionality.<commit_after>from __future__ import division
import datetime
from collections import namedtuple
import numpy as np

def sidereal(jd):
    jd = np.atleast_1d(jd)
    t_ut1 = (jd - 2451545)/36525
    sidereal = 67310.54841 + (876600*3600 + 8640184.812866)*t_ut1 + .093104*t_ut1**2 - 6.2e-6*t_ut1**3
    sidereal = np.remainder(np.radians(sidereal/240),2*np.pi)
    if len(sidereal) == 1:
        return np.asscalar(sidereal)
    else:
        return sidereal

def calendar2jd(year, month, day, hour=0, minute=0, second=0, microsecond=0):
    year = np.atleast_1d(year)
    month = np.atleast_1d(month)
    day = np.atleast_1d(day)
    hour = np.atleast_1d(hour)
    minute = np.atleast_1d(minute)
    second = np.atleast_1d(second)
    microsecond = np.atleast_1d(microsecond)
    jd = (367 * year
            - np.floor((7 * (year + np.floor((month + 9) / 12.0))) * 0.25)
            + np.floor(275 * month / 9)
            + day + 1721013.5
            + (((microsecond / 1e6 + second) / 60 + minute) / 60 + hour) / 24)
    if len(jd) == 1:
        return np.asscalar(jd)
    else:
        return jd

def datetime2jd(dt):
    try:
        years = [d.year for d in dt]
        months = [d.month for d in dt]
        days = [d.day for d in dt]
        hours = [d.hour for d in dt]
        minutes = [d.minute for d in dt]
        seconds = [d.second for d in dt]
        microseconds = [d.microsecond for d in dt]
    except TypeError:
        years = dt.year
        months = dt.month
        days = dt.day
        hours = dt.hour
        minutes = dt.minute
        seconds = dt.second
        microseconds = dt.microsecond
    return calendar2jd(years, months, days, hours, minutes, seconds, microseconds)

def jd2calendar(jd):
    jd = np.atleast_1d(jd) + .5
    z = np.trunc(jd)
    f = jd - z
    return f, z
    z_smaller = z < 2299161
    z_greater = z > 2299161
    a = np.zeros(jd.shape)
    alpha = np.zeros(jd.shape)
    if np.any(z_smaller):
        a[z_smaller] = z[z_smaller]
    if np.any(z_greater):
        alpha[z_greater] = np.trunc((z[z_greater] - 1867216.25)/36524.25)
        a[z_greater] = z[z_greater] + 1 + alpha - np.trunc(alpha/4)
    b = a + 1524
    c = np.trunc((b - 122.1)/365.25)
    d = np.trunc(365.25*c)
    e = np.trunc((b - d)/30.6001)
    day = np.floor(b - d - np.trunc(30.6001*e) + f)
    day = day.astype(np.int64, copy=False)
    e_smaller = e < 14
    e_greater = e > 14
    month = np.zeros(jd.shape, dtype=np.int64)
    if np.any(e_smaller):
        month[e_smaller] = e[e_smaller] - 1
    if np.any(e_greater):
        month[e_greater] = e[e_greater] - 13
    month_smaller = month > 2
    month_greater = month < 2
    year = np.zeros(jd.shape, dtype=np.int64)
    if np.any(month_smaller):
        year[month_smaller] = c[month_smaller] - 4716
    if np.any(month_greater):
        year[month_greater] = c[month_greater] - 4715
    hours = f*24
    hour = np.floor(hours).astype(np.int64, copy=False)
    minutes = (hours - hour) * 60
    minute = np.floor(minutes).astype(np.int64, copy=False)
    seconds = (minutes - minute) * 60
    second = np.floor(seconds).astype(np.int64, copy=False)
    microsecond = ((seconds - second) * 1e6).astype(np.int64, copy=False)
    # t = f*86400
    # hmod = np.mod(t, 3600)
    # second = np.mod(hmod, 60)
    # hour = np.trunc((t - hmod)/3600)
    # hour = hour.astype(np.int64, copy=False)
    # minute = np.trunc((hmod - second)/60)
    # minute = minute.astype(np.int64, copy=False)
    if len(year) == 1:
        return (np.asscalar(year),
                np.asscalar(month),
                np.asscalar(day),
                np.asscalar(hour),
                np.asscalar(minute),
                np.asscalar(second),
                np.asscalar(microsecond),
                )
    else:
        return year, month, day, hour, minute, second, microsecond

def jd2datetime(jd):
    jd = np.atleast_1d(jd)
    cal = jd2calendar(jd)
    try:
        return [datetime.datetime(year, month, day, hour, minute, second, microsecond)
                for year, month, day, hour, minute, second, microsecond in cal]
    except TypeError:
        return datetime.datetime(*cal)

<|endoftext|>


<commit_before># built-ins
import itertools as it

# libraries
import numpy as np
import networkx as nx

# local modules

def lowest_common_ancestor(t, u, v):
    """Find the lowest common ancestor of two nodes

    Parameters
    ----------
    t : an `Ultrametric` tree or `nx.DiGraph`
        The tree or directed acyclic graph on which to find ancestors.
    u, v : int
        The node IDs in `t` for which to find the common ancestor

    Returns
    -------
    c : int or None
        The lowest common ancestor of `u` and `v` in `t`. ``None`` if
        they do not have any common ancestors.

    Examples
    --------
    >>> t = Ultrametric([0, 1, 2])
    >>> t.merge(0, 1)
    3
    >>> t.merge(2, 3)
    4
    >>> t.ancestors(0)
    [3, 4]
    >>> t.ancestors(1)
    [3, 4]
    >>> t.ancestors(2)
    [4]
    >>> lowest_common_ancestor(t, 0, 1)
    3
    """
    au = t.ancestors(u)
    av = t.ancestors(v)
    common = set(au) & set(av)
    short = au if len(au) < len(av) else av
    for c in short:
        if c in common:
            return c
    return None

lca = lowest_common_ancestor

class Ultrametric(nx.DiGraph):
    """An ultrametric tree data structure"""
    def __init__(self, init_nodes=[]):
        super(Ultrametric, self).__init__()
        self.maxw = -np.inf
        self.add_nodes_from(init_nodes, w=self.maxw, num_leaves=1)
        self.id_counter = it.count(max([0] + init_nodes) + 1)

    def merge(self, u, v, w=0.0):
        """Merge two nodes u, v and return the ID of the new node.

        Parameters
        ----------
        u, v : int
            The nodes being merged.
        w : float
            The weight of the merge.

        Returns
        -------
        node_id : int
            The generated ID of the node resulting from the (u, v)
            merge.

        Notes
        -----
        To preserve the ultrametric condition, the true weight of the
        merge is the maximum of w, weight(u), and weight(v).
        """
        node_id = self.id_counter.next()
        self.maxw = max(w, self.maxw)
        subtree_maxw = max(w, self.node[u]['w'], self.node[v]['w'])
        num_leaves = self.num_leaves(u) + self.num_leaves(v)
        self.add_node(node_id, w=subtree_maxw, num_leaves=num_leaves)
        self.add_edges_from([(node_id, u), (node_id, v)])
        return node_id

    def split(self, u, v):
        split_node = lowest_common_ancestor(self, u, v)
        if split_node is None:
            return
        a = self.ancestors(split_node)
        num_leaves = self.node[split_node]['num_leaves']
        self.remove_node(split_node)
        for n in a:
            self.node[n]['num_leaves'] -= num_leaves

    def get_map(self, t=np.inf, source=None):
        """Compute a map from leaf nodes to roots at a certain height.

        Parameters
        ----------
        t : float, optional
            The threshold at which to cut the tree.
        source : int, optional
            Compute the map only for the tree rooted at `source`.

        Examples
        --------
        >>> t = Ultrametric(np.arange(6)) # tree with 6 leaves
        >>> t.merge(0, 1, 0.1) # merge nodes 1 and 2
        6
        >>> t.merge(2, 6, 0.2)
        7
        >>> t.merge(3, 7, 0.3)
        8
        >>> t.merge(4, 5, 0.1)
        9
        >>> t.merge(7, 8, 0.2)
        10
        >>> t.get_map(0.25)
        array([7, 7, 7, 3, 9, 9])
        >>> t.get_map(source=7)
        array([7, 7, 7])
        >>> t.get_map(source=9)
        array([0, 0, 0, 0, 9, 9])
        >>> t.get_map(0.15, 7)
        array([6, 6, 2])
        """
        if source is not None:
            des = nx.algorithms.dag.descendants(self, source)
            des.add(source)
            g = self.subgraph(des)
        else:
            g = self
        nodes = filter(lambda n: self.node[n]['w'] < t, g.nodes())
        g = self.subgraph(nodes)
        ccs = nx.algorithms.connected_components(g.to_undirected())
        ccs = [self.subgraph(ns) for ns in ccs]
        leavess = [filter(h.is_leaf, h.nodes()) for h in ccs]
        roots = [filter(h.is_root, h.nodes()) for h in ccs]
        max_leaf = max(map(max, leavess))
        forward_map = np.zeros(max_leaf + 1, int)
        for root, leaves in zip(roots, leavess):
            forward_map[leaves] = root
        return forward_map

    def is_leaf(self, node):
        return self.out_degree(node) == 0

    def is_root(self, node):
        return self.in_degree(node) == 0

    def num_leaves(self, n):
        return self.node[n]['num_leaves']

    def parent(self, n):
        p = self.predecessors(n)
        if len(p) == 1:
            return p[0]
        return None

    def ancestors(self, n):
        a = []
        node = n
        while self.parent(node) is not None:
            node = self.parent(node)
            a.append(node)
        return a

    def children(self, n):
        return self.successors(n)

def num_leaves(g, n):
    return g.node[n]['num_leaves']
<commit_msg>Run doctests if file is main<commit_after># built-ins
import itertools as it

# libraries
import numpy as np
import networkx as nx

# local modules

def lowest_common_ancestor(t, u, v):
    """Find the lowest common ancestor of two nodes

    Parameters
    ----------
    t : an `Ultrametric` tree or `nx.DiGraph`
        The tree or directed acyclic graph on which to find ancestors.
    u, v : int
        The node IDs in `t` for which to find the common ancestor

    Returns
    -------
    c : int or None
        The lowest common ancestor of `u` and `v` in `t`. ``None`` if
        they do not have any common ancestors.

    Examples
    --------
    >>> t = Ultrametric([0, 1, 2])
    >>> t.merge(0, 1)
    3
    >>> t.merge(2, 3)
    4
    >>> t.ancestors(0)
    [3, 4]
    >>> t.ancestors(1)
    [3, 4]
    >>> t.ancestors(2)
    [4]
    >>> lowest_common_ancestor(t, 0, 1)
    3
    """
    au = t.ancestors(u)
    av = t.ancestors(v)
    common = set(au) & set(av)
    short = au if len(au) < len(av) else av
    for c in short:
        if c in common:
            return c
    return None

lca = lowest_common_ancestor

class Ultrametric(nx.DiGraph):
    """An ultrametric tree data structure"""
    def __init__(self, init_nodes=[]):
        super(Ultrametric, self).__init__()
        self.maxw = -np.inf
        self.add_nodes_from(init_nodes, w=self.maxw, num_leaves=1)
        self.id_counter = it.count(max([0] + init_nodes) + 1)

    def merge(self, u, v, w=0.0):
        """Merge two nodes u, v and return the ID of the new node.

        Parameters
        ----------
        u, v : int
            The nodes being merged.
        w : float
            The weight of the merge.

        Returns
        -------
        node_id : int
            The generated ID of the node resulting from the (u, v)
            merge.

        Notes
        -----
        To preserve the ultrametric condition, the true weight of the
        merge is the maximum of w, weight(u), and weight(v).
        """
        node_id = self.id_counter.next()
        self.maxw = max(w, self.maxw)
        subtree_maxw = max(w, self.node[u]['w'], self.node[v]['w'])
        num_leaves = self.num_leaves(u) + self.num_leaves(v)
        self.add_node(node_id, w=subtree_maxw, num_leaves=num_leaves)
        self.add_edges_from([(node_id, u), (node_id, v)])
        return node_id

    def split(self, u, v):
        split_node = lowest_common_ancestor(self, u, v)
        if split_node is None:
            return
        a = self.ancestors(split_node)
        num_leaves = self.node[split_node]['num_leaves']
        self.remove_node(split_node)
        for n in a:
            self.node[n]['num_leaves'] -= num_leaves

    def get_map(self, t=np.inf, source=None):
        """Compute a map from leaf nodes to roots at a certain height.

        Parameters
        ----------
        t : float, optional
            The threshold at which to cut the tree.
        source : int, optional
            Compute the map only for the tree rooted at `source`.

        Examples
        --------
        >>> t = Ultrametric(np.arange(6)) # tree with 6 leaves
        >>> t.merge(0, 1, 0.1) # merge nodes 1 and 2
        6
        >>> t.merge(2, 6, 0.2)
        7
        >>> t.merge(3, 7, 0.3)
        8
        >>> t.merge(4, 5, 0.1)
        9
        >>> t.merge(7, 8, 0.2)
        10
        >>> t.get_map(0.25)
        array([7, 7, 7, 3, 9, 9])
        >>> t.get_map(source=7)
        array([7, 7, 7])
        >>> t.get_map(source=9)
        array([0, 0, 0, 0, 9, 9])
        >>> t.get_map(0.15, 7)
        array([6, 6, 2])
        """
        if source is not None:
            des = nx.algorithms.dag.descendants(self, source)
            des.add(source)
            g = self.subgraph(des)
        else:
            g = self
        nodes = filter(lambda n: self.node[n]['w'] < t, g.nodes())
        g = self.subgraph(nodes)
        ccs = nx.algorithms.connected_components(g.to_undirected())
        ccs = [self.subgraph(ns) for ns in ccs]
        leavess = [filter(h.is_leaf, h.nodes()) for h in ccs]
        roots = [filter(h.is_root, h.nodes()) for h in ccs]
        max_leaf = max(map(max, leavess))
        forward_map = np.zeros(max_leaf + 1, int)
        for root, leaves in zip(roots, leavess):
            forward_map[leaves] = root
        return forward_map

    def is_leaf(self, node):
        return self.out_degree(node) == 0

    def is_root(self, node):
        return self.in_degree(node) == 0

    def num_leaves(self, n):
        return self.node[n]['num_leaves']

    def parent(self, n):
        p = self.predecessors(n)
        if len(p) == 1:
            return p[0]
        return None

    def ancestors(self, n):
        a = []
        node = n
        while self.parent(node) is not None:
            node = self.parent(node)
            a.append(node)
        return a

    def children(self, n):
        return self.successors(n)

def num_leaves(g, n):
    return g.node[n]['num_leaves']

if __name__ == '__main__':
    import doctest
    doctest.testmod()

<|endoftext|>


<commit_before>from BeautifulSoup import BeautifulSoup
from config import *
import mechanize
import getpass
import time
import smtplib

def email(subj_code, subj_no, crn_no):
    #http://www.pythonforbeginners.com/code-snippets-source-code/using-python-to-send-email/
    header  = 'From: %s\n' % gmail[0]
    header += 'To: %s\n' % ','.join(email_to)
    header += 'Cc: %s\n' % ','.join([])
    header += 'Subject: COURSE REGISTRATION!!!\n\n'
    message = header + "REGISTER FAST!!! "+subj_code+" "+str(subj_no)+" "+str(crn_no)+" is available!"

    server = smtplib.SMTP('smtp.gmail.com:587')
    server.starttls()
    server.login(gmail[0],gmail[1])
    problems = server.sendmail(gmail[0], email_to, message)
    server.quit()

def login(br):
    #go past the login page
    br.set_handle_robots(False)
    url = "https://eas.admin.uillinois.edu/eas/servlet/EasLogin?redirect=https://webprod.admin.uillinois.edu/ssa/servlet/SelfServiceLogin?appName=edu.uillinois.aits.SelfServiceLogin&dad=BANPROD1"
    br.open(url)
    br.select_form(name="easForm")
    br["inputEnterpriseId"] = enterprise[0]
    br["password"] = enterprise[1]
    resp = br.submit()

def get_course_table(br, subj_code, subj_no):
    #go to the registration page
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_StuMainMnu"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_RegMnu"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_RegAgreementAdd"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/bwskfreg.P_AltPin" #fall 2013 selection page
    br.open(url)
    br.select_form(nr=1)
    br.submit()

    #on the reg page
    br.select_form(nr=1)
    resp = br.submit(name='REG_BTN', label='Class Search')

    br.select_form(nr=1)
    resp = br.submit(name='SUB_BTN', label = 'Advanced Search')

    br.select_form(nr=1)
    br.find_control(name="sel_subj", nr=1).value = [subj_code]
    br["sel_crse"] = R_COURSE = str(subj_no)
    resp = br.submit().read()

    # f_name = str(subj_code)+"_"+str(subj_no)+".txt"
    # f2 = open("raw_"+f_name, "w")
    # f2.write(resp)
    # f2.close()

    return resp

def parse_page(resp, crn_no):
    soup = BeautifulSoup(resp)
    table = soup.findAll("table",{"class":"datadisplaytable"})
    if(len(table) == 0):
        append_to_log("Can't find the course table")
        return False
    table = table[0]

    crn_nav_txt = table.find(text=str(crn_no))
    if crn_nav_txt is None:
        append_to_log("Can't find the CRN "+str(crn_no))
        return False

    crn_td = crn_nav_txt.parent.parent
    avail_td = crn_td.findPreviousSibling("td")

    if(avail_td.find("input") is not None):
        return True
    return False

def check_course(br, subj_code, subj_no, crn_no):
    resp = get_course_table(br, subj_code, subj_no)
    if parse_page(resp, crn_no):
        text = subj_code+" "+str(subj_no)+" is available"
        append_to_log(text)
        email(subj_code, subj_no, crn_no) #email the user
    else:
        text = subj_code+" "+str(subj_no)+" is un-available"
        append_to_log(text)

def run_routine():
    br = mechanize.Browser()
    login(br)
    for each in courses:
        check_course(br, each[0], each[1], each[2])
    br.close()

def append_to_log(text):
    f = open(log_file, 'a')
    text = time.ctime()+":"+text+"\n"
    f.write(text)
    f.close()

def check_log_file():
    try:
       with open(log_file): pass
    except IOError:
       f = open(log_file, 'w')
       f.write("created on:"+time.ctime()+"\n")
       f.close()

check_log_file()
while(1):
    append_to_log("running check")
    run_routine()
    time.sleep(time_interval)
    <commit_msg>added email test, prevents emaling failure<commit_after>from BeautifulSoup import BeautifulSoup
from config import *
import mechanize
import getpass
import time
import smtplib

def email(to, subj, msg):
    #http://www.pythonforbeginners.com/code-snippets-source-code/using-python-to-send-email/
    header  = 'From: %s\n' % gmail[0]
    header += 'To: %s\n' % ','.join(to)
    header += 'Cc: %s\n' % ','.join([])
    header += 'Subject: '+subj+'\n\n'
    message = header +  msg

    server = smtplib.SMTP('smtp.gmail.com:587')
    server.starttls()
    server.login(gmail[0],gmail[1])
    problems = server.sendmail(gmail[0], email_to, message)
    server.quit()

def course_email(subj_code, subj_no, crn_no):
    subject = "Course Registration!!!"
    message = "REGISTER FAST!!! "+subj_code+" "+str(subj_no)+" "+str(crn_no)+" is available!"
    to = email_to
    email(to, subject, message)

def test_email():
    to = email_to
    subj = "Test Course Check"
    message = "This is just a test email. Script to check the following courses has started. It will run every "+str(time_interval/60)+" mins"
    for each in courses:
        message += "\n"+each[0]+str(each[1])+" crn:"+str(each[2])
    email(to, subj, message)

def login(br):
    #go past the login page
    br.set_handle_robots(False)
    url = "https://eas.admin.uillinois.edu/eas/servlet/EasLogin?redirect=https://webprod.admin.uillinois.edu/ssa/servlet/SelfServiceLogin?appName=edu.uillinois.aits.SelfServiceLogin&dad=BANPROD1"
    br.open(url)
    br.select_form(name="easForm")
    br["inputEnterpriseId"] = enterprise[0]
    br["password"] = enterprise[1]
    resp = br.submit()

def get_course_table(br, subj_code, subj_no):
    #go to the registration page
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_StuMainMnu"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_RegMnu"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/twbkwbis.P_GenMenu?name=bmenu.P_RegAgreementAdd"
    br.open(url)
    url = "https://ui2web1.apps.uillinois.edu/BANPROD1/bwskfreg.P_AltPin" #fall 2013 selection page
    br.open(url)
    br.select_form(nr=1)
    br.submit()

    #on the reg page
    br.select_form(nr=1)
    resp = br.submit(name='REG_BTN', label='Class Search')

    br.select_form(nr=1)
    resp = br.submit(name='SUB_BTN', label = 'Advanced Search')

    br.select_form(nr=1)
    br.find_control(name="sel_subj", nr=1).value = [subj_code]
    br["sel_crse"] = R_COURSE = str(subj_no)
    resp = br.submit().read()

    # f_name = str(subj_code)+"_"+str(subj_no)+".txt"
    # f2 = open("raw_"+f_name, "w")
    # f2.write(resp)
    # f2.close()

    return resp

def parse_page(resp, crn_no):
    soup = BeautifulSoup(resp)
    table = soup.findAll("table",{"class":"datadisplaytable"})
    if(len(table) == 0):
        append_to_log("Can't find the course table")
        return False
    table = table[0]

    crn_nav_txt = table.find(text=str(crn_no))
    if crn_nav_txt is None:
        append_to_log("Can't find the CRN "+str(crn_no))
        return False

    crn_td = crn_nav_txt.parent.parent
    avail_td = crn_td.findPreviousSibling("td")

    if(avail_td.find("input") is not None):
        return True
    return False

def check_course(br, subj_code, subj_no, crn_no):
    resp = get_course_table(br, subj_code, subj_no)
    if parse_page(resp, crn_no):
        text = subj_code+" "+str(subj_no)+" is available"
        append_to_log(text)
        course_email(subj_code, subj_no, crn_no) #email the user
    else:
        text = subj_code+" "+str(subj_no)+" is un-available"
        append_to_log(text)

def run_routine():
    br = mechanize.Browser()
    login(br)
    for each in courses:
        check_course(br, each[0], each[1], each[2])
    br.close()

def append_to_log(text):
    f = open(log_file, 'a')
    text = time.ctime()+":"+text+"\n"
    f.write(text)
    f.close()

def check_log_file():
    try:
       with open(log_file): pass
    except IOError:
       f = open(log_file, 'w')
       f.write("created on:"+time.ctime()+"\n")
       f.close()

check_log_file()
test_email()
while(1):
    append_to_log("running check")
    run_routine()
    time.sleep(time_interval)
    <|endoftext|>