text:
package org.apache.nutch.crawl;

import org.apache.commons.jexl2.Expression;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.mapred.lib.MultipleSequenceFileOutputFormat;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.nutch.metadata.Nutch;
import org.apache.nutch.net.URLFilterException;
import org.apache.nutch.net.URLFilters;
import org.apache.nutch.net.URLNormalizers;
import org.apache.nutch.scoring.ScoringFilterException;
import org.apache.nutch.scoring.ScoringFilters;
import org.apache.nutch.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

// rLogging imports

/**
 * Generates a subset of a crawl db to fetch. This version allows to generate
 * fetchlists for several segments in one go. Unlike in the initial version
 * (OldGenerator), the IP resolution is done ONLY on the entries which have been
 * selected for fetching. The URLs are partitioned by IP, domain or host within
 * a segment. We can chose separately how to count the URLS i.e. by domain or
 * host to limit the entries.
 **/
public class Generator extends NutchTool implements Tool {

    protected static final Logger LOG = LoggerFactory
            .getLogger(MethodHandles.lookup().lookupClass());

    public static final String GENERATE_UPDATE_CRAWLDB = "generate.update.crawldb";
    public static final String GENERATOR_MIN_SCORE = "generate.min.score";
    public static final String GENERATOR_MIN_INTERVAL = "generate.min.interval";
    public static final String GENERATOR_RESTRICT_STATUS = "generate.restrict.status";
    public static final String GENERATOR_FILTER = "generate.filter";
    public static final String GENERATOR_NORMALISE = "generate.normalise";
    public static final String GENERATOR_MAX_COUNT = "generate.max.count";
    public static final String GENERATOR_COUNT_MODE = "generate.count.mode";
    public static final String GENERATOR_COUNT_VALUE_DOMAIN = "domain";
    public static final String GENERATOR_COUNT_VALUE_HOST = "host";
    public static final String GENERATOR_TOP_N = "generate.topN";
    public static final String GENERATOR_CUR_TIME = "generate.curTime";
    public static final String GENERATOR_DELAY = "crawl.gen.delay";
    public static final String GENERATOR_MAX_NUM_SEGMENTS = "generate.max.num.segments";
    public static final String GENERATOR_EXPR = "generate.expr";

    /**
     * SelectorEntry 存放了 url,CrawlDatum,以及 segnum。
     * 这里为什么会使用这个结构体呢?
     * Selector map 任务完成后,可以将 score 直接放入 CrawlDatum 对象的
     * metadata 中,将 <url, datum> 作为输出,但是这样做就无法使用 score
     * 进行排序了。如果使用 <score, datum> 作为输出,那么 url 就被丢掉了,
     * 这显然是不合适的。所以这里需要一个结构体同时包含 url 和 datum,这样
     * score 和 url 就可以被同时访问。
     */
    public static class SelectorEntry implements Writable {
        public Text url;
        public CrawlDatum datum;
        public IntWritable segnum;

        public SelectorEntry() {
            url = new Text();
            datum = new CrawlDatum();
            segnum = new IntWritable(0);
        }

        public void readFields(DataInput in) throws IOException {
            url.readFields(in);
            datum.readFields(in);
            segnum.readFields(in);
        }

        public void write(DataOutput out) throws IOException {
            url.write(out);
            datum.write(out);
            segnum.write(out);
        }

        public String toString() {
            return "url=" + url.toString() + ", datum=" + datum.toString()
                    + ", segnum=" + segnum.toString();
        }
    }

    /** Selects entries due for fetch. */
    public static class Selector implements
            Mapper<Text, CrawlDatum, FloatWritable, SelectorEntry>,
            Partitioner<FloatWritable, Writable>,
            Reducer<FloatWritable, SelectorEntry, FloatWritable, SelectorEntry> {
        private LongWritable genTime = new LongWritable(System.currentTimeMillis());
        private long curTime;
        private long limit;
        private long count;
        private HashMap<String, int[]> hostCounts = new HashMap<>();
        private int segCounts[];
        private int maxCount;
        private boolean byDomain = false;
        private Partitioner<Text, Writable> partitioner = new URLPartitioner();
        private URLFilters filters;
        private URLNormalizers normalizers;
        private ScoringFilters scfilters;
        private SelectorEntry entry = new SelectorEntry();
        private FloatWritable sortValue = new FloatWritable();
        private boolean filter;
        private boolean normalise;
        private long genDelay;
        private FetchSchedule schedule;
        private float scoreThreshold = 0f;
        private int intervalThreshold = -1;
        private String restrictStatus = null;
        private int maxNumSegments = 1;
        private Expression expr = null;
        private int currentsegmentnum = 1;

        public void configure(JobConf job) {
            curTime = job.getLong(GENERATOR_CUR_TIME, System.currentTimeMillis());
            limit = job.getLong(GENERATOR_TOP_N, Long.MAX_VALUE)
                    / job.getNumReduceTasks();
            maxCount = job.getInt(GENERATOR_MAX_COUNT, -1);
            if (maxCount == -1) {
                byDomain = false;
            }
            if (GENERATOR_COUNT_VALUE_DOMAIN.equals(job.get(GENERATOR_COUNT_MODE)))
                byDomain = true;
            filters = new URLFilters(job);
            normalise = job.getBoolean(GENERATOR_NORMALISE, true);
            if (normalise)
                normalizers = new URLNormalizers(job,
                        URLNormalizers.SCOPE_GENERATE_HOST_COUNT);
            scfilters = new ScoringFilters(job);
            partitioner.configure(job);
            filter = job.getBoolean(GENERATOR_FILTER, true);
            genDelay = job.getLong(GENERATOR_DELAY, 7L) * 3600L * 24L * 1000L;
            long time = job.getLong(Nutch.GENERATE_TIME_KEY, 0L);
            if (time > 0)
                genTime.set(time);
            schedule = FetchScheduleFactory.getFetchSchedule(job);
            scoreThreshold = job.getFloat(GENERATOR_MIN_SCORE, Float.NaN);
            intervalThreshold = job.getInt(GENERATOR_MIN_INTERVAL, -1);
            restrictStatus = job.get(GENERATOR_RESTRICT_STATUS, null);
            expr = JexlUtil.parseExpression(job.get(GENERATOR_EXPR, null));
            maxNumSegments = job.getInt(GENERATOR_MAX_NUM_SEGMENTS, 1);
            segCounts = new int[maxNumSegments];
        }

        public void close() {
        }

        /** Select & invert subset due for fetch. */
        /**
         * map 主要用途是找出符合条件的链接,比如根据时间调度,找出符合 lasterFetchTime + interval <= curTime 条件
         * @param key
         * @param value
         * @param output
         * @param reporter
         * @throws IOException
         */
        public void map(Text key, CrawlDatum value,
                        OutputCollector<FloatWritable, SelectorEntry> output, Reporter reporter)
                throws IOException {
            Text url = key;
            if (filter) {
                // If filtering is on don't generate URLs that don't pass
                // URLFilters
                try {
                    if (filters.filter(url.toString()) == null)
                        return;
                } catch (URLFilterException e) {
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("Couldn't filter url: " + url + " (" + e.getMessage() + ")");
                    }
                }
            }
            CrawlDatum crawlDatum = value;

            // check fetch schedule
            // 根据上次的抓取时间,判断本次是否应该抓取
            if (!schedule.shouldFetch(url, crawlDatum, curTime)) {
                LOG.debug("-shouldFetch rejected '" + url + "', fetchTime="
                        + crawlDatum.getFetchTime() + ", curTime=" + curTime);
                return;
            }

            // 在有大量抓取任务的情况下,上一次 generate 阶段生成的 url 可能还没抓取完。对于这部分还未抓取的 url,
            // 本轮 generate 显然不能将上一轮的记录覆盖掉,所以这里要判断一下生成时间是否过期,未过期的话本轮不生成。
            LongWritable oldGenTime = (LongWritable) crawlDatum.getMetaData().get(
                    Nutch.WRITABLE_GENERATE_TIME_KEY);
            if (oldGenTime != null) { // awaiting fetch & update
                if (oldGenTime.get() + genDelay > curTime) // still wait for
                    // update
                    return;
            }
            float sort = 1.0f;
            try {
                sort = scfilters.generatorSortValue(key, crawlDatum, sort);
            } catch (ScoringFilterException sfe) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("Couldn't filter generatorSortValue for " + key + ": " + sfe);
                }
            }

            // check expr
            if (expr != null) {
                if (!crawlDatum.evaluate(expr)) {
                    return;
                }
            }

            if (restrictStatus != null
                    && !restrictStatus.equalsIgnoreCase(CrawlDatum
                    .getStatusName(crawlDatum.getStatus())))
                return;

            // consider only entries with a score superior to the threshold
            // 链接得分低于阈值,不抓取。
            if (scoreThreshold != Float.NaN && sort < scoreThreshold)
                return;

            // consider only entries with a retry (or fetch) interval lower than
            // threshold
            // 如果抓取间隔阈值不等于-1,且链接抓取间隔小于等于间隔阈值(在阈值范围内),则抓取,否则,不抓取。
            if (intervalThreshold != -1
                    && crawlDatum.getFetchInterval() > intervalThreshold)
                return;

            // sort by decreasing score, using DecreasingFloatComparator
            sortValue.set(sort);
            // record generation time
            crawlDatum.getMetaData().put(Nutch.WRITABLE_GENERATE_TIME_KEY, genTime);
            entry.datum = crawlDatum;
            entry.url = key;
            // 在由内存写入磁盘过程中,hadoop 对 key 进行排序,这里按照 score 降序的形式排序
            output.collect(sortValue, entry); // invert for sort by score
        }

        /** Partition by host / domain or IP.
         *
         * 根据 partition.url.mode 配置决定使用那种方式对 url 进行分区。假设以 host 方式分区,
         * 那么同一 host 下的所有 url 都会被分到一个 partition 中。在抓取阶段,Fetcher 就可以
         * 采取控制手段,调整对某个 host 的并发数,减小目标站点的压力。
         */
        public int getPartition(FloatWritable key, Writable value,
                                int numReduceTasks) {
            return partitioner.getPartition(((SelectorEntry) value).url, key,
                    numReduceTasks);
        }

        /** Collect until limit is reached. */
        /**
         * reduce函数的核心功能就是计算该url属于哪一个segment中,分为两种情况:
         *
1. maxCount > 0,某一segment中的某host或domain下的url数量不得超过maxCount,超过则放在下一个segment
         *
2. maxCount <= 0, 次情况下,url按segment顺序存放,放满一个segment,剩下的url放入下一个segment中
         * Generator 之所以要分片的原因是:将同一 host/domain 下的 url,尽量分布到不同的 segment 中。防止 fetcher 顺序
         * 访问 fetchlist 时,部分 host/domain 下的 url 长时间得不到访问。
         * @param key
         * @param values
         * @param output
         * @param reporter
         * @throws IOException
         */
        public void reduce(FloatWritable key, Iterator<SelectorEntry> values,
                           OutputCollector<FloatWritable, SelectorEntry> output, Reporter reporter)
                throws IOException {

            while (values.hasNext()) {

                if (count == limit) {
                    // do we have any segments left?
                    if (currentsegmentnum < maxNumSegments) {
                        count = 0;
                        currentsegmentnum++;
                    } else
                        break;
                }

                SelectorEntry entry = values.next();
                Text url = entry.url;
                String urlString = url.toString();
                URL u = null;

                String hostordomain = null;

                try {
                    if (normalise && normalizers != null) {
                        urlString = normalizers.normalize(urlString,
                                URLNormalizers.SCOPE_GENERATE_HOST_COUNT);
                    }
                    u = new URL(urlString);
                    if (byDomain) {
                        hostordomain = URLUtil.getDomainName(u);
                    } else {
                        hostordomain = new URL(urlString).getHost();
                    }
                } catch (Exception e) {
                    LOG.warn("Malformed URL: '" + urlString + "', skipping ("
                            + StringUtils.stringifyException(e) + ")");
                    reporter.getCounter("Generator", "MALFORMED_URL").increment(1);
                    continue;
                }

                hostordomain = hostordomain.toLowerCase();

                // only filter if we are counting hosts or domains
                // 如果maxCount设定正值,则生成某个hostordomain下的url数量要小于maxCount
                if (maxCount > 0) {
                    int[] hostCount = hostCounts.get(hostordomain);
                    if (hostCount == null) {
                        // hostCount[0] 表示某 host 下的url应该存储的段的编号
                        // hostCount[1] 表示,在某一段下,当前 host 下的 url 数量
                        hostCount = new int[]{1, 0};
                        hostCounts.put(hostordomain, hostCount);
                    }

                    // increment hostCount
                    hostCount[1]++;

                    // check if topN reached, select next segment if it is
                    // 检查某个段是否还有空间,无空间的话,挪到下一个段中存储。
                    while (segCounts[hostCount[0] - 1] >= limit
                            && hostCount[0] < maxNumSegments) {
                        hostCount[0]++;
                        hostCount[1] = 0;
                    }

                    // reached the limit of allowed URLs per host / domain
                    // see if we can put it in the next segment?
                    // 如果某个 host/domain 下的 url 数量超过 maxCount,则将收下的 url 放入下一段中
                    // 如果没有剩余的段可放某个 host 下的剩余url,剩余的 url 将会被抛弃。这些段中可能还有剩余的空间用于放置 url,
                    // 只不过,某个 host 下的 url 数量,在所有段中都达到了 maxCount,再有新的 url 过来,就会被遗弃
                    if (hostCount[1] >= maxCount) {
                        if (hostCount[0] < maxNumSegments) {
                            hostCount[0]++;
                            hostCount[1] = 0;
                        } else {    // 如果没有剩余的段可放某个 host 下的剩余url,剩余的 url 将会被抛弃
                            if (hostCount[1] == maxCount + 1 && LOG.isInfoEnabled()) {
                                LOG.info("Host or domain "
                                        + hostordomain
                                        + " has more than "
                                        + maxCount
                                        + " URLs for all "
                                        + maxNumSegments
                                        + " segments. Additional URLs won't be included in the fetchlist.");
                            }
                            // skip this entry
                            continue;
                        }
                    }
                    entry.segnum = new IntWritable(hostCount[0]);
                    segCounts[hostCount[0] - 1]++;
                } else {
                    entry.segnum = new IntWritable(currentsegmentnum);
                    segCounts[currentsegmentnum - 1]++;
                }

                output.collect(key, entry);

                // Count is incremented only when we keep the URL
                // maxCount may cause us to skip it.
                count++;
            }
        }
    }

    // Allows the reducers to generate one subfile per
    public static class GeneratorOutputFormat extends
            MultipleSequenceFileOutputFormat<FloatWritable, SelectorEntry> {
        // generate a filename based on the segnum stored for this entry
        protected String generateFileNameForKeyValue(FloatWritable key,
                                                     SelectorEntry value, String name) {
            return "fetchlist-" + value.segnum.toString() + "/" + name;
        }

    }

    public static class DecreasingFloatComparator extends
            FloatWritable.Comparator {

        /** Compares two FloatWritables decreasing. */
        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            return super.compare(b2, s2, l2, b1, s1, l1);
        }
    }

    public static class SelectorInverseMapper extends MapReduceBase implements
            Mapper<FloatWritable, SelectorEntry, Text, SelectorEntry> {

        public void map(FloatWritable key, SelectorEntry value,
                        OutputCollector<Text, SelectorEntry> output, Reporter reporter)
                throws IOException {
            SelectorEntry entry = value;
            output.collect(entry.url, entry);
        }
    }

    public static class PartitionReducer extends MapReduceBase implements
            Reducer<Text, SelectorEntry, Text, CrawlDatum> {

        public void reduce(Text key, Iterator<SelectorEntry> values,
                           OutputCollector<Text, CrawlDatum> output, Reporter reporter)
                throws IOException {
            // if using HashComparator, we get only one input key in case of
            // hash collision
            // so use only URLs from values
            // 使用 HashComparator 对URL进行hash运算时,不同的URL可能会存在冲突的可能,所以不应该使用Text key作为URL。
            // hash(url1) == hash(url2), but url1 != url2.
            while (values.hasNext()) {
                SelectorEntry entry = values.next();
                output.collect(entry.url, entry.datum);
            }
        }

    }

    /** Sort fetch lists by hash of URL. */
    public static class HashComparator extends WritableComparator {
        public HashComparator() {
            super(Text.class);
        }

        @SuppressWarnings("rawtypes")
        public int compare(WritableComparable a, WritableComparable b) {
            Text url1 = (Text) a;
            Text url2 = (Text) b;
            int hash1 = hash(url1.getBytes(), 0, url1.getLength());
            int hash2 = hash(url2.getBytes(), 0, url2.getLength());
            return (hash1 < hash2 ? -1 : (hash1 == hash2 ? 0 : 1));
        }

        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            int hash1 = hash(b1, s1, l1);
            int hash2 = hash(b2, s2, l2);
            return (hash1 < hash2 ? -1 : (hash1 == hash2 ? 0 : 1));
        }

        private static int hash(byte[] bytes, int start, int length) {
            int hash = 1;
            // make later bytes more significant in hash code, so that sorting
            // by
            // hashcode correlates less with by-host ordering.
            for (int i = length - 1; i >= 0; i--)
                hash = (31 * hash) + (int) bytes[start + i];
            return hash;
        }
    }

    /**
     * Update the CrawlDB so that the next generate won't include the same URLs.
     */
    public static class CrawlDbUpdater extends MapReduceBase implements
            Mapper<Text, CrawlDatum, Text, CrawlDatum>,
            Reducer<Text, CrawlDatum, Text, CrawlDatum> {
        long generateTime;

        public void configure(JobConf job) {
            // GENERATE_TIME_KEY 在 Generator Job 运行时被设定为 Generator Job 启动时间
            generateTime = job.getLong(Nutch.GENERATE_TIME_KEY, 0L);
        }

        public void map(Text key, CrawlDatum value,
                        OutputCollector<Text, CrawlDatum> output, Reporter reporter)
                throws IOException {
            output.collect(key, value);
        }

        private CrawlDatum orig = new CrawlDatum();
        private LongWritable genTime = new LongWritable(0L);

        public void reduce(Text key, Iterator<CrawlDatum> values,
                           OutputCollector<Text, CrawlDatum> output, Reporter reporter)
                throws IOException {
            genTime.set(0L);
            while (values.hasNext()) {
                CrawlDatum val = values.next();
                if (val.getMetaData().containsKey(Nutch.WRITABLE_GENERATE_TIME_KEY)) {
                    LongWritable gt = (LongWritable) val.getMetaData().get(
                            Nutch.WRITABLE_GENERATE_TIME_KEY);
                    genTime.set(gt.get());
                    if (genTime.get() != generateTime) {
                        orig.set(val);
                        genTime.set(0L);
                        continue;
                    }
                } else {
                    orig.set(val);
                }
            }
            if (genTime.get() != 0L) {
                orig.getMetaData().put(Nutch.WRITABLE_GENERATE_TIME_KEY, genTime);
            }
            output.collect(key, orig);
        }
    }

    public Generator() {
    }

    public Generator(Configuration conf) {
        setConf(conf);
    }

    public Path[] generate(Path dbDir, Path segments, int numLists, long topN,
                           long curTime) throws IOException {

        JobConf job = new NutchJob(getConf());
        boolean filter = job.getBoolean(GENERATOR_FILTER, true);
        boolean normalise = job.getBoolean(GENERATOR_NORMALISE, true);
        return generate(dbDir, segments, numLists, topN, curTime, filter,
                normalise, false, 1, null);
    }

    /**
     * old signature used for compatibility - does not specify whether or not to
     * normalise and set the number of segments to 1
     **/
    public Path[] generate(Path dbDir, Path segments, int numLists, long topN,
                           long curTime, boolean filter, boolean force) throws IOException {
        return generate(dbDir, segments, numLists, topN, curTime, filter, true,
                force, 1, null);
    }

    /**
     * Generate fetchlists in one or more segments. Whether to filter URLs or not
     * is read from the crawl.generate.filter property in the configuration files.
     * If the property is not found, the URLs are filtered. Same for the
     * normalisation.
     *
     * @param dbDir
     *          Crawl database directory
     * @param segments
     *          Segments directory
     * @param numLists
     *          Number of reduce tasks
     * @param topN
     *          Number of top URLs to be selected
     * @param curTime
     *          Current time in milliseconds
     *
     * @return Path to generated segment or null if no entries were selected
     *
     * @throws IOException
     *           When an I/O error occurs
     */
    public Path[] generate(Path dbDir, Path segments, int numLists, long topN,
                           long curTime, boolean filter, boolean norm, boolean force,
                           int maxNumSegments, String expr) throws IOException {

        Path tempDir = new Path(getConf().get("mapred.temp.dir", ".")
                + "/generate-temp-" + java.util.UUID.randomUUID().toString());

        Path lock = new Path(dbDir, CrawlDb.LOCK_NAME);
        FileSystem fs = FileSystem.get(getConf());
        LockUtil.createLockFile(fs, lock, force);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long start = System.currentTimeMillis();
        LOG.info("Generator: starting at " + sdf.format(start));
        LOG.info("Generator: Selecting best-scoring urls due for fetch.");
        LOG.info("Generator: filtering: " + filter);
        LOG.info("Generator: normalizing: " + norm);
        if (topN != Long.MAX_VALUE) {
            LOG.info("Generator: topN: " + topN);
        }
        if (expr != null) {
            LOG.info("Generator: expr: " + expr);
        }

        // map to inverted subset due for fetch, sort by score
        JobConf job = new NutchJob(getConf());
        job.setJobName("generate: select from " + dbDir);

        if (numLists == -1) { // for politeness make
            numLists = job.getNumMapTasks(); // a partition per fetch task
        }
        if ("local".equals(job.get("mapreduce.framework.name")) && numLists != 1) {
            // override
            LOG.info("Generator: running in local mode, generating exactly one partition.");
            numLists = 1;
        }
        job.setLong(GENERATOR_CUR_TIME, curTime);
        // record real generation time
        long generateTime = System.currentTimeMillis();
        job.setLong(Nutch.GENERATE_TIME_KEY, generateTime);
        job.setLong(GENERATOR_TOP_N, topN);
        job.setBoolean(GENERATOR_FILTER, filter);
        job.setBoolean(GENERATOR_NORMALISE, norm);
        job.setInt(GENERATOR_MAX_NUM_SEGMENTS, maxNumSegments);
        if (expr != null) {
            job.set(GENERATOR_EXPR, expr);
        }
        FileInputFormat.addInputPath(job, new Path(dbDir, CrawlDb.CURRENT_NAME));
        job.setInputFormat(SequenceFileInputFormat.class);

        job.setMapperClass(Selector.class);
        job.setPartitionerClass(Selector.class);
        job.setReducerClass(Selector.class);

        FileOutputFormat.setOutputPath(job, tempDir);
        job.setOutputFormat(SequenceFileOutputFormat.class);
        job.setOutputKeyClass(FloatWritable.class);
        job.setOutputKeyComparatorClass(DecreasingFloatComparator.class);
        job.setOutputValueClass(SelectorEntry.class);
        job.setOutputFormat(GeneratorOutputFormat.class);

        try {
            JobClient.runJob(job);
        } catch (IOException e) {
            LockUtil.removeLockFile(fs, lock);
            fs.delete(tempDir, true);
            throw e;
        }

        // read the subdirectories generated in the temp
        // output and turn them into segments
        List<Path> generatedSegments = new ArrayList<>();

        FileStatus[] status = fs.listStatus(tempDir);
        try {
            for (FileStatus stat : status) {
                Path subfetchlist = stat.getPath();
                if (!subfetchlist.getName().startsWith("fetchlist-"))
                    continue;
                // start a new partition job for this segment
                Path newSeg = partitionSegment(fs, segments, subfetchlist, numLists);
                generatedSegments.add(newSeg);
            }
        } catch (Exception e) {
            LOG.warn("Generator: exception while partitioning segments, exiting ...");
            fs.delete(tempDir, true);
            return null;
        }

        if (generatedSegments.size() == 0) {
            LOG.warn("Generator: 0 records selected for fetching, exiting ...");
            LockUtil.removeLockFile(fs, lock);
            fs.delete(tempDir, true);
            return null;
        }

        if (getConf().getBoolean(GENERATE_UPDATE_CRAWLDB, false)) {
            // update the db from tempDir
            Path tempDir2 = new Path(getConf().get("mapred.temp.dir", ".")
                    + "/generate-temp-" + java.util.UUID.randomUUID().toString());

            job = new NutchJob(getConf());
            job.setJobName("generate: updatedb " + dbDir);
            job.setLong(Nutch.GENERATE_TIME_KEY, generateTime);
            for (Path segmpaths : generatedSegments) {
                Path subGenDir = new Path(segmpaths, CrawlDatum.GENERATE_DIR_NAME);
                FileInputFormat.addInputPath(job, subGenDir);
            }
            FileInputFormat.addInputPath(job, new Path(dbDir, CrawlDb.CURRENT_NAME));
            job.setInputFormat(SequenceFileInputFormat.class);
            job.setMapperClass(CrawlDbUpdater.class);
            job.setReducerClass(CrawlDbUpdater.class);
            job.setOutputFormat(MapFileOutputFormat.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(CrawlDatum.class);
            FileOutputFormat.setOutputPath(job, tempDir2);
            try {
                JobClient.runJob(job);
                CrawlDb.install(job, dbDir);
            } catch (IOException e) {
                LockUtil.removeLockFile(fs, lock);
                fs.delete(tempDir, true);
                fs.delete(tempDir2, true);
                throw e;
            }
            fs.delete(tempDir2, true);
        }

        LockUtil.removeLockFile(fs, lock);
        fs.delete(tempDir, true);

        long end = System.currentTimeMillis();
        LOG.info("Generator: finished at " + sdf.format(end) + ", elapsed: "
                + TimingUtil.elapsedTime(start, end));

        Path[] patharray = new Path[generatedSegments.size()];
        return generatedSegments.toArray(patharray);
    }

    private Path partitionSegment(FileSystem fs, Path segmentsDir, Path inputDir,
                                  int numLists) throws IOException {
        // invert again, partition by host/domain/IP, sort by url hash
        if (LOG.isInfoEnabled()) {
            LOG.info("Generator: Partitioning selected urls for politeness.");
        }
        Path segment = new Path(segmentsDir, generateSegmentName());
        Path output = new Path(segment, CrawlDatum.GENERATE_DIR_NAME);

        LOG.info("Generator: segment: " + segment);

        NutchJob job = new NutchJob(getConf());
        job.setJobName("generate: partition " + segment);

        job.setInt("partition.url.seed", new Random().nextInt());

        FileInputFormat.addInputPath(job, inputDir);
        job.setInputFormat(SequenceFileInputFormat.class);

        job.setMapperClass(SelectorInverseMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(SelectorEntry.class);
        job.setPartitionerClass(URLPartitioner.class);
        job.setReducerClass(PartitionReducer.class);
        job.setNumReduceTasks(numLists);

        FileOutputFormat.setOutputPath(job, output);
        job.setOutputFormat(SequenceFileOutputFormat.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(CrawlDatum.class);
        // 按照 url 的 hash 值进行排序,目的在于打散 url 在 segment 中的顺序,防止
        // 同一 host/domain/ip 下的 url 过于集中在一起,导致其他 host/domain/ip
        // 下的 url 长时间得不到下载
        job.setOutputKeyComparatorClass(HashComparator.class);
        JobClient.runJob(job);
        return segment;
    }

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

    public static synchronized String generateSegmentName() {
        try {
            Thread.sleep(1000);
        } catch (Throwable t) {
        }
        ;
        return sdf.format(new Date(System.currentTimeMillis()));
    }

    /**
     * Generate a fetchlist from the crawldb.
     */
    public static void main(String args[]) throws Exception {
        args = new String[]{"/Users/imtian/Temp/nutch/crawl/crawldb", "/Users/imtian/Temp/nutch/crawl/segment"};
        int res = ToolRunner.run(NutchConfiguration.create(), new Generator(), args);
        System.exit(res);
    }

    public int run(String[] args) throws Exception {
        if (args.length < 2) {
            System.out
                    .println("Usage: Generator <crawldb> <segments_dir> [-force] [-topN N] [-numFetchers numFetchers] [-expr <expr>] [-adddays <numDays>] [-noFilter] [-noNorm] [-maxNumSegments <num>]");
            return -1;
        }

        Path dbDir = new Path(args[0]);
        Path segmentsDir = new Path(args[1]);
        long curTime = System.currentTimeMillis();
        long topN = Long.MAX_VALUE;
        int numFetchers = -1;
        boolean filter = true;
        boolean norm = true;
        boolean force = false;
        String expr = null;
        int maxNumSegments = 1;

        for (int i = 2; i < args.length; i++) {
            if ("-topN".equals(args[i])) {
                topN = Long.parseLong(args[i + 1]);
                i++;
            } else if ("-numFetchers".equals(args[i])) {
                numFetchers = Integer.parseInt(args[i + 1]);
                i++;
            } else if ("-adddays".equals(args[i])) {
                long numDays = Integer.parseInt(args[i + 1]);
                curTime += numDays * 1000L * 60 * 60 * 24;
            } else if ("-noFilter".equals(args[i])) {
                filter = false;
            } else if ("-noNorm".equals(args[i])) {
                norm = false;
            } else if ("-force".equals(args[i])) {
                force = true;
            } else if ("-maxNumSegments".equals(args[i])) {
                maxNumSegments = Integer.parseInt(args[i + 1]);
            } else if ("-expr".equals(args[i])) {
                expr = args[i + 1];
            }

        }

        try {
            Path[] segs = generate(dbDir, segmentsDir, numFetchers, topN, curTime,
                    filter, norm, force, maxNumSegments, expr);
            if (segs == null)
                return 1;
        } catch (Exception e) {
            LOG.error("Generator: " + StringUtils.stringifyException(e));
            return -1;
        }
        return 0;
    }

    @Override
    public Map<String, Object> run(Map<String, Object> args, String crawlId) throws Exception {

        Map<String, Object> results = new HashMap<>();

        long curTime = System.currentTimeMillis();
        long topN = Long.MAX_VALUE;
        int numFetchers = -1;
        boolean filter = true;
        boolean norm = true;
        boolean force = false;
        int maxNumSegments = 1;
        String expr = null;

        Path crawlDb;
        if (args.containsKey(Nutch.ARG_CRAWLDB)) {
            Object crawldbPath = args.get(Nutch.ARG_CRAWLDB);
            if (crawldbPath instanceof Path) {
                crawlDb = (Path) crawldbPath;
            } else {
                crawlDb = new Path(crawldbPath.toString());
            }
        } else {
            crawlDb = new Path(crawlId + "/crawldb");
        }

        Path segmentsDir;
        if (args.containsKey(Nutch.ARG_SEGMENTDIR)) {
            Object segDir = args.get(Nutch.ARG_SEGMENTDIR);
            if (segDir instanceof Path) {
                segmentsDir = (Path) segDir;
            } else {
                segmentsDir = new Path(segDir.toString());
            }
        } else {
            segmentsDir = new Path(crawlId + "/segments");
        }

        if (args.containsKey("expr")) {
            expr = (String) args.get("expr");
        }
        if (args.containsKey("topN")) {
            topN = Long.parseLong((String) args.get("topN"));
        }
        if (args.containsKey("numFetchers")) {
            numFetchers = Integer.parseInt((String) args.get("numFetchers"));
        }
        if (args.containsKey("adddays")) {
            long numDays = Integer.parseInt((String) args.get("adddays"));
            curTime += numDays * 1000L * 60 * 60 * 24;
        }
        if (args.containsKey("noFilter")) {
            filter = false;
        }
        if (args.containsKey("noNorm")) {
            norm = false;
        }
        if (args.containsKey("force")) {
            force = true;
        }
        if (args.containsKey("maxNumSegments")) {
            maxNumSegments = Integer.parseInt((String) args.get("maxNumSegments"));
        }

        try {
            Path[] segs = generate(crawlDb, segmentsDir, numFetchers, topN, curTime,
                    filter, norm, force, maxNumSegments, expr);
            if (segs == null) {
                results.put(Nutch.VAL_RESULT, Integer.toString(1));
                return results;
            }

        } catch (Exception e) {
            LOG.error("Generator: " + StringUtils.stringifyException(e));
            results.put(Nutch.VAL_RESULT, Integer.toString(-1));
            return results;
        }
        results.put(Nutch.VAL_RESULT, Integer.toString(0));
        return results;
    }
}


text:
by Webmaster, 30th May 2020
With restrictions lifted in England, we name our top six country walks east of Offa's Dyke
Rejoice! We can travel throughout England and enjoy our fabulous countryside again.
To celebrate, we asked our walking editor Matt Pike to name his top six country walks in the English Border counties. As usual, you can click on the hyperlinks for a printable map and directions. Enjoy the freedom!
1. Water way to go
Magically, you can normally hear a waterfall before you see it. And as soon as you do, you're transfixed, as the distant sound gets louder and louder until you're facing this colossal roar head-on, with more water than you can even contemplate plunging into the pool and onto the rocks below. The characteristic steep, deep valleys of the Borders mean cascades are commonplace, not least on this trail in Carding Mill Valley in Shropshire. "It's a walk through what many regard as Shropshire's finest valley," says Matt. Click here
2. Fortifying
The Borders may be the epitome of tranquillity, but it hasn't always been such an idyllic place, as the multitude of castles built either side of the Welsh Border testify. In the Middle Ages, life here could be turbulent, but fortresses were built as a statement of power as well as a practical defence. And so magnificent were they, even those that suffered extensive damage over the centuries still draw the crowds today, including Goodrich Castle in Herefordshire, where this walk will take you. "It offers the chance to see the Wye Valley at its best," says Matt, "overlooked by the stunning medieval castle." Click here
3. Love Ludlow
This town trail was included in last year's run down of romantic rambles, and it certainly makes a full day of it, with stunning vistas to compete with the charming streets of Ludlow in Shropshire. "It makes for a relaxing stroll through an enchanting historic town, with another castle of course." says Matt. Click here
4. Time to rhyme
As many of you probably know, the Border landscapes have been a terrific source of inspiration for artists. But not just visual artists – there's a long history of poets featuring the Welsh Borders in their work, and retracing their footsteps can be truly inspirational. On this walk you can discover the countryside that was responsible for war poet Wilfred Owen to put pen to paper. "It also offers a rare opportunity to explore lowland heath, while enjoying fine views with minimal effort," says Matt. Click here
5. Manmade marvel
While the Welsh Borders are renowned more for their natural wonder than their buildings, the region is nevertheless home to some beautiful, interesting and important architecture. And one of those has to be Hereford Cathedral, where this wander takes you. "You can take in one of the region's finest buildings, and also enjoy a riverside stroll," says Matt. Click here
6. Bridge over troubled waters
After all the problems with Covid-19, it'd be easy to forget the havoc wreaked by the floods just four months ago. Ironbridge was one of the worst hit areas, but one of the driest Mays on record has seen the waters subside considerably, offering safe passage around this historic Unesco World Heritage Site in Shropshire. "An iconic bridge, scenic gorge and wildlife-rich woodland are all on offer on this walk," says Matt. Click here
Shed the festive pounds - Tramping your way to the top of a hill - be i...
Let there be lights! - Our historic Border towns and cities look spe...
A festive walk in Llangollen - The remains of a hilltop fort and medieval ab...
Ancient woodland wanders - Now is the ideal time to soak up the colours ...
Lakeside strolls - Whether you want to explore peaceful meres or...

redpajama_set_name: RedPajamaCommonCrawl


The Anhänger (1 achs.) für Maschinensatz F (Sd. Ah. 25/1) - one-axled trailer for machine set F - was mainly used by signals units. For example, it was carried along with Fernschreibkraftwagen - teleprinter motor vehicles. Te superstructure of the Sd. Ah. 25/1 was a bit lower than that of the similar Sd. Ah. 25/2. The Sd. Ah. 25/2 was based on the Anhängerfahrgestell A 1 of the Luftwaffet.
Outwardly, the Beleuchtungsanhänger N (1 achs.) (Sd. Ah. 25/3) - one axled illumination trailer - was identical constructed. It was used for transportation of a small or large Beleuchtungssatz N - illumination set N. The power unit of the large Beleuchtungssatz N was a Maschinensatz F, too.


text:
My Brooklyn Story
Red Hook Timelime
Bedford Stuyvesant: Herbert Von King Park
About Coney Island
About Red Hook
About Ocean Hill
About Brownsville
Crown Heights: Ebbets Field
My Bronx Story
Renaissance Youth Center
Bronx High School for the Visual Arts
My Queens Story
My Manhattan Story
My Staten Island Story
Ocean Hill was founded as a separate neighborhood in 1890. Before that, the area had been split between the Bedford-Stuyvesant and Brownsville neighborhoods. It is bordered by Bushwick, Bedford-Stuyvesant, Crown Heights, Brownsville, and Cypress Hill. Until the 1960s the area was mostly Italian. By 1970, Ocean Hill, along with Bedford-Stuyvesant, had become the largest African-American community in the country. In the 1990s the neighborhood underwent a revitalization effort. Buildings and lots that had been abandoned for 15 years were given a new life. The community began to thrive once more. There are almost thirty two thousand residents living in Ocean Hill as of 2010. The neighborhood is described by residents as quiet, safe, and community oriented.
Ocean Hill is served by Brooklyn Community Board 16 and the 73rd Precinct of the New York City Police Department.
William "Red" Holzman: Holzman was an NBA player and coach. He was born and raised in Ocean Hill until he attened the University of Baltimore before transferring to City College of New York. When he graduated in 1942 he joined the United States Navy. After leaving the Navy Holzman played in the NBL, winning a championship, the rookie of the year award and an all-star appearance during his time there. He was then hired by the Knicks as a scout in 1945 before working his way up to head coach in 1967. He coached the New York Knicks from 1967 to 1982. He won two championships with the Knicks and was inducted into the Basketball Hall of Fame in 1986. Ten years later he was names one of the Top 10 Coaches in NBA History by members of the print and broadcast media.
Steve Cuozzo is a writer and editor for the New York Post. Cuozzo was born in Ocean Hill in 1950. After starting school in the area of Ocean Hill, he and his family moved to Long Island where he would finish his education, graduating from Stony Brook University in 1971. Cuozzo began working for the New York Post in 1972 as a copy boy. He eventually worked his way up to the position of executive editor. He now works as a restaurant critic, real estate columnist, and op-ed contributor for the New York Post.
Shannon Briggs is a professional boxer who was born in Brownsville. He started training as a teenager at Jimmy O'Pharrow's Starrett City Boxing Club in Brooklyn. When he went pro in 1992, he immediately gained notoriety. Briggs won his first 25 fights as a professional boxer. Briggs fought George Foreman for the Lineal Championship in 1997. In a close fight Briggs defeated Foreman on a majority decision. He has continued his boxing career and is training to fight David Haye in October of 2016. He now lives in Ocean Hill serving as a role model to the children of the neighborhood.
Shameek
For any comments or questions:
© 2017 New York City Police Foundation
The Options Program


text:
Title: Wonder Woman #308
Story: Pressure
Characters: Huntress (Helena Wayne), Gary Minelli, Nedra Borrower
Creators: Joey Cavalieri (writer), Michael Hernandez (artist)
Available In: Digital
Summary: It has been a wild ride for the Huntress. From the moment she took on the cases of the Mechanic, Pat Pending, and the Undertaker, all of which led her to Arkham Asylum where she was shot with a potent cocktail of hallucinogens, Helena Wayne is more than happy to get back to her normal routine of being a lawyer by day and the Huntress by night. But are things really back to normal for the Huntress, or are things just getting started? As the downward spiral of Helena Wayne's life continues, she is met with a new set of friends and foes, one of which is the ambitious reporter, Nedra Borrower, who is out for the Huntress' blood!
Review: Last we left off before spending the last four months of 2017 celebrating Helena Wayne's 40th anniversary was the conclusion of the Arkham Asylum story arc. In the last chapter of this story, Helena Wayne was made to confront her worst fears after being shot with a very potent hallucinogenic. In this chapter, she seeks to get her life back on track, but it does not appear as though there is any going back to the way things were following those recent events.
After her hallucinogenic trip through the depths of her mind, Helena finds herself waking up inside a strange man's flat, the same one she escaped Arkham Asylum with the night before. She finds herself nude and unmasked, and somehow she is not concerned about that at all. She's not concerned about the fact that a man she had only met a few hours early had undressed her while she was unconscious and the troubling implications of that (aka sexual assault). She equally doesn't seem concerned about the fact that he has seen her face. Instead, Helena is written as being more concerned about the fact that her new acquaintance, Gary Minelli, is not intimidated by her at all, especially as evidenced by the fact that he is clearly hitting on her.
This is admittedly a very strange way to start a new chapter. Aside from the fact that Helena is not reacting appropriately to a situation most women would identify as a sure sign that sexual assault took place, it's also hard to know for certain where Joey Cavalieri is trying to go with the Gary Minelli character. On the one hand, he doesn't hide the fact that he is very much attracted to her despite her lifestyle, and is even turned on by it. This is a significant difference from Helena's romantic partner, Harry Sims, who very strongly rejects this aspect of Helena's life.
The fact that Cavalieri named this new character "Gary" to rhyme with "Harry" and like Harry also serves the law in some capacity (he is established as a detective here), you can start to see that he is clearly juxtaposing this new man in Helena's life with her romantic partner. The question is, is Cavalieri trying to give Helena a new love interest who actually respects her as a person? Well...that's the part that's not really clear because the same chapter also establishes that he is something of a black sheep in Gotham's police department, and not in a good way. The fact that Gary undressed a woman he is attracted to while she was unconscious also doesn't paint him in a positive light, and instead places him in the category of being a creep.
Another character that is introduced in this chapter as an antagonist for Helena Wayne is a female reporter named Nedra Borrower who was only previously mentioned in the earlier chapters. Now, Nedra as an antagonist is quite interesting because in contrast with all the previous antagonists Helena has faced since her comic debut, Nedra is the first who isn't a criminal in any capacity. Nedra is in fact a law abiding citizen who is also an ambitious career woman. So where does the "antagonist" part come in? Well, that's the part that's interesting: She hates the Huntress for pursuing her goals in life. Huh? What? Yeah. Let's talk about that.
When we first meet Nedra in this chapter, she looks like a Lois Lane-type reporter who has latched on to a big story that she feels will get her noticed, in this case, the debacle surrounding the Mechanic, the Undertaker, Pat Pending, and Arkham Asylum. So far, nothing's out of the ordinary...yet. When Nedra learns from the suspects the police took into custody that the Huntress was not responsible for any of the deaths she was originally blamed for, Nedra was immediately disappointed and no longer felt like she had a story. It turns out the criminal ring that was responsible for the events of Arkham Asylum and the deaths of the aforementioned individuals was not the story she wanted to cover. Instead, she wanted to cover the Huntress' involvement in those deaths. So why is Nedra obsessed with painting the Huntress as a murderer?
From the backstory we are provided with from Joey Cavalieri, Nedra started out with ambitious goals as a child, but was frequently discouraged from pursuing them due to her gender. She had a strong desire to compete in male dominated spaces, but was frequently told her pursuits were not "lady-like" and even made people assume she was a lesbian. Feeling trapped, limited, and isolated in a bubble intoxicated with internalised misogyny and homophobia, the Huntress represents to Nedra the person she wants to be: free. But that is also the problem Nedra has with the Huntress.
Given Nedra's reasoning for hating the Huntress, her problem is less about the Huntress actually existing and more about the fact that another woman who isn't herself got to do exactly what she wanted to do. I guess you could say the Huntress is something of a black mirror to Nedra that presents to her the person she sees herself as on the inside, but the face she sees on the outside is that of someone else. Being met with that reality hurts her.
The existence of the Huntress makes Nedra feel as though she has failed herself in a very big way. Nedra has surrendered to her own insecurities, internalised misogyny, and homophobia by doing exactly what other people wanted her to do. She became the person other people wanted her to be and she doesn't feel like she is living an authentic life. The Huntress by contrast symbolises for Nedra the woman who didn't bend to the will of other people and got to pursue the goals she wanted to pursue in life against all odds. She got to be her authentic self. So Nedra thinks.
Truth be told, Nedra does not know the woman who is the Huntress, let alone the reasons that drove that woman to become the Huntress. She doesn't know Helena Wayne's insecurities and other internal struggles, nor does she really care because none of that is relevant to her. She sees the Huntress as the hero the rest of the world sees. So while Nedra has set out to "destroy the hero" and the public's perception of her as her "new goal," in reality, she wants to the destroy the metaphorical black mirror that represents her own failures as a person, not Helena Wayne. The Huntress just happens to be the entity she is projecting all of her anger towards.
With self-loathing driving Nedra Borrower to destroy another woman she doesn't know, this brings us to the last topic of conversation for this chapter: is she succeeding?
One recurring theme that's been prominent since the start of Joey Cavalieri's run that is present here is growing public distrust in the Huntress, leading to public pressure on Gotham's authorities to put an end to her activities. This is probably starting to sound like the plot of the 1986 maxiseries Watchmen by Alan Moore and Dave Gibbons. While Cavalieri's story does explore this same theme, he doesn't do it for the same reason Moore and Gibbons do in their own story.
Within the context of Watchmen (written three years after this Huntress story was published), trust in superheroes was present from the moment the first costumed vigilante appeared in that universe as "Hooded Justice" in 1938. Since one of the themes of Watchmen was to explore the natural consequences of costumed vigilantes existing in the real world, public distrust in superheroes culminating in their public banning in 1977 was handled as a natural consequence of that decades-old distrust.
Within the context of Joey Cavalieri's Huntress story, growing distrust in the Huntress is being explored as a consequence of mass media, specifically the power that media has in shaping public opinion and ultimately human behaviour. This is something that gets explored more in-depth in a future chapter, but it does nonetheless show that Nedra has been manipulating public perception of the Huntress since the beginning of Cavalieri's run. We're just now seeing a face being put to the name.
Labels: earth-2, gary minelli, helena wayne, huntress, joey cavalieri, michael hernandez, nedra borrower, review, the best of huntress
Aromus Baspet 15 January 2018 at 03:25
(Repost!)
Did Gary really undress Helena while she was tripping? Given that she has a towel wrapped around her while she's at the apartment, my own impression was always that she'd been in the shower. I might be wrong though. And if you're right then wow...it really is crazy she's not more freaked out by that...
After the tirade he put through, I was bitterly disappointed that we weren't treated to a well-deserved scene where Helena marches up to Harry and asks what he has to say for himself now that she's been dramatically vindicated in the matter of Pat Pending's death. Followed by her breaking up with him for good.
You have crystallized everything that I thought about Nedra Borrower. Remember the straitjacket I mentioned in my thoughts on the previous story? Not the physical one Helena is bound in when she's committed to the asylum, the metaphorical one that society would impose on her because she's a woman. Unlike Helena, Nedra Borrower is a woman who DID surrender to it. She's as much of a black mirror for Helena is Helena is for her. Helena has seen what's wrong with society and vowed to everything in her power to ensure that the people of Gotham are safe from those who would prey upon them. Therefore she's a huntress of predators. Then you have Nedra who discovers that a mental hospital has been turned into a dungeon for enemies of the mob but decides not to cover the story because it doesn't satisfy her selfish interests. For all her talk, she doesn't give a damn about the city as long as she gets what she wants. Unfortunately for Helena, Nedra has a potent weapon she can bring to bear in their struggle. Where foes like Solomon Grundy and even The Joker failed, the power of the media might just prove to be what finally destroys The Huntress.
That panel of Helena swooping in to bust up (what she believes to be) a drug deal is so beautiful. It could have been the perfect cover illustration for issue #1 of a Helena Wayne comic-book. Which sadly is something that never came to be...
Diane Darcy 15 January 2018 at 12:07
In the same panel where she is wearing the towel, she asks Gary if he brought her to his apartment, making it known that she had just woken up. It would be unusual for her to say something like that after showering since the latter would imply she knew she was staying in his flat.
That splash page of her swooping in to bust a drug deal (that didn't turn out to be a drug deal) is pretty fantastic though. Amazing work there by Michael Hernandez!
(Sorry for all the reposts!)
Do you think the creep factor of Gary's actions toward Helena was deliberate on the writer's part? Later events might suggests that it was. But part of me wonders whether the writer realized how it comes off for him to have undressed and unmasked her (two violations for the price of one as she's a crime-fighter) while she was under the influence of drugs. Since Helena herself doesn't seem weirded out to discover herself naked in a strange man's apartment, I can't help wondering if the skeeviness was an unintentional side-effect of her being written by a male writer.
I've been mulling over the relevance of the Arkham Asylum story. One of the things you mentioned was that its connection with the Undertaker story was unclear. I agree that it seems to have been placed quite oddly for a new story. Why does one story lead directly into another like that? Why aren't they separate? Why didn't Helena discover what was going on behind the closed doors of Arkham as a result of another investigation after wrapping up the Undertaker case? I think it makes a lot more sense when you think of the Arkham story not as the sequel to the Undertaker story but as the midway point of the Helena VS Nedra Borrower story. Think about it: Helena is framed for a criminal's death in the Undertaker story. We then see the press using this apparent evidence of wrongdoing to set the gears of the law moving against her while she's imprisoned at the asylum. And now the storm which has been brewing will finally be unleashed in the arc which has just begun. It's a three-act story and now we've reached the final act where everything will come to a head. Nedra may not be a criminal but she's definitely been behind the scenes all along, stirring up trouble for Helena.
There is so much beautiful art throughout these stories just waiting to take your breath away. I always loved the fight with Nightingale which is coming up in the future...
Earth-2 Power Girl Set To Return in Deathstroke? C...
Justice Society Vol. 2: How Do I Buy Helena Wayne'...
I Can't Buy Huntress: Dark Knight Daughter In Prin...


text:
Saudi to privatize 20 desalination and sewage plants – Saudi Arabia
Saudi Arabia is planning to privatize 20 water desalination and sewage plants within a strategy to expand the private sector and boost public revenue as part of its Vision 2030 development blueprint, an official was quoted on Tuesday as saying.
The projects include 11 water desalination facilities with a combined output capacity of 4.3 million cubic metres per day and 9 sewage plants with a capacity of 1.1 million cubic metres per day, said Abdul Rahman Al-Zughaibi, Undersecretary for Economic and Investment Affairs at the Environment, Water and Agriculture Ministry.
He told the Arabic language daily Aleqtisadia that three targeted desalination plants are based in the Eastern port of Jubail, a desalination unit in the Western Red Sea port of Yanbu and a sewage plant in Taif in West Saudi Arabia.


Apology: By necessity of speaking to a topic as broad as the health of the Japanese Church I must unfortunately make sweeping generalities--we can all think of examples that contradict parts of the following post (and the upcoming series), but I hope you will hear me out and deeply consider whether there are deeply rooted dependencies in the Japanese Church stemming from the past and current shortcomings and failures of missionaries.
A lot has been written in recent years about unhealthy dependencies in global missions. Usually when this topic comes up it primarily has to do with finances. Books like 'When Helping Hurts,' 'Toxic Charity,' and 'When Charity Destroys Dignity,' rightly point out that the way that affluent, mostly Western churches often seek to help the Global Church through financial charity frequently leads to more brokenness than it alleviates.
However, finances are only one way in which the Western Church and the way that it does missions actually serves to weaken the Global Church rather than strengthening it. In the late 19th century, John Livingstone Nevius, a prominent Presbyterian missionary to China identified three ways in which an indigenous church must be self determining in order to truly be considered a mature church--the church must be Self Supporting, Self Governing and Self Propagating (Evangelizing). Later, another missionary theologian, Roland Allen pointed out that to be fully mature, the indigenous church must also be Self Theologizing.
Roland Allen clearly shows in his book 'The Spontaneous Expansion of the Church, And the Causes That Hinder It,' that at his time, many missionaries and missions organizations that claimed to be pursuing Self Supporting, Self Governing and Self Propagating indigenous churches were often doing so only in theory, but not in practice; 'they honor me with their lips, but their hearts are far from me.' In fact, many of them were, and still are, facilitating dependencies in order to maintain power, influence--and many cases out of fear of the potential dangers associated with fully autonomous indigenous churches; namely, syncretism.
The recent interest in financial dependencies in missions and their unhealthy impact on the Global Church really only deals with a quarter of the problem--there are much deeper ways in which Western Missions continues to hamstring indigenous churches, their leaders and their health.
This has been a topic that I have spent my last five months in Japan dwelling on; and one of the main reasons I haven't written much,. It has been rather difficult to write about something that could easily be seen as undermining my very call to ministry here in Japan. However, that has only convicted me further of the importance of grappling with this topic.
Now some people might be wondering why this topic is relevant to Japan--Japan is a first world nation, and apart from the recent influx of finances from the Global Church to deal with the impact of the 2011 Tohoku Earthquake and Tsunami, the Japanese Church for the most part is financially independent of the Western Church. It supports its own institutions and clergy; and therefore on the surface would point to being a mature church.
However, the Japanese Church's dependencies are not quite as easy to point to as the financial dependencies in the churches of many developing nations. The unhealthy dependencies in the Japanese Church are primarily dependencies of Propagation, Theology and Methodology (Governance).
In upcoming posts we will look at some of the dimensions of dependency within the Japanese Church.
(3) and probably most importantly, that the Japanese church continues to be reliant on Western paradigms of church leadership and vocational ministry that hinders the maturity and growth of Christian lay people--meaning that the greatest potential catalyst for reaching the Japanese with the Gospel, the Japanese Church, is largely unequipped for the task of sharing the Gospel with their own people.
What is the role of the missionary in Japan? How does a missionary help without hurting? Probably one of the main reasons that these dependencies are so deeply ingrained within the Japanese Church is because of the effectiveness of Western missionaries (and increasingly missionaries from the Global Church). Has our short-term effectiveness resulted in long-term ineffectiveness for the average Japanese Christian? Are our evangelistic and discipleship methods reproducible by Japanese Christians? Does our reliance on English ministries, the 'Westernness' of Christianity and outreach events undermine the ability of the Japanese church to reproduce similar efforts? We will look at all of these questions and more in upcoming posts.


text:
Protesters disrupt speech by Kathleen Wynne to voice outrage at seven-week York University strike
By Robert BenzieQueen's Park Bureau Chief
Mon., April 23, 2018timer2 min. read
Protesters from York University disrupted Premier Kathleen Wynne's Canadian Club speech to demonstrate their anger at a seven-week strike.
"You've done nothing, Wynne," shouted one of the four young women who posed as journalists in order to sneak into the premier's speech Monday at Arcadian Court.
"CUPE wants to bargain! CUPE wants to bargain!" they chanted as they unfurled a spray-painted banner reading "We Are The Future Of Education" from above the dais.
Some 3,000 academic workers, members of the Canadian Union of Public Employees Local 3903, have been on strike at York since March 5.
The union is seeking improvements to job security and benefits and also looking for higher wages. It is demanding a 10.5-per-cent raise over three years. York has offered 6.6 per cent.
While Wynne briefly appeared rattled by the two-minute protest that ended when the four were peacefully escorted away by staff, she said she shared the demonstrators' frustration at the impasse.
"I agree with you," she said before lauding the young people for voicing their concerns.
"We live in a democratic society and isn't that a wonderful thing," the premier said to cheers from 400 people attending the speech.
"It's democracy."
At Queen's Park, Progressive Conservative MPP Lorne Coe (Whitby-Oshawa) implored the government to "finally take action to get the 51,000 students back into their classrooms."
"This could be the last opportunity the Liberal government has to act to save the semester of tens of thousands of students and allow them to graduate in June," said Coe.
"Classes are on the verge of being cancelled, and many students' summer job placements are at risk."
Advanced Education Minister Mitzie Hunter emphasized that "York is still open and students are still attending classes, as they have been through the duration."
"That being said … this government has taken action," said Hunter, who noted that Labour Minister Kevin Flynn has "appointed an experienced mediator to bring the sides together, to talk to each party and find a path to resolution."
"We are working on this issue and supporting both parties because the best deal is a deal that is made at the table," she said.
"We have been urging both sides to come to the table to resolve their issues and to find the compromise that is necessary in the best interests of the students of York University."
The university has said it is prepared to extend classes into the summer and that students who have found jobs or who cannot remain in Toronto would be eligible for tuition credits.
With files from Kristin Rushowy


text:
Store Spotlight: Barber & Supply x FairEnds Pop Up Shop
By Garrett Munce
[#image: /photos/5583633709f0bee56441d69b]||||||
Because once just wasn't enough, a second collaborative pop-up shop from Barber's new Barber & Supply (a GQ-endorsed spot in the Coolest City on the Planet) opened its doors today in Williamsburg, teaming up with cult favorite FairEnds for one original retail experience. FairEnds, of course, are known for their five-panel caps and the team behind the brand curated the shop with exclusive items like tote bags, backpacks, t-shirts, and even a hot sauce made locally on their Montana farm. The shop is small but expertly edited, stocked as a one-stop emporium (Flint & Tinder underwear, Drifter bags, Danner boots, Taylor Stitch shirts and pants, and, of course, FairEnds hats) for any weekend warrior, whether he's brunching hard or heading out of town to rougher terrain. FairEnds friend and artist Brian Montuori even contributed one hilarious outdoor-inspired mural of a bee-chased bear to the walls and also has a graphic t-shirt up for grabs. The perfect post-haircut shop will be popped-up through July, but don't worry if you can't make it to Williamsburg; a selection of the product will also be available on the Barber & Supply and FairEnds websites.
fair-ends-01.jpg
Related Stories for GQHair


Panoramic view of the ankle joint.
Arthroscopy of joints such as the knee, shoulder and hip are pretty well known in orthopaedic practice. It allows orthopaedic surgeons to make diagnosis via direct visualization of the joint and perform therapeutic procedures should the need arise.
In Foot and Ankle Surgery, this procedure allows me to looking into the ankle and subtalar joints should the need arise. It is very new and the instrumentation used are different than that of the other joints mentioned above.
In addition to the joints being scoped (medical jargon for looked at), the tendon tunnels and sheaths can also be looked at in the area of the foot and ankle.
Should you have pain in your ankle after an injury or for any other reason, consult your consultant foot and ankle surgeon.


It's not only greenhouse gas emissions: Washington's new world order weapons have the ability to trigger climate change.
road. You'll be met by this sign, at the facility's front gates.
and spaced precisely 80 feet apart.
The Northern Lights are normally triggered by solar winds.
But with Haarp, aurora can now be man-made, too.
direct blasts of massively powerful high-frequency radio waves.
In February 1998, responding to a report of Mrs. Maj. Britt Theorin-Swedish MEP and longtime peace advocate--, the European Parliament's Committee on Foreign Affairs, Security and Defense Policy held public hearings in Brussels on the HAARP program.(17) The Committee's "Motion for Resolution" submitted to the European Parliament: "Considers HAARP by virtue of its far-reaching impact on the environment to be a global concern and calls for its legal, ecological and ethical implications to be examined by an international independent body; [the Committee] regrets the repeated refusal of the United States Administration to give evidence to the public hearing into the environmental and public risks [of&] the HAARP program." (18.) The Committee's request to draw up a "Green Paper" on "the environmental impacts of military activities", however, was casually dismissed on the grounds that the European Commission lacks the required jurisdiction to delve into "the links between environment and defense". (19) Brussels was anxious to avoid a showdown with Washington. FULLY OPERATIONAL While there is no concrete evidence of HAARP having been used, scientific findings suggest that it is at present fully operational. What this means is that HAARP could potentially be applied by the US military to selectively modify the climate of an "unfriendly nation" or "rogue state" with a view to destabilizing its national economy. Agricultural systems in both developed and developing countries are already in crisis as a result of New World Order policies including market deregulation, commodity dumping, etc. Amply documented, IMF and World Bank "economic medicine" imposed on the Third World and the countries of the former Soviet block has largely contributed to the destabilization of domestic agriculture. In turn, the provisions of the World Trade Organization (WTO) have supported the interests of a handful of Western agri-biotech conglomerates in their quest to impose genetically modified (GMO) seeds on farmers throughout the World.
Hessdalen lights mystery been solved?


Who's in?!?!?! I love green smoothies! Green smoothies were in large part responsible for my weight loss back in the day and it was completely by accident.
I started drinking green smoothies for my health. I looked at my diet and recognized that I was not eating enough fruits and vegetables. The thought of consuming so much of the good stuff however was a little daunting for me and I thought there must be an easier way.
So I took the lazy girl way out and decided to blend them together every morning in order to get my fill of vitamins and minerals. That was truly the only reason. I wanted to be healthy for my little munchkin, I wanted to encourage her to be healthy as well and so I decided to give green smoothies a try.
It was so easy to blend one and go every morning since the 9-5 life I was living back then lent little time for preparing a healthy breakfast. Or maybe there was enough time but I valued my sleep more. You know I'm not much of a morning person.
So I've decided to get back into the habit of having a green smoothie a day to clear out any muckiness in my insides, do a little detox and get back on track where weight and overall health is concerned. This time I'm hoping you will join me!
Now is the perfect time to partake in such a challenge because why not now? There's never a bad time to commit to good health.
Are you in? Need more convincing? Take this quiz and let's see how you score!
Green smoothies offer max nutrition – There's nothing healthier than a big bowl of green leafy vegetables. Greens like spinach, kale, broccoli, cabbage, collard greens and lettuce pack a powerful punch when it comes to vitamins and minerals. They're great for digestion, for your skin, for your hair, to prevent cancer and just keep you moving, yo!
Green smoothies are easy to digest – because the greens and other ingredients are already blended, your body has much less work to do to break down the foods and get to the good stuff it needs to function. They're an awesome way to give your body a break so it can focus on other functions like detoxifying the meat you eat (or the alcohol you drank last night), the toxins you breathe in, the lotion you put on your skin etc etc.
Green smoothies help you to shed excess pounds – everyone could stand to lose a couple pounds. If the rest of your diet isn't so great then green smoothies won't work a miracle but replacing one meal a day with something so nutritious, filling and low in calories is a great way to get rid of unwanted pounds.
Green smoothies boost energy – because your body didn't have to spend time breaking down your usual breakfast foods but received such a high density of vitamins, minerals and antioxidants you'll be much lighter on your feet, have a clarity of mind and benefit from a great energy boost to get through your day.
Green smoothies boost your immune system – these smoothies are packed with phytonutrients, antioxidants and vitamins and minerals that strengthen your body's immune system meaning your likelihood to catch a common cold when consuming loads of green smoothies are slim! I can attest to this! Green smoothies can also help to lower cholesterol and high blood pressure when consumed at least once per day.
Blending breaks down fruits and vegetables – going back to the point of making smoothies easier to digest than eating a salad, your body is able to receive more of the nutrients than if you chewed up the pieces when you blend them.
You're able to consume a lot more greens in a smoothie than in a salad – you can stuff two (or more) whole cups of spinach into a blender but you're probably not going to eat so much in a salad.
I advocate for blending your vegetables and greens instead of juicing them because juicing removes a lot of the fiber that's helpful to flush out the toxins in your body and to help you feel fuller. I want to keep the fruits and veggies as close to their natural state as possible, juicing removes too much of the benefits I'm after.
Now, there's a specific science to blending your perfect green smoothie. Just like a salad can be laden with unhealthy toppings causing it to be very high in calories and defeating the whole purpose, so can a smoothie tip the scale if you use too much fruit or fruit high in natural sugars. This is especially important for those who are diabetic or watching their sugar intake.
On this challenge, I will be including fruits that rank low and in the middle of the glycemic index as well as some powerful punches to add to your smoothies for added benefits. These will include nuts and seeds for protein and other nutrients, superfood supplements and odd ingredient here and there to add something new to your diet.
You can always add a spoonful of honey or stevia to help with taste in the beginning if you need it. I used to do this until I got used to the taste and found the perfect ratio of fruit to veggies for me.
There's a grocery list below to guide you. This is a super-list and you absolutely do not need to buy everything on it to participate. When you sign up, you will receive this recipe guide plus four recipes to begin with. Mix and match ingredients from the different sections to create a delicious smoothie no matter what you happen to have on hand. After a while, you'll begin to come up with your own creations.
It's important to rotate your ingredients so that you're getting a variety of nutrients and so that you don't get bored. Try not to use the same greens every day. Creativity is encouraged and is super easy with this guide.
Green smoothies are just one of the ways I recommended in this list of ways to lead a happy life, every single day.
Remember to sign up below to receive the grocery list in a handy PDF format as well as your recipes.
Green smoothie tip: Blend your greens with your liquid first so that all the greens are blended out nice and smooth and leaves space for your fruits and nuts. I recommend a Vitamix blender for your smoothies because no matter what you throw at a Vitamix, your smoothies will come out the perfect texture every single time! Ice, nuts or even frozen fruit are no match for a Vitamix! (It also creates chunky chops for salsa etc and kneads dough!) A Ninja blender is also a great option, the Vitamix can be pricey.
Grab your recipe guide below! See you on the healthier side and please, share this with a friend!
Love the list of superfood add ins! My toddler LOVES smoothies and I love that he loves them because of all the nutrients I can pack in, so it is nice to have even more options!
my daughter used to love smoothies now hates the thought of a green one, im on a mission to try and get her back in the mix!
This really look delicious. I have to go back to making my smoothies. You just made me want another one. My Vitamin has been sitting idle for too long. Thank you for sharing.
yes whip out that blender and let's get it!
As good as the benefits are I'm gonna have to tap out on this one. Good luck though.
member seh you don't need all of these 🙂 The smoothie i made this morning had flax seeds, spinach, banana, apple and spirulina. Thats it and so good!
Aww man, wish I seen this earlier so i could join. I will you blessing on your wellness journey.


text:
A Unique Pairing
Bangor, ME — The Bangor Symphony Orchestra and Geaghan Brothers Brewing are joining forces once again to offer a unique Beer & Music Pairing event on Friday, June 29, 7 p.m. in the Bangor Arts Exchange Ballroom. Head Brewer Andrew Geaghan, BSO concertmaster Trond Saeverud, and BSO Music Director and Conductor Lucas Richman are creating a menu of four beers paired in perfect harmony with four musical selections. Tickets are $25 per person, and include a complimentary souvenir glass and four, 10 oz. tasting pours.
The BSO and Geaghan's first partnered in 2015 to mark the Symphony's 120th anniversary with Maestro's Marzen, a custom brew. The unique partnership was recently highlighted in a Forbes Magazine article.
"While brewing beer is a very scientific endeavor, it is also one of my largest creative outputs," says Andrew Geaghan, Owner & Head Brewer. "As a musician I have always had a deep appreciation for the emotive qualities of music, and I am excited to work with the Bangor Symphony and our brewers to pair musical movements that match our beers in feel and spirit."
Tickets are now available online. For more information, call the Bangor Symphony Orchestra office at (207) 942-5555.


Rob and Rhonda Bugh have what one might consider to be a fairy-tale love story.
The two were previously married and widowed, and their pasts were long intertwined. Rob was best friends with Rhonda's first husband, Tom, and the two families often spent weekends together water skiing and snow skiing. Rhonda's three children were well acquainted with Rob's four and grew up together attending church and enjoying vacations.
So when Rob and Rhonda lost their spouses to cancer within eighteen months of each other, it seemed natural to many of their friends that they should get married. "We began to discover that people were praying that Rhonda and I would get together," Rob says.
Rob is the Senior Pastor of Wheaton Bible Church, a church of more than 4,000, located in West Chicago, Illinois, and Rhonda is an established pediatrician. Both are successful, godly people who took the time to study and prepare for the next step of forming a blended family. "We did all the research," Rhonda says.
They even took their children to a therapist to discuss grief and the concerns that might come up. On the day they took their vows, Rob and Rhonda felt confident that even though their dating and engagement period lasted less than a year, they had prevented any major problems from developing and expected an easy transition.
Then they started their life together.
Rob felt a lot of guilt regarding Ryan's needs and feared that his son wasn't getting the kind of love and attention a grieving boy deserves. Rhonda felt that Rob's love for Ryan was putting her at a distant second place. And Ryan was dealing with suppressed bitterness that had developed early on when he saw his dad kissing Rhonda during their courtship. So there was a lot of tension about whose parenting rules were right. A lot of decisions were made within the backdrop of hurt feelings.
The Bughs also discovered that they expressed their love for each other in different ways. Rhonda showed love through practical means, like doing something kind for Rob (known as "acts of service" in Gary Chapman's book The Five Love Languages). And Rob showed love through words that uplift and encourage (known as "words of affirmation"). So the way the couple tried to show affection wasn't connecting.
The couple who had been so much in love just a few months earlier were having serious reservations about the viability of their marriage. "If we hadn't been the strong Christians we are, I don't think we would have made it," Rob says. But they wanted to honor God with their marriage and make things work.
The couple has now been married over eight years, and "it gets better every day," Rob says. He and Rhonda continue to use Ron Deal's resources in their ministry and have hosted Ron's Successful Stepfamily event. They have also led a blended family small group, and Rob and Rhonda are now both on the advisory board for FamilyLife Blended.


text:
Marriott-Rachlin Collection
Anthropologists. Correspondence, research materials and manuscripts of Alice Lee Marriott and Carol Rachlin assembled during their collaboration on several essays and manuscripts relating to Native American society, mythology and peyotism.
Mary Brownlee Frederickson Collection
Typescripts and manuscripts of poetry and short stories regarding the Oklahoma oil fields and the oil industry, all by Mary B. Frederickson. Mary Brownlee Frederickson Photograph Collection also in repository.
Mary Louise Ligon Collection
Collector. A manuscript (n.d.) describing a saloon fight; a typescript (n.d.) entitled"Fundamental Principals of Plant Breeding" by Isaac C. Renfro; newspaper clippings (1925); and a permit (1906) issued to Elkins, Webster and Bayleas to sell insurance in Indian Territory. Mary Louise Ligon Photograph Collection also in repository.
Max Leon Moorhead Collection
Historian. Correspondence (1962-1976), manuscripts (1960's), photocopied documents, research notes, lectures, student papers, book reviews, reprints and other printed materials relating to Moorhead's teaching of Latin American history at the University of Oklahoma and to his publications about Jacob de Ugarte y Loyola, the presidio, other Spanish colonial institutions in the Southwest, and the Indians of Mexico and the southwestern United States. Max L. Moorhead Photograph Collection also in repository.
Micah Pearce Smith Collection
Manuscripts by Smith entitled "The Seminole Presbyterian Mission" "Dr. Emmet Starr", and "Daniel Collins Home"; research notes on the Oklahoma towns of Fred, Ninnekah, Chickasha, and Old Bloomfield, along with a biographical questionnaire relating to Rhoda Gunn Colbert Potts, Daniel Collins, and the Colbert family.
Mike McCarville Collection
Author. A manuscript account of a 1909 lynching in Ada, Oklahoma, written by McCarville; a copy (1957) of Oklahoma City's charter; a program (1965) for a dinner at the John Fitzgerald Kennedy Library in Oklahoma City; and four pieces (n.d.) of United States military scrip. Mike McCarville Photograph Collection also in repository.
Mildred June Tompkins Benson Collection
Mayor. Correspondence (1954-1981), certificates (1953-1981), municipal reports (1948-1961), and agendas and minutes (1954-1981) of the Norman, Oklahoma, city council and of several Norman city government boards, including those governing environmental and noise pollution and the quality of water; publications, including newsletters and brochures published by the League of Women Voters of the United States; and one manuscript, jointly authored by Benson and Cortez A. M. Ewing, regarding presidential nominating politics in 1952. Benson was active in Norman politics and her papers reflect the governance of the municipality for the period 1954-1981.
Morris L. Wardell Collection
Historian. Correspondence (1921-1956) concerning Wardell's tenure as a Professor of History and as an Assistant to the President of the University of Oklahoma, concerning student affairs, curriculum, academic department administration, accreditation, military training, housing, post-war planning, and state legislation; academic and admistrative departmental reports (1929-1945); correspondence concerning Wardell's teaching and publishing activities, and his involvement with civic organizations; lecture notes; research materials; and manuscripts regarding the Cherokee and Osage Indians, historic sites in Oklahoma, and the history of the American West. Morris L. Wardell Photograph Collection also in repository.
Mrs. Hugh Hart Collection
A history of the Presbyterian Church in Pauls Valley, Oklahoma, written by Mrs. Hart.
Mrs. O. R. Hisel Collection
Historian. Bound typescripts of three unpublished works by Mrs. Hisel entitled "Oklahoma Federation of Music Clubs" (1950) "Missionary Federation of Muskogee, Oklahoma" (1950) and "Presbyterian Pioneers and Personalities, 1825-1951" (1951).
Mrs. W. C. Jarboe Collection
An account written in 1913 by Mrs. Jarboe of her family's move from Texas to Indian Territory and their subsequent move to Greer County, Oklahoma. The manuscript contains anecdotes about their experiences with Choctaw and Kiowa Indians, as well as a general description of pioneer life.
Neil Merton Judd Collection
Collection — Box: J-14
Archaeologist. Manuscripts of Judd's book"The Architecture of Pueblo Bonito" published by the Smithsonian Institution (1964), and articles for publication (1964-1967); and a bibliography of his publications (1968).
Nell Kibler Collection
Oath of Allegiance (1862) to the United States; a history (1863) of the Confederate Army's Dublin Provost Guard; a letter (1828) from an ancestor of Kibler regarding the hardships of life in Michigan Territory; and schoolbooks (1885-1888) published in the 19th century.
Nina Louise Phillipi Pond Collection
Homemaker. Typescripts (1893-1950), including the Statement of Intent of the Grant County (Oklahoma) Film Library, and concerning the history of the Grant County Historical Society and of the Medford, Oklahoma Methodist Episcopal Church; programs (1950-1953, 1967) of the Medford Methodist Church, and of various women's civic groups in Medford; minutes (1966-1968), publications (1952-1968) and governing board reports (1965-1967) of the United Methodist Church's Oklahoma Conference Ministry to the Deaf. Nina Phillipi Pond Photograph Collection also in repository.
Oklahoma Geological Survey Collection
Scope and Contents FULL FINDING AID (PDF)Records of the Oklahoma Geological Survey, including correspondence (1908-1982), area geological reports (1915-1948), and field notes (1908-); reports of the governing board (1974-1979); and book manuscripts and galley proofs (n.d.) regarding Oklahoma geology. Also included is correspondence from the University of Oklahoma Department of Geology and Geography. Oklahoma Geological Survey Photograph Collection also in repository.The Oklahoma Geological Survey was established by Senate Bill No. 75 in 1908 with Charles N. Gould serving as its first Director. The Geological Survey exists for the purpose of seeking information on the geology, mineral and underground water resources of the state; for the purpose of determining chemical properties and possible commercial applications of the state's minerals and rocks; to assist mineral producers...
Oren Frank Evans Collection
Geology professor. Manuscripts of fiction stories and geological articles by Evans. Oren Frank Evans Photograph Collection also in repository.
Oscar Brousse Jacobson Collection
Professor. Newspaper editorial cartoons published during World War I regarding the conflict; publications, plans, papers and blueprints detailing civilian defense of the University of Oklahoma, all dating from World War II; and correspondence, biographies and Jacobson's unpublished manuscript regarding Oklahoma artists and the status of art in Oklahoma through 1930. Oscar Brousse Jacobson Photograph Collection also in repository.
Oscar Wolfe Collection
Pipeline Consultant. A two-volume manuscript entitled "The Hydraulic Design of Oil and Gas Pipe Lines" written by Oscar Wolfe.
Patricia K. Ourada Collection
Historian. An annotated manuscript for Ourada's book "The Menominee Indians: A History.".
Peter J. Powell Collection
Priest. An unedited version of Powell's book "Sweet Medicine", published by the University of Oklahoma Press, 1969, which discusses the Cheyenne Indians' Sun Dance and other religious ceremonies.
Pleasant P. Nesbitt Collection
Physician. Typescripts of five articles (n.d.) on medical subjects by Nesbitt, a Tulsa, Oklahoma physician.
Preston Van Buren Duvall Collection
Typed manuscript (1938) concerning the life of Preston Duvall and his experiences in the run into the Cherokee Strip in 1893.
Ralph David Bienfang Collection
Pharmacist and professor. Bound sketchbooks (1933–1952) of animals and plants; manuscripts (1956–1957), notebooks (1932–1948), and workbooks (1939–1941) written by Bienfang and relating to pharmacognosy; correspondence (1942) and printed material (1944–1945) relating to the role of pharmacy in military history; and Bienfang's collection of postage stamps and seals (circa 1930–1960). Ralph Bienfang Photograph Collection also in repository.
Ralph Hayden Records Collection
Professor. Magazine and journal article (1940-1968) regarding historiography; and a manuscript (n.d.) by Loban S. Records entitled "The Recollections of a Cowboy of the Seventies and Eighties" regarding the lives of cowboys and ranchers in Kansas and the Cherokee Strip of Oklahoma in the 1870's and 1880's. The manuscript includes an account of Records's participation in the Land Run of 1893. Ralph Hyden Records Photograph Collection also in repository.
Ramon Frederick Adams Collection
Writer. Manuscripts of three of Adams's books, "A Genius in Chaps," "Cowboy Lingo," and "Come an' Get It," along with his correspondence (1942–1958) with other writers, collectors of western lore, fans, and publishers.
Raymond Stallings McLain Collection
U.S. Army officer. Correspondence (1929-1954) concerning McLain's personal and business affairs; orders, memoranda, reports (1920-1937) and correspondence (1939-1940) from McLain's service with the Oklahoma National Guard; speeches (1947-1954) made by McLain as Chief of Information of the U.S. Army and as a member of the National Security Training Commission; typescript account (n.d.) by McLain of his participation as commander of the 45th Division Artillery in the Allied invasion of Sicily during World War II; and a typescript account by McLain of the 45th Division Artillery's role in the Battle of the Aisne (1918) in World War I.
Richard Gildart Fowler Collection
University of Oklahoma professor. Correspondence (1924-1980); lecture notes and tests for classes in physics taught by Fowler at the University of Oklahoma; manuscripts (1958-1969) for textbooks authored by Fowler; correspondence with Fowler's publishers; research notes (1943-1979) arranged by research project; and project histories and documentation (1952-1972) for contracted research projects conducted by Fowler. Richard Gildart Fowler Photograph Collection also in repository.
Robert C. Carriker Collection
Historian. Manuscript of Carriker's book"Fort Supply, Indian Territory: Frontier Outpost on the Plains" published in 1970 by the University of Oklahoma Press.
Robert P. Chaat Collection
Collection — Box: C-2
Clergyman. Minute book (1900-1908) kept by the Secretary of the Apache Indian Mission at Fort Sill, Oklahoma; manuscripts concerning the history of the Dutch Reformed Schools, the Comanche Mission, the Warm Springs Apaches and various Indians' conversions to Christianity; along with booklets, pamphlets, and newsletters regarding the history and constitution of the Dutch Reformed Church. Robert P. Chaat Photograph Collection also in repository.
Robert R. Benson Collection
Historian. Manuscripts and typescripts by Benson, regarding Lee Vining Creek, California; the boundaries of New Mexico, Colorado, and the Oklahoma Panhandle; and the Pecos River, the Goodnight Cattle Trail, and several other locales in the American West; along with a history of the American Garden Service. Robert R. Benson Photograph Collection also in repository.


Today... Mostly cloudy. A chance of showers this afternoon. Cooler with highs in the upper 50s. Southwest winds around 15 mph. Chance of rain 40 percent.
Tonight... Mostly cloudy. A chance of showers in the evening. Lows in the lower 40s. Southwest winds 10 to 15 mph, diminishing to around 5 mph after midnight. Chance of rain 30 percent.
Sunday... Mostly sunny. Highs in the upper 60s. Northwest winds 5 to 10 mph.
Sunday Night... Mostly clear. Lows in the upper 40s. Northwest winds around 5 mph.
Monday... Sunny. Highs in the upper 70s. Northwest winds around 5 mph.
Wednesday Night... Partly cloudy in the evening, then becoming mostly cloudy. Lows around 60.
Pineville, NC Weather Forecast - Find local 28134 Pineville, North Carolina weather forecasts and current conditions. Continually striving to be your best resource for Local Pineville, North Carolina Weather!


text:
Q: Set end date to a max of 31 days I'm trying to extract weeks from the given Start Date and End Date.
If the date range is more than 31 day's I need to restrict the endDate to max of 31 days.
Example:
Start date : 1st May 2018
default end date: 30th June 2018
New endDate should be: 31st May 2018

Below is my code to extract the weeks
weekByDates() {
  let dates = JSON.parse(localStorage.getItem('pfsessionFilter'));
let stDate = moment((dates[0].value)).format('YYYY-MM-DD');
let eDate = moment((dates[1].value)).format('YYYY-MM-DD');

let startDate = moment(stDate);
let endDate = moment(eDate);
let weekData = [];
if(endDate.diff(startDate, 'days')>31)
endDate = moment(startDate.add(31, 'days'));
while(startDate.isSameOrBefore(endDate)) {
    if(weekData.length > 0) {
        // Update end date
        let lastObj = weekData[weekData.length - 1];
        lastObj['endDate'] =  moment(startDate).format('MM/DD');
        lastObj['label'] = `${lastObj.startDate} - ${lastObj['endDate']} (week${weekData.length})`
        startDate.add(1, 'days');
    }
    weekData.push({startDate: moment(startDate).format('MM/DD')});
    startDate.add(6, 'days');
}
if(startDate.isAfter(endDate)) {
    // Update last object
    let lastObj = weekData[weekData.length - 1];
    lastObj['endDate'] =  moment(endDate).format('MM/DD');
    lastObj['label'] = `${lastObj.startDate} - ${lastObj['endDate']} (week${weekData.length})`
}
console.log(weekData)
return weekData;
}

Please help on how to restrict the endDate to 31 days from start date ?
https://plnkr.co/edit/6rU39T0vIPVQkFnydKbC?p=preview

A: Check if range is greater than 31 days, than use add method as
weekByDates()
{
  let dates = JSON.parse(localStorage.getItem('getDates'));
  let stDate = moment((dates[0].value)).format('YYYY-MM-DD');
  let eDate = moment((dates[1].value)).format('YYYY-MM-DD');
  let startDate = moment(stDate);
  let endDate = moment(eDate);
  if(eDate.diff(stDate, 'days')>31)
    endDate = moment(stDate).add(31, 'days');
  let weekData = [];      while(startDate.isSameOrBefore(endDate))
{ .... }

A: You can restrict the days using add() function,
weekByDates() {
  let dates = JSON.parse(localStorage.getItem('getDates'));
  let stDate = moment((dates[0].value)).format('YYYY-MM-DD');
  let eDate = moment((dates[1].value)).format('YYYY-MM-DD');

  let startDate = moment(stDate);
  let endDate = moment(stDate.add(31, 'days'));
  let weekData = [];
  while(startDate.isSameOrBefore(endDate)) {
    ....
}

Hope it hepls you.

A: I use this judgment
if(endDate.diff(startDate, 'days') > 31){
//More than 31 days
}

redpajama_set_name: RedPajamaStackExchange


text:
package org.jbpm.services.cdi.impl;

import javax.enterprise.context.ApplicationScoped;
import org.jbpm.kie.services.impl.FormManagerServiceImpl;

/**
 *
 * @author salaboy
 */
@ApplicationScoped
public class FormManagerServiceCDIImpl extends FormManagerServiceImpl{

}

redpajama_set_name: RedPajamaGithub


text:
Theodor Fliedner
1800-1864. Founder of the deaconess organization in the German Lutheran church. Born in Eppstein/Taunus, he was educated for the ministry at Giessen and Göttingen. As a young pastor in Kaiserswerth near Düsseldorf he became acquainted with the Mennonite practice of appointing deaconesses, and in 1833 he established a home for ex-convicts which he placed in charge of a woman. In 1835-36 he set up a school for children which also trained women teachers, and a hospital in 1836 which gave nursing instruction. By reviving the office of deaconess, Fliedner provided opportunities for unmarried women to be active in public life. He founded other mother houses in Germany, modeled after the Kaiserswerth Sisters, and introduced the idea into the United States and Palestine.