All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ultreia.gc.service.GcLogService Maven / Gradle / Ivy

There is a newer version: 1.3.4
Show newest version
package io.ultreia.gc.service;

/*-
 * #%L
 * GC toolkit :: API
 * %%
 * Copyright (C) 2017 Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.ultreia.gc.http.GcRequest;
import io.ultreia.gc.http.GcResponse;
import io.ultreia.gc.model.GcArcheoLog;
import io.ultreia.gc.model.GcLog;
import io.ultreia.gc.session.GcSession;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

/**
 * Created by tchemit on 18/04/17.
 *
 * @author Tony Chemit - [email protected]
 */
public class GcLogService extends GcServiceSupport {

    /** Logger. */
    private static final Log log = LogFactory.getLog(GcLogService.class);

    public GcLog getMyLogFromCacheGuid(String guid) {

        GcSession gcSession = getGcSession();

        GcRequest request = gcSession.forGet()
                .addParameter("guid", guid)
                .build("https://www.geocaching.com/seek/cache_details.aspx");
        GcResponse build = gcSession.executeRequest(request);

        Document doc = build.getResponseAsHtml();

        String userToken = null;

        for (Element element : doc.select("script[type='text/javascript']")) {
            String text = element.data();
            if (text.contains("userToken = '")) {
                int i = text.indexOf("userToken =");
                int start = text.indexOf("'", i) + 1;
                int end = text.indexOf("'", start);
                userToken = text.substring(start, end);
                log.info("User token: " + userToken);

            }
        }

        Objects.requireNonNull(userToken);

        Iterator gcLogIterator = newGcLogIterator(userToken, 20);

        GcLog myLog = null;
        while (gcLogIterator.hasNext()) {
            GcLog gcLog = gcLogIterator.next();
            if (gcLog.isFoundByUser(gcSession.getUsername())) {
                myLog = gcLog;
                break;
            }
        }

        return myLog;

    }

    public Optional getMyArcheoLogFromGcName(String gcName, String username) {

        log.info("Seek archeo logs for cache: " + gcName);
        GcSession gcSession = getGcSession();

        GcRequest request = gcSession.forGet()
                .build("https://coord.info/" + gcName);
        GcResponse build = gcSession.executeRequest(request);

        Document doc = build.getResponseAsHtml();

        String userToken = null;

        for (Element element : doc.select("script[type='text/javascript']")) {
            String text = element.data();
            if (text.contains("userToken = '")) {
                int i = text.indexOf("userToken =");
                int start = text.indexOf("'", i) + 1;
                int end = text.indexOf("'", start);
                userToken = text.substring(start, end);
                log.debug("User token: " + userToken);
                break;
            }
        }

        Objects.requireNonNull(userToken);

        Iterator gcLogIterator = newGcLogIterator(userToken, 20);

        GcLog myLog = null;
        while (gcLogIterator.hasNext()) {
            GcLog gcLog = gcLogIterator.next();
            if (gcLog.isFoundByUser(username)) {
                myLog = gcLog;
                break;
            }
        }
        if (myLog == null) {
            return Optional.empty();
        }
        Objects.requireNonNull(myLog);
        GcLog previousLog = null;
        while (gcLogIterator.hasNext()) {
            GcLog gcLog = gcLogIterator.next();
            if (gcLog.isFoundIt()) {
                previousLog = gcLog;
                break;
            }
        }

        return Optional.of(new GcArcheoLog(gcName, "" + myLog.getCacheID(), myLog, previousLog));

    }

    public GcArcheoLogsComputationResult computeArcheoCaches(String username, Collection gcNames, List cache, File output, GcArcheoLogsComputationProgressMonitor progressMonitor) throws IOException {

        Gson gson = new GsonBuilder().setPrettyPrinting().create();

        int gcNamesSize = gcNames.size();
        Set logs = new LinkedHashSet<>();
        logs.addAll(cache);

        progressMonitor.setValue(cache.size());

        GcArcheoLogsComputationResult result = new GcArcheoLogsComputationResult();

        Map existingCacheMapping = new TreeMap<>();
        cache.forEach(archeoLog -> {
            existingCacheMapping.put(archeoLog.getGcName(), archeoLog);
            archeoLog.computeType();
            addResult(result, archeoLog, progressMonitor);
        });

        storeArcheoLogs(output, gson, logs);

//        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "10");

        gcNames.stream().filter(gcName -> !existingCacheMapping.containsKey(gcName)).parallel().forEach(gcName -> {
            Optional optionalArcheoLog = getMyArcheoLogFromGcName(gcName, username);
            if (!optionalArcheoLog.isPresent()) {
                log.warn("Could not find my found it on cache: " + gcName);
                result.addBadGcName(gcName);
                progressMonitor.increment(String.format("[Total points: %d] - Could not find my found it on cache: %s (%d/%d)", result.getTotalPoints(), gcName, result.getBadGcNames().size() + logs.size(), gcNamesSize));
                return;
            }
            GcArcheoLog archeoLog = optionalArcheoLog.get();
            logs.add(archeoLog);
            addResult(result, archeoLog, progressMonitor);

            progressMonitor.increment(String.format("[Total points: %d] - Done for cache: %s (%d/%d)", result.getTotalPoints(), gcName, result.getBadGcNames().size() + logs.size(), gcNamesSize));
            synchronized (GcLogService.this) {
                logs.add(archeoLog);
                if (logs.size() % 20 == 0) {
                    log.info("Store " + logs.size() + " archeo-log(s) to " + output);
                    storeArcheoLogs(output, gson, logs);
                }
            }
        });

        storeArcheoLogs(output, gson, logs);
        return result;
    }

    private void addResult(GcArcheoLogsComputationResult result, GcArcheoLog archeoLog, GcArcheoLogsComputationProgressMonitor progressMonitor) {
        archeoLog.getMyLog().setLogText(null);
        archeoLog.getPreviousLog().ifPresent(l -> l.setLogText(null));
        if (result.addResult(archeoLog)) {
            progressMonitor.onPointsAdded(new GcArcheoLogsComputationProgressMonitor.GcArcheoLogEvent(result, archeoLog, result.getTotalPoints()));
        }
    }

    private void storeArcheoLogs(File output, Gson gson, Set logs) {
        try {
            Files.write(output.toPath(), gson.toJson(logs).getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private HashMap toJson(String content) {
        return new GsonBuilder().setPrettyPrinting().create().fromJson(content, HashMap.class);

    }

    private Iterator newGcLogIterator(String userToken, int pageSize) {
        return new Iterator() {

            int pageIndex = 0;
            int pageTotal = 1;
            Iterator buffer;

            @Override
            public boolean hasNext() {

                if (getBuffer(true).hasNext()) {
                    return true;
                }

                // buffer is consumed
                if (pageIndex == pageTotal) {

                    // no more page to load
                    return false;
                }

                // ask a new buffer
                this.buffer = null;
                return getBuffer(false).hasNext();
            }

            @Override
            public GcLog next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                Map next = buffer.next();
                return GcLog.fromMap(next);
            }

            Iterator getBuffer(boolean computePageTotal) {
                if (buffer == null) {

                    pageIndex++;

                    GcSession gcSession = getGcSession();

                    GcRequest request = gcSession.forGet()
                            .addParameter("tkn", userToken)
                            .addParameter("idx", pageIndex + "")
                            .addParameter("num", pageSize + "")
                            .build("https://www.geocaching.com/seek/geocache.logbook");
                    GcResponse response = gcSession.executeRequest(request);

                    HashMap hashMap = toJson(response.getResponseAsString());
                    if (computePageTotal) {
                        pageTotal = (int) (((Double) ((Map) hashMap.get("pageInfo")).get("totalRows")) / pageSize) + 1;
                    }
                    List data = (List) hashMap.get("data");
                    buffer = data.iterator();
                }
                return buffer;
            }

        };
    }

    public List loadArcheoCaches(File cacheFile) throws IOException {
        Gson gson = new GsonBuilder().create();

        BufferedReader reader = Files.newBufferedReader(cacheFile.toPath());
        return gson.fromJson(reader, new TypeToken>() {
        }.getType());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy