Skip to content

Commit

Permalink
Added ImprintAdaptor [comixed#301]
Browse files Browse the repository at this point in the history
 - Update the publisher/imprint based on scraped data.
 - Made the maximum buffer size 16MB for WebFlux requests.
 - Strip off any leading 0s in the issue number.
 - Force the scraping cache key to use upper case issue and series names.
 - Strip lading 0s on the frontend.
  • Loading branch information
mcpierce committed Jul 24, 2020
1 parent 6df2d78 commit bc55000
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,18 @@ export class ComicDetailsEditorComponent implements OnInit, OnDestroy {
}

private getIssueNumber() {
return this.comicDetailsForm.controls['issueNumber'].value;
let result = this.comicDetailsForm.controls['issueNumber'].value;
// strip any leading 0s
while (
!!result &&
result.length > 0 &&
result !== '0' &&
result.startsWith('0')
) {
result = result.substring(1);
}

return result;
}

private getApiKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,36 @@

package org.comixedproject.scrapers.actions;

import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

/**
* <code>AbstractScrapingAction</code> provides a foundation for creating concrete {@link
* ScrapingAction} types.
*
* @param <T> the result for the action
* @author Darryl L. Pierce
*/
public abstract class AbstractScrapingAction<T> implements ScrapingAction<T> {}
@Log4j2
public abstract class AbstractScrapingAction<T> implements ScrapingAction<T> {
/**
* Creates a consistent {@link WebClient} instance to use for requests.
*
* @param url the url
* @return the instance
*/
protected WebClient createWebClient(final String url) {
log.debug("Creating web client: url={}", url);
return WebClient.builder()
.baseUrl(url)
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.defaultHeaders(
headers -> {
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
headers.add(HttpHeaders.USER_AGENT, "ComiXed/0.7");
})
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.comixedproject.model.comic.Comic;
import org.comixedproject.scrapers.ScrapingException;
import org.comixedproject.scrapers.model.ScrapingIssue;
import org.comixedproject.service.scraping.ScrapingCacheService;
import org.springframework.beans.factory.annotation.Autowired;

Expand All @@ -39,6 +41,7 @@ public abstract class AbstractScrapingAdaptor implements ScrapingAdaptor {

@Autowired protected ObjectMapper objectMapper;
@Autowired protected ScrapingCacheService scrapingCacheService;
@Autowired private ImprintAdaptor imprintAdaptor;

/**
* Generates a consistent key for storing and fetching volume data.
Expand All @@ -48,7 +51,7 @@ public abstract class AbstractScrapingAdaptor implements ScrapingAdaptor {
*/
public String getVolumeKey(final String seriesName) {
log.debug("Generating volume key for: {}", seriesName);
return String.format(VOLUMES_KEY, seriesName);
return String.format(VOLUMES_KEY, seriesName.toUpperCase());
}

/**
Expand All @@ -59,7 +62,7 @@ public String getVolumeKey(final String seriesName) {
* @return the key value
*/
public String getIssuesKey(final Integer volume, final String issueNumber) {
return String.format(ISSUES_KEY, volume, issueNumber);
return String.format(ISSUES_KEY, volume, issueNumber.toUpperCase());
}

/**
Expand Down Expand Up @@ -87,4 +90,54 @@ public void loadEntriesFromCache(
processor.processValue(entries.get(index));
}
}

@Override
public void scrapeComic(
final String apiKey, final String issueId, final Boolean skipCache, final Comic comic)
throws ScrapingException {
this.doScrapeComic(apiKey, issueId, skipCache, comic);
this.imprintAdaptor.update(comic);
}

/**
* Must be overridden by child classes to provide the actual scraping implementation.
*
* @param apiKey the api key
* @param issueId the issue id
* @param skipCache the skip cache flag
* @param comic the comic
* @throws ScrapingException if an error occurs
*/
protected abstract void doScrapeComic(
final String apiKey, final String issueId, final Boolean skipCache, final Comic comic)
throws ScrapingException;

@Override
public ScrapingIssue getIssue(
final String apiKey, final Integer volume, final String issueNumber, final boolean skipCache)
throws ScrapingException {
String issue = issueNumber;
while (!issue.isEmpty()
&& !issue.equals("0")
&& "123456789%ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(issue.toUpperCase().substring(0, 1))
== -1) {
issue = issue.substring(1);
}

return this.doGetIssue(apiKey, volume, issue, skipCache);
}

/**
* Must be overridden by child classes to provide the actual issue fetching implementation.
*
* @param apiKey the api key
* @param volume the vname
* @param issueNumber the issue number
* @param skipCache the skip cache flag
* @return the issue
* @throws ScrapingException
*/
protected abstract ScrapingIssue doGetIssue(
final String apiKey, final Integer volume, final String issueNumber, final boolean skipCache)
throws ScrapingException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* 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 <http://www.gnu.org/licenses>
*/

package org.comixedproject.scrapers.adaptors;

import java.util.HashMap;
import java.util.Map;
import lombok.extern.log4j.Log4j2;
import org.comixedproject.model.comic.Comic;
import org.springframework.stereotype.Component;

/**
* <code>ImprintAdaptor</code> checks if a comic's publisher is an imprint and, if so, updates it
* accordingly.
*
* @author Darryl L. Pierce
*/
@Component
@Log4j2
public class ImprintAdaptor {
private static final Map<String, String> IMPRINT_MAPPINGS = new HashMap<>();

static {
IMPRINT_MAPPINGS.put("2000AD", "DC Comics");
IMPRINT_MAPPINGS.put("Adventure", "Malibu");
IMPRINT_MAPPINGS.put("Aircel Publishing", "Malibu");
IMPRINT_MAPPINGS.put("America's Best Comics", "DC Comics");
IMPRINT_MAPPINGS.put("Wildstorm", "DC Comics");
IMPRINT_MAPPINGS.put("Antimatter", "Amryl Entertainment");
IMPRINT_MAPPINGS.put("Apparat", "Avatar Press");
IMPRINT_MAPPINGS.put("Black Bull", "Wizard");
IMPRINT_MAPPINGS.put("Blu Manga", "Tokyopop");
IMPRINT_MAPPINGS.put("Chaos! Comics", "Dynamite Entertainment");
IMPRINT_MAPPINGS.put("Cliffhanger", "DC Comics");
IMPRINT_MAPPINGS.put("CMX", "DC Comics");
IMPRINT_MAPPINGS.put("Comic Bom Bom", "Kodansha");
IMPRINT_MAPPINGS.put("ComicsLit", "Nbm");
IMPRINT_MAPPINGS.put("Curtis Magazines", "Marvel");
IMPRINT_MAPPINGS.put("Dark Horse Books", "Dark Horse Comics");
IMPRINT_MAPPINGS.put("Dark Horse Manga", "Dark Horse Comics");
IMPRINT_MAPPINGS.put("Desperado Publishing", "Image");
IMPRINT_MAPPINGS.put("Epic", "Marvel");
IMPRINT_MAPPINGS.put("Eternity", "Malibu");
IMPRINT_MAPPINGS.put("Focus", "DC Comics");
IMPRINT_MAPPINGS.put("Helix", "DC Comics");
IMPRINT_MAPPINGS.put("Hero Comics", "Heroic Publishing");
IMPRINT_MAPPINGS.put("Homage comics", "DC Comics");
IMPRINT_MAPPINGS.put("Hudson Street Press", "Penguin Group");
IMPRINT_MAPPINGS.put("Icon Comics", "Marvel");
IMPRINT_MAPPINGS.put("Impact", "DC Comics");
IMPRINT_MAPPINGS.put("Jets Comics", "Hakusensha");
IMPRINT_MAPPINGS.put("KiZoic", "Ape Entertainment");
IMPRINT_MAPPINGS.put("Marvel Digital Comics Unlimited", "Marvel");
IMPRINT_MAPPINGS.put("Marvel Knights", "Marvel");
IMPRINT_MAPPINGS.put("Marvel Music", "Marvel");
IMPRINT_MAPPINGS.put("Marvel Soleil", "Marvel");
IMPRINT_MAPPINGS.put("Marvel UK", "Marvel");
IMPRINT_MAPPINGS.put("Maverick", "Dark Horse Comics");
IMPRINT_MAPPINGS.put("Max", "Marvel");
IMPRINT_MAPPINGS.put("Milestone", "DC Comics");
IMPRINT_MAPPINGS.put("Minx", "DC Comics");
IMPRINT_MAPPINGS.put("Papercutz", "Nbm");
IMPRINT_MAPPINGS.put("Paradox Press", "DC Comics");
IMPRINT_MAPPINGS.put("Piranha Press", "DC Comics");
IMPRINT_MAPPINGS.put("Razorline", "Marvel");
IMPRINT_MAPPINGS.put("ShadowLine", "Image");
IMPRINT_MAPPINGS.put("Sin Factory Comix", "Radio Comix");
IMPRINT_MAPPINGS.put("Skybound", "Image");
IMPRINT_MAPPINGS.put("Slave Labor", "Slg Publishing");
IMPRINT_MAPPINGS.put("Star Comics", "Marvel");
IMPRINT_MAPPINGS.put("Tangent Comics", "DC Comics");
IMPRINT_MAPPINGS.put("Tokuma Comics", "Tokuma Shoten");
IMPRINT_MAPPINGS.put("Ultraverse", "Malibu");
IMPRINT_MAPPINGS.put("Vertigo", "DC Comics");
IMPRINT_MAPPINGS.put("Zuda Comics", "DC Comics");
}

/**
* Updates the given comic's publisher and imprint if necessary.
*
* @param comic the comic
*/
public void update(final Comic comic) {
final String publisher = comic.getPublisher();
if (this.IMPRINT_MAPPINGS.containsKey(publisher)) {
final String imprint = IMPRINT_MAPPINGS.get(publisher);
log.debug("Updating publisher and imprint: {} => {}", publisher, imprint);
comic.setPublisher(imprint);
comic.setImprint(publisher);
} else {
log.debug("Clearing imprint field: publisher={}", publisher);
comic.setImprint("");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,22 +232,4 @@ protected String getMaskedApiKey() {
}
return this.maskedApiKey;
}

/**
* Creates a consistent {@link WebClient} instance to use for requests.
*
* @param url the url
* @return the instance
*/
protected WebClient createWebClient(final String url) {
log.debug("Creating web client: url={}", url);
return WebClient.builder()
.baseUrl(url)
.defaultHeaders(
headers -> {
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
headers.add(HttpHeaders.USER_AGENT, "ComiXed/0.7");
})
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public List<ScrapingVolume> getVolumes(
}

@Override
public ScrapingIssue getIssue(
public ScrapingIssue doGetIssue(
final String apiKey, final Integer volume, final String issueNumber, final boolean skipCache)
throws ScrapingException {
log.debug(
Expand Down Expand Up @@ -163,7 +163,7 @@ public ScrapingIssue getIssue(
}

@Override
public void scrapeComic(
public void doScrapeComic(
final String apiKey, final String issueId, final Boolean skipCache, final Comic comic)
throws ScrapingException {
ScrapingIssueDetails details = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* 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 <http://www.gnu.org/licenses>
*/

package org.comixedproject.scrapers.adaptors;

import org.comixedproject.model.comic.Comic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ImprintAdaptorTest {
private static final String TEST_PUBLISHER = "Marvel";
private static final String TEST_IMPRINT = "Marvel Soleil";

@InjectMocks private ImprintAdaptor imprintAdaptor;
@Mock private Comic comic;

@Test
public void testComicWithImprint() {
Mockito.when(comic.getPublisher()).thenReturn(TEST_IMPRINT);

imprintAdaptor.update(comic);

Mockito.verify(comic, Mockito.times(1)).setImprint(TEST_IMPRINT);
Mockito.verify(comic, Mockito.times(1)).setPublisher(TEST_PUBLISHER);
}

@Test
public void testComicWithoutImprint() {
Mockito.when(comic.getPublisher()).thenReturn(TEST_PUBLISHER);

imprintAdaptor.update(comic);

Mockito.verify(comic, Mockito.times(1)).setImprint("");
Mockito.verify(comic, Mockito.never()).setPublisher(Mockito.anyString());
}
}

0 comments on commit bc55000

Please sign in to comment.