text:
package com.socrata.datasync.validation;

import com.socrata.datasync.DatasetUtils;
import com.socrata.datasync.HttpUtility;
import com.socrata.datasync.PublishMethod;
import com.socrata.datasync.SocrataConnectionInfo;
import com.socrata.datasync.Utils;
import com.socrata.datasync.config.CommandLineOptions;
import com.socrata.datasync.config.controlfile.ControlFile;
import com.socrata.datasync.config.controlfile.FileTypeControl;
import com.socrata.datasync.config.controlfile.LocationColumn;
import com.socrata.datasync.config.userpreferences.UserPreferences;
import com.socrata.datasync.job.IntegrationJob;
import com.socrata.datasync.job.JobStatus;
import com.socrata.model.importer.Dataset;
import org.apache.commons.cli.CommandLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class IntegrationJobValidity {

    public static final List<String> allowedFileToPublishExtensions = Arrays.asList("csv", "tsv");
    public static final List<String> allowedControlFileExtensions = Arrays.asList("json");
    public static final String supportedTimeFormat = "ISO8601";
    public static final String jodaLink = "http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html";
    public static final String charsetsPath = "/datasync/charsets.json";

    /**
     * Checks that the command line arguments are sensible
     * NB: it is expected that this is run before 'configure'.
     * @param cmd the commandLine object constructed from the user's options
     * @return true if the commandLine is approved, false otherwise
     */
    public static boolean validateArgs(CommandLine cmd) {
        CommandLineOptions options = new CommandLineOptions();
        return  validateDatasetIdArg(cmd, options) &&
                validateFileToPublishArg(cmd, options) &&
                validatePublishMethodArg(cmd, options) &&
                validateHeaderRowArg(cmd, options) &&
                validatePublishViaFtpArg(cmd, options) &&
                validatePublishViaDi2HttpArg(cmd, options) &&
                validatePathToControlFileArg(cmd, options) &&
                validateProxyArgs(cmd, options);
    }

    /**
     * @return an error JobStatus if any input is invalid, otherwise JobStatus.VALID
     */
    public static JobStatus validateJobParams(UserPreferences userPrefs, IntegrationJob job) {
        if(userPrefs.getConnectionInfo().getUrl().equals("") || userPrefs.getConnectionInfo().getUrl().equals("https://"))
            return JobStatus.INVALID_DOMAIN;

        if(!Utils.uidIsValid(job.getDatasetID()))
            return JobStatus.INVALID_DATASET_ID;

        String fileToPublish = job.getFileToPublish();
        if(fileToPublish.equals(""))
            return JobStatus.MISSING_FILE_TO_PUBLISH;

        File publishFile = new File(fileToPublish);
        if(!publishFile.exists() || publishFile.isDirectory()) {
            JobStatus errorStatus = JobStatus.FILE_TO_PUBLISH_DOESNT_EXIST;
            errorStatus.setMessage(fileToPublish + ": File to publish does not exist");
            return errorStatus;
        }

        String fileExtension = Utils.getFileExtension(fileToPublish);
        if(!allowedFileToPublishExtensions.contains(fileExtension))
            return JobStatus.FILE_TO_PUBLISH_INVALID_TABULAR_FORMAT;

        Dataset schema;
        try {
            schema = DatasetUtils.getDatasetInfo(userPrefs, job.getDatasetID());

            if(job.getPublishViaDi2Http() || job.getPublishViaFTP()) {

                ControlFile control = job.getControlFile();
                FileTypeControl fileControl = null;
                switch (fileExtension) {
                    case "csv": fileControl = control.getCsvFtc(); break;
                    case "tsv": fileControl = control.getTsvFtc(); break;
                }
                String action = control.action;
                //See if it was passed on the command line
                if (action == null)
                    action = job.getPublishMethod().name();
                JobStatus actionOkay = checkAction(action, job, schema);
                if (actionOkay.isError())
                    return actionOkay;

                JobStatus controlOkay = checkControl(control,fileControl,schema,publishFile,userPrefs.getConnectionInfo().getUrl());
                if (controlOkay.isError())
                    return controlOkay;

            }
        } catch (Exception e) {
            // Not going to fail jobs on the validation check
        }
        return JobStatus.VALID;
    }

    public static JobStatus checkControl(ControlFile control,FileTypeControl fileControl, Dataset schema, File publishFile, String urlBase){

        String fileExtension = Utils.getFileExtension(publishFile.getAbsolutePath());

        if (fileControl == null && !control.action.equalsIgnoreCase(PublishMethod.delete.name())) {
            JobStatus noFileTypeContent = JobStatus.PUBLISH_ERROR;
            noFileTypeContent.setMessage("The control file for '" + publishFile.getName() +
                    "' requires that the '" + fileExtension + "' option be filled in");
            return noFileTypeContent;
        }

        try {
            String[] headers = getHeaders(fileControl, publishFile);
            if (headers == null) {
                JobStatus noHeaders = JobStatus.PUBLISH_ERROR;
                noHeaders.setMessage("Headers must be specified in one of " + publishFile.getName() + " or the control file using 'columns'");
                return noHeaders;
            }

            PublishMethod method = PublishMethod.valueOf(control.action.toLowerCase());
            JobStatus csvDatasetAgreement = checkColumnAgreement(fileControl, method, schema, headers ,publishFile.getName());
            if (csvDatasetAgreement.isError())
                return csvDatasetAgreement;

            JobStatus controlHeaderAgreement = checkControlAgreement(fileControl, schema, headers, publishFile.getName());
            if (controlHeaderAgreement.isError())
                return controlHeaderAgreement;

            JobStatus controlSensibility = validateControlFile(fileControl, urlBase);
            if (controlSensibility.isError())
                return controlSensibility;
        }
        catch (IOException e){
            JobStatus cannotConnect = JobStatus.PUBLISH_ERROR;
            cannotConnect.setMessage("Cannot determine the headers for dataset: " + schema.getId());
            return cannotConnect;
        }
        return JobStatus.VALID;

    }

    private static boolean validatePathToControlFileArg(CommandLine cmd, CommandLineOptions options) {
        String controlFilePath = cmd.getOptionValue(options.PATH_TO_CONTROL_FILE_FLAG);
        if (controlFilePath == null) controlFilePath = cmd.getOptionValue(options.PATH_TO_FTP_CONTROL_FILE_FLAG);

        if(controlFilePath != null) {
            String publishingWithFtp = cmd.getOptionValue(options.PUBLISH_VIA_FTP_FLAG);
            String publishingWithDi2 = cmd.getOptionValue(options.PUBLISH_VIA_DI2_FLAG);
            if (isNullOrFalse(publishingWithFtp) && isNullOrFalse(publishingWithDi2)) {
                System.err.println("Invalid argument: Neither -sc,--" + options.PATH_TO_FTP_CONTROL_FILE_FLAG +
                        " -cf,--" + options.PATH_TO_CONTROL_FILE_FLAG + " can be supplied " +
                        "unless -pf,--" + options.PUBLISH_VIA_FTP_FLAG + " is 'true' or " +
                        "unless -ph,--" + options.PUBLISH_VIA_DI2_FLAG + " is 'true'");
                return false;
            }
        }

        if(controlFilePath != null) {
            if(cmd.getOptionValue(options.HAS_HEADER_ROW_FLAG) != null) {
                System.out.println("WARNING: -h,--" + options.HAS_HEADER_ROW_FLAG + " is being ignored because " +
                        "-sc,--" + options.PATH_TO_FTP_CONTROL_FILE_FLAG +  " or " +
                        "-cf,--" + options.PATH_TO_CONTROL_FILE_FLAG +  " was supplied");
            }
        }
        return true;
    }

    private static boolean validatePublishViaFtpArg(CommandLine cmd, CommandLineOptions options) {
        String publishingWithFtp = cmd.getOptionValue(options.PUBLISH_VIA_FTP_FLAG);
        if (publishingWithFtp == null)
            return true;

        if (!publishingWithFtp.equalsIgnoreCase("true") && !publishingWithFtp.equalsIgnoreCase("false")) {
            System.err.println("Invalid argument: -pf,--" + options.PUBLISH_VIA_FTP_FLAG + " must be 'true' or 'false'");
            return false;
        }
        if (publishingWithFtp.equalsIgnoreCase("true")) {
            String publishingWithDi2 = cmd.getOptionValue(options.PUBLISH_VIA_DI2_FLAG);
            if (publishingWithDi2 != null && publishingWithDi2.equalsIgnoreCase("true")) {
                System.err.println("Only one of -pf,--" + options.PUBLISH_VIA_DI2_FLAG + " and " +
                        "-ph,--" + options.PUBLISH_VIA_DI2_FLAG + " may be set to 'true'");
                return false;
            }
            String controlFilePath = cmd.getOptionValue(options.PATH_TO_CONTROL_FILE_FLAG);
            if (controlFilePath == null) controlFilePath = cmd.getOptionValue(options.PATH_TO_FTP_CONTROL_FILE_FLAG);
            if (controlFilePath == null) {
                System.err.println("A control file must be specified when " +
                        "-pf,--" + options.PUBLISH_VIA_FTP_FLAG + " is set to 'true'");
                return false;
            }
        }
        return true;
    }

    private static boolean validatePublishViaDi2HttpArg(CommandLine cmd, CommandLineOptions options) {
        String publishingWithDi2 = cmd.getOptionValue(options.PUBLISH_VIA_DI2_FLAG);
        if (publishingWithDi2 == null)
            return true;

        if(!publishingWithDi2.equalsIgnoreCase("true") && !publishingWithDi2.equalsIgnoreCase("false")) {
            System.err.println("Invalid argument: -pf,--" + options.PUBLISH_VIA_DI2_FLAG + " must be 'true' or 'false'");
            return false;
        }
        if (publishingWithDi2.equalsIgnoreCase("true")) {
            String publishingWithFtp = cmd.getOptionValue(options.PUBLISH_VIA_FTP_FLAG);
            if (publishingWithFtp != null && publishingWithFtp.equalsIgnoreCase("true")) {
                System.err.println("Only one of -pf,--" + options.PUBLISH_VIA_DI2_FLAG + " and " +
                        "-ph,--" + options.PUBLISH_VIA_DI2_FLAG + " may be set to 'true'");
                return false;
            }
            String controlFilePath = cmd.getOptionValue(options.PATH_TO_CONTROL_FILE_FLAG);
            if (controlFilePath == null) controlFilePath = cmd.getOptionValue(options.PATH_TO_FTP_CONTROL_FILE_FLAG);
            if (controlFilePath == null) {
                System.err.println("A control file must be specified when " +
                        "-ph,--" + options.PUBLISH_VIA_DI2_FLAG + " is set to 'true'");
                return false;
            }
        }
        return true;
    }

    private static boolean validateHeaderRowArg(CommandLine cmd, CommandLineOptions options) {
        String haveHeader = cmd.getOptionValue(options.HAS_HEADER_ROW_FLAG);
        String publishingWithDi2 = cmd.getOptionValue(options.PUBLISH_VIA_DI2_FLAG);
        String publishingWithFtp = cmd.getOptionValue(options.PUBLISH_VIA_FTP_FLAG);
        String controlFilePath = cmd.getOptionValue(options.PATH_TO_CONTROL_FILE_FLAG);
        if(haveHeader == null) {
            if (controlFilePath == null) {
                if (isNullOrFalse(publishingWithFtp) && isNullOrFalse(publishingWithDi2)) {
                    System.err.println("Missing required argument: -h,--" + options.HAS_HEADER_ROW_FLAG + " is required");
                    return false;
                } else {
                    // if publishing via ftp or di2/http, we want to err about the control file, not the header arg
                    return true;
                }
            } else { // have a control file, don't need the header arg (and would ignore it anyway)
                return true;
            }
        } else {  // have non-null header arg
            if (!cmd.getOptionValue(options.HAS_HEADER_ROW_FLAG).equalsIgnoreCase("true")
                    && !cmd.getOptionValue(options.HAS_HEADER_ROW_FLAG).equalsIgnoreCase("false")) {
                System.err.println("Invalid argument: -h,--" + options.HAS_HEADER_ROW_FLAG + " must be 'true' or 'false'");
                return false;
            }
            return true;
        }
    }

    private static boolean validateFileToPublishArg(CommandLine cmd, CommandLineOptions options) {
        if(cmd.getOptionValue(options.FILE_TO_PUBLISH_FLAG) == null) {
            System.err.println("Missing required argument: -f,--" + options.FILE_TO_PUBLISH_FLAG + " is required");
            return false;
        }
        return true;
    }

    private static boolean validateDatasetIdArg(CommandLine cmd, CommandLineOptions options) {
        if(cmd.getOptionValue(options.DATASET_ID_FLAG) == null) {
            System.err.println("Missing required argument: -i,--" + options.DATASET_ID_FLAG + " is required");
            return false;
        }
        return true;
    }

    private static boolean validatePublishMethodArg(CommandLine cmd, CommandLineOptions options) {
        String method = cmd.getOptionValue("m");
        String publishingWithDi2 = cmd.getOptionValue(options.PUBLISH_VIA_DI2_FLAG);
        String publishingWithFtp = cmd.getOptionValue(options.PUBLISH_VIA_FTP_FLAG);
        String controlFilePath = cmd.getOptionValue(options.PATH_TO_CONTROL_FILE_FLAG);
        if(method == null) {
            if (controlFilePath == null) {
                if (isNullOrFalse(publishingWithFtp) && isNullOrFalse(publishingWithDi2)) {
                    System.err.println("Missing required argument: -m,--" + options.PUBLISH_METHOD_FLAG + " is required");
                    return false;
                } else {
                    // if publishing via ftp or di2/http, we want to err about the control file, not the method arg
                    return true;
                }
            } else { // have a control file, don't need the method arg (and would ignore it anyway)
                return true;
            }
        } else {  // non-null method given
            boolean publishMethodValid = false;
            for (PublishMethod m : PublishMethod.values()) {
                if (m.name().equalsIgnoreCase(method))
                    publishMethodValid = true;
            }
            if (!publishMethodValid) {
                System.err.println("Invalid argument: -m,--" + options.PUBLISH_METHOD_FLAG + " must be " +
                        Arrays.toString(PublishMethod.values()));
                return false;
            }
            return true;
        }
    }

    private static boolean validateProxyArgs(CommandLine cmd, CommandLineOptions options) {
        String username = cmd.getOptionValue(options.PROXY_USERNAME_FLAG);
        String password = cmd.getOptionValue(options.PROXY_PASSWORD_FLAG);
        if(username == null && password != null) {
            System.err.println("Missing required argument: -pun,--" + options.PROXY_USERNAME_FLAG + " is required if" +
                    " supplying proxy credentials with -ppw, --" + options.PROXY_PASSWORD_FLAG);
            return false;
        } else if(username != null && password == null) {
            System.err.println("Missing required argument: -ppw,--" + options.PROXY_PASSWORD_FLAG + " is required if" +
                    " supplying proxy credentials with -pun, --" + options.PROXY_USERNAME_FLAG);
            return false;
        }
        return true;
    }

    private static String[] getHeaders(FileTypeControl fileControl, File csvOrTsvFile) throws IOException {
        String[] columns = fileControl.columns;
        if (columns == null) {
            int skip = fileControl.skip == null ? 0 : fileControl.skip;
            columns = Utils.pullHeadersFromFile(csvOrTsvFile, fileControl, skip);
        }

        if (fileControl.hasIgnoredColumns()) {
            ArrayList<String> ignoredColumns = new ArrayList<>(Arrays.asList(fileControl.ignoreColumns));
            if (columns.length < ignoredColumns.size()) {
                throw new ArrayIndexOutOfBoundsException("You cannot ignore more columns than are present in your file");
            }
            String[] headers = new String[columns.length - ignoredColumns.size()];
            int i = 0;
            for (String header: columns) {
                if (!ignoredColumns.contains(header)) {
                    headers[i] = header;
                    i++;
                }
            }
            return headers;
        } else {
            return columns;
        }
    }

    private static JobStatus validateControlFile(FileTypeControl fileControl, String urlBase) {

        if (fileControl == null) return JobStatus.VALID;

        JobStatus goodTimestampFormats = checkTimeFormattingValidity(fileControl);
        if (goodTimestampFormats.isError())
            return goodTimestampFormats;

        JobStatus goodEncoding = checkEncodingValidity(fileControl, urlBase);
        if (goodEncoding.isError())
            return goodEncoding;

        return JobStatus.VALID;
    }

    private static JobStatus checkAction(String action, IntegrationJob job, Dataset schema) {
        StringBuilder methods = new StringBuilder();
        boolean okAction = false;
        for (PublishMethod m : PublishMethod.values()) {
            methods.append("\t" + m.name() + "\n");
            if (m.name().equalsIgnoreCase(action))
                okAction = true;
        }
        if (!okAction) {
            JobStatus status = JobStatus.PUBLISH_ERROR;
            status.setMessage("Unknown Publish Method: " +
                    "The control file must specify the publishing method via the 'action' option as one of: \n" +
                    methods.toString());
            return status;
        }
        if (!PublishMethod.replace.name().equalsIgnoreCase(action) && job.getPublishViaFTP()) {
            JobStatus status = JobStatus.PUBLISH_ERROR;
            status.setMessage("FTP does not currently support upsert, append or delete");
            return status;
        }
        PublishMethod publishMethod = job.getPublishMethod();
        if (publishMethod != null && !action.equalsIgnoreCase(publishMethod.name())) {
            JobStatus status = JobStatus.PUBLISH_ERROR;
            status.setMessage("Conflicting Publish Methods: " +
                    "The publish method selected was '" + publishMethod.name() +
                    "', but the 'action' option in the control file specifies the publish method as '" + action + ".");
            return status;
        }
        String rowIdentifier = DatasetUtils.getRowIdentifierName(schema);
        if (rowIdentifier == null && PublishMethod.delete.name().equalsIgnoreCase(action)) {
            JobStatus status = JobStatus.PUBLISH_ERROR;
            status.setMessage("Dataset Requirement Unfulfilled: " +
                    "To delete from a dataset, a row identifier must be set. Dataset '" + schema.getId() +
                    "' does not have a row identifier set");
            return status;
        }
        return JobStatus.VALID;
    }

    private static JobStatus checkTimeFormattingValidity(FileTypeControl fileControl) {
        if (!fileControl.hasTimestampFormatting())
            return JobStatus.VALID;

        Set<String> timeFormats = fileControl.lookupTimestampFormatting();
        DateTimeFormatter formatter;
        for (String format : timeFormats) {
            if (format.equalsIgnoreCase(supportedTimeFormat))
                continue;
            try { formatter = DateTimeFormat.forPattern(format); }
            catch (IllegalArgumentException e) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                status.setMessage("Unsupported Date Time Format: The time format '" + format +
                        "' specified in the control file is not a valid pattern." +
                        "\nPlease consult " + jodaLink + " for more information");
                return status;
            }
        }

        return JobStatus.VALID;
    }

    private static JobStatus checkEncodingValidity(FileTypeControl fileControl, String urlBase) {
        if (!fileControl.hasEncoding())
            return JobStatus.VALID;

        String encoding = fileControl.encoding;
        if (encoding.equalsIgnoreCase("ISO-8859-1") ||
                encoding.equalsIgnoreCase("UTF-8"))
            return JobStatus.VALID;

        HttpUtility http = new HttpUtility();
        String charsetUri = urlBase + charsetsPath;
        ObjectMapper mapper = new ObjectMapper();
        try(CloseableHttpResponse response = http.get(new URI(charsetUri), ContentType.APPLICATION_JSON.getMimeType())) {
            String[] charsets = mapper.readValue(response.getEntity().getContent(), String[].class);
            boolean encodingFound = false;
            for (String charset : charsets) {
                if (encoding.equalsIgnoreCase(charset)) {
                    encodingFound = true;
                    break;
                }
            }
            if (!encodingFound) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                status.setMessage("Unsupported Encoding: The encoding '" + encoding + "' in the control file is not supported." +
                        "\nPlease consult " + charsetUri + " for a listing of supported encodings");
                return status;
            }
        } catch (Exception e) {
            // no reason to fail jobs because of encoding check
        }
        return JobStatus.VALID;
    }

    /**
     * This check compares the information found in the control file to the headers and dataset schema.
     * We check 3 things:
     *   1) that the headers don't already contain any synthetic locations
     *   2) that the components of each synthetic location are present in the headers
     *   3) that the datatypes used in a synthetic location column are supported types
     * @param schema the soda-java Dataset that provides dataset info
     * @param headers the columns that will be uploaded; taken from either the file or the control
     *                file and minus the ignoredColumns
     * @param csvFilename the file name, for printing purposes.
     * @return
     */
    private static JobStatus checkControlAgreement(FileTypeControl fileControl, Dataset schema, String[] headers, String csvFilename) {

        if (schema == null || fileControl == null || !fileControl.hasSyntheticLocations()) return JobStatus.VALID;

        Map<String, LocationColumn> syntheticLocations = fileControl.syntheticLocations;
        for (String field : syntheticLocations.keySet()) {     // O(N) N = 1,2 (small, probably)
            LocationColumn location = syntheticLocations.get(field);
            String[] locationComponents = new String[]{location.address, location.city,
                    location.state, location.zip, location.latitude, location.longitude};
            boolean locationInFile = false;
            boolean[] componentsInFile = new boolean[]{location.address == null, location.city == null,
                    location.state == null, location.zip == null, location.latitude == null, location.longitude == null};
            for (String header : headers) {     // O(M)  M = a couple dozen?
                if (field.equalsIgnoreCase(header)) {
                    locationInFile = true;
                    break;
                }
                for (int j = 0; j < componentsInFile.length; j++) {   // O(1)  constant at 6
                    if (!componentsInFile[j])
                        componentsInFile[j] = header.equalsIgnoreCase(locationComponents[j]);
                }
            }
            if (locationInFile) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                status.setMessage("Ambiguous Column Name: Synthetic location '" + field + "' specified in the control file may conflict with '" +
                        field + "' provided in '" + csvFilename + "'." +
                        "\nPlease ensure '" + field + "' is not currently mapped to fields in the CSV.");
                return status;
            }

            //The documentation says that this should work, so I'm commenting it out

//            for (int i = 0; i < componentsInFile.length; i++) {
//                if (!componentsInFile[i]) {
//                    JobStatus status = JobStatus.PUBLISH_ERROR;
//                    status.setMessage("Synthetic Location Not Found: The synthetic location column '" + field +
//                            "' references a component '" + locationComponents[i] + "' which is not present in '" +
//                            csvFilename + "'." +
//                            "\nPlease check your control file to ensure that the column name is spelled correctly, " +
//                            "and that '" + locationComponents[i] + "' is not included in the 'ignoreColumns' array.");
//                    return status;
//                }
//            }
            JobStatus typesSupported = locationTypesSupported(field, location, DatasetUtils.getDatasetTypeMapping(schema));
            if (typesSupported.isError())
                return typesSupported;
        }

        return JobStatus.VALID;
    }

    /**
     * checks that the datatypes associated with each synthetic location component column are supported
     * @param location a LocationColumn
     * @param typeMapping a map from dataset column field names to their type
     * @return true if each component column has a supported type; false if any have an unsupported type
     */
    private static JobStatus locationTypesSupported(String locationName, LocationColumn location, Map<String,String> typeMapping) {
        Map<String,String> componentColumns = location.findComponentColumns();
        for (String component : componentColumns.keySet()){
            String fieldname = componentColumns.get(component);
            String[] supportedTypes;
            if (component.equalsIgnoreCase("zip") || component.equalsIgnoreCase("latitude") || component.equalsIgnoreCase("longitude")) {
                supportedTypes = new String[]{"text", "number"};
            } else {
                supportedTypes = new String[]{"text"};
            }
            String unsupportedType = getUnsupportedType(fieldname, supportedTypes, typeMapping);
            if (unsupportedType != null) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                StringBuilder message = new StringBuilder("Unsupported Datatype: " + Utils.capitalizeFirstLetter(component) + " component  '" +
                        locationName + "' is of type '" + unsupportedType + "'; " + component + " components require ");
                if (supportedTypes.length == 1) message.append(" a ");
                message.append("'" + supportedTypes[0] + "'");
                for (int i=1; i<supportedTypes.length; i++)
                    message.append(" or '" + supportedTypes[i] + "'");
                message.append(" column");
                if (supportedTypes.length > 1) message.append('s');
                message.append(".\nPlease specify a column that matches a supported type.");
                status.setMessage(message.toString());
                return status;
            }
        }
        return  JobStatus.VALID;
    }

    /**
     * returns null if the component type is okay, otherwise the bad type.
     */
    private static String getUnsupportedType(String component, String[] supportedTypes, Map<String, String> typeMapping) {
        String componentType = typeMapping.get(component);
        if (component == null || componentType == null)
            return null;

        for (String okayType: supportedTypes) {
            if (okayType.equalsIgnoreCase(componentType))
                return null;
        }
        return componentType;
    }

    /**
     * This check compares the information found in the headers to the columns found in the dataset.
     * We check 3 things:
     *   1) that the headers have a row identifier column if the dataset does
     *   2) that the headers don't contain columns not in the dataset
     *   3) that the headers aren't missing columns in the dataset IF there is no row identifier
     * @param schema the soda-java Dataset that provides dataset info
     * @param headers the columns that will be uploaded; taken from either the file or the control
     *                file and minus the ignoredColumns
     * @param csvFilename the file name, for printing purposes.
     * @return
     */
    private static JobStatus checkColumnAgreement(FileTypeControl fileControl, PublishMethod method, Dataset schema, String[] headers, String csvFilename) {

        String[] ignoredColumns = fileControl.ignoreColumns;

        if (schema == null) return JobStatus.VALID;

        String rowIdentifier = DatasetUtils.getRowIdentifierName(schema);
        Set<String> columnNames = DatasetUtils.getFieldNamesSet(schema);

        boolean headerHasRowId = false;
        for (String field : headers) {
            field = field.toLowerCase();
            if (field.equalsIgnoreCase(rowIdentifier)) headerHasRowId = true;

            if (!columnNames.contains(field)) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                status.setMessage("Extra Columns Specified: File '" + csvFilename + "' contains column '" + field +
                        "', but dataset '" + schema.getId() + "' does not." +
                        "\nPlease check that your headers are using the field name rather than the human readable name." +
                        "\nConsider using 'ignoreColumns' in the control file if '" + field +
                        "' should not be included in the dataset");
                return status;
            } else {
                columnNames.remove(field);
            }
        }
        for (String ignored : ignoredColumns) {
            if (columnNames.contains(ignored))
                columnNames.remove(ignored);
        }

        Map<String,?> syntheticColumnsMap =  fileControl.syntheticLocations;

        if (syntheticColumnsMap != null) {
            Set<String> syntheticColumns = syntheticColumnsMap.keySet();
            columnNames.removeAll(syntheticColumnsMap.keySet());
        }

        syntheticColumnsMap = fileControl.syntheticPoints;
        if (syntheticColumnsMap != null) {
            columnNames.removeAll(syntheticColumnsMap.keySet());
        }

        if (columnNames.size() > 0 && method.equals(PublishMethod.replace)) {
            if (rowIdentifier == null) {
                JobStatus status = JobStatus.MISSING_COLUMNS;
                StringBuilder message = new StringBuilder("Missing Fields: Dataset " + schema.getId() +
                        " contains the following field(s) that are not mapped: ");//available in '" + csvFilename + "': ");
                boolean writtenFirstValue = false;
                for (String colName : columnNames) {
                    if (writtenFirstValue)
                        message.append(", ").append(colName);
                    else{
                        message.append(colName);
                        writtenFirstValue = true;
                    }
                }
                status.setMessage(message.toString());
                return status;
            } else if (!headerHasRowId) {
                JobStatus status = JobStatus.PUBLISH_ERROR;
                status.setMessage("Missing Row Identifier: Dataset '" + schema.getId() + "' contains a row identifier in column '" + rowIdentifier +
                        "'. This column must be present in '" + csvFilename + "'.");
                return status;
            }
        }
        return JobStatus.VALID;
    }

    private static boolean isNullOrFalse(String s) {
        return s == null || s.equalsIgnoreCase("false");
    }

}

redpajama_set_name: RedPajamaGithub


William Shakespeare gave the world some of its most lasting love stories.
The now-Bupperts were working at the UA Bookstores when they met. Dove Mittelman Bupperts tells the story that, at the time, Matthew Bupperts was considering work elsewhere, but a friend told him to stay at the bookstore, expecting that the two would grow closer.
He recalled: "The first words she ever said to me were, 'You're Matt? I hear we're going to be best friends forever.'"
The first time Matthew took Dove to Santa Barbara, California, to meet his parents, she said she thought: "We are definitely getting married here." At the time, the two had been dating for four months.
After graduating from the UA (Matthew in 2007 from the Eller College of Management, Dove in 2008 with a degree in communication), they moved to Phoenix and have since supported each other "through big moves, new jobs, ice cream flavor selections and Friday night Netflix movie picks," Dove said.
Eventually, Matthew proposed. The Bupperts have been together for nine years and were married on Sept. 19, 2015, in Santa Barbara.
Sogge and Soto met about four years ago during what was then an LGBTQ campus social club called SPRITE.
The two had been friends for five months "and fell into a relationship naturally," said Sogge, a graduate student in the UA's Center for the Study of Higher Education.
One of Sogge's favorite memories of the two came during a trip to Flagstaff to visit his family.
"After a nice hike in Sedona and some local dinner, we drove up to the top of Mars Hill to watch the stars. Flagstaff is where Pluto was discovered, so the skies are incredibly pristine and awesome," Sogge said. "We stayed in the car with the hatch open, listening to music, and watching falling stars until I lost track of time. Seeing the sky makes me feel so small compared to the infinite vastness of the universe, but I also felt infinite with Eddie by my side."
Sogge graduates from the UA in May; Soto graduated last year from the UA with a degree in neuroscience.
"Here is to four years of support, love, challenging me to grow, and to many, many more years to come," Sogge said.
In March 2016, Ginger and her husband, James, will celebrate their 15-year anniversary.
The two met nearly 30 years ago in Huntsville, Alabama, at the U.S. Space Camp while in the eighth grade.
"It was love at first sight," said Ginger, an instructional designer in the UA Office of Digital Learning. James was from Boston and she was living in Des Moines.
"The day I left Space Academy broke my heart. Over the next year, we saw each other once. We spoke on the phone once a week. We mailed actual letters to each other via USPS. The relationship lasted one year. It disappeared due to distance. We sent a few letters during college, but the circumstances weren't quite right.
"In 2000, as a K-12 teacher, I was preparing to chaperone eight of my middle-school students to Space Academy. I wondered what happened to that boy I couldn't forget from Boston. At the same time, my now mother-in-law suggested he call that girl from Iowa. He did."
It had been 12 years since the two had spoken. The first conversation lasted four hours.
"We never missed another day with each other," Ginger said. Today, the couple have two children, two pups "and an amazing life."
At the end of a workday in August 2015, Johnson picked up Hardesty from her UA office, where she works as a health educator for Campus Health Service, telling her that had heard about an evening at the McKale Center. He coaxed her into attending.
After parking near the facility, they walked toward McKale, hearing the Pride of Arizona practicing in the distance. As season-ticket holders, the two were regulars at the facility. Johnson led Hardesty through the same doors they went through during the basketball season.
He began talking about what a special place McKale was to both of them, and how it held so many memories of their six-plus years together. He then turned his attention to her, expressing his love and appreciation.
"He then got down on one knee," Hardesty recalled. "He pulled a black box out of his boot and opened it to reveal a beautiful ring."
Stunned, Hardesty did not know what to say. Johnson pulled her into a hug.
"Carrie, you haven't said anything," he said, to which she responded with a "yes." Hardesty recalls that, at that moment, the Pride of Arizona coincidentally erupted into a cheer.
Hardesty graduated from the UA in 2010 with a degree in health education. Johnson graduated in 2011 with a degree in public management and policy. The two will wed in Tucson in September 2016.
Shelly and her husband, Chris, fell in love through a shared appreciation for the Piano Trio No. 2, Op. 67 by Dmitri Shostakovich, a Russian composer.
"I met my significant other at a gig where he was performing on violin," said Shelly, an associate marketing specialist for UA Libraries. "After it ended, we began chatting, and discovered that we both had a current fascination with the piano trio. It was serendipitous."
The two married about 1.5 years later, "and my husband rearranged that Shostakovich piece to be played at our ceremony."
The Ballards met at the UA in 1997 while both were working as lifeguards at the Student Recreation Center.
"Ever ambitious, she quickly became my boss," said Dan, who graduated from the UA in 1999 with a degree in accounting.
"We became great friends," he said.
Although they dated other people for a while, Dan said, "I think that we always knew deep down that if we ever got together, we would end up getting married. At least I did."
Heather graduated in 1999 with a degree in molecular and cellular biology, and they married in 2001. They now have four children — or future Wildcats, as Dan likes to say.
"She's my best memory from the UA, and the best thing that ever happened to me," he says. "I am forever grateful to the University for bringing us together."


Abstract: One of the most important ways to enjoy life after retirement is to trade inactive time for active time. That is, instead of being on the computer, go bowling with friends. Instead of watching television, try going out with Airwheel H3S power chair that makes you get up and move.
If you have always loved adventure, retirement is a great time to travel. You now have all the time you need to visit the places you always wanted to see. Plus, seeing the world will keep you engaged and interested in the world. Also, you can have some short to medium trips with Airwheel H3S Smart Electric Wheelchairs. There are three key words to describe Airwheel H3S: compact, elegant, practical.
From the aspect of externality, Airwheel H3S folding power chair is equipped with environment friendly shell, anti-slip pedals and adjustable joystick controller. With a small diameter measuring just 8 inch and 12.5 inch, they allow for extremely precise movements, and all the sensitivity required to perform even the most complex manoeuvres in reduced spaces. It gains better outdoor performance thanks to the specially designed wheels, which enable H3S to adapt to more road conditions, with brilliant traffic ability. The compact size of Airwheel H3S is surely to leave a deep impression on you thanks to the firstly introduced automatic folding system. That is, riders just press the button to make it fold or unfold.
To gain elegance, Airwheel H3S bids farewell to laborious riding and riders just sit on the saddle and push the joystick controller to go. H3S motorized chair has dual ride modes, one is the electric mode, which gives riders an effortless ride and allows riders to stop and chat freely and the other one is the manual mode so that they can enjoy the company of friends and families. In addition, the headlight under the controller and the LED brake taillight allows riders to ride in light-sufficient environment and gives timely reminder to the rear vehicles.
There are many ways for retirees to enjoy life and travelling is suitable for many old people. Then, the Airwheel H3S Electric Wheelchair will be an effective helper.


In accordance with the ASTM E74-13a standard, Morehouse reports the Lower Limit Factor (LLF) which is reported on page one of the calibration certificate and not uncertainty. ASTM E74-06 used to call LLF the uncertainty. The ASTM E74-06 standard was very misleading as the uncertainty reported was only one component of the overall system uncertainty. ASTM E74-13a addressed this issue by changing the term uncertainty to LLF. The LLF is calculated in accordance with section 8 Calculation and Analysis of Data found in ASTM E74-13a and is a Type A uncertainty component.
Morehouse has developed a guidance document to help you calculate your uncertainty. This guidance document incorporates suggestions found in the ASTM E74-13a appendix. You can click here to download the excel CMC calculator.
Once you open the EXCEL file download provided select the Morehouse Uncertainty Worksheet there is a field to enter the Lower Limit Factor from the calibration certificate. You will be asked to enter additional information that will be found on page #1 and the ASTM data page.
The Lower Limit Factor and additional information can then be entered into the spreadsheet as per the example from the download to calculate your measurement uncertainty.
The International Laboratory Accreditation Cooperation (ILAC) P-14 section 6.4 requires contributions such as the resolution of the Unit Under Test (UUT) be included in the uncertainty per point value reported on the certificate of calibration. Euramet CG4 v2.0 Uncertainty of Force Measurements also requires the resolution of both the standard and unit under test be reported as standard contributors. The per point analysis consists of the UUT resolution and the CMC of our standard (which will include the resolution of the reference standard if primary standards were not used). When you run a full uncertainty analysis per ILAC P-14 as required by ISO/IEC 17025 guidelines you can then derive your CMC (Calibration and Measurement Capability) which is then reported to your customer, among other things. There is a tab in this spreadsheet to help with calculating your CMC per point. Morehouse can give guidance on using our sheet. It is recommended a professional consultant be contacted if you are having problems figuring out CMC or what to report. We offer training with a focus on measurement uncertainty and an independent consultant two to three times per year. Upcoming training information can be found on our training page.
Pull your force calibration data from anywhere using your smartphone.


In what way are we different to a mainstream school?
are in Years 7 to 11.
Unlike mainstream school, most of our pupils are admitted after Year 7. Transition is flexible to suit each pupil and will always discuss plans with parents/carers. We gather all the available information necessary to meet the social, behaviour and learning needs of our new arrivals. To help with this we talk to other schools and adults involved (including teachers, Education Psychologists or social worker) and we arrange to do some testing to find out what levels new pupils are able to work at. Our Y7 transition normally involves a visit with parents and carers, and then two or three sessions here is school often in late June early July. All our pupils have a history of disrupting their own learning as well as that of others.
In both of these situations, a lot of discussion is always had between home, school, the pupil and Local Authority.
Should any pupil require additional physical support in order to access the building and our learning, then we will ensure we make reasonable adjustments as we have been obliged to since September 2012 under the Equality Act 2010.
Size We are small. This means we get to know our pupils really well, and they get to know use really well too. It also means we are able to ensure that we are much better able to support all the needs of pupils as they arise during the day. This also means that our higher numbers of more vulnerable pupils, whether they are Looked After Children, or in receipt of free school meals, can benefit from higher levels of attention and support comparted to that available in mainstream schools.
Staff Our staff have chosen to work at Woodlands, which is a challenging, interesting and rewarding environment in equal measure. They are experienced at working with pupils who need extra support, and many have gained more qualifications to help with this. While there are many professionals in other schools who are exceptional in supporting all pupils, we expect all our staff to be the best in the 'behaviour' specialism they work in. Staff are skilled at combining friendliness, support, warmth, flexibility and humour with firmness when needed. We have a Home Liaison Manager who can work with parents to support attendance.
As both are involved in the admissions process.
Curriculum. Pupils do not have the range of options available as they would in a large school, although we do offer a good range of subjects. All pupils who are able can achieve up to 9 GCSE passes in English, Maths, Science, ICT, PE, Food Technology or Resistant Materials (DT), Art and Humanities. We do not offer foreign language, as many of our pupils need help mastering skills in English such as reading and writing.
Teaching supports learners who are resistant to heavily literacy based learning and so is often active (kinaesthetic) and practical where appropriate. We do not have after school clubs or a range of school teams, as pupils have to travel together in taxis to get home each day. Instead we timetable these as lessons through our two-week timetable, meaning pupils have access to a range of activities such as swimming, cycling, gym, horticulture, dog walking, bee keeping, trampolining and climbing, to name a few. We also play some fixtures against other schools, for example in football. We also ensure pupils access opportunities that may be denied them in mainstream school, such as Arthog Outdoor Education Centre and a work experience in Year 10.
Organisation We are set up to work with the pupils we have, who are a minority at a mainstream school and who stand out as 'challenging' or different' there. Although every pupil is an individual, they fit in here as they are with other pupils who are like them. Our small groups of up to 8 pupils supported by 2 staff, high staffing and supervision levels and tight routines mean that support is built into everything we do, reducing opportunities for things to go wrong, and making it easier to sort if they do.
Pupils arriving in Year 7 are taught by our specialist primary trained teacher in a dedicated area for much of the week, providing an extended transition from primary to secondary schooling. This means they can feel settled quickly in a nurturing environment with very structured routines and high expectations. Year 8 pupils are taught as a class together. In Year 9, pupils are streamed along with other Key Stage 4 pupils and start a three year examination timetable planned and taught at their level, leading to a range of qualifications.
Expectations although expectations are high, we also know that our pupils at times find it hard to sustain good behaviour at all times. When things aren't as positive as we would like we will isolate the behaviour from the individual, ensuring that our pupils are confronted by consequences of their choices whilst expecting and challenging the pupil to do better next time. We do not therefore accept poor choices or behaviour, but instead try to ensure the pupil knows what is expected, point out what they did wrong and what it is they need to do better next time, and to then give opportunities to show improvement in future. We are very good at then rewarding positives to help pupils appreciate any improvements made, and will celebrate any success, however small.
Support each pupil chooses a mentor who supports them and helps plan, monitor and reward success and who communicates with home regularly. Behind this mentor system we have a full time dedicated Pastoral Manager who helps ensure that issues affecting pupil attendance and success are dealt with as quickly and effectively as possible. Staff have all chosen to work here in this school. That means they want to help all pupils enjoy everything the school has to offer and are flexible and very supportive of each child so that everyone is able to take part in all we do. Sometimes this means we have to make special plans to help some pupils take part in things like trips out. This might mean an extra car and member of staff going to help for example.
We invest a lot in our rewards system as we look to reward behaviours and attitudes to learning that would be taken for granted in most schools. Pupils receive feedback in terms of marks in every lesson. These marks are linked to our VIVOS reward system. There are staff available at all times to ensure that any pupil needing extra help with learning or behaviour will get it. Pupils start to follow varied examination courses in Year 9, being split into one of 4 groups depending on ability. This means that we can make sure teaching is at the right level for each child, on a course selected to enable them to di as well as possible. Pupils can take GCSEs from Year 9 if ready, and repeat them in Years 10 and 11 if appropriate. We offer targeted learning interventions, for example in 1:1 reading sessions where needed so pupils gain better access to the curriculum.
We try really hard to make sure that we talk to parents and carers at every opportunity – our mentors will be known to adults at home and will make contact at least once a week, and whenever there are any issues. On top of this our full time Pastoral Manager is always available to speak to meet with or visit parents/carers to discuss any concerns affecting home and school. We hold an annual BBQ and parents evening here each July, and as our pupils come from across Shropshire we have started to hold some parents evenings in the areas where pupils live, to make it easier for parents to come and see us.
In what way are we similar to a mainstream school?
Curriculum Although choice in subjects is limited we do offer English, Maths, Science, ICT, PE, Art, DT, Product Design, Food Technology, Personal, Social & Health Education, Humanities (History, Geography and RE). We follow the national curriculum and enter pupils for public examinations in all subjects bar PSHE. We supplement the formal curriculum with activities that are designed to expand pupil experience.
Like a mainstream school we test and assess pupils, and provide intervention if necessary, for example in reading. We also teach pupils in Year 9, 10 and 11 in ability groups, so that we can more accurately teach to the ability of pupils to enable them to fulfil their potential.
Expectations We expect that all our pupils are able to exceed the potential that they feel they may have. Although many arrive well behind their peers, our aim is to ensure that they catch up to where they should be so that they can achieve as well as possible in their examinations. We set targets for pupils based on their starting points with us, and then expect them to make exactly the same levels of progress that pupils of their age would make in mainstream schools. We review and share progress pupils are making twice a year, although in fact we expect our teachers to know how much progress all pupils are making at all times. We regularly monitor this and teachers ensure that pupils are well taught in well planned lessons. We therefore expect our pupils to manage themselves well enough to work hard and allow others to also do well.
Staff All planned lessons are taught by qualified teachers, and our Teaching Assistants have Level 3 qualifications. Many staff have additional qualifications in SEN. We not only teach but provide mentoring to pupils in order that they have some pastoral support.
Facilities We have specialist classroom areas that are very well equipped in Science, Art, DT, Food Technology and ICT. We have a small gym and pitch area where we can play sports. Most classrooms have the latest whiteboard technology and learning is helped by pupils having access to lap tops.
Support We have many visitors, some coming to provide additional support to pupils as they would in a mainstream school. This includes Social workers, colleagues from Targeted Youth Support, the Substance Misuse or the Looked After Children Teams, School Nurse, our careers advisor, or Family Solutions who provider a variety of services aimed at supporting pupils in school and their wider lives. We frequently meet with colleagues from these services, often with the families and pupils, in our efforts to ensure our pupils achieve the best that they can. Our Pastoral Manager is always available to talk to parents, arrange meetings and enable families to access services that might help them. She has a lot of specialist training, qualifications and expertise in support of this work.
Preparation for leaving Towards the end of Year 10 pupils supported by our careers advisor have a short work experience, preferably at an organisation they have identified within their local area. This enables them to gain valuable insight into the adult world of work. Some pupils can, if appropriate and if all parties agree, continue with a more regular experience where they can develop their skills and experiences more fully. This is, and may help them secure a job when they leave school. Our careers advisor produces a report which can go to employers or places of further education. We are always happy to help pupils in their applications, through discussion, visits, or practical help like filling in forms.
Ofsted inspect the school on a regular basis, and compare us to mainstream schools, making sure that our pupils do as well as children in mainstream school, and holding us responsible if they do not. This means that just like mainstream schools we have to make our own judgements about how well we are doing and then plan to make things better. We make an Action Plan which we look at each year and which shows the areas that we want to make sure we focus on. The Local Authority help us to do this by looking at what we do, how well we do it and how we might be able to make it better. Our Governors also help us by checking that we are doing everything we say we will do, and asking us questions and setting us tasks if they feel we can be doing anything better. They do this on a regular basis, coming in to see us, and by holding committee meetings and meetings of the full governing body every half term.


text:
Help for Stressed-Out Moms
Guests: Rob & Dianne Parsons
FREE PDF Download Website
Care for the Family Website
Sixty Minute Mother Website
Bringing Up Boys Website
Parent Burnout Website
"I'm just a mom." If this represents your sentiment, or if you're hurtling toward burnout, tune into Family Talk with Dr. James Dobson, where our guest discusses her struggle to find her own identity in the midst of being the wife of a high-powered husband and mother of two demanding toddlers. For insight into balancing multiple roles, tune in today!
Best-selling author and international speaker Rob Parsons has been speaking and writing on family and work-life issues for over 20 years, since he launched Care for the Family in 1988. More than 500,000 people have benefited from Rob's wisdom and insights in live seminars around the world, and his ten books have sold worldwide in over a dozen languages.
Rob and Dianne live in Cardiff and have two grown-up children, Katie and Lloyd.
Dianne has been very involved with the work of Care for the Family since it began in 1988, and over the years has spoken to thousands of people at Care for the Family and other events. She especially enjoys speaking at events for women.

redpajama_set_name: RedPajamaCommonCrawl


Photoland – the center for all things photography on The Evergreen State College campus welcomes students and the Olympia community.
Located in the library basement, Photoland Incorporates Evergreen's Instructional Photography and Photo Production Services, including spaces for darkroom developing, digital editing and printing with supplies available for sale.
The world of photography today is predominantly digital, but people still show interest in pursuing more traditional methods. Because of this, Photoland appeals to old and new techniques. The facilities allow you to experiment with variety of photo formats, and come away with good results. Some still prefer 35mm film over digital and insist that it offers the best quality, and the Photoland darkroom gets plenty of attention from students in photography classes as well as recreational photographers.
Regardless of your photography method, Photoland has everything you need, with 21 stations for film processing, and 12 computers for digital editing. Each computer is complete with the latest and best photo software and equipped with two monitors that simulate the appearance of the photo on the web versus print quality. They are also connected to film scanners for anyone who wants to make prints from negatives but would like to bypass the hassle of the darkroom.
Evergreen Photography Professor Steve Davis oversees the department and helped expand Photoland. He said that the goal was to set up "a specific photo center that was not a part of the computer center, unlike many other institutions." This separation was a great idea since the computer center on the floor above is usually crammed with students trying to print papers before class. This separation also makes Photoland its own little community; a place where all types of photographers can create and share their work and help each other.
Photoland is meant to assist students and classes, but they "keep an open door to community members," however not that many non-students come in. While access is free for students, it's $25 for one day, and $125 for the academic quarter (10 weeks) for community use. The price may seem steep, but unlike photography specialists and drugstore photo centers, Photoland enables any photographer to be involved in the process, and caters to their vision.
Davis says "It's always great to see when people are able to create or print good photos." Some of the software can be overwhelming to new users, and darkroom work takes much attention to detail. Luckily there are always two or three lab aids available to help with software, hardware and printmaking who are avid photographers themselves. When you walk in, they help you get set up on a computer or darkroom station and have no problem answering your questions or helping you make that perfect print.
When asked about his favorite aspect of Photoland, Professor Davis referred to "the people." He appreciates having the lab aids, students and community members working under the same roof. Davis also mentioned the "exhibition quality" printers available. These state of the art Epson models use archival ink, with one printing at a width of 24 inches and the other at 44 inches. These can be utilized not only with digital images, but through film scanning as well.
Some of the newer additions to Photoland are to its environment. This includes the expansion of the Photoland gallery near the entrance, and The Alcove where you can pick up a snack or beverage. One of the more technical works in progress is incorporating video, which is being used more and more by photographers. Because of improvements like these every year, it is difficult to find any areas in which the facilities are lacking and there are very few complaints from Photoland users, including myself. So whether you have some old rolls of film you'd like to print, or if your hard drive is overflowing with years of digital images, Photoland wants to help you with all of your photography needs.
If you'd like more information on Photoland, go to photo.evergreen.edu or follow their Facebook page.


text:
p. +61 3 6256 7888 Call Now Check Rates
FROM AUD$2100
Welcome to Saffire Freycinet
Luxury Travel Magazine Gold List 2018, "Best Australian Small Hotel"
QANTAS Australian Tourism Awards Winner 2017 "Best Luxury Accommodation"
Book Now View Full Awards List
"There was something magical about Saffire during winter – the air seemed clearer, the colours brighter and every moment lasted a little longer."
Saffire Freycinet is Australia's premier luxury lodge, providing an authentic and immersive connection to the magnificent Freycinet National Park and Wineglass Bay in Tasmania. Distinct in its design, exclusive in its features and set apart by its approach to individually tailored experiences and service, Saffire features 20 private suites, a luxury day spa, an unforgettable culinary offering and so much more. Previously voted the World's Best Boutique Hotel and World's Best Luxury Hotel, Saffire Freycinet has also been named as Best Luxury Hotel in Australia by Trip Advisor for the past six years. To experience Saffire Freycinet is to detach from ordinary life and become truly immersed in every moment.
Even the journey was spectacular. The closer we got, the more the anticipation built."
Before I know it, I'm standing knee deep in an estuary having fresh oysters and local sparkling for breakfast."
We opened the door and every expectation was suddenly exceeded. The warmth, the elegance – and that view!"
It's a view that's impossible to describe and impossible to escape. Not that you'd want to!"
SAFFIRE MOMENTS
The Saffire experience is made up of moments. Meaningful moments that capture the essence of Saffire and its close connection to the Freycinet area. Moments as complex as the luxurious pleasures of a day spa experience, or as simple as toes feeling the squeaky white sand. As inspiring as the sunset splashed across the Hazards, and as intimate as the morning sun creeping up the wall of your suite.
The moments can be as unique as wading out to partake of fresh oysters in the waters of a nearby estuary, or as surprising as realising your wine glass hasn't been empty since sitting down to dinner. Only Saffire has these moments. By delivering them, we are celebrating what is different about Saffire – not what is the same.
It was like they could read our minds. They seemed to offer us things we didn't even know we needed"
All of the suites are luxuriously appointed, featuring the best in design and furnishings and capturing the unique nature of the surrounding coastline. All suites are designed for the utmost privacy while taking in the magnificent views of The Hazards, crystal-clear blue waters and white sandy beaches.
The suites are designed to relax and rejuvenate, indulge in reading and lazing the days away, and to find connection with the environment to heal and restore balance.
Saffire's restaurant, Palate, prides itself on showcasing the best Tasmania has to offer, and provides tailor-made experiences to suit all guests.
From early morning to late evening, food innovation in the kitchen does not compromise the true flavours of home-grown produce. Palate specialises in tailoring dining to all guests' requirements.
Genuine and relaxed, yet cultured and discerning service awaits you. Our knowledgeable and skilled team is warm and friendly and presents au guéridon cooking and silver service with panache.
Dining Virtual Tour
Everything had a local connection – a farm down the road; the vineyard over the hill; seafood pulled dripping from the bay that afternoon."
Before I know it, I'm standing knee deep in an estuary having fresh oysters and local sparkling for breakfast."
It's a view that's impossible to describe – and impossible to escape. Not that you'd want to!"
Everything had a local connection - a farm down the road; the vineyard over the hill; seafood pulled dripping from the bay that afternoon."
View our Experiences
Tag us in your photos during your stay
#saffirefreycinet
Keen to 'visit' before you stay? Come with us for a sneak peek of our luxury hotel, restaurant, beautifully appointed suites, generous lounge area, relaxing spa and alluring views.
Sign up to receive exclusive updates and offers from Saffire Freycinet
Email Address * City
Country Afghanistan Åland Islands Albania Algeria American Samoa Andorra Angola Anguilla Antarctica Antigua and Barbuda Argentina Armenia Aruba Australia Austria Azerbaijan Bahamas Bahrain Bangladesh Barbados Belarus Belgium Belize Benin Bermuda Bhutan Bolivia Bosnia and Herzegovina Botswana Bouvet Island Brazil British Indian Ocean Territory Brunei Darussalam Bulgaria Burkina Faso Burundi Cambodia Cameroon Canada Cape Verde Cayman Islands Central African Republic Chad Chile China Christmas Island Cocos (Keeling) Islands Colombia Comoros Congo Congo, The Democratic Republic of The Cook Islands Costa Rica Cote D'ivoire Croatia Cuba Cyprus Czech Republic Denmark Djibouti Dominica Dominican Republic Ecuador Egypt El Salvador Equatorial Guinea Eritrea Estonia Ethiopia Falkland Islands (Malvinas) Faroe Islands Fiji Finland France French Guiana French Polynesia French Southern Territories Gabon Gambia Georgia Germany Ghana Gibraltar Greece Greenland Grenada Guadeloupe Guam Guatemala Guernsey Guinea Guinea-bissau Guyana Haiti Heard Island and Mcdonald Islands Holy See (Vatican City State) Honduras Hong Kong Hungary Iceland India Indonesia Iran, Islamic Republic of Iraq Ireland Isle of Man Israel Italy Jamaica Japan Jersey Jordan Kazakhstan Kenya Kiribati Korea, Democratic People's Republic of Korea, Republic of Kuwait Kyrgyzstan Lao People's Democratic Republic Latvia Lebanon Lesotho Liberia Libyan Arab Jamahiriya Liechtenstein Lithuania Luxembourg Macao Macedonia, The Former Yugoslav Republic of Madagascar Malawi Malaysia Maldives Mali Malta Marshall Islands Martinique Mauritania Mauritius Mayotte Mexico Micronesia, Federated States of Moldova, Republic of Monaco Mongolia Montenegro Montserrat Morocco Mozambique Myanmar Namibia Nauru Nepal Netherlands Netherlands Antilles New Caledonia New Zealand Nicaragua Niger Nigeria Niue Norfolk Island Northern Mariana Islands Norway Oman Pakistan Palau Palestinian Territory, Occupied Panama Papua New Guinea Paraguay Peru Philippines Pitcairn Poland Portugal Puerto Rico Qatar Reunion Romania Russian Federation Rwanda Saint Helena Saint Kitts and Nevis Saint Lucia Saint Pierre and Miquelon Saint Vincent and The Grenadines Samoa San Marino Sao Tome and Principe Saudi Arabia Senegal Serbia Seychelles Sierra Leone Singapore Slovakia Slovenia Solomon Islands Somalia South Africa South Georgia and The South Sandwich Islands Spain Sri Lanka Sudan Suriname Svalbard and Jan Mayen Swaziland Sweden Switzerland Syrian Arab Republic Taiwan, Province of China Tajikistan Tanzania, United Republic of Thailand Timor-leste Togo Tokelau Tonga Trinidad and Tobago Tunisia Turkey Turkmenistan Turks and Caicos Islands Tuvalu Uganda Ukraine United Arab Emirates United Kingdom United States United States Minor Outlying Islands Uruguay Uzbekistan Vanuatu Venezuela Viet Nam Virgin Islands, British Virgin Islands, U.S. Wallis and Futuna Western Sahara Yemen Zambia Zimbabwe Postcode
Date of Birth Year 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009 2008 2007 2006 2005 2004 2003 2002 2001 2000 1999 1998 1997 1996 1995 1994 1993 1992 1991 1990 1989 1988 1987 1986 1985 1984 1983 1982 1981 1980 1979 1978 1977 1976 1975 1974 1973 1972 1971 1970 1969 1968 1967 1966 1965 1964 1963 1962 1961 1960 1959 1958 1957 1956 1955 1954 1953 1952 1951 1950 1949 1948 1947 1946 1945 1944 1943 1942 1941 1940 1939 1938 1937 1936 1935 1934 1933 1932 1931 1930 1929 1928 1927 1926 1925 1924 1923 1922 1921 1920 1919 1918 Month 01 02 03 04 05 06 07 08 09 10 11 12 Day 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
A | Saffire Freycinet, 2352 Coles Bay Rd, Coles Bay Tasmania Australia 7215
NATIONAL | 1800 SAFFIRE (1800 723 347) • INTERNATIONAL | +61 3 6256 7888
E | stay@saffire-freycinet.com.au


What's Hondarribia Historic Center like?
If you're looking to discover somewhere new, look no further than Hondarribia Historic Center. Whether you're planning to stay for a night or for the week, the area around Hondarribia Historic Center has accommodation to fit every need. Search for hotels in Hondarribia Historic Center with Hotels.com by checking our online map. Our map displays the areas and neighbourhoods around all Hondarribia Historic Center hotels so you can see how close you are from landmarks and attractions, and then refine your search within the larger area. The best Hondarribia Historic Center hotel deals are here with our lowest price guarantee.
Where are the best places to stay in Hondarribia Historic Center?
When is the best time to visit Hondarribia Historic Center?


text:
Q: xml tree with php + header when i open this page with FireFox or Opera all looks fine (show xml tree), but when open with IE8 or Chrome I get this xml text in one line. Why? How fix?
<?php
    $xml = '
    <catalog>
        <book id="bk101">
            <author>Gambardella, Matthew</author>
            <title>XML Developer Guide</title>
        </book>
        <book id="bk102">
            <author>Ralls, Kim</author>
            <title>Midnight Rain</title>
        </book>
    </catalog>';

    header("Content-type: text/xml;charset:UTF-8");
    echo '<?xml version="1.0" encoding="UTF-8"?>';
    echo $xml;
    die;
    ?>

A: Internet Explorer and Chrome doesn't have fancy XML rendering built in, whereas Firefox and Opera does.
You can see if they actually do get the documents correctly by pressing viewing the source of the pages. (Ctrl-U in Chrome, Alt-VC in Internet Explorer)
If you want to rely on an XML document rendering a specific way, you'll have to use an XSL/XSLT to format it correctly.
For Chrome there are also a number of extensions you could try, which aim to add what you seek:

*

*XML Viewer

*XML Tree

redpajama_set_name: RedPajamaStackExchange


Hello! I am a long-term supporter of Biarritz Olympique and am writing to enquire about Dave O'Callaghan. My friends in the Basque Country are asking why he has only played 9 games in the last two years. We have signed him for next season. I think he will be a great signing if he has the heart of a Munsterman.
Biarritz have had a disastrous time over the last 7 years, but the new owners have a good plan and the finances are being sorted. The aim is develop 50% of the 1st team through the Academy within 5 years.
My favourite rugby weekend of all time was the 2005 quarter-final in San Sebastion. A life-long memory.
Any information will be tremendous and I will send it back into the Gambas and Independents Forums.
i also remember the 2010 final in Paris, spent with Munster fans from Cork who got us tickets. I hope that the two clubs will meet again in the future. The bond between us is special.
Wait, what now? DOC has signed for Biarritz? I know we all expected him to be leaving, but has that even been a rumour over here?
How highly is he rated by the fans?
Good player, always rated him. Good to see the squad being trimmed too though.
He's a tough competitor who plays more frequently in the back row but can also play at lock. He would have had a lot more appearances for us, but has had a lot of misfortune with various injuries, as detailed above. I'm sorry to see him leaving but we have so many players competing for places that I guess it was decided that there's no more room for him - that said, I haven't seen any official news to say he's definitely leaving us ... assuming it's true, he should be an asset to you.
A player in bad need of a change of scene so would be happy for him going to Biarritz.
A real disapointment how he didn't push on really. I remember him playing a blinder against Saint's in that 40 phase ROG drop goal game. He was doing as well as POM. Then he just totally stalled, largely due to injuries but he was fit when opportunities were there to make inroads, especially at second row.
A great move for him if true, has had awful luck with injuries, has never really had a chance to build momentum with a run of games, if he could get that he would be a tremendous player.
Do you remember the Saracens home match in 2012 when he and Dave Kilcoyne showed they belong at least at Heineken Cup level? I had to Google it for the dates and details, but we won 15-9. I had great hopes back then, even more for O'Callaghan than Kilcoyne.
A super player if he can stay fit.Excellent line out operator and always gave 100%.I am sad to see him go but I hope it works out for him in Biarro.
If I had to chose where in France to go play rugby, Biarritz would be choice no.1.
That was the first game came to my mind when I read this thread.
A serious battle of the forwards & both Pom & Doc were excellent. Farrell missed a few kicks that night as well the silence seemed to effect him.
was i at that match. cant remember?