diff --git a/appointment-import/.classpath b/appointment-import/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..f7b62f180179a74fbb8400870e0e56b071065daa
--- /dev/null
+++ b/appointment-import/.classpath
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/appointment-import/.gitignore b/appointment-import/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b83d22266ac8aa2f8df2edef68082c789727841d
--- /dev/null
+++ b/appointment-import/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/appointment-import/.project b/appointment-import/.project
new file mode 100644
index 0000000000000000000000000000000000000000..eee5e1c7df61a5e6865e9a8584bb8c4c4becfe6a
--- /dev/null
+++ b/appointment-import/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>appointment-import</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/appointment-import/.settings/org.eclipse.core.resources.prefs b/appointment-import/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..04cfa2c1a8566d64dd12fcf7a8e895fe02e1856f
--- /dev/null
+++ b/appointment-import/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/<project>=UTF-8
diff --git a/appointment-import/.settings/org.eclipse.jdt.core.prefs b/appointment-import/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..bbcbc93486d53c73668b275ea5daf41b66ba9c41
--- /dev/null
+++ b/appointment-import/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/appointment-import/.settings/org.eclipse.m2e.core.prefs b/appointment-import/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..14b697b7bbb0d85e8d8ee19141a2a92d9ce211be
--- /dev/null
+++ b/appointment-import/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/appointment-import/pom.xml b/appointment-import/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d1022957a5e69e7f4f44fd69f5d63c9751999fcf
--- /dev/null
+++ b/appointment-import/pom.xml
@@ -0,0 +1,67 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>smash</groupId>
+  <artifactId>appointment-import</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>appointment-import</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+<dependency>
+    <groupId>log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>1.2.17</version>
+</dependency>
+<dependency>
+    <groupId>org.apache.poi</groupId>
+    <artifactId>poi-ooxml</artifactId>
+    <version>3.15</version>
+</dependency>    
+  </dependencies>
+  
+	<build>
+		<plugins>
+<plugin>
+    <groupId>org.apache.maven.plugins</groupId>
+    <artifactId>maven-compiler-plugin</artifactId>
+    <version>3.5.1</version>
+    <configuration>
+        <source>1.7</source>
+        <target>1.7</target>
+    </configuration>
+</plugin> 		
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+        	<source>1.7</source>
+        	<target>1.7</target>
+        	<archive>
+						<manifest>
+             <addClasspath>true</addClasspath>
+          	 <mainClass>smash.appointment.parse.Main</mainClass>
+						</manifest>
+					</archive>
+					<descriptorRefs>
+          <descriptorRef>jar-with-dependencies</descriptorRef>
+        </descriptorRefs>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	  
+</project>
diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..8db01548e360c728d0b554e32b4c23eb29ad4203
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentEntry.java
@@ -0,0 +1,99 @@
+package smash.appointment.parse;
+
+public class AppointmentEntry {
+	private String					day;
+	private String					time;
+	private Subject					subject;
+	private AppointmentType	type;
+	private String					source;
+
+	/**
+	 * @return the time
+	 * @see #time
+	 */
+	public String getTime() {
+		return time;
+	}
+
+	/**
+	 * @param time
+	 *          the time to set
+	 * @see #time
+	 */
+	public void setTime(String time) {
+		this.time = time;
+	}
+
+	/**
+	 * @return the subject
+	 * @see #subject
+	 */
+	public Subject getSubject() {
+		return subject;
+	}
+
+	/**
+	 * @param subject
+	 *          the subject to set
+	 * @see #subject
+	 */
+	public void setSubject(Subject subject) {
+		this.subject = subject;
+	}
+
+	/**
+	 * @return the type
+	 * @see #type
+	 */
+	public AppointmentType getType() {
+		return type;
+	}
+
+	/**
+	 * @param type
+	 *          the type to set
+	 * @see #type
+	 */
+	public void setType(AppointmentType type) {
+		this.type = type;
+	}
+
+	/**
+	 * @return the day
+	 * @see #day
+	 */
+	public String getDay() {
+		return day;
+	}
+
+	/**
+	 * @param day
+	 *          the day to set
+	 * @see #day
+	 */
+	public void setDay(String day) {
+		this.day = day;
+	}
+
+	/**
+	 * @return the source
+	 * @see #source
+	 */
+	public String getSource() {
+		return source;
+	}
+
+	/**
+	 * @param source
+	 *          the source to set
+	 * @see #source
+	 */
+	public void setSource(String source) {
+		this.source = source;
+	}
+
+	@Override
+	public String toString() {
+		return day + " " + time + " " + subject + " " + type + "\t\t[source: " + source + "]";
+	}
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java b/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb60199f8bc60e7e03a4b872dc016388e9ef70d3
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/AppointmentType.java
@@ -0,0 +1,34 @@
+package smash.appointment.parse;
+
+public enum AppointmentType {
+	
+	//most complex should be first
+	LEVEL_BV_BG_SB(new String[] { "evel BV + BG + SB","BV + BG + SB" }), // 
+	LEVEL_BV_SB(new String[] { "evel BV + SB","BV + SB" }), //
+	LEVEL_BV_BG(new String[] { "evel BV + BG","BV + BG" }), //
+	LEVEL_BG_SB(new String[] { "evel BG + SB","BG + SB" }), //
+	LEVEL_BV(new String[] { "evel BV", "BV" }), //
+	LEVEL_BG(new String[] { "evel BG","BG" }), //
+	LEVEL_SB(new String[] { "evel SB", "SB" }), //
+	
+	LEVEL_A(new String[] { "level A" }), //
+	OTHER(new String[] {}), //
+	LEVEL_B(new String[] { "evel B" }), //
+	LEVEL_B_M_POWER(new String[] { "mPower" }), // 
+	;
+
+	private String[] queryStrings;
+
+	private AppointmentType(String[] queryStrings) {
+		this.queryStrings = queryStrings;
+
+	}
+
+	/**
+	 * @return the queryStrings
+	 * @see #queryStrings
+	 */
+	public String[] getQueryStrings() {
+		return queryStrings;
+	}
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/CellParser.java b/appointment-import/src/main/java/smash/appointment/parse/CellParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..efc2e38837080ba883a10342b79f2092d8bae27d
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/CellParser.java
@@ -0,0 +1,158 @@
+package smash.appointment.parse;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+public class CellParser {
+	Logger						 logger			 = Logger.getLogger(CellParser.class);
+
+	private SubjectDao subjectDao;
+
+	Pattern						 timePattern = Pattern.compile("^[0-9][0-9]\\:[0-9][0-9]");
+
+	public String extractTime(String content) {
+		String result = null;
+		Matcher matcher = timePattern.matcher(content);
+		if (matcher.find()) {
+			result = matcher.group();
+		}
+		return result;
+	}
+
+	public String removeTime(String content) {
+		Matcher matcher = timePattern.matcher(content);
+		if (matcher.find()) {
+			content = matcher.replaceFirst("").trim();
+		}
+		return content;
+	}
+
+	public AppointmentEntry parseAppointment(String query, String defaultTime) {
+		AppointmentEntry result = new AppointmentEntry();
+
+		String time = extractTime(query);
+		if (time != null) {
+			query = removeTime(query);
+		} else {
+			time = defaultTime;
+		}
+		result.setTime(time);
+
+		Subject subject = extractSubject(query);
+		result.setSubject(subject);
+
+		AppointmentType type = extractType(query);
+		if (type == null) {
+			type = AppointmentType.OTHER;
+		}
+		result.setType(type);
+
+		result.setSource(query);
+		return result;
+	}
+
+	private AppointmentType extractType(String query) {
+		String simplifiedQuery = Utils.simplifyString(query);
+
+		AppointmentType result = null;
+
+		String usedString = null;
+		for (AppointmentType type : AppointmentType.values()) {
+			boolean matchFound = false;
+			for (String string : type.getQueryStrings()) {
+				if (!matchFound) {
+					String simplifiedString = Utils.simplifyString(string);
+
+					if (simplifiedQuery.contains(simplifiedString)) {
+						matchFound = true;
+						if (result == null) {
+							result = type;
+							usedString = string;
+						} else {
+							if (string.contains(usedString)) {
+								result = type;
+								usedString = string;
+							} else if (usedString.contains(string)) {
+								//new one is a substring of old
+							} else { //if there is no substring then we might have a problem
+								AppointmentType newType = result;
+								if (usedString.length() < string.length()) {
+									result = type;
+									usedString = string;
+								}
+								logger.warn("More than one type possible for query: " + query + ". Type 1: " + result + ". Type 2: " + type + ". Choosing: " + newType);
+							}
+						}
+
+					}
+				}
+			}
+		}
+		return result;
+	}
+
+	private Subject extractSubject(String query) {
+		Subject result = null;
+		String simplifiedQuery = Utils.simplifyString(query);
+
+		SubjectIndexer[] mainIndices = new SubjectIndexer[] { //
+				new NameSurnameIndexer(), //
+				new SurnameNameIndexer(), //
+				new NdNumberIndexer(),//
+		};
+
+		result = getByIndices(query, simplifiedQuery, mainIndices);
+		if (result == null) {
+			SubjectIndexer[] secondaryIndices = new SubjectIndexer[] { //
+					new SurnameIndexer(), //
+			};
+			result = getByIndices(query, simplifiedQuery, secondaryIndices);
+		}
+		return result;
+	}
+
+	private Subject getByIndices(String query, String simplifiedQuery, SubjectIndexer[] mainIndices) {
+		Subject result = null;
+		for (Subject subject : subjectDao.getSubjects()) {
+			boolean matchFound = false;
+			for (SubjectIndexer indexer : mainIndices) {
+				if (!matchFound) {
+					if (indexer.match(subject, simplifiedQuery)) {
+						matchFound = true;
+						if (result == null) {
+							result = subject;
+						} else {
+							Subject newResult = result;
+							if (indexer.isBetter(subject, result)) {
+								newResult = subject;
+							}
+							logger.warn(
+									"More than one subject possible for query: " + query + ". Subject 1: " + result + ". Subject 2: " + subject + ". Choosing: " + newResult);
+							result = newResult;
+						}
+					}
+				}
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * @return the subjectDao
+	 * @see #subjectDao
+	 */
+	public SubjectDao getSubjectDao() {
+		return subjectDao;
+	}
+
+	/**
+	 * @param subjectDao
+	 *          the subjectDao to set
+	 * @see #subjectDao
+	 */
+	public void setSubjectDao(SubjectDao subjectDao) {
+		this.subjectDao = subjectDao;
+	}
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/Main.java b/appointment-import/src/main/java/smash/appointment/parse/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..051de5b0c3a5f0019c10fa02654ccf525a684f75
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/Main.java
@@ -0,0 +1,27 @@
+package smash.appointment.parse;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class Main {
+	private static Logger logger = Logger.getLogger(Main.class);
+
+	public static void main(String[] args) throws Exception {
+		if (args.length < 2) {
+			System.out.println("Usage: command <agenda.xlsx> <subjects.txt>");
+		} else {
+			SubjectDao subjectDao = new SubjectDao();
+			subjectDao.readFile(args[1]);
+
+			XlsxCalendarProcessor processor = new XlsxCalendarProcessor();
+			processor.setSubjectDao(subjectDao);
+
+			List<AppointmentEntry> entries = processor.processExcel(args[0]);
+			for (AppointmentEntry appointmentEntry : entries) {
+				logger.debug(appointmentEntry);
+			}
+		}
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/NameSurnameIndexer.java b/appointment-import/src/main/java/smash/appointment/parse/NameSurnameIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ed820ad2e1b4609692d2eaf7233254a6784c0fc
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/NameSurnameIndexer.java
@@ -0,0 +1,9 @@
+package smash.appointment.parse;
+
+public class NameSurnameIndexer extends SubjectIndexer {
+
+	public String getIndexedString(Subject subject) {
+		return Utils.simplifyString(subject.getName() + subject.getSurname());
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/NdNumberIndexer.java b/appointment-import/src/main/java/smash/appointment/parse/NdNumberIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd96a4a1a0149f439d7436689350279fe87d92fb
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/NdNumberIndexer.java
@@ -0,0 +1,9 @@
+package smash.appointment.parse;
+
+public class NdNumberIndexer extends SubjectIndexer {
+
+	public String getIndexedString(Subject subject) {
+		return Utils.simplifyString(subject.getNdNumber());
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/Subject.java b/appointment-import/src/main/java/smash/appointment/parse/Subject.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee5cc02d6c6d686db57fa55826d001e0326c2526
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/Subject.java
@@ -0,0 +1,89 @@
+package smash.appointment.parse;
+
+public class Subject {
+	private String name;
+	private String surname;
+	private String ndNumber;
+	private String screeningNumber;
+
+	public Subject(String name, String surname, String ndNumber, String screeningNumber) {
+		this.name = name;
+		this.surname = surname;
+		this.ndNumber = ndNumber;
+		this.screeningNumber = screeningNumber;
+	}
+
+	/**
+	 * @return the name
+	 * @see #name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @param name
+	 *          the name to set
+	 * @see #name
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * @return the surname
+	 * @see #surname
+	 */
+	public String getSurname() {
+		return surname;
+	}
+
+	/**
+	 * @param surname
+	 *          the surname to set
+	 * @see #surname
+	 */
+	public void setSurname(String surname) {
+		this.surname = surname;
+	}
+
+	/**
+	 * @return the ndNumber
+	 * @see #ndNumber
+	 */
+	public String getNdNumber() {
+		return ndNumber;
+	}
+
+	/**
+	 * @param ndNumber
+	 *          the ndNumber to set
+	 * @see #ndNumber
+	 */
+	public void setNdNumber(String ndNumber) {
+		this.ndNumber = ndNumber;
+	}
+
+	/**
+	 * @return the screeningNumber
+	 * @see #screeningNumber
+	 */
+	public String getScreeningNumber() {
+		return screeningNumber;
+	}
+
+	/**
+	 * @param screeningNumber
+	 *          the screeningNumber to set
+	 * @see #screeningNumber
+	 */
+	public void setScreeningNumber(String screeningNumber) {
+		this.screeningNumber = screeningNumber;
+	}
+
+	@Override
+	public String toString() {
+		return this.getName() + " " + this.getSurname() + " (" + this.getNdNumber() + "; " + this.getScreeningNumber() + ")";
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java b/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..c852c84c6242cc2004c0af2184a8db68e4bb74bc
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/SubjectDao.java
@@ -0,0 +1,43 @@
+package smash.appointment.parse;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SubjectDao {
+	private List<Subject> subjects = new ArrayList<Subject>();
+
+	public void addSubject(Subject subject) {
+		subjects.add(subject);
+	}
+
+	public void readFile(String filename) throws IOException {
+		try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
+			String line;
+			while ((line = br.readLine()) != null) {
+				String tmp[] = line.split("\t");
+				addSubject(new Subject(tmp[0], tmp[1], tmp[2], tmp[3]));
+			}
+		}
+	}
+
+	/**
+	 * @return the subjects
+	 * @see #subjects
+	 */
+	public List<Subject> getSubjects() {
+		return subjects;
+	}
+
+	/**
+	 * @param subjects
+	 *          the subjects to set
+	 * @see #subjects
+	 */
+	public void setSubjects(List<Subject> subjects) {
+		this.subjects = subjects;
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/SubjectIndexer.java b/appointment-import/src/main/java/smash/appointment/parse/SubjectIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff89c24faad3525f473abdc8d8ca9c1fdaf4ab10
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/SubjectIndexer.java
@@ -0,0 +1,22 @@
+package smash.appointment.parse;
+
+import org.apache.log4j.Logger;
+
+public abstract class SubjectIndexer {
+	Logger logger = Logger.getLogger(SubjectIndexer.class);
+
+	public abstract String getIndexedString(Subject subject);
+
+	public boolean match(Subject subject, String simplifiedQuery) {
+		String indexedString = getIndexedString(subject);
+//		logger.debug("Check: " + simplifiedQuery + " against: " + indexedString);
+		if (simplifiedQuery.startsWith(indexedString)) {
+			return true;
+		}
+		return false;
+	}
+
+	public boolean isBetter(Subject subject, Subject oldSubject) {
+		return getIndexedString(subject).length()>getIndexedString(oldSubject).length();
+	}
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/SurnameIndexer.java b/appointment-import/src/main/java/smash/appointment/parse/SurnameIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..10c1a29f3e39236ed33c0c819e6a30d4420de99c
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/SurnameIndexer.java
@@ -0,0 +1,9 @@
+package smash.appointment.parse;
+
+public class SurnameIndexer extends SubjectIndexer {
+
+	public String getIndexedString(Subject subject) {
+		return Utils.simplifyString(subject.getSurname());
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/SurnameNameIndexer.java b/appointment-import/src/main/java/smash/appointment/parse/SurnameNameIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..7de6d7c4ef96e23ee9b75a72fa5d32eb1f6b5ad1
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/SurnameNameIndexer.java
@@ -0,0 +1,9 @@
+package smash.appointment.parse;
+
+public class SurnameNameIndexer extends SubjectIndexer {
+
+	public String getIndexedString(Subject subject) {
+		return Utils.simplifyString(subject.getSurname() + subject.getName());
+	}
+
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/Utils.java b/appointment-import/src/main/java/smash/appointment/parse/Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0667ea3eb5cdc466b5a573e75f12ae3f546b89df
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/Utils.java
@@ -0,0 +1,7 @@
+package smash.appointment.parse;
+
+public class Utils {
+	public static String simplifyString(String query) {
+		return query.replaceAll("[\\s\\-©]", "").toLowerCase();
+	}
+}
diff --git a/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java b/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..9066b95799bd7bfded806ef23b8ad07b2300f59a
--- /dev/null
+++ b/appointment-import/src/main/java/smash/appointment/parse/XlsxCalendarProcessor.java
@@ -0,0 +1,142 @@
+package smash.appointment.parse;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+
+public class XlsxCalendarProcessor {
+	Logger						 logger	= Logger.getLogger(XlsxCalendarProcessor.class);
+
+	private SubjectDao subjectDao;
+
+	public List<AppointmentEntry> processExcel(String filename) throws EncryptedDocumentException, InvalidFormatException, IOException, ParseException {
+		List<AppointmentEntry> result = new ArrayList<AppointmentEntry>();
+		InputStream inp = new FileInputStream(filename);
+		Workbook workbook = WorkbookFactory.create(inp);
+		Iterator<Sheet> sheetIter = workbook.sheetIterator();
+		while (sheetIter.hasNext()) {
+			Sheet sheet = sheetIter.next();
+			String name = sheet.getSheetName().trim();
+			if (name.contains("2017")) {
+				String monthName = name.split(" ")[0];
+				String monthNumber = parseMonth(monthName);
+				result.addAll(processSheet(sheet, "2017-" + monthNumber));
+			} else {
+				logger.debug("Skipping sheet: " + name);
+			}
+		}
+		return result;
+	}
+
+	int[]	dayColumns						 = new int[] { 3, 4, 5, 6, 7 };
+
+	int[]	weekStartRows					 = new int[] { 5, 23, 41, 60, 78, 96 };
+
+	int		hourColum							 = 0;
+	int		dayOfMonthRowOffset		 = 0;
+	int		calendarRowStartOffset = 3;
+	int		calendarRowEndOffset	 = 18;
+
+	private List<AppointmentEntry> processSheet(Sheet sheet, String string) {
+		List<AppointmentEntry> result = new ArrayList<AppointmentEntry>();
+
+		CellParser parser = new CellParser();
+		parser.setSubjectDao(subjectDao);
+		
+		for (int weekOffset : weekStartRows) {
+			Row weekRow = sheet.getRow(weekOffset + dayOfMonthRowOffset);
+			for (int dayColumnOffset : dayColumns) {
+				Cell dayCell = weekRow.getCell(dayColumnOffset);
+				String dayOfMonth = ((int) dayCell.getNumericCellValue()) + "";
+				if (dayOfMonth.length() == 1) {
+					dayOfMonth = "0" + dayOfMonth;
+				}
+				if (!dayOfMonth.equals("00")) {
+					String day = string + "-" + dayOfMonth;
+
+					String hour = "08:00";
+					for (int hourOffset = calendarRowStartOffset; hourOffset < calendarRowEndOffset; hourOffset++) {
+						Row hourRow = sheet.getRow(weekOffset + hourOffset);
+
+						Cell hourCell = hourRow.getCell(hourColum);
+						if (hourCell.getCellTypeEnum().equals(CellType.NUMERIC)) {
+
+							SimpleDateFormat formatTime = new SimpleDateFormat("HH:mm");
+							String hourString = formatTime.format(hourCell.getDateCellValue());
+
+							if (isHour(hourString)) {
+								hour = hourString;
+							}
+						}
+
+						String query = hourRow.getCell(dayColumnOffset).getStringCellValue();
+
+						if (query != null && !query.isEmpty()) {
+							AppointmentEntry entry = parser.parseAppointment(query, hour);
+							entry.setDay(day);
+							
+							result.add(entry);
+						}
+					}
+				}
+			}
+
+		}
+		return result;
+	}
+
+	Pattern timePattern = Pattern.compile("^[0-9][0-9]\\:[0-9][0-9]");
+
+	private boolean isHour(String hourString) {
+		Matcher matcher = timePattern.matcher(hourString);
+		return matcher.find();
+	}
+
+	private String parseMonth(String monthName) throws ParseException {
+		Date date = new SimpleDateFormat("MMMM", Locale.ENGLISH).parse(monthName);
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		String result = (cal.get(Calendar.MONTH) + 1) + "";
+		if (result.length() == 1) {
+			result = "0" + result;
+		}
+		return result;
+	}
+
+	/**
+	 * @return the subjectDao
+	 * @see #subjectDao
+	 */
+	public SubjectDao getSubjectDao() {
+		return subjectDao;
+	}
+
+	/**
+	 * @param subjectDao
+	 *          the subjectDao to set
+	 * @see #subjectDao
+	 */
+	public void setSubjectDao(SubjectDao subjectDao) {
+		this.subjectDao = subjectDao;
+	}
+}
diff --git a/appointment-import/src/main/resources/log4j.properties b/appointment-import/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000000000000000000000000000000..e108a78cf8570a6f5bfc3e00dfaee3c45a1e829e
--- /dev/null
+++ b/appointment-import/src/main/resources/log4j.properties
@@ -0,0 +1,8 @@
+#Set root logger 's level and its appender to an appender called CONSOLE which is defined below.
+log4j.rootLogger=debug, CONSOLE
+
+#Set the behavior of the CONSOLE appender 
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
+#log4j.appender.CONSOLE.layout.ConversionPattern=%m%n
diff --git a/appointment-import/src/test/java/smash/appointment/parse/AllTests.java b/appointment-import/src/test/java/smash/appointment/parse/AllTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..80dc2e923c352fd7f99c0a18378f0ac8cec14ba2
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/AllTests.java
@@ -0,0 +1,14 @@
+package smash.appointment.parse;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({ CellParserTest.class, //
+		SubjectDaoTest.class, //
+		XlsxCalendarProcessorTest.class, //
+})
+public class AllTests {
+
+}
diff --git a/appointment-import/src/test/java/smash/appointment/parse/CellParseTestCase.java b/appointment-import/src/test/java/smash/appointment/parse/CellParseTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bc5156649eb06657b40d4b7f4210e7ea593c924
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/CellParseTestCase.java
@@ -0,0 +1,15 @@
+package smash.appointment.parse;
+
+class CellParseTestCase {
+	String					query;
+	Subject					subject;
+	String					time;
+	AppointmentType	type;
+
+	public CellParseTestCase(String query, Subject subject, String time, AppointmentType type) {
+		this.query = query;
+		this.subject = subject;
+		this.time = time;
+		this.type = type;
+	}
+};
diff --git a/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java b/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..40311eba2e5bb3b9e53956fb2903a3960908b4ce
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/CellParserTest.java
@@ -0,0 +1,77 @@
+package smash.appointment.parse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CellParserTest extends TestBase {
+	List<CellParseTestCase>	testCases;
+	CellParser parser;
+
+	@Before
+	public void setUp() {
+		super.setUp();
+		
+		parser = new CellParser();
+		parser.setSubjectDao(subjectDao);
+		
+		testCases = new ArrayList<CellParseTestCase>();
+
+		testCases.add(new CellParseTestCase("Piotr Gawron level A FU V3", piotrGawron, null, AppointmentType.LEVEL_A));
+		testCases.add(new CellParseTestCase("09:00 Jan Kowalski-Nowak level A", janKowalskiNowak, "09:00", AppointmentType.LEVEL_A));
+		testCases.add(new CellParseTestCase("ND0002 l664574645 (sms)evel BV ©  + SB ©", janKowalskiNowak, null, AppointmentType.LEVEL_BV_SB));
+		testCases.add(new CellParseTestCase("ND0001 654654631 level B ©", piotrGawron, null, AppointmentType.LEVEL_B));
+		testCases.add(new CellParseTestCase("John Doe BV + BG + SB", johnDoe, null, AppointmentType.LEVEL_BV_BG_SB));
+		testCases.add(new CellParseTestCase("Kowalski-Nowak m-Power", janKowalskiNowak, null, AppointmentType.LEVEL_B_M_POWER));
+		testCases.add(new CellParseTestCase("ND0004 Name BV ©", cateKowalsky, null, AppointmentType.LEVEL_BV));
+		
+	}
+
+
+	@Test
+	public void testExtractTime() {
+		for (CellParseTestCase testCase : testCases) {
+			String result = parser.extractTime(testCase.query);
+			assertEquals("Invalid time parsed from query: " + testCase.query, testCase.time, result);
+		}
+	}
+
+	@Test
+	public void testRemoveTime() {
+		for (CellParseTestCase testCase : testCases) {
+			String result = parser.removeTime(testCase.query);
+			if (testCase.time == null) {
+				assertEquals("query after removing time should be the same for query: " + testCase.query, testCase.query, result);
+			} else {
+				assertFalse(testCase.query.equals(result));
+			}
+		}
+	}
+
+	@Test
+	public void testRemoveTime2() {
+		String result = parser.removeTime("09:00 John Doe level B");
+		assertEquals("John Doe level B", result);
+	}
+
+	@Test
+	public void testExtractAppointment() {
+		String defaultTime = "23:55";
+		for (CellParseTestCase testCase : testCases) {
+			AppointmentEntry appointment = parser.parseAppointment(testCase.query, defaultTime);
+			if (testCase.time != null) {
+				assertEquals("Invalid time parsed from query: " + testCase.query, testCase.time, appointment.getTime());
+			} else {
+				assertEquals("Invalid time parsed from query (default value expected): " + testCase.query, defaultTime, appointment.getTime());
+			}
+			assertEquals("Invalid subject parsed from query: " + testCase.query, testCase.subject, appointment.getSubject());
+			assertEquals("Invalid type parsed from query: " + testCase.query, testCase.type, appointment.getType());
+		}
+	}
+
+}
diff --git a/appointment-import/src/test/java/smash/appointment/parse/SubjectDaoTest.java b/appointment-import/src/test/java/smash/appointment/parse/SubjectDaoTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..66fc18c54fc04749163cbd38d5a4cbc0c29a5e9f
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/SubjectDaoTest.java
@@ -0,0 +1,32 @@
+package smash.appointment.parse;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SubjectDaoTest {
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception {
+	}
+
+	@Before
+	public void setUp() throws Exception {
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testReadFile() throws Exception {
+		SubjectDao subjectDao = new SubjectDao();
+		subjectDao.readFile("testFiles/subjects.txt");
+		assertEquals(2, subjectDao.getSubjects().size());
+		assertEquals("Piotr", subjectDao.getSubjects().get(0).getName());
+	}
+
+}
diff --git a/appointment-import/src/test/java/smash/appointment/parse/TestBase.java b/appointment-import/src/test/java/smash/appointment/parse/TestBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3b909e929095ed6ebd890a29195e0f03625364d
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/TestBase.java
@@ -0,0 +1,26 @@
+package smash.appointment.parse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+
+public class TestBase {
+	SubjectDao subjectDao;
+
+	Subject		 piotrGawron			= new Subject("Piotr", "Gawron", "ND0001", "1");
+	Subject		 janKowalskiNowak	= new Subject("Jan", "Kowalski-Nowak", "ND0002", "2");
+	Subject		 johnDoe					= new Subject("John", "Doe", "ND0003", "3");
+	Subject		 cateKowalsky					= new Subject("Cate", "Kowalsky", "ND0004", "4");
+
+	public void setUp() {
+		subjectDao = new SubjectDao();
+
+		subjectDao.addSubject(piotrGawron);
+		subjectDao.addSubject(janKowalskiNowak);
+		subjectDao.addSubject(johnDoe);
+		subjectDao.addSubject(cateKowalsky);
+	}
+
+
+}
diff --git a/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java b/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c07b60137cb34d5f039f4cf99411dfdc3a80456
--- /dev/null
+++ b/appointment-import/src/test/java/smash/appointment/parse/XlsxCalendarProcessorTest.java
@@ -0,0 +1,42 @@
+package smash.appointment.parse;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+public class XlsxCalendarProcessorTest extends TestBase{
+	Logger logger = Logger.getLogger(XlsxCalendarProcessorTest .class);
+	
+	XlsxCalendarProcessor processor = new XlsxCalendarProcessor();
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception {
+	}
+
+	@Before
+	public void setUp()  {
+		super.setUp();
+		processor.setSubjectDao(subjectDao);
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testReadExcel() throws Exception {
+		List<AppointmentEntry> entries = processor.processExcel("testFiles/calendarExample.xlsx");
+		assertTrue(entries.size() > 0);
+//		for (AppointmentEntry appointmentEntry : entries) {
+//			logger.debug(appointmentEntry);
+//		}
+	}
+
+}
diff --git a/appointment-import/src/test/resources/log4j.properties b/appointment-import/src/test/resources/log4j.properties
new file mode 100644
index 0000000000000000000000000000000000000000..e108a78cf8570a6f5bfc3e00dfaee3c45a1e829e
--- /dev/null
+++ b/appointment-import/src/test/resources/log4j.properties
@@ -0,0 +1,8 @@
+#Set root logger 's level and its appender to an appender called CONSOLE which is defined below.
+log4j.rootLogger=debug, CONSOLE
+
+#Set the behavior of the CONSOLE appender 
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
+#log4j.appender.CONSOLE.layout.ConversionPattern=%m%n
diff --git a/appointment-import/testFiles/calendarExample.xlsx b/appointment-import/testFiles/calendarExample.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..516d8e548c74f5a7b2e1050b80918e0acfe80fc8
Binary files /dev/null and b/appointment-import/testFiles/calendarExample.xlsx differ
diff --git a/appointment-import/testFiles/subjects.txt b/appointment-import/testFiles/subjects.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c55594f7ee41d31b0b81aaf1b38b46eb949d5bb4
--- /dev/null
+++ b/appointment-import/testFiles/subjects.txt
@@ -0,0 +1,2 @@
+Piotr	Gawron	ND0001	1
+John	Doe	ND0002	2