text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 행방불명.Game.Map;
using 행방불명.Game;

using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;

namespace 행방불명.Framework.UI
{
	public class MapView
		: View
	{
		Program app;
		GameStage stage;
		Player player;
		Mouse mouse;
		bool showMap, pressed;

		SolidColorBrush brush;
		Bitmap button, map, ping, buttonOn;
		Ellipse playerPos;
		TextFormat format;
		Matrix3x2 matrix;
		float scaleRate;

		public MapView(GameStage stage)
		{
			this.app = stage.App;
			this.stage = stage;
			this.mouse = app.Mouse;
			this.player = stage.Player;

			playerPos = new Ellipse(new Vector2(), 30, 30);
			brush = new SolidColorBrush(app.Graphics2D.RenderTarget, new Color4(1, 0, 0, 1));
			button = app.Media.BitmapDic["map_icon"];
            buttonOn = app.Media.BitmapDic["map_icon_pressed"];
			map = app.Media.BitmapDic["map"];
			ping = app.Media.BitmapDic["ping_danger"];
			format = app.Media.FormatDic["default"];
			Rect = new RectangleF(0, 400, button.Size.Width, button.Size.Height);

			Draw += DrawButton;
			Update += CheckClick;
		}

		private void CheckClick(float delta)
		{
			scaleRate += delta;
			if (scaleRate > 1)
				scaleRate = 0;

			if (!pressed && mouse[0])
				pressed = true;
			if (pressed && !mouse[0])
			{
                pressed = false;
                if (showMap)
                {
                    showMap = !showMap;
                    mouse.IsUsed = true;
                }
                else if (!mouse.IsUsed
                    && Rect.Contains(mouse.X, mouse.Y))
                {
                    showMap = !showMap;
                    mouse.IsUsed = true;
                }
			}
		}

		private void DrawButton()
		{
            Bitmap bitmap;
            if (Rect.Contains(mouse.X, mouse.Y))
                bitmap = buttonOn;
            else
                bitmap = button;

			app.Graphics2D.RenderTarget.DrawBitmap(
				bitmap,
				Rect,
				1,
				BitmapInterpolationMode.Linear
				);

			if (showMap)
				DrawMap();
		}

		private void DrawMap()
		{
			var w = stage.GameView.MapWidth;
			var h = stage.GameView.MapHeight;
			var mw = map.PixelSize.Width;
			var mh = map.PixelSize.Height;
			var pos = player.CurrentPosition;

			var mx = (app.Width - mw) / 2;
			var my = (app.Height - mh) / 2;

			var px = pos.X / w * mw * 0.975f;
			var py = pos.Y / h * mh * 0.975f;

			app.Graphics2D.Draw(
				map,
				mx,
				my
				);

			playerPos.Point = new Vector2(mx + px, my + py);

			float scale = (float)Math.Pow(Math.Sin(scaleRate * 6.243f) / 7 + 1, 2);

			Matrix3x2.Scaling(scale, scale, ref playerPos.Point, out matrix);
			app.Graphics2D.RenderTarget.Transform = matrix;
			app.Graphics2D.DrawCenter(
				ping,
				mx + px,
				my + py
				);
			app.Graphics2D.RenderTarget.Transform = Matrix3x2.Identity;

			/*
			app.Graphics2D.RenderTarget.DrawText(
				String.Format(
					"{0}, {1}\n"+
					"{2}, {3}\n"+
					"{4}, {5}",
					playerPos.Point.X, playerPos.Point.Y,
					pos.X, pos.Y,
					w, h
					),
				format,
				app.RectF,
				brush
				);
			 */
		}
	}
}


text:
```
gradlew unitypluginsdk:makeJar
```

### output jar path

unitypluginsdk/release/laiblitzLibrary.jar

## usage

Unity side

Copy to Unity's plugin folder

```
cp unitypluginsdk/release/laiblitzLibrary.jar [UnityRoot]/Assets/Plugin/Android/
```

### Toast

```csharp
using (var util = new AndroidJavaClass("jp.co.laiblitz.android.unity.ToastUtil"))
{
    util.CallStatic("showLong", "Toast long length.");
    util.CallStatic("showShort", "Toast short length.");
}
```

### Get version code

```csharp
#if UNITY_ANDROID
/// <summary>
/// Get android version code
/// </summary>
private static int GetAppVersionCode_Android()
{
    int code;
    using (var util = new AndroidJavaClass("jp.co.laiblitz.android.unity.PackageInfoUtil"))
        code = util.CallStatic<int>("getVersionCode");
    return code;
}

/// <summary>
/// Get android version name
/// </summary>
private static string GetAppVersionName_Android()
{
    string name;
    using (var util = new AndroidJavaClass("jp.co.laiblitz.android.unity.PackageInfoUtil"))
        name = util.CallStatic<string>("getVersionName");
    return name;
}
#endif
```
### Get customScheme

```csharp
/// <summary>
/// get customscheme
/// </summary>
/// <param name="isClean">with cache clear</param>
public static string GetCustomScheme(bool isClean = false)
{
#if UNITY_EDITOR
    return string.Empty;
#else
    var jObj = new AndroidJavaObject("jp.co.laiblitz.android.ApplicationCache");
    var instance = jObj.CallStatic<AndroidJavaObject>("getInstance");
    var customScheme = instance.Get<string>("customScheme");

    if (isClean)
        instance.Set<string>("customScheme", null);

    return customScheme;
#endif
    }
}
```

AndroidManifest.xml

```xml
<!-- URL scheme (com.example.unity://) -->
<activity android:name="jp.co.laiblitz.android.IntentReceiveActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="com.example.unity" />
  </intent-filter>
</activity>
```

### Local notification

```csharp
using UnityEngine;
using System.Runtime.InteropServices;
using System;

namespace App.Util
{
    /// <summary>
    /// Local notification utility
    /// </summary>
    public static class LocalNotificationUtil
    {
#if UNITY_EDITOR
#elif UNITY_ANDROID
        private static readonly string javaPackageNamge = "jp.co.laiblitz.android.unity.NotificationUtil";

        private static AndroidJavaObject javaObj
        {
            get
            {
                if (_javaObj == null)
                    _javaObj = new AndroidJavaObject(javaPackageNamge);
                return _javaObj;
            }
        }
        private static AndroidJavaObject _javaObj = null;
#endif

        /// <summary>
        /// Add schedule
        /// </summary>
        /// <param name="notificationId">Identification id</param>
        /// <param name="title">message title</param>
        /// <param name="message">message body</param>
        /// <param name="delay">notification delay time</param>
        public static void SetLocalNotification(int notificationId, string title, string message, int delay)
        {
#if UNITY_EDITOR
#elif UNITY_ANDROID
            javaObj.CallStatic("setLocalNotification", notificationId, title, message, delay);
#endif
        }

        /// <summary>
        /// Delete added schedule
        /// </summary>
        /// <param name="notificationId">Identification id</param>
        public static void CancelLocalNotification(int notificationId)
        {
#if UNITY_EDITOR
#elif UNITY_ANDROID
            javaObj.CallStatic("cancelLocalNotification", notificationId);
#endif
        }
    }
}

```

AndroidManifest.xml

```xml
<!-- local notification receiver -->
<receiver android:name="jp.co.laiblitz.android.NotificationReceiver" />
```

redpajama_set_name: RedPajamaGithub


text:
package org.keycloak.protocol.oidc;

import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.AbstractLoginProtocolFactory;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.mappers.AddressMapper;
import org.keycloak.protocol.oidc.mappers.FullNameMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.services.managers.AuthenticationManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;

/**
 * @author Bill Burke
 * @version $Revision: 1 $
 */
public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {

    public static final String USERNAME = "username";
    public static final String EMAIL = "email";
    public static final String EMAIL_VERIFIED = "email verified";
    public static final String GIVEN_NAME = "given name";
    public static final String FAMILY_NAME = "family name";
    public static final String FULL_NAME = "full name";
    public static final String LOCALE = "locale";
    public static final String USERNAME_CONSENT_TEXT = "${username}";
    public static final String EMAIL_CONSENT_TEXT = "${email}";
    public static final String EMAIL_VERIFIED_CONSENT_TEXT = "${emailVerified}";
    public static final String GIVEN_NAME_CONSENT_TEXT = "${givenName}";
    public static final String FAMILY_NAME_CONSENT_TEXT = "${familyName}";
    public static final String FULL_NAME_CONSENT_TEXT = "${fullName}";
    public static final String LOCALE_CONSENT_TEXT = "${locale}";

    @Override
    public LoginProtocol create(KeycloakSession session) {
        return new OIDCLoginProtocol().setSession(session);
    }

    @Override
    public List<ProtocolMapperModel> getBuiltinMappers() {
        return builtins;
    }

    @Override
    public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
        return defaultBuiltins;
    }

    static List<ProtocolMapperModel> builtins = new ArrayList<>();
    static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();

    static {

        ProtocolMapperModel model;
        model = UserPropertyMapper.createClaimMapper(USERNAME,
                "username",
                "preferred_username", "String",
                true, USERNAME_CONSENT_TEXT,
                true, true);
        builtins.add(model);
        defaultBuiltins.add(model);
        model = UserPropertyMapper.createClaimMapper(EMAIL,
                "email",
                "email", "String",
                true, EMAIL_CONSENT_TEXT,
                true, true);
        builtins.add(model);
        defaultBuiltins.add(model);
        model = UserPropertyMapper.createClaimMapper(GIVEN_NAME,
                "firstName",
                "given_name", "String",
                true, GIVEN_NAME_CONSENT_TEXT,
                true, true);
        builtins.add(model);
        defaultBuiltins.add(model);
        model = UserPropertyMapper.createClaimMapper(FAMILY_NAME,
                "lastName",
                "family_name", "String",
                true, FAMILY_NAME_CONSENT_TEXT,
                true, true);
        builtins.add(model);
        defaultBuiltins.add(model);
        model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
                "emailVerified",
                "email_verified", "boolean",
                false, EMAIL_VERIFIED_CONSENT_TEXT,
                true, true);
        builtins.add(model);
        model = UserAttributeMapper.createClaimMapper(LOCALE,
                "locale",
                "locale", "String",
                false, LOCALE_CONSENT_TEXT,
                true, true, false);
        builtins.add(model);

        ProtocolMapperModel fullName = new ProtocolMapperModel();
        fullName.setName(FULL_NAME);
        fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID);
        fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
        fullName.setConsentRequired(true);
        fullName.setConsentText(FULL_NAME_CONSENT_TEXT);
        Map<String, String> config = new HashMap<String, String>();
        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
        fullName.setConfig(config);
        builtins.add(fullName);
        defaultBuiltins.add(fullName);

        ProtocolMapperModel address = AddressMapper.createAddressMapper();
        builtins.add(address);

        model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
                KerberosConstants.GSS_DELEGATION_CREDENTIAL,
                KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
                true, "${gssDelegationCredential}",
                true, false);
        builtins.add(model);
    }

    @Override
    protected void addDefaults(ClientModel client) {
        for (ProtocolMapperModel model : defaultBuiltins) client.addProtocolMapper(model);
    }

    @Override
    public Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
        return new OIDCLoginProtocolService(realm, event, authManager);
    }

    @Override
    public String getId() {
        return "openid-connect";
    }
}


text:
category: "sermon"
layout: sermon
series: ""
title: "Living and Being a Christian in Different Places."
date: "2015-08-30 10:30:00"
audio: "http://media.coggesparish.com/2015-08-30 Mission.mp3"
speaker: "Jono Fisher, Hilary Warner, Andrew Kingston, Julia Morgan, Andy and Hannah Bailey"
speaker_info: ""
duration: "50"
youtube_videos: [""]
vimeo_videos: [""]
slides: ["http://media.coggesparish.com/2015-08-30 Gasenyi.pdf","http://media.coggesparish.com/2015-08-30 Besom.pdf","http://media.coggesparish.com/2015-08-30 The Missional Creed.pdf"]
files: ["http://media.coggesparish.com/2015-08-30 Worship Gasenyi.wmv","http://media.coggesparish.com/2015-08-30 Christians Against Poverty.wmv"]
bible_passages: ["Isaiah 58:6-9 The Message"]
bible_links: ["Isaiah 58:6-9&version=MSG"]
bible_videos: [""]
bible_videos_links: [""]
---


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
Mycotaxon 16(1): 64 (1982)

#### Original name
Pseudocercospora oroxyligena J.M. Yen, A.K. Kar & B.K. Das

### Remarks
null


text:
import copy
import logging
import time
import traceback
import types

from quixote import form2
from quixote.html import htmltext

import canary.context
from canary.gazeteer import Feature
from canary.qx_defs import MyForm
from canary.utils import DTable, render_capitalized
import dtuple

class ExposureRoute (DTable):

    # A Methodology can have one to many ROUTEs
    ROUTE = {
        '-': -1,
        'ingestion' : 1,
        'inhalation' : 2,
        'mucocutaneous' : 3,
        'vector' : 4,
        'other' : 5,
        }

    def __init__ (self):
        self.uid = -1
        self.study_id = -1
        self.methodology_id = -1
        self.route = self.ROUTE['-']

    def __str__ (self):
        out = []
        out.append('<Route uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\troute=%s' % self.get_text_value(self.ROUTE, self.route))
        out.append('\tmethodology_id=%s' % self.methodology_id)
        out.append('/>')
        return '\n'.join(out)

    def get_text_value (self, lookup_table, value):
        for k, v in lookup_table.iteritems():
            if v == value:
                return k
        return ''

    def set_route (self, route):

        if type(route) is types.StringType:

            if route in self.ROUTE.keys():
                self.route = self.ROUTE[route]

        elif type(route) is types.IntType:

            if route in self.ROUTE.values():
                self.route = route

    def get_route (self, text=False):
        if text:
            return self.get_text_value(self.ROUTE, self.route)
        else:
            return self.route

    def delete (self, context):
        """
        Delete this route from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM exposure_routes
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('ExposureRoute: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO exposure_routes
                (uid, study_id, methodology_id, route)
                VALUES
                (NULL, %s, %s, %s)
                """, (self.study_id, self.methodology_id, self.route)
                )
            self.uid = self.get_new_uid(context)
        else:
            # Assume all calls to save() are after all routes have been removed
            # already by "DELETE FROM exposure_routes" in methodology.save()
            try:
                cursor.execute("""
                    INSERT INTO exposure_routes
                    (uid, study_id, methodology_id, route)
                    VALUES
                    (%s, %s, %s, %s)
                    """, (self.uid, self.study_id, self.methodology_id, self.route)
                )
            except Exception, e:
                context.logger.error('ExposureRoute: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

class Methodology (DTable):

    TABLE_NAME = 'methodologies'

    # A Methodology must have one TYPE
    TYPES = {
        'experimental' : 1,
        'descriptive' : 2,
        'aggregate' : 3,
        'cross sectional' : 4,
        'cohort' : 5,
        'case control' : 6,
        'disease model' : 7,
        }

    # A Methodology can have at most one TIMING
    TIMING = {
        '-': -1,
        'unknown' : 0,
        'historical' : 1,
        'concurrent' : 2,
        'repeated' : 3,
        'mixed' : 4,
        }

    # A Methodology can have at most one SAMPLING
    SAMPLING = {
        '-': -1,
        'unknown' : 0,
        'exposure' : 1,
        'outcome' : 2,
        'both' : 3,
        }

    # A Methodology can have at most one CONTROLS
    CONTROLS = {
        '-': -1,
        'no' : 0,
        'yes' : 1,
        'both' : 2,
        }

    def __init__ (self, uid=-1):
        self.uid = uid
        self.study_id = -1
        self.study_type_id = -1
        self.sample_size = ''
        self.timing = -1
        self.sampling = -1
        self.controls = -1
        self.is_mesocosm = False
        self.is_enclosure = False
        self.exposure_routes = []
        self.comments = ''
        self.date_modified = None
        self.date_entered = None

    def __str__ (self):
        out = []
        out.append('<Methodology uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tstudy_type=%s' % self.get_text_value(self.TYPES, self.study_type_id))
        out.append('\tsample_size=%s' % self.sample_size)
        for item in ['timing', 'sampling', 'controls', 'exposure_routes']:
            out.append('\t%s=%s' % (item, getattr(self, 'get_' + item)(text=True)))
        out.append('\tis_mesocosm=%s, is_enclosure=%s' % (self.is_mesocosm, self.is_enclosure))
        out.append('\tcomments=%s' % self.comments or '')
        out.append('/>')
        return '\n'.join(out)

    def evidence_level (self):
        """
        Return the evidence level relative to the type of study
        performed.
        """
        text_value = self.get_text_value(self.TYPES, self.study_type_id)
        if text_value in ['experimental', 'cohort']:
            return 3
        elif text_value in ['case control', 'cross sectional', 'aggregate']:
            return 2
        elif text_value in ['descriptive', 'disease model']:
            return 1
        else:
            return 0

    def get_text_value (self, lookup_table, value):
        for k, v in lookup_table.iteritems():
            if v == value:
                return k
        return ''

    def set_timing (self, timing):

        if type(timing) is types.StringType:

            if timing in self.TIMING.keys():
                self.timing = self.TIMING[timing]

        elif type(timing) is types.IntType:

            if timing in self.TIMING.values():
                self.timing = timing

    def get_timing (self, text=False):
        if text:
            return self.get_text_value(self.TIMING, self.timing)
        else:
            return self.timing

    def set_sampling (self, sampling):

        if type(sampling) is types.StringType:

            if sampling in self.SAMPLING.keys():
                self.sampling = self.SAMPLING[sampling]

        elif type(sampling) is types.IntType:

            if sampling in self.SAMPLING.values():
                self.sampling = sampling

    def get_sampling (self, text=False):
        if text:
            return self.get_text_value(self.SAMPLING, self.sampling)
        else:
            return self.sampling

    def set_controls (self, controls):

        if type(controls) is types.StringType:

            if controls in self.CONTROLS.keys():
                self.controls = self.CONTROLS[controls]

        elif type(controls) is types.IntType:

            if controls in self.CONTROLS.values():
                self.controls = controls

    def get_controls (self, text=False):
        if text:
            return self.get_text_value(self.CONTROLS, self.controls)
        else:
            return self.controls

    def set_routes (self, routes):

        for route in routes:
            self.add_route(route)

        # Remove routes no longer specified
        for route in self.exposure_routes:
            if not route.get_route() in [r.get_route() for r in routes]:
                self.exposure_routes.remove(route)

    def add_route (self, route):

        if not route.get_route() in [r.get_route() for r in self.exposure_routes]:
            route.methodology_id = self.uid
            route.study_id = self.study_id
            self.exposure_routes.append(route)

    def get_routes (self, text=False):

        if text:
            return [r.get_text_value(r.ROUTE, r.route) for r in self.exposure_routes]
        else:
            return self.exposure_routes

    def set_study_type (self, value):
        """
        Each methodology has exactly one type.
        """
        if type(value) is types.StringType:

            if value in self.TYPES.keys():
                self.study_type_id = self.TYPES[value]

        elif type(value) == type(htmltext('a')):

            str_value = str(value)
            if str_value in self.TYPES.keys():
                self.study_type_id = self.TYPES[str_value]

        elif type(value) is types.IntType:

            if value in self.TYPES.values():
                self.study_type_id = value

        self.update_values()

    def get_study_type (self, text=False):
        """
        Return the study design type.
        """
        if text:
            return self.get_text_value(self.TYPES, self.study_type_id)
        else:
            return self.study_type_id

    def update_values (self):
        """
        To keep values consistent with methodology type, "blank out"
        inapplicable ones; called by set_study_type() on update.
        """
        if self.get_study_type() in [
            self.TYPES['experimental'],
            self.TYPES['descriptive'],
            self.TYPES['disease model'],
            ]:
            self.set_timing('-')

        if not self.get_study_type() in [
            self.TYPES['cross sectional'],
            self.TYPES['cohort'],
            self.TYPES['case control']
            ]:
            self.set_controls('-')

        if not self.get_study_type() in [
            self.TYPES['cross sectional']
            ]:
            self.set_sampling('-')

    def delete (self, context):
        """
        Delete this methodology, and its exposure_routes, from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM methodologies
                    WHERE uid = %s
                    """, self.uid)

                cursor.execute("""
                    DELETE FROM exposure_routes
                    where methodology_id = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('Methodology: %s (%s)', self.uid, e)

    def load_routes (self, context):
        cursor = context.get_cursor()
        cursor.execute("""
            SELECT * FROM exposure_routes
            WHERE methodology_id = %s
            """, (self.uid))
        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            exp_route = ExposureRoute()
            for field in fields:
                exp_route.set(field, row[field])
            self.add_route(exp_route)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO methodologies
                (uid, study_id, study_type_id,
                sample_size, timing,
                sampling, controls, comments,
                is_mesocosm, is_enclosure,
                date_modified, date_entered)
                VALUES
                (NULL, %s, %s,
                %s, %s,
                %s, %s, %s,
                %s, %s,
                NOW(), NOW())
                """, (self.study_id, self.study_type_id,
                self.sample_size, self.timing,
                self.sampling, self.controls, self.comments,
                int(self.is_mesocosm), int(self.is_enclosure))
                )
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE methodologies
                    SET study_id = %s, study_type_id = %s,
                    sample_size = %s, timing = %s,
                    sampling = %s, controls = %s, comments = %s,
                    is_mesocosm = %s, is_enclosure = %s,
                    date_modified = NOW()
                    WHERE uid = %s
                    """, (self.study_id, self.study_type_id,
                    self.sample_size, self.timing,
                    self.sampling, self.controls, self.comments,
                    int(self.is_mesocosm), int(self.is_enclosure),
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Methodology: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

        # Refill these values every time
        cursor.execute("""
            DELETE FROM exposure_routes
            WHERE methodology_id = %s
            """, self.uid)

        for route in self.exposure_routes:
            route.save(context)

    def create_form (self, context):

        form = MyForm(context)

        # all methodology types get a sample size
        form.add(form2.StringWidget, 'sample_size',
            title='Sample size (study n)',
            size=10, value=self.sample_size,
            required=False)

        # all methodology types get one or more routes
        route_options = [(route, text, route) for text, route in ExposureRoute.ROUTE.items()]
        # FIXME: what else to do about leaving out the default/empty?
        route_options.remove((-1, '-', -1))
        select_size = len(route_options)
        form.add(form2.MultipleSelectWidget, 'exposure_routes',
            title='Routes of exposure (ctrl-click to select or change multiple)',
            value=[r.route for r in self.get_routes()],
            options=route_options,
            size=select_size,
            sort=False,
            required=True)

        # experimental can be is_mesocosm=True
        if self.get_study_type() == self.TYPES['experimental']:
            form.add(form2.CheckboxWidget, 'is_mesocosm',
                title='Is mesocosm?',
                value=self.is_mesocosm)

        # methodology types except experimental get timing
        if not self.get_study_type() == self.TYPES['experimental']:
            form.add(form2.SingleSelectWidget, 'timing',
                title='Timing',
                value=self.get_timing(),
                options=[(val, name, val) for name, val in self.TIMING.items()],
                sort=True,
                required=True)

        # all the 'c*' methodology types get controls
        if self.get_study_type() in [
            self.TYPES['cross sectional'],
            self.TYPES['cohort'],
            self.TYPES['case control']
            ]:
            form.add(form2.SingleSelectWidget, 'controls',
                title='Controls from same population?',
                value=self.get_controls(),
                options=[(val, name, val) for name, val in self.CONTROLS.items()],
                sort=True,
                required=True)

        # cohort can be is_enclosure=True
        if self.get_study_type() == self.TYPES['cohort']:
            form.add(form2.CheckboxWidget, 'is_enclosure',
                title='Is enclosure?',
                value=self.is_enclosure)

        # only cross sectional methodologies get sampling
        if self.get_study_type() == self.TYPES['cross sectional']:
            form.add(form2.SingleSelectWidget, 'sampling',
                title='Sampling',
                value=self.get_sampling(),
                options=[(val, name, val) for name, val in self.SAMPLING.items()],
                sort=True,
                required=True)

        # every methodology type has comments
        form.add(form2.TextWidget, 'comments',
            title='Comments',
            rows='4', cols='60',
            wrap='virtual',
            value=self.comments)

        form.add_submit('update', value='update')
        form.add_submit('finish', value='finish')

        return form

    def process_form (self, form):

        # all methodology types get a sample size
        if form['sample_size']:
            self.sample_size = form['sample_size']

        # all methodology types get one or more routes
        if form['exposure_routes']:
            routes = []
            for r in form['exposure_routes']:
                route = ExposureRoute()
                route.set_route(r)
                routes.append(route)
            self.set_routes(routes)
        else:
            form.set_error('exposure_routes', 'You must choose at least one route of exposure.')

        # experimental can be is_mesocosm=True
        if self.get_study_type() == self.TYPES['experimental']:
            if form['is_mesocosm']:
                self.is_mesocosm = True
            else:
                self.is_mesocosm = False

        # all methodology types but experimental get timing
        if not self.get_study_type() == self.TYPES['experimental']:
            if form['timing'] == self.TIMING['-']:
                form.set_error('timing', 'You must specifiy the timing.')
            else:
                self.set_timing(form['timing'])

        # all 'c*' methodology types get controls
        if self.get_study_type() in [
            self.TYPES['cross sectional'],
            self.TYPES['cohort'],
            self.TYPES['case control']
            ]:
            if form['controls'] == self.CONTROLS['-']:
                form.set_error('controls', 'You must specify the controls.')
            else:
                self.set_controls(form['controls'])

        # cohort can be is_enclosure=True
        if self.get_study_type() == self.TYPES['cohort']:
            if form['is_enclosure']:
                self.is_enclosure = True
            else:
                self.is_enclosure = False

        # only cross sectional gets sampling
        if self.get_study_type() == self.TYPES['cross sectional']:
            if form['sampling'] == self.SAMPLING['-']:
                form.set_error('sampling', 'You must specify the sampling.')
            else:
                self.set_sampling(form['sampling'])

        # every methodology type can have comments
        if form['comments']:
            self.comments = form['comments']

def find_exposures (context, search_term):
    exposures = {}
    if search_term \
        and len(search_term) > 0:

        cursor = context.get_cursor()
        query_term = search_term.strip().replace(' ', '% ') + '%'
        cursor.execute("""
            SELECT umls_terms.umls_concept_id, term, preferred_name, umls_source_id
            FROM umls_terms, umls_concepts, umls_concepts_sources
            WHERE term LIKE %s
            AND umls_concepts.umls_concept_id = umls_terms.umls_concept_id
            AND umls_concepts_sources.umls_concept_id = umls_concepts.umls_concept_id
            ORDER BY term, preferred_name
            """, query_term)

        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            if not exposures.has_key((row['umls_concept_id'], row['umls_source_id'])):
                exp = Exposure()
                exp.concept_source_id = row['umls_source_id']
                exp.concept_id = row['umls_concept_id']
                exp.term = row['preferred_name']
                exp.synonyms.append(row['term'])
                exposures[(exp.concept_id, exp.concept_source_id)] = exp
            else:
                exp = exposures[(row['umls_concept_id'], row['umls_source_id'])]
                if not row['term'] in exp.synonyms:
                    exp.synonyms.append(row['term'])
                exposures[(exp.concept_id, exp.concept_source_id)] = exp

        # Try to bump up coarse "relevance" of exact matches
        exposures_ranked = exposures.values()
        for exp in exposures_ranked:
            if exp.term.lower() == search_term.lower()\
                or search_term.lower() in [syn.lower() for syn in exp.synonyms]:
                exposures_ranked.remove(exp)
                exposures_ranked.insert(0, exp)

        return exposures_ranked

    else:
        return exposures.values()

class Exposure (DTable):

    TABLE_NAME = 'exposures'

    UMLS_SOURCES = {
        75: 'MeSH',
        85: 'NCBI Taxonomy',
        501: 'ITIS',
        }

    def __init__ (self):
        self.uid = -1
        self.study_id = -1
        self.concept_id = -1
        self.concept_source_id = -1
        self.term = ''
        self.synonyms = []

    def __str__ (self):
        out = []
        out.append('<Exposure uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tconcept_id=%s (%s)' % (self.concept_id, self.concept_source_id))
        out.append('\tterm=%s' % self.term)
        out.append('/>')
        return '\n'.join(out)

    def delete (self, context):
        """
        Delete this exposure from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM exposures
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('Exposure: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO exposures
                (uid, study_id, concept_id,
                concept_source_id, term)
                VALUES
                (NULL, %s, %s,
                %s, %s)
                """, (self.study_id, self.concept_id,
                self.concept_source_id, self.term)
                )
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE exposures
                    SET study_id = %s, concept_id = %s,
                    concept_source_id = %s, term = %s
                    WHERE uid = %s
                    """, (self.study_id, self.concept_id,
                    self.concept_source_id, self.term,
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Exposure: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

def find_outcomes (context, search_term):
    # Note: for now, limit to only MeSH (umls_source_id==75)

    outcomes = {}
    if search_term \
        and len(search_term) > 0:
        cursor = context.get_cursor()
        query_term = search_term.strip().replace(' ', '% ') + '%'
        cursor.execute("""
            SELECT umls_terms.umls_concept_id, term, preferred_name, umls_source_id
            FROM umls_terms, umls_concepts, umls_concepts_sources
            WHERE term LIKE %s
            AND umls_source_id = %s
            AND umls_concepts.umls_concept_id = umls_terms.umls_concept_id
            AND umls_concepts_sources.umls_concept_id = umls_concepts.umls_concept_id
            ORDER BY term, preferred_name
            """, (query_term, 75))

        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            if not outcomes.has_key((row['umls_concept_id'], row['umls_source_id'])):
                outcome = Outcome()
                outcome.concept_source_id = row['umls_source_id']
                outcome.concept_id = row['umls_concept_id']
                outcome.term = row['preferred_name']
                outcome.synonyms.append(row['term'])
                outcomes[(outcome.concept_id, outcome.concept_source_id)] = outcome
            else:
                outcome = outcomes[(row['umls_concept_id'], row['umls_source_id'])]
                if not row['term'] in outcome.synonyms:
                    outcome.synonyms.append(row['term'])
                outcomes[(outcome.concept_id, outcome.concept_source_id)] = outcome

        # Try to bump up coarse "relevance" of exact matches
        outcomes_ranked = outcomes.values()
        for outcome in outcomes_ranked:
            if outcome.term.lower() == search_term.lower()\
                or search_term.lower() in [syn.lower() for syn in outcome.synonyms]:
                outcomes_ranked.remove(outcome)
                outcomes_ranked.insert(0, outcome)
        return outcomes_ranked

    else:
        return outcomes.values()

class Outcome (DTable):

    TABLE_NAME = 'outcomes'

    UMLS_SOURCES = {
        75: 'MeSH',
        85: 'NCBI Taxonomy',
        501: 'ITIS',
        }

    def __init__ (self):
        self.uid = -1
        self.study_id = -1
        self.concept_id = -1
        self.concept_source_id = -1
        self.term = ''
        self.synonyms = []

    def __str__ (self):
        out = []
        out.append('<Outcome uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tconcept_id=%s (%s)' % (self.concept_id, self.concept_source_id))
        out.append('\tterm=%s' % self.term)
        out.append('/>')
        return '\n'.join(out)

    def delete (self, context):
        """
        Delete this outcome from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM outcomes
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('Outcome: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO outcomes
                (uid, study_id, concept_id,
                concept_source_id, term)
                VALUES
                (NULL, %s, %s,
                %s, %s)
                """, (self.study_id, self.concept_id,
                self.concept_source_id, self.term)
                )
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE outcomes
                    SET study_id = %s, concept_id = %s,
                    concept_source_id = %s, term = %s
                    WHERE uid = %s
                    """, (self.study_id, self.concept_id,
                    self.concept_source_id, self.term,
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Outcome: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

def find_risk_factors (context, search_term):
    # Note: for now, limit to only MeSH (umls_source_id==75)

    risk_factors = {}
    if search_term \
        and len(search_term) > 0:
        cursor = context.get_cursor()
        query_term = search_term.strip().replace(' ', '% ') + '%'
        cursor.execute("""
            SELECT umls_terms.umls_concept_id, term, preferred_name, umls_source_id
            FROM umls_terms, umls_concepts, umls_concepts_sources
            WHERE term LIKE %s
            AND umls_source_id = %s
            AND umls_concepts.umls_concept_id = umls_terms.umls_concept_id
            AND umls_concepts_sources.umls_concept_id = umls_concepts.umls_concept_id
            ORDER BY term, preferred_name
            """, (query_term, 75))

        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            if not risk_factors.has_key((row['umls_concept_id'], row['umls_source_id'])):
                risk_factor = RiskFactor()
                risk_factor.concept_source_id = row['umls_source_id']
                risk_factor.concept_id = row['umls_concept_id']
                risk_factor.term = row['preferred_name']
                risk_factor.synonyms.append(row['term'])
                risk_factors[(risk_factor.concept_id, risk_factor.concept_source_id)] = risk_factor
            else:
                risk_factor = risk_factors[(row['umls_concept_id'], row['umls_source_id'])]
                if not row['term'] in risk_factor.synonyms:
                    risk_factor.synonyms.append(row['term'])
                risk_factors[(risk_factor.concept_id, risk_factor.concept_source_id)] = risk_factor

        # Try to bump up coarse "relevance" of exact matches
        risk_factors_ranked = risk_factors.values()
        for risk_factor in risk_factors_ranked:
            if risk_factor.term.lower() == search_term.lower()\
                or search_term.lower() in [syn.lower() for syn in risk_factor.synonyms]:
                risk_factors_ranked.remove(risk_factor)
                risk_factors_ranked.insert(0, risk_factor)
        return risk_factors_ranked

    else:
        return risk_factors.values()

class RiskFactor (DTable):

    UMLS_SOURCES = {
        75: 'MeSH',
        85: 'NCBI Taxonomy',
        501: 'ITIS',
        }

    def __init__ (self):
        self.uid = -1
        self.study_id = -1
        self.concept_id = -1
        self.concept_source_id = -1
        self.term = ''
        self.synonyms = []

    def __str__ (self):
        out = []
        out.append('<RiskFactor uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tconcept_id=%s (%s)' % (self.concept_id, self.concept_source_id))
        out.append('\tterm=%s' % self.term)
        out.append('/>')
        return '\n'.join(out)

    def delete (self, context):
        """
        Delete this risk_factor from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM risk_factors
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('RiskFactor: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO risk_factors
                (uid, study_id, concept_id,
                concept_source_id, term)
                VALUES
                (NULL, %s, %s,
                %s, %s)
                """, (self.study_id, self.concept_id,
                self.concept_source_id, self.term)
                )
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE risk_factors
                    SET study_id = %s, concept_id = %s,
                    concept_source_id = %s, term = %s
                    WHERE uid = %s
                    """, (self.study_id, self.concept_id,
                    self.concept_source_id, self.term,
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('RiskFactor: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

def find_species (context,search_term):

    species_map = {}
    if search_term \
        and len(search_term) > 0:
        cursor = context.get_cursor()
        query_term = search_term.strip().replace(' ', '% ') + '%'
        cursor.execute("""
            SELECT umls_terms.umls_concept_id, term, preferred_name, umls_source_id
            FROM umls_terms, umls_concepts, umls_concepts_sources
            WHERE term LIKE %s
            AND umls_concepts.umls_concept_id = umls_terms.umls_concept_id
            AND umls_concepts_sources.umls_concept_id = umls_concepts.umls_concept_id
            ORDER BY term, preferred_name
            """, query_term)

        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            if not species_map.has_key((row['umls_concept_id'], row['umls_source_id'])):
                spec = Species()
                spec.concept_source_id = row['umls_source_id']
                spec.concept_id = row['umls_concept_id']
                spec.term = row['preferred_name']
                spec.synonyms.append(row['term'])
                species_map[(spec.concept_id, spec.concept_source_id)] = spec
            else:
                spec = species_map[(row['umls_concept_id'], row['umls_source_id'])]
                if not row['term'] in spec.synonyms:
                    spec.synonyms.append(row['term'])
                species_map[(spec.concept_id, spec.concept_source_id)] = spec

        # Try to bump up coarse "relevance" of exact matches
        species_ranked = species_map.values()
        for spec in species_ranked:
            if spec.term.lower() == search_term.lower()\
                or search_term.lower() in [syn.lower() for syn in spec.synonyms]:
                species_ranked.remove(spec)
                species_ranked.insert(0, spec)
        return species_ranked

    else:
        return species_map.values()

class Species (DTable):

    TABLE_NAME = 'species'

    UMLS_SOURCES = {
        75: 'MeSH',
        85: 'NCBI Taxonomy',
        501: 'ITIS',
        }

    TYPES = [
        'companion',
        'livestock',
        'wildlife',
        'laboratory',
        ]

    def __init__ (self):
        self.uid = -1
        self.study_id = -1
        self.concept_id = -1
        self.concept_source_id = -1
        self.term = ''
        self.synonyms = []
        self.__dict__['types'] = []

    def __str__ (self):
        out = []
        out.append('<Species uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tconcept_id=%s (%s)' % (self.concept_id, self.concept_source_id))
        out.append('\tterm=%s' % self.term)
        out.append('\tsynonyms=%s' % '; '.join(self.synonyms))
        out.append('\ttypes=%s' % '; '.join(self.types))
        out.append('/>')
        return '\n'.join(out)

    def __setattr__ (self, name, value):
        # self.types should be a list, but the auto-loader from Study
        # will try to assign it a string.  Catch here, and assume it
        # will be the only time a direct assignment to self.types is
        # called.
        if name == 'types':
            if value.__class__ == ''.__class__:
                self.set_types(value)
            else:
                self.__dict__[name] = value
        else:
            self.__dict__[name] = value

    def add_type (self, type):
        if type in self.TYPES:
            if not type in self.types:
                self.types.append(type)

    def clear_types (self):
        self.__dict__['types'] = []

    def set_types (self, types):
        self.clear_types()
        if types.__class__ == ''.__class__:
            type_dict = dict(zip([t[0:2] for t in self.TYPES], self.TYPES))
            # pass through every two chars in types
            for i in range(0, len(types), 2):
                type = types[i:i+2]
                species_type = type_dict.get(type, None)
                if species_type:
                    self.add_type(species_type)

        elif types.__class__ == [].__class__:
            for type in types:
                if type in self.TYPES:
                    self.add_type(type)

    def get_types (self, shorthand=False):
        if shorthand:
            sh = ''.join([type[0:2] for type in self.types])
            return sh
        else:
            return self.types

    def delete (self, context):
        """
        Delete this species from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM species
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('Species: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO species
                (uid, study_id, concept_id,
                concept_source_id, term, types)
                VALUES
                (NULL, %s, %s,
                %s, %s, %s)
                """, (self.study_id, self.concept_id,
                self.concept_source_id, self.term, self.get_types(shorthand=True))
                )
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE species
                    SET study_id = %s, concept_id = %s,
                    concept_source_id = %s, term = %s, types = %s
                    WHERE uid = %s
                    """, (self.study_id, self.concept_id,
                    self.concept_source_id, self.term, self.get_types(shorthand=True),
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Species: %s (%s)', self.uid, e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

class Location (DTable):

    TABLE_NAME = 'locations'

    def __init__ (self, uid=-1):
        self.uid = uid
        self.study_id = -1
        self.feature_id = -1
        self.name = ''
        self.country = ''
        self.designation = ''

    def __str__ (self):
        out = []
        out.append('<Location uid=%s study_id=%s' % (self.uid, self.study_id))
        out.append('\tfeature_id=%s' % self.feature_id)
        out.append('/>')
        return '\n'.join(out)

    def delete (self, context):
        """
        Delete this location from the database.
        """
        cursor = context.get_cursor()
        if not self.uid == -1:
            try:
                cursor.execute("""
                    DELETE FROM locations
                    WHERE uid = %s
                    """, self.uid)
            except Exception, e:
                context.logger.error('Location: %s (%s)', self.uid, e)

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            cursor.execute("""
                INSERT INTO locations
                (uid, study_id, feature_id)
                VALUES
                (NULL, %s, %s)
                """, (self.study_id, self.feature_id))
            self.uid = self.get_new_uid(context)
        else:
            try:
                cursor.execute("""
                    UPDATE locations
                    SET study_id = %s, feature_id = %s
                    WHERE uid = %s
                    """, (self.study_id, self.feature_id,
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Location: %s (%s)', self.uid, e)

class Study (canary.context.Cacheable, DTable):

    TABLE_NAME = 'studies'

    # FIXME:  does this only belong here or on loader.QueuedRecord?
    # A Study has only one STATUS_TYPE
    STATUS_TYPES = {
        'unclaimed' : 0,
        'claimed' : 1,
        'curated' : 2,
        }

    # A Study has only one ARTICLE_TYPE
    ARTICLE_TYPES = {
        'unknown' : 0,
        'irrelevant' : 1,
        'traditional' : 2,
        'general' : 3,
        'review' : 4,
        'outcomes only' : 5,
        'exposures only' : 6,
        'curated' : 7,
        'duplicate' : 8,
        }

    # For dynamic iteration over related tables
    TABLES = {
        'methodologies' : Methodology,
        'exposures': Exposure,
        'risk_factors': RiskFactor,
        'outcomes': Outcome,
        'species': Species,
        'locations': Location,
        }

    CACHE_KEY = 'study'

    def __init__ (self, context=None, uid=-1, record_id=-1):
        try:
            if self.record_id >= 0:
                return
        except AttributeError:
            pass
        self.uid = uid
        self.record_id = -1
        self.status = self.STATUS_TYPES['unclaimed']
        self.article_type = self.ARTICLE_TYPES['unknown']
        self.curator_user_id = ''
        self.has_outcomes = False
        self.has_exposures = False
        self.has_relationships = False
        self.has_interspecies = False
        self.has_exposure_linkage = False
        self.has_outcome_linkage = False
        self.has_genomic = False
        self.comments = ''
        self.methodologies = []
        self.exposures = []
        self.risk_factors = []
        self.outcomes = []
        self.species = []
        self.locations = []
        self.date_modified = None
        self.date_entered = None
        self.date_curated = None
        self.history = {}

    def __str__ (self):
        out = []
        out.append('<Study uid=%s record_id=%s' % (self.uid, self.record_id))
        out.append('\tstatus=%s' % self.get_text_value(self.STATUS_TYPES, self.status))
        out.append('\tcurator_user_id=%s' % self.curator_user_id)
        out.append('\tarticle_type=%s' % self.get_text_value(self.ARTICLE_TYPES, self.article_type))
        out.append('\thas_outcomes=%s' % self.has_outcomes)
        out.append('\thas_exposures=%s' % self.has_exposures)
        out.append('\thas_relationships=%s' % self.has_relationships)
        out.append('\thas_interspecies=%s' % self.has_interspecies)
        out.append('\thas_exposure_linkage=%s' % self.has_exposure_linkage)
        out.append('\thas_outcome_linkage=%s' % self.has_outcome_linkage)
        out.append('\thas_genomic=%s' % self.has_genomic)
        # What are you wanting here?  TYPES is not like OUTCOMES, is it?
        #for table_name in self.TABLES:
        #    if len(getattr(self, table_name)) > 0:
        #        out.append('\t%s=' % table_name + \
        #            ','.join(getattr(self, 'get_' + table_name)(text=True)))
        #if len(self.types) > 0:
        #    out.append('\ttypes=' + ','.join(self.get_types(text=True)))
        out.append('\tcomments=%s' % self.comments or '')
        out.append('/>')
        return '\n'.join(out)

    def get_text_value (self, lookup_table, value):
        for k, v in lookup_table.iteritems():
            if v == value:
                return k
        return ''

    """Simple accessors for basic study parameters."""
    # FIXME: some of these could be parameterized.

    def set_status (self, value):
        if value in self.STATUS_TYPES.keys():
            self.status = self.STATUS_TYPES[value]

    def get_status (self, text=False):
        if text:
            return self.get_text_value(self.STATUS_TYPES, self.status)
        else:
            return self.status

    def set_article_type (self, value):
        try:
            if str(value) in self.ARTICLE_TYPES.keys():
                self.article_type = self.ARTICLE_TYPES[str(value)]
        except:
            # FIXME: proper error here
            pass

    def get_article_type (self, text=False):
        if text:
            return self.get_text_value(self.ARTICLE_TYPES, self.article_type)
        else:
            return self.article_type

    def get_concept_from_concept (self, concept):
        """
        For use in matching searches for exposure/species/outcome against
        summary data.

        NOTE: not checking 'risk_factor', but that should be refactored in
        with a broader concept code refactoring.
        """
        for concept_type in ('exposures', 'outcomes', 'species'):
            for c in getattr(self, concept_type):
                if c.concept_id == concept.uid:
                    # Eliminate trailing 's'
                    if concept_type in ('exposures', 'outcomes'):
                        concept_type = concept_type[:-1]
                    return c, concept_type
        return None, None

    def add_methodology (self, methodology):
        for meth in self.methodologies:
            if meth.uid == methodology.uid:
                return
        methodology.study_id = self.uid
        self.methodologies.append(methodology)

    def delete_methodology (self, context, methodology):
        for meth in self.methodologies:
            if meth.uid == methodology.uid:
                self.methodologies.remove(meth)
                meth.delete(context)

    def get_methodology (self, id):
        for methodology in self.methodologies:
            if methodology.uid == id:
                return methodology
        return None

    def has_exposure (self, exposure):
        """
        Returns True if this exposure has already been added to this Study.

        Note that has_exposure may be used before exposure is added,
        hence it does not check exposure.uid.
        """
        for exp in self.exposures:
            if exp.concept_id == exposure.concept_id:
                return True
        return False

    def add_exposure (self, exposure):
        if not self.has_exposure(exposure):
            exposure.study_id = self.uid
            self.exposures.append(exposure)

    def delete_exposure (self, context, exposure):
        for exp in self.exposures:
            if exp.concept_id == exposure.concept_id:
                self.exposures.remove(exp)
                exp.delete(context)

    def get_exposure (self, id):
        """
        Return the matching exposure, if added.

        Note that get_exposure is for use in matching or deleting exposures,
        i.e., only after an exposure has been added to the Study, so uid
        matching is required.
        """
        for exp in self.exposures:
            if exp.uid == id:
                return exp
        return None

    def get_exposure_from_exposure (self, exposure):
        for exp in self.exposures:
            if exp.concept_id == exposure.concept_id:
                return exp
        return None

    def has_risk_factor (self, risk_factor):
        """
        Returns True if this risk_factor has already been added to this Study.

        Note that has_risk_factor may be used before risk_factor is added,
        hence it does not check risk_factor.uid.
        """
        for rf in self.risk_factors:
            if rf.concept_id == risk_factor.concept_id:
                return True
        return False

    def add_risk_factor (self, risk_factor):
        if not self.has_risk_factor(risk_factor):
            risk_factor.study_id = self.uid
            self.risk_factors.append(risk_factor)

    def delete_risk_factor (self, context, risk_factor):
        for rf in self.risk_factors:
            if rf.concept_id == risk_factor.concept_id:
                self.risk_factors.remove(rf)
                rf.delete(context)

    def get_risk_factor (self, id):
        """
        Return the matching risk_factor, if added.

        Note that get_risk_factor is for use in matching or deleting risk_factors,
        i.e., only after an risk_factor has been added to the Study, so uid
        matching is required.
        """
        for risk_factor in self.risk_factors:
            if risk_factor.uid == id:
                return risk_factor
        return None

    def get_risk_factor_from_risk_factor (self, risk_factor):
        for rf in self.risk_factors:
            if rf.concept_id == risk_factor.concept_id:
                return rf
        return None

    def has_outcome (self, outcome):
        """
        Returns True if this outcome has already been added to this Study.

        Note that has_outcome may be used before outcome is added,
        hence it does not check outcome.uid.
        """
        for outc in self.outcomes:
            if outc.concept_id == outcome.concept_id:
                return True
        return False

    def add_outcome (self, outcome):
        if not self.has_outcome(outcome):
            outcome.study_id = self.uid
            self.outcomes.append(outcome)

    def delete_outcome (self, context, outcome):
        for outc in self.outcomes:
            if outc.concept_id == outcome.concept_id:
                self.outcomes.remove(outc)
                outc.delete(context)

    def get_outcome (self, id):
        """
        Return the matching outcome, if added.

        Note that get_outcome is for use in matching or deleting outcomes,
        i.e., only after an outcome has been added to the Study, so uid
        matching is required.
        """
        for outcome in self.outcomes:
            if outcome.uid == id:
                return outcome
        return None

    def get_outcome_from_outcome (self, outcome):
        for outc in self.outcomes:
            if outc.concept_id == outcome.concept_id:
                return outc
        return None

    def has_species (self, species):
        """
        Returns True if this species has already been added to this Study.

        Note that has_species may be used before species is added,
        hence it does not check species.uid.
        """
        for spec in self.species:
            if spec.concept_id == species.concept_id:
                return True
        return False

    def add_species (self, species):
        if not self.has_species(species):
            species.study_id = self.uid
            self.species.append(species)

    def delete_species (self, context, species):
        for spec in self.species:
            if spec.concept_id == species.concept_id:
                self.species.remove(spec)
                spec.delete(context)

    def get_species (self, id):
        """
        Return the matching species, if added.

        Note that get_species is for use in matching or deleting species,
        i.e., only after an species has been added to the Study, so uid
        matching is required.
        """
        for species in self.species:
            if species.uid == id:
                return species
        return None

    def get_species_from_species (self, species):
        for spec in self.species:
            if spec.concept_id == species.concept_id:
                return spec
        return None

    def has_location (self, location):
        """
        Returns True if this location has already been added to this Study.

        Note that has_location may be used before location is added,
        hence it does not check location.uid.
        """
        for loc in self.locations:
            if loc.feature_id == location.feature_id:
                return True
        return False

    def has_feature (self, feature):
        """
        Returns True if this feature has already been added to this Study.
        """
        for loc in self.locations:
            if loc.feature_id == feature.uid:
                return True
        return False

    def add_location (self, location):
        if not self.has_location(location):
            location.study_id = self.uid
            self.locations.append(location)

    def delete_location (self, context, location):
        for loc in self.locations:
            if loc.uid == location.uid:
                self.locations.remove(loc)
                loc.delete(context)

    def get_location (self, id):
        """
        Return the matching location, if added.

        Note that get_location is for use in matching or deleting locations,
        i.e., only after an location has been added to the Study, so uid
        matching is required.
        """
        for loc in self.locations:
            if loc.uid == id:
                return loc
        return None

    def get_location_from_feature (self, feature):
        for loc in self.locations:
            if loc.feature_id == feature.uid:
                return loc
        return None

    def get_locations_sorted (self, context):
        """
        For a set of canary record locations, return them in sort order
        by lower((country_name, region_name, feature_name)).
        """
        gazeteer = context.get_gazeteer()
        locs = []
        for location in self.locations:
            feature = Feature(uid=location.feature_id)
            feature.load(context)
            if gazeteer.fips_codes.has_key((feature.country_code, feature.adm1)):
                region_name = gazeteer.fips_codes[(feature.country_code, feature.adm1)]
            else:
                region_name = ''
            name = feature.name
            type = gazeteer.feature_codes[feature.feature_type]
            region_name = render_capitalized(region_name)
            country_name = render_capitalized(gazeteer.country_codes[feature.country_code])
            locs.append(
                ((country_name.lower(), region_name.lower(), name.lower()),
                (name, type, region_name, country_name))
                )
        locs.sort()
        return locs

    def get_lat_longs (self, context, dms=False):
        """
        For a set of canary record locations, return their latitudes
        and longitudes as two lists.
        """
        lats = longs = []
        for location in self.locations:
            feature = Feature(uid=location.feature_id)
            feature.load(context)
            if dms:
                lats.append(feature.dms_latitude)
                longs.append(feature.dms_longitude)
            else:
                lats.append(feature.latitude)
                longs.append(feature.longitude)
        return lats, longs

    def add_history (self, uid=-1, curator_user_id='', message='', modified=''):
        """
        Add a history record; only one history record can be added to a
        study_history at a time (because the key is set to -1).  Maybe
        that's bad design. :\
        """
        # Convert w/str() in case htmltext is passed by mistake
        curator_user_id = str(curator_user_id)
        message = str(message)
        new_history = {
            'uid': uid,
            'study_id': self.uid,
            'curator_user_id': curator_user_id,
            'message': message,
            'modified': modified
            }
        self.history[new_history['uid']] = new_history

    def load (self, context):

        # Can't load a new study; it hasn't been saved yet.
        if self.uid == -1:
            return

        # Is it already loaded?  Convenience check for client calls
        # don't need to verify loads from the cache.
        if context.config.use_cache:
            try:
                if self.record_id >= 0:
                    # Already loaded
                    return
            except AttributeError:
                # Note already loaded, so continue
                pass

        cursor = context.get_cursor()
        cursor.execute("""
            SELECT *
            FROM studies
            WHERE uid = %s
            """, self.uid)
        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        if rows and len(rows) > 0:
            row = dtuple.DatabaseTuple(desc, rows[0])
            for field in fields:
                self.set(field, row[field])

        # Every table_class is a DTable
        for table_name, table_class in self.TABLES.items():
            select_phrase = """SELECT * FROM %s """ % table_name
            cursor.execute(select_phrase + """
                WHERE study_id = %s
                """, (self.uid))
            fields = [d[0] for d in cursor.description]
            desc = dtuple.TupleDescriptor([[f] for f in fields])
            rows = cursor.fetchall()
            for row in rows:
                row = dtuple.DatabaseTuple(desc, row)
                table_class_instance = table_class()
                for field in fields:
                    table_class_instance.set(field, row[field])
                getattr(self, table_name).append(table_class_instance)

        for meth in self.methodologies:
            meth.load_routes(context)

        cursor.execute("""
            SELECT *
            FROM study_history
            WHERE study_id = %s
            """, self.uid)
        fields = [d[0] for d in cursor.description]
        desc = dtuple.TupleDescriptor([[f] for f in fields])
        rows = cursor.fetchall()
        for row in rows:
            row = dtuple.DatabaseTuple(desc, row)
            history_record = {}
            for field in fields:
                history_record[field] = row[field]
            self.add_history(uid=history_record['uid'],
                curator_user_id=history_record['curator_user_id'],
                message=history_record['message'],
                modified=history_record['modified'])

    def save (self, context):
        cursor = context.get_cursor()
        if self.uid == -1:
            try:
                cursor.execute("""
                    INSERT INTO studies
                    (uid,
                    record_id, status, article_type, curator_user_id,
                    has_outcomes, has_exposures,
                    has_relationships, has_interspecies,
                    has_exposure_linkage, has_outcome_linkage,
                    has_genomic, comments,
                    date_modified, date_entered, date_curated)
                    VALUES
                    (NULL,
                    %s, %s, %s, %s,
                    %s, %s,
                    %s, %s,
                    %s, %s,
                    %s, %s,
                    NOW(), NOW(), %s)
                    """, (self.record_id, self.status, self.article_type, self.curator_user_id,
                    int(self.has_outcomes), int(self.has_exposures),
                    int(self.has_relationships), int(self.has_interspecies),
                    int(self.has_exposure_linkage), int(self.has_outcome_linkage),
                    int(self.has_genomic), self.comments,
                    self.date_curated)
                    )
            except Exception, e:
                context.logger.error('Save study: %s (%s)', self.uid, e)

            self.uid = self.get_new_uid(context)

        else:
            try:
                cursor.execute("""
                    UPDATE studies
                    SET record_id = %s, status = %s, article_type = %s, curator_user_id = %s,
                    has_outcomes = %s, has_exposures = %s,
                    has_relationships = %s, has_interspecies = %s,
                    has_exposure_linkage = %s, has_outcome_linkage = %s,
                    has_genomic = %s, comments = %s,
                    date_modified = NOW(), date_curated = %s
                    WHERE uid = %s
                    """, (self.record_id, self.status, self.article_type, self.curator_user_id,
                    int(self.has_outcomes), int(self.has_exposures),
                    int(self.has_relationships), int(self.has_interspecies),
                    int(self.has_exposure_linkage), int(self.has_outcome_linkage),
                    int(self.has_genomic), self.comments,
                    self.date_curated,
                    self.uid)
                    )
            except Exception, e:
                context.logger.error('Update study: %s', e)
        # FIXME: should this be set from the SQL?
        self.date_modified = time.strftime(str('%Y-%m-%d'))

        # update all the related table values
        for table_name in self.TABLES.keys():
            for item in getattr(self, table_name):
                item.save(context)

        # Save new history records; assume only one can be added at a time,
        # new record will necessarily have uid == -1
        if self.history:
            new_history_record = self.history.get(-1, None)
            if new_history_record:
                try:
                    cursor.execute("""
                        INSERT INTO study_history
                        (uid, study_id, curator_user_id,
                        message, modified)
                        VALUES
                        (NULL, %s, %s,
                        %s, NOW())
                        """, (self.uid, new_history_record['curator_user_id'],
                        new_history_record['message']))
                    new_history_record_id = self.get_new_uid(context)
                    del(self.history[-1])
                    self.history[new_history_record_id] = new_history_record
                except Exception, e:
                    context.logger.error('Save study history: %s (%s)', self.uid, e)

        if context.config.use_cache:
            # Force reload on next call to flush history times
            context.cache_delete('%s:%s' % (self.CACHE_KEY, self.uid))

    def delete (self, context):
        cursor = context.get_cursor()
        try:
            for table_name in self.TABLES.keys():
                for item in getattr(self, table_name):
                    item.delete(context)

            cursor.execute("""
                DELETE FROM studies
                WHERE uid = %s
                """, self.uid)

            if context.config.use_cache:
                context.cache_delete('%s:%s' % (self.CACHE_KEY, self.uid))

        except Exception, e:
            context.logger.error('Delete study: %s', e)


text:
package com.sequenceiq.environment.api.v1.proxy.model.request;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.sequenceiq.environment.api.doc.proxy.ProxyConfigDescription;
import com.sequenceiq.environment.api.v1.proxy.model.ProxyBase;
import com.sequenceiq.environment.api.v1.proxy.validation.ValidProxyConfigAuthRequest;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel(description = ProxyConfigDescription.DESCRIPTION)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
@ValidProxyConfigAuthRequest
public class ProxyRequest extends ProxyBase {
    @ApiModelProperty(ProxyConfigDescription.USERNAME)
    private String userName;

    @ApiModelProperty(ProxyConfigDescription.PASSWORD)
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return super.toString() + ", " + "ProxyRequest{" +
                "userName='" + userName + '\'' +
                '}';
    }
}


text:
Module Name: AzureRM.RecoveryServices
Module Guid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Download Help Link: {{Please enter FwLink manually}}
Help Version: {{Please enter version of help manually (X.X.X.X) format}}
Locale: en-US
ms.assetid: DC5E0ECC-BFFA-4030-8BCB-BD180CAD77B8
---

# AzureRM.RecoveryServices Module
## Description
This topic displays help topics for the Azure Recovery Services cmdlets.

## AzureRM.RecoveryServices Cmdlets
### [Get-AzureRmRecoveryServicesBackupProperties](Get-AzureRmRecoveryServicesBackupProperties.md)
Gets Backup properties.

### [Get-AzureRmRecoveryServicesVault](Get-AzureRmRecoveryServicesVault.md)
Gets a list of Recovery Services vaults.

### [Get-AzureRmRecoveryServicesVaultSettingsFile](Get-AzureRmRecoveryServicesVaultSettingsFile.md)
Gets the Azure Site Recovery vault settings file.

### [New-AzureRmRecoveryServicesVault](New-AzureRmRecoveryServicesVault.md)
Creates a new Recovery Services vault.

### [Remove-AzureRmRecoveryServicesVault](Remove-AzureRmRecoveryServicesVault.md)
Deletes a Recovery Services vault.

### [Set-AzureRmRecoveryServicesBackupProperties](Set-AzureRmRecoveryServicesBackupProperties.md)
Sets the properties for backup management.

### [Set-AzureRmRecoveryServicesVaultContext](Set-AzureRmRecoveryServicesVaultContext.md)
Sets vault context.


text:
<?xml version="1.0" encoding="UTF-8"?>
<query>
    <folder>/datatypes/sbac07</folder>
    <name>QueryDifficulty</name>
    <version>3</version>
    <label>QueryDifficulty</label>
    <description></description>
    <creationDate>2013-03-16T13:12:24.000-06:00</creationDate>
    <updateDate>2012-12-17T16:23:19.000-07:00</updateDate>
    <language>sql</language>
    <queryString>select * from difficulty</queryString>
    <dataSource>
        <uri>/Data_Sources/cdesbacdb</uri>
    </dataSource>
</query>


text:
.. _translation:

Translation
===========

Translation in this project is set up pretty typically for a Django
project - see https://docs.djangoproject.com/en/2.2/topics/i18n/translation/

The exception to that rule is that our SMS response messages are handled
differently.  See sms_responses.rst for documentation of how that works.

This document refers to all other strings in the application.

Transifex
---------

The ``requirements/dev.txt`` will install the Transifex client.

Before using the command-line Transifex client, create a ``~/.transifexrc``
file with your credentials - see
http://support.transifex.com/customer/portal/articles/1000855-configuring-the-client

Adding features and fixing bugs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As a developer, when you work on a branch, please do `not` regenerate
the django.po file. Merging branches with different updates to django.po
is a pain, and not really necessary. Of course, if development is not
active, you may ignore this rule since you'll be the only one touching
the relevant files.

Updating messages on Transifex
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If there have been changes to the messages in the code or templates
that have been merged to develop, someone should update the messages on
Transifex as follows:

1. regenerate the English (only) .po file::

    python manage.py makemessages -l en --no-location

   (Never update the Arabic .po files using makemessages. We'll update the English file, upload it
   to Transifex, then later pull the .po files with translations down from Transifex.)

#. push the updated source file to Transifex (http://support.transifex.com/customer/portal/articles/996211-pushing-new-translations)::

     tx push -s

#. Commit and push the changes to github::

     git commit -m "Updated messages" locale/en/LC_MESSAGES/*
     git push

Updating translations from Transifex
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Anytime translations on Transifex have been updated, someone should update
our translation files on the develop branch as follows:

1. pull the updated Arabic files (http://support.transifex.com/customer/portal/articles/996157-getting-translations)::

    tx pull -af

2. Use ``git diff`` to see if any translations have actually changed. If not, you
   can just revert the Arabic .po file changes and stop here.

3. Compile the messages::

    python manage.py compilemessages -l ar

4. Run your test suite one more time::

    python manage.py test

5. Commit and push the changes to github::

    git commit -m "Updated translations" locale/ar/LC_MESSAGES/*
    git push


text:
var w = 900,
    h = 450;

var monthNames = [ "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December" ];

var maxDataPointsForDots = 50,
	transitionDuration = 1000;

var svg = null,
	yAxisGroup = null,
	xAxisGroup = null,
	dataCirclesGroup = null,
	dataLinesGroup = null;

function draw(options) {
	var data = options.data;

	if(data == null || data.length === 0){
		return false;
	}

	var margin = 40;
	var max = d3.max(data, function(d) { return d.value });
	var min = 0;
	var pointRadius = 4;
	var x = d3.time.scale().range([0, options.width - margin * 2]).domain([data[0].date, data[data.length - 1].date]);
	var y = d3.scale.linear().range([options.height - margin * 2, 0]).domain([min, max]);

	var xAxis = d3.svg.axis().scale(x).tickSize(options.height - margin * 2).tickPadding(10).ticks(7);
	var yAxis = d3.svg.axis().scale(y).orient('left').tickSize(-options.width + margin * 2).tickPadding(10);
	var t = null;

	svg = d3.select(options.container).select('svg').select('g');

	if (svg.empty()) {
		svg = d3.select(options.container)
			.append('svg:svg')
				.attr('width', options.width)
				.attr('height', options.height)
				.attr('class', 'viz')
			.append('svg:g')
				.attr('transform', 'translate(' + margin + ',' + margin + ')');
	}

	t = svg.transition().duration(transitionDuration);

	// y ticks and labels
	if (!yAxisGroup) {
		yAxisGroup = svg.append('svg:g').attr('class', 'yTick').call(yAxis);
	} else {
		t.select('.yTick').call(yAxis);
	}

	// x ticks and labels
	if (!xAxisGroup) {
		xAxisGroup = svg.append('svg:g').attr('class', 'xTick').call(xAxis);
	} else {
		t.select('.xTick').call(xAxis);
	}

	// Draw the lines
	if (!dataLinesGroup) {
		dataLinesGroup = svg.append('svg:g');
	}

	var dataLines = dataLinesGroup.selectAll('.data-line').data([data]);

	var line = d3.svg.line().x(function(d,i) {  return x(d.date); }).y(function(d) { return y(d.value); }).interpolate("linear");

	var garea = d3.svg.area().interpolate("linear").x(function(d) {
		return x(d.date);
	}).y0(options.height - margin * 2).y1(function(d) {
		return y(d.value);
	});

	dataLines.enter().append('svg:path').attr("class", "area").attr("d", garea(data));

	dataLines.enter().append('path').attr('class', 'data-line').style('opacity', 0.3).attr("d", line(data));

	dataLines.transition().attr("d", line).duration(transitionDuration).style('opacity', 1).attr("transform", function(d) { return "translate(" + x(d.date) + "," + y(d.value) + ")"; });

	dataLines.exit().transition().attr("d", line).duration(transitionDuration).attr("transform", function(d) { return "translate(" + x(d.date) + "," + y(0) + ")"; }).style('opacity', 1e-6).remove();

	d3.selectAll(".area").transition().duration(transitionDuration).attr("d", garea(data));

	// Draw the points
	if (!dataCirclesGroup) {
		dataCirclesGroup = svg.append('svg:g');
	}

	var circles = dataCirclesGroup.selectAll('.data-point').data(data);

	circles.enter().append('svg:circle')
		.attr('class', function(d) {
			return "data-point " + d.mood_name;
		})
		.style('opacity', 1e-6)
		.attr('cx', function(d) { return x(d.date) })
		.attr('data-mood-name', function(d) {
			return d.mood_name;
		})
		.attr('cy', function() { return y(0) })
		.attr('r', function() { return (data.length <= maxDataPointsForDots) ? pointRadius : 0 })
	.transition().duration(transitionDuration)
		.style('opacity', 1)
		.attr('cx', function(d) { return x(d.date) })
		.attr('cy', function(d) { return y(d.value) });

	circles.transition().duration(transitionDuration)
		.attr('cx', function(d) { return x(d.date) })
		.attr('cy', function(d) { return y(d.value) })
		.attr('r', function() { return (data.length <= maxDataPointsForDots) ? pointRadius : 0 })
		.style('opacity', 1);

	circles.exit().transition().duration(transitionDuration)
		.attr('cy', function() { return y(0) })
		.style("opacity", 1e-6)
		.remove();

      $('svg circle').tipsy({
			gravity: 'w',
			html: true,
			title: function() {
				var d = this.__data__;
				var now = Date.now()
				var pDate = d.date.getTime();
				var when = ((now - pDate) / 60000).toFixed(2);
				var display_text = options.moods[Number(d.value)].name;
			  	return when + ' minutes ago<br>Score: ' + display_text;
			}
      });
}

function generateData() {

	var data = [];

	var i = 30//Math.max(Math.round(Math.random()*100), 3);

	var getRandomInt = function() {
		var min = 1;
		var max = 5;
	    return Math.floor(Math.random() * (max - min + 1)) + min;
	}

	while (i--) {
		var date = new Date();
		//date.setDate(date.getDate() - i);
		date.setHours(date.getHours(), -i, 0, 0);//setHours(hour,min,sec,millisec)
		data.push({'value' : getRandomInt(), 'date' : date});
	}
	return data;
}

d3.select('#button').on('click', draw);


text:
<!DOCTYPE html>

		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>Pixels Test</title>

		<link href="css/bootstrap.css" rel="stylesheet">
		<link href="css/pixels.css" rel="stylesheet">

		<canvas id="pixels" style="display: none;"></canvas>


text:
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** @author  John Miller
 *  @version 1.2
 *  @date    Wed Feb 26 18:20:39 EST 2014
 *  @see     LICENSE (MIT style license file).
 */

package scalation.stat

import scala.collection.mutable.ListBuffer

import scalation.scala2d.{Frame, ScrollPane, Table}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** The `StatTable` class display statistical results in a frame's table.
 *  @param _title  the title of the frame
 *  @param stats   the statistics to be displayed in the table.
 */
class StatTable (_title: String, stats: ListBuffer [Statistic])
      extends Frame (_title)
{
    val rows  = stats.size + 1
    val cols  = Statistic.label.length
    val table = new Table (rows, cols)
    for (j <- 0 until cols) table.setValueAt (Statistic.label(j), 0, j)
    for (i <- 0 until rows - 1) {
        val st = stats(i).statRow
        for (j <- 0 until cols) table.setValueAt (st(j), i+1, j)
    } // for

    getContentPane.add (new ScrollPane (table))
    pack ()
    setVisible (true)

} // StatTable class

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** The `StatTableTest` is used to test the `StatTable` class.
 */
object StatTableTest extends App
{
     println ("Create a StatTable called Test")
     val stats = ListBuffer [Statistic] ()
     for (i <- 0 until 50) stats += new Statistic ()
     for (j <- 0 until 50) {
         for (i <- 0 until 50) stats(j).tally (i)
     } // for
     new StatTable ("Test", stats)

} // StatTableTest object


text:
import pytest

from goetia import libgoetia
from goetia.traversal import STATES

from .utils import *

def test_cursor(graph, ksize):
    seed = 'A' * ksize

    graph.cursor = seed
    assert graph.cursor == seed

def test_cursor_wrong_size(graph, ksize):
    seed = 'A' * (ksize - 1)

    with pytest.raises(BaseException):
        graph.cursor = seed

class TestWalk:

    def test_to_string(self, ksize, linear_path, graph, consume, check_fp):
        contig = linear_path()
        check_fp()
        consume()

        walk = graph.walk_right(contig[0:ksize])
        assert walk.to_string() == contig
        assert str(walk) == contig

    def test_tail(self, ksize, linear_path, graph, consume, check_fp):
        contig = linear_path()
        check_fp()
        consume()

        walk = graph.walk_right(contig[0:ksize])
        assert walk.tail() == graph.get_hash()

    def test_glue(self, ksize, linear_path, graph, consume, check_fp):
        contig = linear_path()
        check_fp()
        consume()

        lwalk, rwalk = graph.walk(contig[1:ksize+1])
        assert lwalk.glue(rwalk) == contig
        assert rwalk.glue(lwalk) == contig

class TestLinear:

    def test_all_start_positions(self, ksize, linear_path, graph, consume, check_fp):
        # assemble entire contig, starting from wherever
        contig = linear_path()
        check_fp()
        consume()

        for start in range(0, len(contig), 150):
            if len(contig) - start < ksize:
                continue
            graph.clear_seen()
            lwalk, rwalk = graph.walk(contig[start:start + ksize])
            path = lwalk.glue(rwalk)

            assert path == contig, (len(path), len(contig), start)
            assert lwalk.end_state == rwalk.end_state == STATES.STOP_FWD

    @using(ksize=21, length=81)
    def test_all_left_to_beginning(self, ksize, length, linear_path, graph, consume, check_fp):
        # assemble directed left
        contig = linear_path()
        check_fp()
        consume()

        for start in range(0, len(contig), length // 5):
            if len(contig) - start < ksize:
                continue
            graph.clear_seen()
            walk = graph.walk_left(contig[start:start + ksize])
            assert walk.to_string() == contig[:start + ksize], start
            assert walk.end_state == STATES.STOP_FWD

    @using(ksize=21, length=81)
    def test_all_right_to_end(self, ksize, length, linear_path, graph, consume, check_fp):
        # assemble directed right
        contig = linear_path()
        check_fp()
        consume()

        for start in range(0, len(contig), length // 5):
            if len(contig) - start < ksize:
                continue
            graph.clear_seen()
            walk = graph.walk_right(contig[start:start + ksize])
            assert walk.to_string() == contig[start:], start
            assert walk.end_state == STATES.STOP_FWD

    def test_circular(self, ksize, circular, graph, consume, check_fp):
        contig = circular()
        check_fp()
        consume()

        walk = graph.walk_right(contig[:ksize])
        path = walk.to_string()
        print(path, ',', contig)
        assert path == contig[:len(path)]
        assert walk.end_state == STATES.STOP_SEEN

class TestDecisions:

    def test_decision_fwd(self, ksize, right_fork, graph, consume, check_fp):
        (sequence, branch), S = right_fork()
        check_fp()
        consume()

        walk = graph.walk_right(sequence[:ksize])
        path = walk.to_string()
        assert walk.end_state == STATES.DECISION_FWD
        assert path == sequence[:S+ksize]
        assert walk.tail() == graph.hash(sequence[S:S+ksize])
        assert graph.right_degree(path[-ksize:]) == 2

        lwalk, rwalk = graph.walk(branch[-ksize:])
        assert branch == lwalk.glue(rwalk)

    def test_decision_rev(self, ksize, right_fork, graph, consume, check_fp):
        '''Test that we assemble through fork from the right when we haven't
        assembled the core path already.'''
        (sequence, branch), S = right_fork()
        check_fp()
        consume()

        lwalk, rwalk = graph.walk(branch[-ksize:])
        path = lwalk.glue(rwalk)

        assert branch == path
        assert graph.left_degree(path[:ksize]) == 1
        assert graph.right_degree(path[:ksize]) == 1
        assert lwalk.end_state == STATES.DECISION_BKW
        print('Hash:', graph.hash(sequence[S:S+ksize]))
        assert lwalk.tail() == graph.hash(branch[:ksize])
        assert rwalk.end_state == STATES.STOP_FWD
        assert rwalk.tail() == graph.hash(branch[-ksize:])

    def test_start_from_reverse_decision_left(self, ksize, right_fork, graph, consume, check_fp):
        (sequence, branch), S = right_fork()
        check_fp()
        consume()

        walk = graph.walk_left(sequence[S:S+ksize])
        assert walk.to_string() == sequence[:S+ksize]
        assert walk.end_state == STATES.STOP_FWD

    def test_start_from_reverse_decision_right(self, ksize, left_fork, graph, consume, check_fp):
        (sequence, branch), S = left_fork()
        check_fp()
        consume()

        walk = graph.walk_right(sequence[S:S+ksize])
        assert walk.to_string() == sequence[S:]
        assert walk.end_state == STATES.STOP_FWD

    def test_triple_decision_fwd(self, ksize, right_triple_fork,
                                 graph, consume, check_fp):
        (core, top, bottom), S = right_triple_fork()
        check_fp()
        consume()

        rwalk = graph.walk_right(core[:ksize])
        assert str(rwalk) == core[:S+ksize]

        lwalk, rwalk = graph.walk(top[-ksize:])
        assert top == lwalk.glue(rwalk)

        lwalk, rwalk = graph.walk(bottom[-ksize:])
        assert bottom == lwalk.glue(rwalk)

@using(ksize=21, length=100)
class TestMasked:

    def test_linear_masked_right(self, ksize, linear_path, graph, consume, check_fp):
        # test that masking a k-mer stops traversal
        contig = linear_path()
        check_fp()
        consume()

        mask = std.set[graph.hash_type]()
        stopper = contig[5:5+ksize]
        mask.insert(graph.hash(stopper))
        masked = libgoetia.Masked[graph.storage_type, graph.shifter_type, std.set[graph.hash_type]](graph, mask)

        start = contig[:ksize]
        walk_unmasked = graph.walk_right(start)
        walk_masked = masked.walk_right(start)

        assert walk_unmasked.to_string() == contig
        assert walk_masked.to_string() == contig[:ksize+4]

    def test_masked_right_decision_fwd(self, ksize, right_fork, graph, consume, check_fp):
        '''Tests that the mask turns a forward decision node into a non-decision node.
        '''
        (sequence, branch), S = right_fork()
        check_fp()
        consume()

        branch_start = branch[:ksize]
        mask = std.set[graph.hash_type]()
        mask.insert(graph.hash(branch_start))
        masked = libgoetia.Masked[graph.storage_type, graph.shifter_type, std.set[graph.hash_type]](graph, mask)

        walk = masked.walk_right(sequence[:ksize])
        path = walk.to_string()
        assert walk.end_state == STATES.STOP_FWD
        assert path == sequence
        assert walk.tail() == graph.hash(sequence[-ksize:])

    def test_linear_masked_left(self, ksize, linear_path, graph, consume, check_fp):
        # test that masking a k-mer stops traversal
        contig = linear_path()
        check_fp()
        consume()

        mask = std.set[graph.hash_type]()
        stopper = contig[5:5+ksize]
        mask.insert(graph.hash(stopper))
        masked = libgoetia.Masked[graph.storage_type, graph.shifter_type, std.set[graph.hash_type]](graph, mask)

        start = contig[-ksize:]
        walk_unmasked = graph.walk_left(start)
        walk_masked = masked.walk_left(start)

        assert walk_unmasked.to_string() == contig
        assert walk_masked.to_string() == contig[6:]

    def test_masked_left_decision_fwd(self, ksize, left_fork, graph, consume, check_fp):
        '''Tests that the mask turns a forward decision node into a non-decision node.
        '''
        (sequence, branch), S = left_fork()
        check_fp()
        consume()

        branch_start = branch[-ksize:]
        mask = std.set[graph.hash_type]()
        mask.insert(graph.hash(branch_start))
        masked = libgoetia.Masked[graph.storage_type, graph.shifter_type, std.set[graph.hash_type]](graph, mask)

        walk = masked.walk_left(sequence[-ksize:])
        path = walk.to_string()
        assert walk.end_state == STATES.STOP_FWD
        assert path == sequence
        assert walk.tail() == graph.hash(sequence[:ksize])

@using(ksize=21, length=100)
class TestWalkStopperFunctor:

    def test_linear_stop_functor_right(self, ksize, linear_path, graph, consume, check_fp):
        ''' Test that stop functor stops walk_right
        '''
        contig = linear_path()
        check_fp()
        consume()

        stop_set = std.set[graph.hash_type]()
        stop_kmer = contig[5:5+ksize]
        stop_set.insert(graph.hash(stop_kmer))
        stopper = libgoetia.WalkStopper[graph.hash_type](stop_set)

        start = contig[:ksize]
        walk = graph.walk_right(start, stopper)

        assert walk.to_string() == contig[:ksize+4]

    def test_linear_stop_functor_left(self, ksize, linear_path, graph, consume, check_fp):
        ''' Test that stop functor stops walk_left
        '''
        contig = linear_path()
        check_fp()
        consume()

        stop_set = std.set[graph.hash_type]()
        stop_kmer = contig[5:5+ksize]
        stop_set.insert(graph.hash(stop_kmer))
        stopper = libgoetia.WalkStopper[graph.hash_type](stop_set)

        start = contig[-ksize:]
        walk = graph.walk_left(start, stopper)

        assert walk.to_string() == contig[6:]

    def test_right_decision_fwd_stopper(self, ksize, right_fork, graph, consume, check_fp):
        ''' Test that the stop functor does NOT prevent a DECISION_FWD state.
        '''
        (sequence, branch), S = right_fork()
        check_fp()
        consume()

        branch_start = branch[:ksize]
        stop_set = std.set[graph.hash_type]()
        stop_set.insert(graph.hash(branch_start))
        stopper = libgoetia.WalkStopper[graph.hash_type](stop_set)

        walk = graph.walk_right(sequence[:ksize], stopper)
        assert walk.end_state == STATES.DECISION_FWD

    def test_left_decision_fwd_stopper(self, ksize, left_fork, graph, consume, check_fp):
        ''' Test that the stop functor does NOT prevent a DECISION_FWD state.
        '''
        (sequence, branch), S = left_fork()
        check_fp()
        consume()

        branch_start = branch[-ksize:]
        stop_set = std.set[graph.hash_type]()
        stop_set.insert(graph.hash(branch_start))
        stopper = libgoetia.WalkStopper[graph.hash_type](stop_set)

        walk = graph.walk_left(sequence[-ksize:], stopper)
        assert walk.end_state == STATES.DECISION_FWD

@using(ksize=21, length=100)
class TestPythonWalkFunctor:

    @pytest.mark.xfail
    def test_simple_python_functor(self, ksize, linear_path, graph, consume, check_fp):
        ''' Test that a Python functor can be passed to walk.
        '''
        contig = linear_path()
        check_fp()
        consume()

        stop_hash = graph.hash(contig[5:5+ksize])
        class StopperFunctor(graph.null_walk_func_t):
            def __call__(self, node):
                return node != stop_hash
        stopper = StopperFunctor()

        start = contig[:ksize]
        walk = graph.walk_right(start, stopper)

        assert walk.to_string() == contig[:ksize+4]