Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/sales.git
database/vendors/list.csv
1,Thrifty Foods,com_thriftyfoods
2,Safeway,ca_safeway
-3,Costco,ca_costco
-4,Hudson's Bay,com_thebay
+3,Hudson's Bay,com_thebay
+4,Costco,ca_costco
install.sh
-#!/bin/bash
-
-# Installs the Web Harvest Java archive.
-mvn install:install-file -Dfile=lib/web-harvest/webharvest-2.1.jar -DgroupId=org.webharvest -DartifactId=webharvest -Dversion=2.1 -Dpackaging=jar
lib/web-harvest/install.sh
+#!/bin/bash
+mvn install:install-file -Dfile=webharvest-2.1.jar -DgroupId=net.sourceforge.web-harvest -DartifactId=webharvest -Dversion=2.1 -Dpackaging=jar
+
nbactions.xml
</properties>
</action>
+ <action>
+ <actionName>run</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
+ </goals>
+ <properties>
+ <exec.args>-classpath %classpath ${packageClassName}</exec.args>
+ <exec.executable>java</exec.executable>
+ </properties>
+ </action>
+ <action>
+ <actionName>debug</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
+ </goals>
+ <properties>
+ <exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args>
+ <exec.executable>java</exec.executable>
+ <jpda.listen>true</jpda.listen>
+ </properties>
+ </action>
+ <action>
+ <actionName>profile</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
+ </goals>
+ <properties>
+ <exec.args>-classpath %classpath ${packageClassName}</exec.args>
+ <exec.executable>java</exec.executable>
+ </properties>
+ </action>
</actions>
pom.xml
</dependency>
<dependency>
- <groupId>org.webharvest</groupId>
- <artifactId>webharvest</artifactId>
- <version>2.1</version>
- </dependency>
- <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.pegdown</groupId>
+ <artifactId>pegdown</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>9.4.1208</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>4.3.1.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.web-harvest</groupId>
+ <artifactId>webharvest</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>unknown.binary</groupId>
+ <artifactId>hibernate-jpamodelgen-4.3.1.Final</artifactId>
+ <version>SNAPSHOT</version>
+ <scope>provided</scope>
</dependency>
</dependencies>
</build>
<name>Sales</name>
+ <repositories>
+ <repository>
+ <id>unknown-jars-temp-repo</id>
+ <name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace the dependencies in this repository with correct ones and delete this repository.</name>
+ <url>file:${project.basedir}/lib</url>
+ </repository>
+ </repositories>
</project>
src/main/java/com/whitemagicsoftware/notify/HttpTransport.java
} catch( UnsupportedEncodingException e ) {
throw new MessagingException(
- "Invalid default encoding: '" + charset + "'", e );
+ "Encoding not found: '" + charset + "'", e );
}
}
src/main/java/com/whitemagicsoftware/sales/BusinessEntity.java
package com.whitemagicsoftware.sales;
+import java.io.Serializable;
+
/**
* Provides fluent interface for constructing objects.
*
* @author White Magic Software, Ltd.
*/
-public class BusinessEntity {
+public class BusinessEntity implements Serializable {
/**
src/main/java/com/whitemagicsoftware/sales/Main.java
import java.util.List;
import javax.mail.internet.AddressException;
-import org.apache.log4j.BasicConfigurator;
import org.webharvest.definition.ScraperConfiguration;
import org.webharvest.runtime.Scraper;
private final static String DIRECTORY_PREFIX = "scrape";
- private void Main() {
- }
+ private final static int DEFAULT_STRING_SIZE = 8192;
- private void run() throws Exception {
- process( getSubscribers() );
+ private void Main() {
}
- private void process(
- List<Subscriber> subscribers ) {
- for( Subscriber subscriber : subscribers ) {
+ /**
+ * Iterates over all the subscribers to notify them of when their
+ * subscriptions are ready.
+ *
+ * @throws Exception
+ */
+ private void process() throws Exception {
+ for( Subscriber subscriber : getSubscribers() ) {
try {
- String message = process( subscriber, getVendors( subscriber ) );
- notify( subscriber, message );
+ notify( subscriber, process( subscriber ) );
} catch( Exception e ) {
notify( e );
}
}
}
- private String process( Subscriber subscriber, List<Vendor> vendors )
+ private String process( Subscriber subscriber )
throws Exception {
- StringBuilder result = new StringBuilder();
+ StringBuilder result = new StringBuilder( DEFAULT_STRING_SIZE );
- for( Vendor vendor : vendors ) {
- String s = process( subscriber, vendor, getProducts( subscriber, vendor ) );
- result.append( s );
+ for( Vendor vendor : getVendors() ) {
+ result.append( process( subscriber, vendor ) );
}
return result.toString();
}
- private String process(
- Subscriber subscriber,
- Vendor vendor,
- List<Product> products ) throws Exception {
- StringBuilder result = new StringBuilder();
+ private String process( Subscriber subscriber, Vendor vendor )
+ throws Exception {
+ StringBuilder result = new StringBuilder( DEFAULT_STRING_SIZE );
// Include the vendor name on the first products loop iteration.
boolean includeVendorName = true;
- for( Product product : products ) {
+ for( Product product : getProducts( subscriber, vendor ) ) {
Scraper scraper = getScraper( vendor );
- scraper.addVariableToContext( "includeVendorName", includeVendorName );
- scraper.addVariableToContext( "locationCode", subscriber.getLocationCode() );
- scraper.addVariableToContext( "vendorName", vendor.getName() );
- scraper.addVariableToContext( "productName", product.getName() );
- scraper.addVariableToContext( "productPath", product.getUrlPath() );
+ scraper.addVariableToContext( "vendor_name_include", includeVendorName );
+ scraper.addVariableToContext( "location_code", subscriber.getLocationCode() );
+ scraper.addVariableToContext( "product_name", product.getName() );
+ scraper.addVariableToContext( "product_path", product.getUrlPath() );
scraper.execute();
- Variable message = scraper.getContext().getVar( "message" );
+ Variable message = scraper.getContext().getVar( "message_body" );
result.append( message.toString() );
/**
* Root directory containing scripts/ and templates/ directories.
- *
+ *
* @return A non-null String to the resources' root directory.
*/
}
- private List<Vendor> getVendors( Subscriber subscriber ) {
- return getVendorService().list( subscriber );
+ private List<Vendor> getVendors() throws Exception {
+ return getVendorService().list();
}
private List<Product> getProducts( Subscriber subscriber, Vendor vendor ) {
return getProductService().list( subscriber, vendor );
}
+ /**
+ * Run with <code>-Djavax.persistence.jdbc.password=</code> set to the
+ * database password.
+ *
+ * @param args Unused.
+ *
+ * @throws Exception Failed to notify subscribers of product sales.
+ */
public static void main( String args[] ) throws Exception {
- new Main().run();
+ new Main().process();
}
}
src/main/java/com/whitemagicsoftware/sales/Subscriber.java
package com.whitemagicsoftware.sales;
-import java.util.List;
+import java.util.Date;
import javax.mail.Address;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
/**
* Represents a subscriber to receive notifications of sales.
*
* @author White Magic Software, Ltd.
*/
-public final class Subscriber extends BusinessEntity {
+@Entity
+@Table( name = "subscriber" )
+public class Subscriber extends BusinessEntity {
+
+ private static final long serialVersionUID = 0L;
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ private int id;
/* Notifications are sent to this address. */
private Address address;
-
+
+ /* Required by JPA. */
+ @Column( name = "email" )
+ private String email;
+
/* Postal code, zip code, etc. */
+ @Column( name = "location_code" )
private String locationCode;
- private Subscriber() {
+ /* When the subscriber was added. */
+ @Temporal( javax.persistence.TemporalType.DATE )
+ private Date added;
+
+ /* When the subscriber information was updated. */
+ @Temporal( javax.persistence.TemporalType.DATE )
+ private Date updated;
+
+ protected Subscriber() {
}
- public Address getAddress() {
+ /**
+ * Returns a unique identifier for this subscriber.
+ *
+ * @return The primary key, or 0 if not initialized.
+ */
+ private int getId() {
+ return this.id;
+ }
+
+ /**
+ * Returns the address (usually e-mail) for message delivery to this
+ * subscriber.
+ *
+ * @return Usually an InternetAddress instance, never null.
+ *
+ * @throws AddressException Could not parse the e-mail address.
+ */
+ protected synchronized Address getAddress() throws AddressException {
+ if( this.address == null ) {
+ this.address = createAddress();
+ }
+
return this.address;
}
- protected void setAddress(Address address) {
+ /**
+ * Returns the e-mail address associated with the subscriber.
+ *
+ * @return A non-null, possibly empty, instance.
+ */
+ private synchronized String getEmail() {
+ return nullSafe(this.email);
+ }
+
+ /**
+ * Used by the builder to set the address.
+ *
+ * @param address
+ */
+ protected void setAddress( Address address ) {
this.address = address;
}
-
- protected void setLocationCode(String locationCode){
+
+ protected void setLocationCode( String locationCode ) {
this.locationCode = locationCode;
}
-
- public String getLocationCode() {
- return this.locationCode;
+
+ protected String getLocationCode() {
+ return nullSafe(this.locationCode);
+ }
+
+ protected Address createAddress() throws AddressException {
+ return new InternetAddress( getEmail() );
}
}
- public Builder withAddress(Address address) {
- getObject().setAddress(address);
+ public Builder withAddress( Address address ) {
+ getObject().setAddress( address );
return getBuilder();
}
-
- public Builder withLocationCode(String locationCode) {
- getObject().setLocationCode(locationCode);
+
+ public Builder withLocationCode( String locationCode ) {
+ getObject().setLocationCode( locationCode );
return getBuilder();
}
src/main/java/com/whitemagicsoftware/sales/Subscription.java
-/*
- * The MIT License
- *
- * Copyright 2016 White Magic Software, Ltd..
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package com.whitemagicsoftware.sales;
-
-/**
- * Associates a product with a subscriber.
- *
- * @author White Magic Software, Ltd.
- */
-public final class Subscription {
-
- private Subscriber subscriber;
- private Product product;
-
- public Subscription() {
-
- }
-}
src/main/java/com/whitemagicsoftware/sales/Vendor.java
package com.whitemagicsoftware.sales;
-import java.net.URL;
-
/**
* Represents a company that sells products.
*
* @author White Magic Software, Ltd.
*/
public final class Vendor extends BusinessEntity {
-
- /**
- * Vendor web site.
- */
- private URL webSite;
-
/**
* The script to run for this Vendor.
}
+ /**
+ * Script name must have an extension of .xml.
+ *
+ * @param scriptName The name of the script for a particular vendor.
+ * @return this.builder
+ */
public Builder withScriptName(String scriptName) {
- getObject().setScriptName(scriptName + ".xml");
+ getObject().setScriptName(scriptName);
return getBuilder();
}
src/main/java/com/whitemagicsoftware/sales/service/SubscriberService.java
import com.whitemagicsoftware.sales.Subscriber;
import java.util.List;
-import javax.mail.Message;
import javax.mail.internet.AddressException;
src/main/java/com/whitemagicsoftware/sales/service/VendorService.java
package com.whitemagicsoftware.sales.service;
-import com.whitemagicsoftware.sales.Subscriber;
import com.whitemagicsoftware.sales.Vendor;
import java.util.List;
/**
- * Returns a list of all vendors for a given subscriber.
+ * Returns a list of all vendors.
*
- * @param subscriber The subscriber that would like to be notified when vendor
- * products go on sale.
* @return A non-null list, possibly empty.
+ * @throws java.lang.Exception Could not obtain the vendor list.
*/
- public List<Vendor> list(Subscriber subscriber);
+ public List<Vendor> list() throws Exception;
}
src/main/java/com/whitemagicsoftware/sales/service/impl/NotifyServiceImpl.java
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
+import org.pegdown.PegDownProcessor;
/**
String textBody = markdown;
String htmlBody = toHtml( markdown );
-
+
Message message = createMessage( recipient, subject );
message.setContent( new TextHtmlMultipart( textBody, htmlBody ) );
*/
private String toHtml( String markdown ) {
- return markdown;
+ return new PegDownProcessor().markdownToHtml( markdown );
}
src/main/java/com/whitemagicsoftware/sales/service/impl/ServiceImpl.java
import com.whitemagicsoftware.sales.service.Service;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
/**
* Superclass to all services.
- *
+ *
* @author White Magic Software, Ltd.
* @param <T> The type of classes this service returns.
*/
public abstract class ServiceImpl<T> implements Service {
-
+
+ private final static String PERSISTENCE_PASSWORD_PROPERTY
+ = "javax.persistence.jdbc.password";
+
/**
* Creates a new list of items.
- * @return
+ *
+ * @return
*/
protected List<T> createList() {
return new ArrayList<>();
+ }
+
+ protected CriteriaBuilder getCriteriaBuilder() {
+ return getEntityManager().getCriteriaBuilder();
+ }
+
+ private EntityManager getEntityManager() {
+ return getEntityManagerFactory().createEntityManager();
+ }
+
+ private EntityManagerFactory getEntityManagerFactory() {
+ return Persistence.createEntityManagerFactory( getPersistenceUnitName(),
+ getPersistenceProperties() );
+ }
+
+ private Map getPersistenceProperties() {
+ Map result = new HashMap();
+ result.put( PERSISTENCE_PASSWORD_PROPERTY, getPersistencePassword() );
+
+ return result;
+ }
+
+ private String getPersistencePassword() {
+ return nullSafe( System.getProperty( PERSISTENCE_PASSWORD_PROPERTY ) );
+ }
+
+ /**
+ * Returns the empty string if the given string is null.
+ *
+ * @param s The string to un-nullify.
+ *
+ * @return s or "" if s is null.
+ */
+ private String nullSafe( String s ) {
+ return s == null ? "" : s;
+ }
+
+ private String getPersistenceUnitName() {
+ return "SALES";
+ }
+
+ /**
+ * Returns the list of all entries in the database for the given class type.
+ *
+ * @param clazz The class that represents an entity that has rows in a table
+ * within a database.
+ *
+ * @return The instances of the class populated with data from the database.
+ */
+ protected List<T> getResultList( Class<T> clazz ) {
+ CriteriaBuilder cb = getCriteriaBuilder();
+ CriteriaQuery<T> cq = cb.createQuery( clazz );
+ Root<T> root = cq.from( clazz );
+
+ CriteriaQuery<T> all = cq.select( root );
+ TypedQuery<T> query = getEntityManager().createQuery( all );
+ return query.getResultList();
}
}
src/main/java/com/whitemagicsoftware/sales/service/impl/SubscriberServiceImpl.java
import java.util.List;
import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
/**
* @author White Magic Software, Ltd.
*/
public class SubscriberServiceImpl extends ServiceImpl<Subscriber>
implements SubscriberService {
@Override
public List<Subscriber> list() throws AddressException {
- List<Subscriber> result = createList();
+ return getResultList(Subscriber.class);
+ /*
Subscriber subscriber = new Subscriber.Builder()
- .withAddress(new InternetAddress("pennysavr@gmail.com"))
+ .withAddress( new InternetAddress( "Dave.Jarvis@gmail.com" ) )
.build();
- result.add(subscriber);
+ result.add( subscriber );
return result;
+ */
}
}
src/main/java/com/whitemagicsoftware/sales/service/impl/VendorServiceImpl.java
import com.whitemagicsoftware.sales.Vendor;
import com.whitemagicsoftware.sales.service.VendorService;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
import java.util.List;
/**
* @author White Magic Software, Ltd.
*/
public class VendorServiceImpl extends ServiceImpl<Vendor>
implements VendorService {
@Override
- public List<Vendor> list(Subscriber subscriber) {
+ public List<Vendor> list() throws Exception {
List<Vendor> result = createList();
- Vendor vendor = new Vendor.Builder()
- .withName("Thrifty Foods")
- .withScriptName("com_thriftyfoods")
- .build();
-
- result.add(vendor);
+ for( final String filename : getScriptFilenames() ) {
+ Vendor vendor = new Vendor.Builder()
+ .withScriptName( filename )
+ .build();
+
+ result.add( vendor );
+
+ }
return result;
+ }
+
+ private List<String> getScriptFilenames() throws IOException {
+ List<String> filenames = new ArrayList<>();
+
+ try(
+ InputStream in = getResourceAsStream( "scripts" );
+ BufferedReader br = new BufferedReader( new InputStreamReader( in ) ) ) {
+ String resource;
+
+ while( (resource = br.readLine()) != null ) {
+ filenames.add( resource );
+ }
+ }
+
+ return filenames;
+ }
+
+ private InputStream getResourceAsStream( String resource ) {
+ final InputStream in
+ = getContextClassLoader().getResourceAsStream( resource );
+
+ return in == null ? getClass().getResourceAsStream( resource ) : in;
+ }
+
+ private ClassLoader getContextClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
}
}
src/main/resources/META-INF/persistence.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
+ <persistence-unit name="SALES" transaction-type="RESOURCE_LOCAL">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+ <properties>
+ <property name="javax.persistence.jdbc.url" value="jdbc:postgresql:sales"/>
+ <property name="javax.persistence.jdbc.user" value="sales"/>
+ <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
+ <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
+ <property name="hibernate.hbm2ddl.auto" value="validate"/>
+
+ <!-- Password is set using a command line property of the same name. -->
+ <!--property name="javax.persistence.jdbc.password" value=""/-->
+ </properties>
+ </persistence-unit>
+</persistence>
src/main/resources/log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-<log4j:configuration debug="true"
- xmlns:log4j='http://jakarta.apache.org/log4j/'>
+<log4j:configuration debug="false"
+ xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern"
- value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
- </layout>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern"
+ value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+ </layout>
</appender>
<root>
<level value="WARN" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
-
src/main/resources/scripts/1_com_thriftyfoods.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<config charset="UTF-8">
+ <var-def name="vendor_url">https://www.thriftyfoods.com/product/</var-def>
+ <var-def name="vendor_name">Thrifty Foods</var-def>
+
+ <var-def name="product_page">
+ <html-to-xml outputtype="pretty" prunetags="script">
+ <http url="${vendor_url}${product_path}" />
+ </html-to-xml>
+ </var-def>
+
+ <var-def name="product_price">
+ <xpath expression="(//span[@class='price' and @itemprop='price'])[last()]/text()">
+ <var name="product_page" />
+ </xpath>
+ </var-def>
+
+ <var-def name="product_sale">
+ <xpath expression="boolean(//div[contains(@class,'on-sale') and @itemprop='offerDetails'])">
+ <var name="product_page" />
+ </xpath>
+ </var-def>
+
+ <var-def name="message_body">
+ <case>
+ <if condition="${vendor_name_include}">
+ <template>
+ <file path="templates/header-vendor-name.md"/>
+ </template>
+ </if>
+ </case>
+
+ <case>
+ <if condition="${product_sale}">
+ <template>
+ <file path="templates/header-product-name.md"/>
+ <file path="templates/product-price.md"/>
+ </template>
+ </if>
+ </case>
+ </var-def>
+</config>
src/main/resources/scripts/2_ca_safeway.xml
src/main/resources/scripts/3_com_thebay.xml
src/main/resources/scripts/com_thriftyfoods.xml
-<?xml version="1.0" encoding="UTF-8"?>
-<config charset="UTF-8">
-
- <var-def name="vendor">https://www.thriftyfoods.com/product/</var-def>
-
- <var-def name="page">
- <html-to-xml outputtype="pretty" prunetags="script">
- <http url="${vendor}${productPath}" />
- </html-to-xml>
- </var-def>
-
- <var-def name="price">
- <xpath expression="(//span[@class='price' and @itemprop='price'])[last()]/text()">
- <var name="page" />
- </xpath>
- </var-def>
-
- <var-def name="sale">
- <xpath expression="boolean(//div[contains(@class,'on-sale') and @itemprop='offerDetails'])">
- <var name="page" />
- </xpath>
- </var-def>
-
- <var-def name="message">
- <case>
- <if condition="${includeVendorName}">
- <template>
- <file path="templates/header-vendor-name.md"/>
- </template>
- </if>
- </case>
-
- <template>
- <file path="templates/header-product-name.md"/>
- <file path="templates/product-price.md"/>
- </template>
- </var-def>
-</config>
src/main/resources/templates/header-product-name.md
-${productName}
+${product_name}
----------------------------
-
-
src/main/resources/templates/header-vendor-name.md
-${vendorName}
+${vendor_name}
============================
src/main/resources/templates/product-description.md
-* ${productDescription}
+* ${product_description}
src/main/resources/templates/product-price.md
-${productName}
-----------------------------
-Sale price is ${productPrice}.
+Sale price is ${product_price}.
src/main/sql/functions/vendors.sql
+CREATE OR REPLACE FUNCTION vendors()
+ RETURNS TABLE(id integer, script text) AS
+$BODY$
+BEGIN
+ DROP TABLE IF EXISTS vendor_scripts;
+ CREATE TEMP TABLE vendor_scripts(id int, script text);
+ COPY vendor_scripts FROM PROGRAM 'find -L /home/sales/scripts -maxdepth 1 -type f -printf "%f\n" | sed s/_/,/' DELIMITER ',' CSV;
+ RETURN QUERY SELECT * FROM vendor_scripts ORDER BY id;
+END;
+$BODY$
+ LANGUAGE plpgsql VOLATILE SECURITY DEFINER
+ COST 100
+ ROWS 1000;
+ALTER FUNCTION vendors()
+ OWNER TO postgres;
+
src/test/java/com/whitemagicsoftware/sales/service/impl/SubscriberServiceImplTest.java
+/*
+ * The MIT License
+ *
+ * Copyright 2016 White Magic Software, Ltd..
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.whitemagicsoftware.sales.service.impl;
+
+import com.whitemagicsoftware.sales.Subscriber;
+import com.whitemagicsoftware.sales.service.SubscriberService;
+import java.util.List;
+import javax.mail.internet.AddressException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests the Subscriber Service.
+ *
+ * @author White Magic Software, Ltd.
+ */
+public class SubscriberServiceImplTest {
+
+ public SubscriberServiceImplTest() {
+ }
+
+ @Test
+ public void testSubscriberList() throws AddressException {
+ SubscriberService ss = getSubscriberService();
+ List<Subscriber> subscribers = ss.list();
+
+ Assert.assertTrue( "Could not obtain subscribers from database.",
+ subscribers.size() > 0 );
+ }
+
+ private SubscriberService getSubscriberService() {
+ return new SubscriberServiceImpl();
+ }
+}

Migrated CSV data into database. Added vendors function to emulate a table that dynamically retrieves the list of vendor scripts from the file system. Added persistence mechanism (JPA and Hibernate) to code. Added test for subscriber service. Code simplifications. Updated templates used to build notification message.

Author djarvis <email>
Date 2016-06-19 22:28:21 GMT-0700
Commit 79c2c12f06a2c204ddadc2e274685bf73543ad70
Parent 9e9d4ec
Delta 477 lines added, 183 lines removed, 294-line increase