diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java
index 7c9fc27f..677a6de0 100644
--- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java
+++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java
@@ -4,8 +4,12 @@ import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
 
 import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 
 import io.github.wulkanowy.api.SnP;
 import io.github.wulkanowy.api.generic.Day;
@@ -16,25 +20,34 @@ public class AttendanceTable {
 
     private SnP snp;
 
-    private String attendancePageUrl = "Frekwencja.mvc?data=";
+    private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data=";
 
     public AttendanceTable(SnP snp) {
         this.snp = snp;
     }
 
-    public Week<Day> getWeekTable() throws IOException {
+    public Week<Day> getWeekTable() throws IOException, ParseException {
         return getWeekTable("");
     }
 
-    public Week<Day> getWeekTable(String tick) throws IOException {
-        Element table = snp.getSnPPageDocument(attendancePageUrl + tick)
+    public Week<Day> getWeekTable(String tick) throws IOException, ParseException {
+        Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick)
                 .select(".mainContainer .presentData").first();
 
         Elements headerCells = table.select("thead th");
         List<Day> days = new ArrayList<>();
 
         for (int i = 1; i < headerCells.size(); i++) {
-            days.add(new Day().setDate(headerCells.get(i).html().split("<br>")[1]));
+            String[] dayHeaderCell = headerCells.get(i).html().split("<br>");
+
+            SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
+            Date d = sdf.parse(dayHeaderCell[1].trim());
+            sdf.applyPattern("yyyy-MM-dd");
+
+            Day day = new Day();
+            day.setDayName(dayHeaderCell[0]);
+            day.setDate(sdf.format(d));
+            days.add(day);
         }
 
         Elements hoursInDays = table.select("tbody tr");
@@ -46,26 +59,29 @@ public class AttendanceTable {
             // fill hours in day
             int size = hours.size();
             for (int i = 1; i < size; i++) {
-                days.get(i - 1).setLesson(getNewLesson(hours.get(i)));
+                Lesson lesson = new Lesson();
+                lesson.setDate(days.get(i - 1).getDate());
+                lesson.setNumber(hours.get(0).text());
+
+                addLessonDetails(lesson, hours.get(i));
+
+                days.get(i - 1).setLesson(lesson);
             }
         }
 
-        String[] dayDescription = headerCells.get(1).html().split("<br>");
-
         return new Week<Day>()
-                .setStartDayDate(dayDescription[1])
+                .setStartDayDate(days.get(0).getDate())
                 .setDays(days);
     }
 
-    private Lesson getNewLesson(Element cell) {
-        Lesson lesson = new Lesson();
+    private void addLessonDetails(Lesson lesson, Element cell) {
         lesson.setSubject(cell.select("span").text());
 
         if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) {
             lesson.setNotExist(true);
             lesson.setEmpty(true);
 
-            return lesson;
+            return;
         }
 
         switch (cell.select("div").attr("class")) {
@@ -95,7 +111,5 @@ public class AttendanceTable {
                 lesson.setEmpty(true);
                 break;
         }
-
-        return lesson;
     }
 }
diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java
index 1420a475..40ff4f06 100644
--- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java
+++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java
@@ -20,8 +20,8 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
 
     @Test
     public void getWeekStartByDate() throws Exception {
-        Assert.assertEquals("31.08.2015", excellent.getWeekTable().getStartDayDate());
-        Assert.assertEquals("05.09.2016", full.getWeekTable().getStartDayDate());
+        Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate());
+        Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate());
     }
 
     @Test
@@ -38,13 +38,13 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
 
     @Test
     public void getDayDate() throws Exception {
-        Assert.assertEquals("31.08.2015", excellent.getWeekTable().getDay(0).getDate());
-        Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate());
-        Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate());
+        Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate());
+        Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate());
+        Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate());
 
-        Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate());
-        Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate());
-        Assert.assertEquals("09.09.2016", full.getWeekTable().getDay(4).getDate());
+        Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate());
+        Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate());
+        Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate());
     }
 
     @Test
diff --git a/app/build.gradle b/app/build.gradle
index d4374e2d..4966d9f0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -62,7 +62,7 @@ android {
 }
 
 greendao {
-    schemaVersion 20
+    schemaVersion 22
     generateTests = true
 }
 
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java
new file mode 100644
index 00000000..492d642d
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.data.db.dao.entities;
+
+import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
+
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
+
+public class AttendanceLessonTest extends AbstractDaoTestLongPk<AttendanceLessonDao, AttendanceLesson> {
+
+    public AttendanceLessonTest() {
+        super(AttendanceLessonDao.class);
+    }
+
+    @Override
+    protected AttendanceLesson createEntity(Long key) {
+        AttendanceLesson entity = new AttendanceLesson();
+        entity.setId(key);
+        entity.setIsPresence(false);
+        entity.setIsAbsenceUnexcused(false);
+        entity.setIsAbsenceExcused(false);
+        entity.setIsUnexcusedLateness(false);
+        entity.setIsAbsenceForSchoolReasons(false);
+        entity.setIsExcusedLateness(false);
+        entity.setIsExemption(false);
+        return entity;
+    }
+
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java
similarity index 50%
rename from app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java
rename to app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java
index c8690ff9..b215b6be 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java
@@ -2,15 +2,18 @@ package io.github.wulkanowy.data.db.dao.entities;
 
 import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
 
-public class LessonTest extends AbstractDaoTestLongPk<LessonDao, Lesson> {
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
 
-    public LessonTest() {
-        super(LessonDao.class);
+public class TimetableLessonTest extends AbstractDaoTestLongPk<TimetableLessonDao, TimetableLesson> {
+
+    public TimetableLessonTest() {
+        super(TimetableLessonDao.class);
     }
 
     @Override
-    protected Lesson createEntity(Long key) {
-        Lesson entity = new Lesson();
+    protected TimetableLesson createEntity(Long key) {
+        TimetableLesson entity = new TimetableLesson();
         entity.setId(key);
         entity.setIsEmpty(false);
         entity.setIsDivisionIntoGroups(false);
diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java
index 04460a07..2637b08c 100644
--- a/app/src/main/java/io/github/wulkanowy/data/Repository.java
+++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java
@@ -1,6 +1,5 @@
 package io.github.wulkanowy.data;
 
-
 import java.io.IOException;
 import java.text.ParseException;
 import java.util.List;
@@ -13,6 +12,7 @@ import io.github.wulkanowy.api.login.BadCredentialsException;
 import io.github.wulkanowy.api.login.NotLoggedInErrorException;
 import io.github.wulkanowy.api.login.VulcanOfflineException;
 import io.github.wulkanowy.data.db.dao.entities.Account;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
 import io.github.wulkanowy.data.db.dao.entities.DaoSession;
 import io.github.wulkanowy.data.db.dao.entities.Grade;
 import io.github.wulkanowy.data.db.dao.entities.GradeDao;
@@ -21,6 +21,7 @@ import io.github.wulkanowy.data.db.dao.entities.WeekDao;
 import io.github.wulkanowy.data.db.resources.ResourcesContract;
 import io.github.wulkanowy.data.db.shared.SharedPrefContract;
 import io.github.wulkanowy.data.sync.SyncContract;
+import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
 import io.github.wulkanowy.data.sync.login.LoginSyncContract;
 import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
 import io.github.wulkanowy.di.annotations.SyncGrades;
@@ -38,6 +39,8 @@ public class Repository implements RepositoryContract {
 
     private final LoginSyncContract loginSync;
 
+    private final AttendanceSyncContract attendanceSync;
+
     private final TimetableSyncContract timetableSync;
 
     private final SyncContract gradeSync;
@@ -49,6 +52,7 @@ public class Repository implements RepositoryContract {
                ResourcesContract resources,
                DaoSession daoSession,
                LoginSyncContract loginSync,
+               AttendanceSyncContract attendanceSync,
                TimetableSyncContract timetableSync,
                @SyncGrades SyncContract gradeSync,
                @SyncSubjects SyncContract subjectSync) {
@@ -56,6 +60,7 @@ public class Repository implements RepositoryContract {
         this.resources = resources;
         this.daoSession = daoSession;
         this.loginSync = loginSync;
+        this.attendanceSync = attendanceSync;
         this.timetableSync = timetableSync;
         this.gradeSync = gradeSync;
         this.subjectSync = subjectSync;
@@ -81,6 +86,11 @@ public class Repository implements RepositoryContract {
         return resources.getErrorLoginMessage(e);
     }
 
+    @Override
+    public String getAttendanceLessonDescription(AttendanceLesson lesson) {
+        return resources.getAttendanceLessonDescription(lesson);
+    }
+
     @Override
     public void loginUser(String email, String password, String symbol)
             throws NotLoggedInErrorException, AccountPermissionException, IOException,
@@ -104,6 +114,16 @@ public class Repository implements RepositoryContract {
         subjectSync.sync();
     }
 
+    @Override
+    public void syncAttendance() throws NotLoggedInErrorException, ParseException, IOException {
+        attendanceSync.syncAttendance();
+    }
+
+    @Override
+    public void syncAttendance(String date) throws NotLoggedInErrorException, ParseException, IOException {
+        attendanceSync.syncAttendance(date);
+    }
+
     @Override
     public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
         timetableSync.syncTimetable();
@@ -118,6 +138,7 @@ public class Repository implements RepositoryContract {
     public void syncAll() throws NotLoggedInErrorException, IOException, ParseException {
         syncSubjects();
         syncGrades();
+        syncAttendance();
         syncTimetable();
     }
 
diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java
index fcdf29ed..90aaeef8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java
@@ -11,12 +11,13 @@ import io.github.wulkanowy.data.db.dao.entities.Account;
 import io.github.wulkanowy.data.db.dao.entities.Grade;
 import io.github.wulkanowy.data.db.dao.entities.Week;
 import io.github.wulkanowy.data.db.resources.ResourcesContract;
+import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
 import io.github.wulkanowy.data.sync.login.LoginSyncContract;
 import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
 
 @Singleton
 public interface RepositoryContract extends ResourcesContract, LoginSyncContract,
-        TimetableSyncContract {
+        AttendanceSyncContract, TimetableSyncContract {
 
     long getCurrentUserId();
 
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java
new file mode 100644
index 00000000..9250518a
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java
@@ -0,0 +1,254 @@
+package io.github.wulkanowy.data.db.dao.entities;
+
+import org.greenrobot.greendao.DaoException;
+import org.greenrobot.greendao.annotation.Entity;
+import org.greenrobot.greendao.annotation.Generated;
+import org.greenrobot.greendao.annotation.Id;
+import org.greenrobot.greendao.annotation.Index;
+import org.greenrobot.greendao.annotation.Property;
+import org.greenrobot.greendao.annotation.Transient;
+
+import java.io.Serializable;
+
+@Entity(
+        nameInDb = "AttendanceLessons",
+        active = true,
+        indexes = {@Index(value = "dayId,date,number", unique = true)}
+)
+public class AttendanceLesson implements Serializable {
+
+    @Id(autoincrement = true)
+    private Long id;
+
+    @Property(nameInDb = "DAY_ID")
+    private Long dayId;
+
+    @Property(nameInDb = "DATE")
+    private String date = "";
+
+    @Property(nameInDb = "NUMBER_OF_LESSON")
+    private int number = 0;
+
+    @Property(nameInDb = "SUBJECT_NAME")
+    private String subject = "";
+
+    @Property(nameInDb = "IS_PRESENCE")
+    private boolean isPresence = false;
+
+    @Property(nameInDb = "IS_ABSENCE_UNEXCUSED")
+    private boolean isAbsenceUnexcused = false;
+
+    @Property(nameInDb = "IS_ABSENCE_EXCUSED")
+    private boolean isAbsenceExcused = false;
+
+    @Property(nameInDb = "IS_UNEXCUSED_LATENESS")
+    private boolean isUnexcusedLateness = false;
+
+    @Property(nameInDb = "IS_ABSENCE_FOR_SCHOOL_REASONS")
+    private boolean isAbsenceForSchoolReasons = false;
+
+    @Property(nameInDb = "IS_EXCUSED_LATENESS")
+    private boolean isExcusedLateness = false;
+
+    @Property(nameInDb = "IS_EXEMPTION")
+    private boolean isExemption = false;
+
+    @Transient
+    private String description = "";
+
+    private static final long serialVersionUID = 42L;
+
+    /**
+     * Used to resolve relations
+     */
+    @Generated(hash = 2040040024)
+    private transient DaoSession daoSession;
+
+    /**
+     * Used for active entity operations.
+     */
+    @Generated(hash = 1936953859)
+    private transient AttendanceLessonDao myDao;
+
+    @Generated(hash = 1428129046)
+    public AttendanceLesson(Long id, Long dayId, String date, int number,
+                            String subject, boolean isPresence, boolean isAbsenceUnexcused,
+                            boolean isAbsenceExcused, boolean isUnexcusedLateness,
+                            boolean isAbsenceForSchoolReasons, boolean isExcusedLateness,
+                            boolean isExemption) {
+        this.id = id;
+        this.dayId = dayId;
+        this.date = date;
+        this.number = number;
+        this.subject = subject;
+        this.isPresence = isPresence;
+        this.isAbsenceUnexcused = isAbsenceUnexcused;
+        this.isAbsenceExcused = isAbsenceExcused;
+        this.isUnexcusedLateness = isUnexcusedLateness;
+        this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
+        this.isExcusedLateness = isExcusedLateness;
+        this.isExemption = isExemption;
+    }
+
+    @Generated(hash = 921806575)
+    public AttendanceLesson() {
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getDayId() {
+        return this.dayId;
+    }
+
+    public void setDayId(Long dayId) {
+        this.dayId = dayId;
+    }
+
+    public String getDate() {
+        return this.date;
+    }
+
+    public AttendanceLesson setDate(String date) {
+        this.date = date;
+        return this;
+    }
+
+    public int getNumber() {
+        return this.number;
+    }
+
+    public AttendanceLesson setNumber(int number) {
+        this.number = number;
+        return this;
+    }
+
+    public String getSubject() {
+        return this.subject;
+    }
+
+    public AttendanceLesson setSubject(String subject) {
+        this.subject = subject;
+        return this;
+    }
+
+    public boolean getIsPresence() {
+        return this.isPresence;
+    }
+
+    public AttendanceLesson setIsPresence(boolean isPresence) {
+        this.isPresence = isPresence;
+        return this;
+    }
+
+    public boolean getIsAbsenceUnexcused() {
+        return this.isAbsenceUnexcused;
+    }
+
+    public AttendanceLesson setIsAbsenceUnexcused(boolean isAbsenceUnexcused) {
+        this.isAbsenceUnexcused = isAbsenceUnexcused;
+        return this;
+    }
+
+    public boolean getIsAbsenceExcused() {
+        return this.isAbsenceExcused;
+    }
+
+    public AttendanceLesson setIsAbsenceExcused(boolean isAbsenceExcused) {
+        this.isAbsenceExcused = isAbsenceExcused;
+        return this;
+    }
+
+    public boolean getIsUnexcusedLateness() {
+        return this.isUnexcusedLateness;
+    }
+
+    public AttendanceLesson setIsUnexcusedLateness(boolean isUnexcusedLateness) {
+        this.isUnexcusedLateness = isUnexcusedLateness;
+        return this;
+    }
+
+    public boolean getIsAbsenceForSchoolReasons() {
+        return this.isAbsenceForSchoolReasons;
+    }
+
+    public AttendanceLesson setIsAbsenceForSchoolReasons(boolean isAbsenceForSchoolReasons) {
+        this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
+        return this;
+    }
+
+    public boolean getIsExcusedLateness() {
+        return this.isExcusedLateness;
+    }
+
+    public AttendanceLesson setIsExcusedLateness(boolean isExcusedLateness) {
+        this.isExcusedLateness = isExcusedLateness;
+        return this;
+    }
+
+    public boolean getIsExemption() {
+        return this.isExemption;
+    }
+
+    public AttendanceLesson setIsExemption(boolean isExemption) {
+        this.isExemption = isExemption;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public AttendanceLesson setDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    /**
+     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
+     * Entity must attached to an entity context.
+     */
+    @Generated(hash = 128553479)
+    public void delete() {
+        if (myDao == null) {
+            throw new DaoException("Entity is detached from DAO context");
+        }
+        myDao.delete(this);
+    }
+
+    /**
+     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
+     * Entity must attached to an entity context.
+     */
+    @Generated(hash = 1942392019)
+    public void refresh() {
+        if (myDao == null) {
+            throw new DaoException("Entity is detached from DAO context");
+        }
+        myDao.refresh(this);
+    }
+
+    /**
+     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
+     * Entity must attached to an entity context.
+     */
+    @Generated(hash = 713229351)
+    public void update() {
+        if (myDao == null) {
+            throw new DaoException("Entity is detached from DAO context");
+        }
+        myDao.update(this);
+    }
+
+    /** called by internal mechanisms, do not call yourself. */
+    @Generated(hash = 1157101112)
+    public void __setDaoSession(DaoSession daoSession) {
+        this.daoSession = daoSession;
+        myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null;
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java
index 99574e1d..fb3ce414 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java
@@ -5,6 +5,7 @@ import org.greenrobot.greendao.annotation.Entity;
 import org.greenrobot.greendao.annotation.Generated;
 import org.greenrobot.greendao.annotation.Id;
 import org.greenrobot.greendao.annotation.Index;
+import org.greenrobot.greendao.annotation.OrderBy;
 import org.greenrobot.greendao.annotation.Property;
 import org.greenrobot.greendao.annotation.ToMany;
 
@@ -13,7 +14,7 @@ import java.util.List;
 @Entity(
         nameInDb = "Days",
         active = true,
-        indexes ={@Index(value = "userId,weekId,date", unique = true)}
+        indexes = {@Index(value = "userId,weekId,date", unique = true)}
 )
 public class Day {
 
@@ -39,7 +40,11 @@ public class Day {
     private String freeDayName = "";
 
     @ToMany(referencedJoinProperty = "dayId")
-    private List<Lesson> lessons;
+    private List<TimetableLesson> timetableLessons;
+
+    @ToMany(referencedJoinProperty = "dayId")
+    @OrderBy("number ASC")
+    private List<AttendanceLesson> attendanceLessons;
 
     /**
      * Used to resolve relations
@@ -113,12 +118,12 @@ public class Day {
         return this;
     }
 
-    public boolean isFreeDay() {
-        return isFreeDay;
+    public boolean getIsFreeDay() {
+        return this.isFreeDay;
     }
 
-    public Day setFreeDay(boolean freeDay) {
-        isFreeDay = freeDay;
+    public Day setIsFreeDay(boolean isFreeDay) {
+        this.isFreeDay = isFreeDay;
         return this;
     }
 
@@ -135,30 +140,58 @@ public class Day {
      * To-many relationship, resolved on first access (and after reset).
      * Changes to to-many relations are not persisted, make changes to the target entity.
      */
-    @Generated(hash = 1552857303)
-    public List<Lesson> getLessons() {
-        if (lessons == null) {
+    @Generated(hash = 218588195)
+    public List<TimetableLesson> getTimetableLessons() {
+        if (timetableLessons == null) {
             final DaoSession daoSession = this.daoSession;
             if (daoSession == null) {
                 throw new DaoException("Entity is detached from DAO context");
             }
-            LessonDao targetDao = daoSession.getLessonDao();
-            List<Lesson> lessonsNew = targetDao._queryDay_Lessons(id);
+            TimetableLessonDao targetDao = daoSession.getTimetableLessonDao();
+            List<TimetableLesson> timetableLessonsNew = targetDao
+                    ._queryDay_TimetableLessons(id);
             synchronized (this) {
-                if (lessons == null) {
-                    lessons = lessonsNew;
+                if (timetableLessons == null) {
+                    timetableLessons = timetableLessonsNew;
                 }
             }
         }
-        return lessons;
+        return timetableLessons;
+    }
+
+    /** Resets a to-many relationship, making the next get call to query for a fresh result. */
+    @Generated(hash = 1687683740)
+    public synchronized void resetTimetableLessons() {
+        timetableLessons = null;
     }
 
     /**
-     * Resets a to-many relationship, making the next get call to query for a fresh result.
+     * To-many relationship, resolved on first access (and after reset).
+     * Changes to to-many relations are not persisted, make changes to the target entity.
      */
-    @Generated(hash = 1769801440)
-    public synchronized void resetLessons() {
-        lessons = null;
+    @Generated(hash = 1166820581)
+    public List<AttendanceLesson> getAttendanceLessons() {
+        if (attendanceLessons == null) {
+            final DaoSession daoSession = this.daoSession;
+            if (daoSession == null) {
+                throw new DaoException("Entity is detached from DAO context");
+            }
+            AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao();
+            List<AttendanceLesson> attendanceLessonsNew = targetDao
+                    ._queryDay_AttendanceLessons(id);
+            synchronized (this) {
+                if (attendanceLessons == null) {
+                    attendanceLessons = attendanceLessonsNew;
+                }
+            }
+        }
+        return attendanceLessons;
+    }
+
+    /** Resets a to-many relationship, making the next get call to query for a fresh result. */
+    @Generated(hash = 1343075564)
+    public synchronized void resetAttendanceLessons() {
+        attendanceLessons = null;
     }
 
     /**
@@ -197,14 +230,6 @@ public class Day {
         myDao.update(this);
     }
 
-    public boolean getIsFreeDay() {
-        return this.isFreeDay;
-    }
-
-    public void setIsFreeDay(boolean isFreeDay) {
-        this.isFreeDay = isFreeDay;
-    }
-
     /** called by internal mechanisms, do not call yourself. */
     @Generated(hash = 1409317752)
     public void __setDaoSession(DaoSession daoSession) {
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java
similarity index 72%
rename from app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java
rename to app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java
index db9be944..390f679e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java
@@ -10,11 +10,11 @@ import org.greenrobot.greendao.annotation.Property;
 import java.io.Serializable;
 
 @Entity(
-        nameInDb = "Lessons",
+        nameInDb = "TimetableLessons",
         active = true,
         indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)}
 )
-public class Lesson implements Serializable {
+public class TimetableLesson implements Serializable {
 
     @Id(autoincrement = true)
     private Long id;
@@ -78,14 +78,15 @@ public class Lesson implements Serializable {
     /**
      * Used for active entity operations.
      */
-    @Generated(hash = 610143130)
-    private transient LessonDao myDao;
+    @Generated(hash = 1119360138)
+    private transient TimetableLessonDao myDao;
 
-    @Generated(hash = 140778287)
-    public Lesson(Long id, Long dayId, String number, String subject, String teacher, String room,
-                  String description, String groupName, String startTime, String endTime, String date,
-                  boolean isEmpty, boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized,
-                  boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) {
+    @Generated(hash = 627457324)
+    public TimetableLesson(Long id, Long dayId, String number, String subject,
+                           String teacher, String room, String description, String groupName,
+                           String startTime, String endTime, String date, boolean isEmpty,
+                           boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized,
+                           boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) {
         this.id = id;
         this.dayId = dayId;
         this.number = number;
@@ -105,8 +106,8 @@ public class Lesson implements Serializable {
         this.isNewMovedInOrChanged = isNewMovedInOrChanged;
     }
 
-    @Generated(hash = 1669664117)
-    public Lesson() {
+    @Generated(hash = 1878030142)
+    public TimetableLesson() {
     }
 
     public Long getId() {
@@ -126,186 +127,138 @@ public class Lesson implements Serializable {
     }
 
     public String getNumber() {
-        return number;
+        return this.number;
     }
 
-    public Lesson setNumber(String number) {
+    public TimetableLesson setNumber(String number) {
         this.number = number;
         return this;
     }
 
     public String getSubject() {
-        return subject;
+        return this.subject;
     }
 
-    public Lesson setSubject(String subject) {
+    public TimetableLesson setSubject(String subject) {
         this.subject = subject;
         return this;
     }
 
     public String getTeacher() {
-        return teacher;
+        return this.teacher;
     }
 
-    public Lesson setTeacher(String teacher) {
+    public TimetableLesson setTeacher(String teacher) {
         this.teacher = teacher;
         return this;
     }
 
     public String getRoom() {
-        return room;
+        return this.room;
     }
 
-    public Lesson setRoom(String room) {
+    public TimetableLesson setRoom(String room) {
         this.room = room;
         return this;
     }
 
     public String getDescription() {
-        return description;
+        return this.description;
     }
 
-    public Lesson setDescription(String description) {
+    public TimetableLesson setDescription(String description) {
         this.description = description;
         return this;
     }
 
     public String getGroupName() {
-        return groupName;
+        return this.groupName;
     }
 
-    public Lesson setGroupName(String groupName) {
+    public TimetableLesson setGroupName(String groupName) {
         this.groupName = groupName;
         return this;
     }
 
     public String getStartTime() {
-        return startTime;
+        return this.startTime;
     }
 
-    public Lesson setStartTime(String startTime) {
+    public TimetableLesson setStartTime(String startTime) {
         this.startTime = startTime;
         return this;
     }
 
     public String getEndTime() {
-        return endTime;
+        return this.endTime;
     }
 
-    public Lesson setEndTime(String endTime) {
+    public TimetableLesson setEndTime(String endTime) {
         this.endTime = endTime;
         return this;
     }
 
     public String getDate() {
-        return date;
+        return this.date;
     }
 
-    public Lesson setDate(String date) {
+    public TimetableLesson setDate(String date) {
         this.date = date;
         return this;
     }
 
-    public boolean isEmpty() {
-        return isEmpty;
-    }
-
-    public Lesson setEmpty(boolean empty) {
-        isEmpty = empty;
-        return this;
-    }
-
-    public boolean isDivisionIntoGroups() {
-        return isDivisionIntoGroups;
-    }
-
-    public Lesson setDivisionIntoGroups(boolean divisionIntoGroups) {
-        isDivisionIntoGroups = divisionIntoGroups;
-        return this;
-    }
-
-    public boolean isPlanning() {
-        return isPlanning;
-    }
-
-    public Lesson setPlanning(boolean planning) {
-        isPlanning = planning;
-        return this;
-    }
-
-    public boolean isRealized() {
-        return isRealized;
-    }
-
-    public Lesson setRealized(boolean realized) {
-        isRealized = realized;
-        return this;
-    }
-
-    public boolean isMovedOrCanceled() {
-        return isMovedOrCanceled;
-    }
-
-    public Lesson setMovedOrCanceled(boolean movedOrCanceled) {
-        isMovedOrCanceled = movedOrCanceled;
-        return this;
-    }
-
-    public boolean isNewMovedInOrChanged() {
-        return isNewMovedInOrChanged;
-    }
-
-    public Lesson setNewMovedInOrChanged(boolean newMovedInOrChanged) {
-        isNewMovedInOrChanged = newMovedInOrChanged;
-        return this;
-    }
-
     public boolean getIsEmpty() {
         return this.isEmpty;
     }
 
-    public void setIsEmpty(boolean isEmpty) {
+    public TimetableLesson setEmpty(boolean isEmpty) {
         this.isEmpty = isEmpty;
+        return this;
     }
 
     public boolean getIsDivisionIntoGroups() {
         return this.isDivisionIntoGroups;
     }
 
-    public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) {
+    public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) {
         this.isDivisionIntoGroups = isDivisionIntoGroups;
+        return this;
     }
 
     public boolean getIsPlanning() {
         return this.isPlanning;
     }
 
-    public void setIsPlanning(boolean isPlanning) {
+    public TimetableLesson setPlanning(boolean isPlanning) {
         this.isPlanning = isPlanning;
+        return this;
     }
 
     public boolean getIsRealized() {
         return this.isRealized;
     }
 
-    public void setIsRealized(boolean isRealized) {
+    public TimetableLesson setRealized(boolean isRealized) {
         this.isRealized = isRealized;
+        return this;
     }
 
     public boolean getIsMovedOrCanceled() {
         return this.isMovedOrCanceled;
     }
 
-    public void setIsMovedOrCanceled(boolean isMovedOrCanceled) {
+    public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) {
         this.isMovedOrCanceled = isMovedOrCanceled;
+        return this;
     }
 
     public boolean getIsNewMovedInOrChanged() {
         return this.isNewMovedInOrChanged;
     }
 
-    public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
+    public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
         this.isNewMovedInOrChanged = isNewMovedInOrChanged;
+        return this;
     }
 
     /**
@@ -344,10 +297,34 @@ public class Lesson implements Serializable {
         myDao.update(this);
     }
 
+    public void setIsEmpty(boolean isEmpty) {
+        this.isEmpty = isEmpty;
+    }
+
+    public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) {
+        this.isDivisionIntoGroups = isDivisionIntoGroups;
+    }
+
+    public void setIsPlanning(boolean isPlanning) {
+        this.isPlanning = isPlanning;
+    }
+
+    public void setIsRealized(boolean isRealized) {
+        this.isRealized = isRealized;
+    }
+
+    public void setIsMovedOrCanceled(boolean isMovedOrCanceled) {
+        this.isMovedOrCanceled = isMovedOrCanceled;
+    }
+
+    public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
+        this.isNewMovedInOrChanged = isNewMovedInOrChanged;
+    }
+
     /** called by internal mechanisms, do not call yourself. */
-    @Generated(hash = 2078826279)
+    @Generated(hash = 1885258429)
     public void __setDaoSession(DaoSession daoSession) {
         this.daoSession = daoSession;
-        myDao = daoSession != null ? daoSession.getLessonDao() : null;
+        myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null;
     }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java
index c82f3277..778f6ca3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java
@@ -26,6 +26,12 @@ public class Week {
     @Property(nameInDb = "START_DATE")
     private String startDayDate = "";
 
+    @Property(nameInDb = "IS_ATTENDANCE_SYNCED")
+    private boolean isAttendanceSynced = false;
+
+    @Property(nameInDb = "IS_TIMETABLE_SYNCED")
+    private boolean isTimetableSynced = false;
+
     @ToMany(referencedJoinProperty = "weekId")
     private List<Day> dayList;
 
@@ -37,11 +43,14 @@ public class Week {
     @Generated(hash = 1019310398)
     private transient WeekDao myDao;
 
-    @Generated(hash = 36829814)
-    public Week(Long id, Long userId, String startDayDate) {
+    @Generated(hash = 1745118398)
+    public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced,
+            boolean isTimetableSynced) {
         this.id = id;
         this.userId = userId;
         this.startDayDate = startDayDate;
+        this.isAttendanceSynced = isAttendanceSynced;
+        this.isTimetableSynced = isTimetableSynced;
     }
 
     @Generated(hash = 2135529658)
@@ -75,6 +84,22 @@ public class Week {
         return this;
     }
 
+    public boolean getIsAttendanceSynced() {
+        return this.isAttendanceSynced;
+    }
+
+    public void setIsAttendanceSynced(boolean isAttendanceSynced) {
+        this.isAttendanceSynced = isAttendanceSynced;
+    }
+
+    public boolean getIsTimetableSynced() {
+        return this.isTimetableSynced;
+    }
+
+    public void setIsTimetableSynced(boolean isTimetableSynced) {
+        this.isTimetableSynced = isTimetableSynced;
+    }
+
     /**
      * To-many relationship, resolved on first access (and after reset).
      * Changes to to-many relations are not persisted, make changes to the target entity.
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java
index 8e7227d3..e9b85464 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java
+++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java
@@ -13,6 +13,7 @@ import javax.inject.Singleton;
 import io.github.wulkanowy.R;
 import io.github.wulkanowy.api.login.NotLoggedInErrorException;
 import io.github.wulkanowy.api.login.VulcanOfflineException;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
 import io.github.wulkanowy.di.annotations.ApplicationContext;
 import io.github.wulkanowy.utils.AppConstant;
 import io.github.wulkanowy.utils.LogUtils;
@@ -56,4 +57,35 @@ public class AppResources implements ResourcesContract {
             return exception.getMessage();
         }
     }
+
+    @Override
+    public String getAttendanceLessonDescription(AttendanceLesson lesson) {
+        int id = R.string.attendance_present;
+
+        if (lesson.getIsAbsenceForSchoolReasons()) {
+            id = R.string.attendance_absence_for_school_reasons;
+        }
+
+        if (lesson.getIsAbsenceExcused()) {
+            id =  R.string.attendance_absence_excused;
+        }
+
+        if (lesson.getIsAbsenceUnexcused()) {
+            id =  R.string.attendance_absence_unexcused;
+        }
+
+        if (lesson.getIsExemption()) {
+            id =  R.string.attendance_exemption;
+        }
+
+        if (lesson.getIsExcusedLateness()) {
+            id =  R.string.attendance_excused_lateness;
+        }
+
+        if (lesson.getIsUnexcusedLateness()) {
+            id =  R.string.attendance_unexcused_lateness;
+        }
+
+        return resources.getString(id);
+    }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java
index 3768ad2c..0ef45a76 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java
+++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java
@@ -1,5 +1,7 @@
 package io.github.wulkanowy.data.db.resources;
 
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+
 public interface ResourcesContract {
 
     String[] getSymbolsKeysArray();
@@ -7,4 +9,6 @@ public interface ResourcesContract {
     String[] getSymbolsValuesArray();
 
     String getErrorLoginMessage(Exception e);
+
+    String getAttendanceLessonDescription(AttendanceLesson lesson);
 }
diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java
new file mode 100644
index 00000000..19de88ef
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java
@@ -0,0 +1,156 @@
+package io.github.wulkanowy.data.sync.attendance;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import io.github.wulkanowy.api.Vulcan;
+import io.github.wulkanowy.api.generic.Lesson;
+import io.github.wulkanowy.api.login.NotLoggedInErrorException;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
+import io.github.wulkanowy.data.db.dao.entities.DaoSession;
+import io.github.wulkanowy.data.db.dao.entities.Day;
+import io.github.wulkanowy.data.db.dao.entities.DayDao;
+import io.github.wulkanowy.data.db.dao.entities.Week;
+import io.github.wulkanowy.data.db.dao.entities.WeekDao;
+import io.github.wulkanowy.data.db.shared.SharedPrefContract;
+import io.github.wulkanowy.utils.DataObjectConverter;
+import io.github.wulkanowy.utils.LogUtils;
+import io.github.wulkanowy.utils.TimeUtils;
+
+@Singleton
+public class AttendanceSync implements AttendanceSyncContract {
+
+    private final DaoSession daoSession;
+
+    private final SharedPrefContract sharedPref;
+
+    private final Vulcan vulcan;
+
+    private long userId;
+
+    @Inject
+    AttendanceSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
+        this.daoSession = daoSession;
+        this.sharedPref = sharedPref;
+        this.vulcan = vulcan;
+    }
+
+    @Override
+    public void syncAttendance() throws IOException, NotLoggedInErrorException, ParseException {
+        syncAttendance(null);
+    }
+
+    @Override
+    public void syncAttendance(String date) throws IOException, NotLoggedInErrorException, ParseException {
+        this.userId = sharedPref.getCurrentUserId();
+
+        io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
+        Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
+
+        long weekId = updateWeekInDb(weekDb, weekApi);
+
+        List<AttendanceLesson> lessonList = updateDays(weekApi.getDays(), weekId);
+
+        daoSession.getAttendanceLessonDao().saveInTx(lessonList);
+
+        LogUtils.debug("Synchronization lessons (amount = " + lessonList.size() + ")");
+    }
+
+    private String getNormalizedDate(String date) throws ParseException {
+        return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
+    }
+
+    private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
+            throws IOException, NotLoggedInErrorException, ParseException {
+        return vulcan.getAttendanceTable().getWeekTable(date);
+    }
+
+    private Week getWeekFromDb(String date) {
+        return daoSession.getWeekDao()
+                .queryBuilder()
+                .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
+                .unique();
+    }
+
+    private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) {
+        if (dbWeekEntity != null) {
+            dbWeekEntity.setIsAttendanceSynced(true);
+            dbWeekEntity.update();
+
+            return dbWeekEntity.getId();
+        }
+
+        Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
+        apiWeekEntity.setIsAttendanceSynced(true);
+
+        return daoSession.getWeekDao().insert(apiWeekEntity);
+    }
+
+    private List<AttendanceLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
+        List<AttendanceLesson> updatedLessonList = new ArrayList<>();
+
+        for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
+
+            Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
+
+            Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
+
+            long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
+
+            updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
+        }
+
+        return updatedLessonList;
+    }
+
+    private Day getDayFromDb(String date) {
+        return daoSession.getDayDao()
+                .queryBuilder()
+                .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
+                .unique();
+    }
+
+    private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) {
+        if (null != dbDayEntity) {
+            return dbDayEntity.getId();
+        }
+
+        apiDayEntity.setUserId(userId);
+        apiDayEntity.setWeekId(weekId);
+
+        return daoSession.getDayDao().insert(apiDayEntity);
+    }
+
+    private void updateLessons(List<Lesson> lessons, List<AttendanceLesson> updatedLessons, long dayId) {
+        List<AttendanceLesson> lessonsFromApiEntities = DataObjectConverter
+                .lessonsToAttendanceLessonsEntities(lessons);
+
+        for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) {
+            AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
+
+            apiLessonEntity.setDayId(dayId);
+
+            if (lessonFromDb != null) {
+                apiLessonEntity.setId(lessonFromDb.getId());
+            }
+
+            if (!"".equals(apiLessonEntity.getSubject())) {
+                updatedLessons.add(apiLessonEntity);
+            }
+        }
+    }
+
+    private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) {
+        return daoSession.getAttendanceLessonDao().queryBuilder()
+                .where(AttendanceLessonDao.Properties.DayId.eq(dayId),
+                        AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()),
+                        AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber()))
+                .unique();
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java
new file mode 100644
index 00000000..503086ba
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.data.sync.attendance;
+
+import java.io.IOException;
+import java.text.ParseException;
+
+import io.github.wulkanowy.api.login.NotLoggedInErrorException;
+
+public interface AttendanceSyncContract {
+
+    void syncAttendance(String date) throws NotLoggedInErrorException, IOException, ParseException;
+
+    void syncAttendance() throws NotLoggedInErrorException, IOException, ParseException;
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java
index dcb048f1..11819bed 100644
--- a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java
+++ b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java
@@ -9,12 +9,13 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 
 import io.github.wulkanowy.api.Vulcan;
+import io.github.wulkanowy.api.generic.Lesson;
 import io.github.wulkanowy.api.login.NotLoggedInErrorException;
 import io.github.wulkanowy.data.db.dao.entities.DaoSession;
 import io.github.wulkanowy.data.db.dao.entities.Day;
 import io.github.wulkanowy.data.db.dao.entities.DayDao;
-import io.github.wulkanowy.data.db.dao.entities.Lesson;
-import io.github.wulkanowy.data.db.dao.entities.LessonDao;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
 import io.github.wulkanowy.data.db.dao.entities.Week;
 import io.github.wulkanowy.data.db.dao.entities.WeekDao;
 import io.github.wulkanowy.data.db.shared.SharedPrefContract;
@@ -27,9 +28,11 @@ public class TimetableSync implements TimetableSyncContract {
 
     private final DaoSession daoSession;
 
+    private final SharedPrefContract sharedPref;
+
     private final Vulcan vulcan;
 
-    private final SharedPrefContract sharedPref;
+    private long userId;
 
     @Inject
     TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
@@ -38,83 +41,122 @@ public class TimetableSync implements TimetableSyncContract {
         this.vulcan = vulcan;
     }
 
-    @Override
-    public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
-        long userId = sharedPref.getCurrentUserId();
-
-        io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekFromNet =
-                date == null ? vulcan.getTimetable().getWeekTable()
-                : vulcan.getTimetable().getWeekTable(String.valueOf(TimeUtils.getNetTicks(date)));
-
-        Week weekFromDb = daoSession.getWeekDao().queryBuilder()
-                .where(WeekDao.Properties.UserId.eq(userId),
-                        WeekDao.Properties.StartDayDate.eq(weekFromNet.getStartDayDate()))
-                .unique();
-
-        Long weekId;
-
-        if (weekFromDb == null) {
-            Week weekFromNetEntity = DataObjectConverter.weekToWeekEntity(weekFromNet).setUserId(userId);
-            weekId = daoSession.getWeekDao().insert(weekFromNetEntity);
-        } else {
-            weekId = weekFromDb.getId();
-        }
-
-        List<io.github.wulkanowy.api.generic.Day> dayListFromNet = weekFromNet.getDays();
-
-        List<Lesson> updatedLessonList = new ArrayList<>();
-
-        for (io.github.wulkanowy.api.generic.Day dayFromNet : dayListFromNet) {
-            Day dayFromNetEntity = DataObjectConverter.dayToDayEntity(dayFromNet);
-
-            Day dayFromDb = daoSession.getDayDao().queryBuilder()
-                    .where(DayDao.Properties.UserId.eq(userId),
-                            DayDao.Properties.WeekId.eq(weekId),
-                            DayDao.Properties.Date.eq(dayFromNetEntity.getDate()))
-                    .unique();
-
-            dayFromNetEntity.setUserId(userId);
-            dayFromNetEntity.setWeekId(weekId);
-
-            Long dayId;
-
-            if (dayFromDb != null) {
-                dayFromNetEntity.setId(dayFromDb.getId());
-                daoSession.getDayDao().save(dayFromNetEntity);
-                dayId = dayFromNetEntity.getId();
-            } else {
-                dayId = daoSession.getDayDao().insert(dayFromNetEntity);
-            }
-
-            List<Lesson> lessonListFromNetEntities = DataObjectConverter
-                    .lessonsToLessonsEntities(dayFromNet.getLessons());
-
-            for (Lesson lessonFromNetEntity : lessonListFromNetEntities) {
-                Lesson lessonFromDb = daoSession.getLessonDao().queryBuilder()
-                        .where(LessonDao.Properties.DayId.eq(dayId),
-                                LessonDao.Properties.Date.eq(lessonFromNetEntity.getDate()),
-                                LessonDao.Properties.StartTime.eq(lessonFromNetEntity.getStartTime()),
-                                LessonDao.Properties.EndTime.eq(lessonFromNetEntity.getEndTime()))
-                        .unique();
-
-                if (lessonFromDb != null) {
-                    lessonFromNetEntity.setId(lessonFromDb.getId());
-                }
-
-                lessonFromNetEntity.setDayId(dayFromNetEntity.getId());
-
-                if (!"".equals(lessonFromNetEntity.getSubject())) {
-                    updatedLessonList.add(lessonFromNetEntity);
-                }
-            }
-        }
-        daoSession.getLessonDao().saveInTx(updatedLessonList);
-
-        LogUtils.debug("Synchronization lessons (amount = " + updatedLessonList.size() + ")");
-    }
-
     @Override
     public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
         syncTimetable(null);
     }
+
+    @Override
+    public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
+        this.userId = sharedPref.getCurrentUserId();
+
+        io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
+        Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
+
+        long weekId = updateWeekInDb(weekDb, weekApi);
+
+        List<TimetableLesson> lessonList = updateDays(weekApi.getDays(), weekId);
+
+        daoSession.getTimetableLessonDao().saveInTx(lessonList);
+
+        LogUtils.debug("Synchronization lessons (amount = " + lessonList.size() + ")");
+    }
+
+    private String getNormalizedDate(String date) throws ParseException {
+        return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
+    }
+
+    private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
+            throws IOException, NotLoggedInErrorException, ParseException {
+        return vulcan.getTimetable().getWeekTable(date);
+    }
+
+    private Week getWeekFromDb(String date) {
+        return daoSession.getWeekDao()
+                .queryBuilder()
+                .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
+                .unique();
+    }
+
+    private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) {
+        if (dbEntity != null) {
+            dbEntity.setIsTimetableSynced(true);
+            dbEntity.update();
+
+            return dbEntity.getId();
+        }
+
+        Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
+        apiEntity.setIsTimetableSynced(true);
+
+        return daoSession.getWeekDao().insert(apiEntity);
+    }
+
+    private List<TimetableLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
+        List<TimetableLesson> updatedLessonList = new ArrayList<>();
+
+        for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
+
+            Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
+
+            Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
+
+            long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
+
+            updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
+        }
+
+        return updatedLessonList;
+    }
+
+    private Day getDayFromDb(String date) {
+        return daoSession.getDayDao()
+                .queryBuilder()
+                .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
+                .unique();
+    }
+
+    private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) {
+        apiDayEntity.setUserId(userId);
+        apiDayEntity.setWeekId(weekId);
+
+        if (null != dayFromDb) {
+            apiDayEntity.setId(dayFromDb.getId());
+
+            daoSession.getDayDao().save(apiDayEntity);
+            dayFromDb.refresh();
+
+            return dayFromDb.getId();
+        }
+
+        return daoSession.getDayDao().insert(apiDayEntity);
+    }
+
+    private void updateLessons(List<Lesson> lessons, List<TimetableLesson> updatedLessons, long dayId) {
+        List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
+                .lessonsToTimetableLessonsEntities(lessons);
+
+        for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
+            TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
+
+            apiLessonEntity.setDayId(dayId);
+
+            if (lessonFromDb != null) {
+                apiLessonEntity.setId(lessonFromDb.getId());
+            }
+
+            if (!"".equals(apiLessonEntity.getSubject())) {
+                updatedLessons.add(apiLessonEntity);
+            }
+        }
+    }
+
+    private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) {
+        return daoSession.getTimetableLessonDao().queryBuilder()
+                .where(TimetableLessonDao.Properties.DayId.eq(dayId),
+                        TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()),
+                        TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()),
+                        TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
+                .unique();
+    }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java
index 01a0fc02..b26abaaf 100644
--- a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java
+++ b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java
@@ -4,6 +4,7 @@ import dagger.Component;
 import io.github.wulkanowy.di.annotations.PerFragment;
 import io.github.wulkanowy.di.modules.FragmentModule;
 import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
+import io.github.wulkanowy.ui.main.attendance.AttendanceTabFragment;
 import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
 import io.github.wulkanowy.ui.main.grades.GradesFragment;
 import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
@@ -17,6 +18,8 @@ public interface FragmentComponent {
 
     void inject(AttendanceFragment attendanceFragment);
 
+    void inject(AttendanceTabFragment attendanceTabFragment);
+
     void inject(DashboardFragment dashboardFragment);
 
     void inject(TimetableFragment timetableFragment);
diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java
index 58e9909b..46744c90 100644
--- a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java
+++ b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java
@@ -21,6 +21,8 @@ import io.github.wulkanowy.data.db.resources.ResourcesContract;
 import io.github.wulkanowy.data.db.shared.SharedPref;
 import io.github.wulkanowy.data.db.shared.SharedPrefContract;
 import io.github.wulkanowy.data.sync.SyncContract;
+import io.github.wulkanowy.data.sync.attendance.AttendanceSync;
+import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
 import io.github.wulkanowy.data.sync.grades.GradeSync;
 import io.github.wulkanowy.data.sync.login.LoginSync;
 import io.github.wulkanowy.data.sync.login.LoginSyncContract;
@@ -122,6 +124,12 @@ public class ApplicationModule {
         return timetableSync;
     }
 
+    @Singleton
+    @Provides
+    AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) {
+        return attendanceSync;
+    }
+
     @Provides
     FirebaseJobDispatcher provideDispatcher() {
         return new FirebaseJobDispatcher(new GooglePlayDriver(application));
diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java
index 8999bca8..3479a8c4 100644
--- a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java
+++ b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java
@@ -7,7 +7,11 @@ import dagger.Provides;
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import io.github.wulkanowy.di.annotations.PerFragment;
 import io.github.wulkanowy.ui.main.attendance.AttendanceContract;
+import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem;
+import io.github.wulkanowy.ui.main.attendance.AttendancePagerAdapter;
 import io.github.wulkanowy.ui.main.attendance.AttendancePresenter;
+import io.github.wulkanowy.ui.main.attendance.AttendanceTabContract;
+import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter;
 import io.github.wulkanowy.ui.main.dashboard.DashboardContract;
 import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter;
 import io.github.wulkanowy.ui.main.grades.GradeHeaderItem;
@@ -47,6 +51,22 @@ public class FragmentModule {
         return dashboardPresenter;
     }
 
+    @PerFragment
+    @Provides
+    AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) {
+        return timetableTabPresenter;
+    }
+
+    @Provides
+    AttendancePagerAdapter provideAttendancePagerAdapter() {
+        return new AttendancePagerAdapter(fragment.getChildFragmentManager());
+    }
+
+    @Provides
+    FlexibleAdapter<AttendanceHeaderItem> provideAttendanceTabAdapter() {
+        return new FlexibleAdapter<>(null);
+    }
+
     @PerFragment
     @Provides
     TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) {
@@ -65,12 +85,12 @@ public class FragmentModule {
     }
 
     @Provides
-    FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
+    FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
         return new FlexibleAdapter<>(null);
     }
 
     @Provides
-    FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
+    FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
         return new FlexibleAdapter<>(null);
     }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java
index d4c0b9da..cc9b1dc8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java
@@ -18,6 +18,7 @@ import butterknife.ButterKnife;
 import io.github.wulkanowy.R;
 import io.github.wulkanowy.services.SyncJob;
 import io.github.wulkanowy.ui.base.BaseActivity;
+import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
 import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
 import io.github.wulkanowy.ui.main.grades.GradesFragment;
 import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
@@ -134,7 +135,7 @@ public class MainActivity extends BaseActivity implements MainContract.View,
 
     private void initiationViewPager() {
         pagerAdapter.addFragment(new GradesFragment());
-        pagerAdapter.addFragment(new DashboardFragment());
+        pagerAdapter.addFragment(new AttendanceFragment());
         pagerAdapter.addFragment(new DashboardFragment());
         pagerAdapter.addFragment(new TimetableFragment());
         pagerAdapter.addFragment(new DashboardFragment());
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java
similarity index 64%
rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java
rename to app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java
index 156d1759..3af29534 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java
@@ -1,35 +1,35 @@
-package io.github.wulkanowy.ui.main.timetable;
+package io.github.wulkanowy.ui.main;
 
 import android.support.v4.app.Fragment;
 
 import java.util.ArrayList;
 import java.util.List;
 
-class TabsData {
+public class TabsData {
 
     private List<Fragment> fragments = new ArrayList<>();
 
     private List<String> titles = new ArrayList<>();
 
-    Fragment getFragment(int index) {
+    public Fragment getFragment(int index) {
         return fragments.get(index);
     }
 
-    void addFragment(Fragment fragment) {
+    public void addFragment(Fragment fragment) {
         if (fragment != null) {
             fragments.add(fragment);
         }
     }
 
-    int getFragmentsCount() {
+    public int getFragmentsCount() {
         return fragments.size();
     }
 
-    String getTitle(int index) {
+    public String getTitle(int index) {
         return titles.get(index);
     }
 
-    void addTitle(String title) {
+    public void addTitle(String title) {
         if (title != null) {
             titles.add(title);
         }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java
index fe403717..f8d3b302 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java
@@ -1,8 +1,9 @@
 package io.github.wulkanowy.ui.main.attendance;
 
-import io.github.wulkanowy.di.annotations.PerFragment;
+import io.github.wulkanowy.di.annotations.PerActivity;
 import io.github.wulkanowy.ui.base.BaseContract;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
 
 public interface AttendanceContract {
 
@@ -10,14 +11,26 @@ public interface AttendanceContract {
 
         void setActivityTitle();
 
+        void scrollViewPagerToPosition(int position);
+
+        void setTabDataToAdapter(TabsData tabsData);
+
+        void setAdapterWithTabLayout();
+
+        void setChildFragmentSelected(int position, boolean selected);
+
         boolean isMenuVisible();
     }
 
-    @PerFragment
+    @PerActivity
     interface Presenter extends BaseContract.Presenter<View> {
 
-        void onStart(View view, OnFragmentIsReadyListener listener);
-
         void onFragmentVisible(boolean isVisible);
+
+        void onTabSelected(int position);
+
+        void onTabUnselected(int position);
+
+        void onStart(View view, OnFragmentIsReadyListener listener);
     }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java
new file mode 100644
index 00000000..f5bbcb41
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java
@@ -0,0 +1,91 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import io.github.wulkanowy.R;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+
+public class AttendanceDialogFragment extends DialogFragment {
+
+    private static final String ARGUMENT_KEY = "Item";
+
+    private AttendanceLesson lesson;
+
+    @BindView(R.id.attendance_dialog_subject_value)
+    TextView subject;
+
+    @BindView(R.id.attendance_dialog_date_value)
+    TextView date;
+
+    @BindView(R.id.attendance_dialog_number_value)
+    TextView number;
+
+    @BindView(R.id.attendance_dialog_description_value)
+    TextView description;
+
+    public AttendanceDialogFragment() {
+        //empty constructor for fragment
+    }
+
+    public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) {
+        AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment();
+
+        Bundle bundle = new Bundle();
+        bundle.putSerializable(ARGUMENT_KEY, lesson);
+
+        dialogFragment.setArguments(bundle);
+
+        return dialogFragment;
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY);
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.attendance_dialog, container, false);
+
+        ButterKnife.bind(this, view);
+
+        if (!lesson.getSubject().isEmpty()) {
+            subject.setText(lesson.getSubject());
+        }
+
+        if (!lesson.getDate().isEmpty()) {
+            date.setText(lesson.getDate());
+        }
+
+        if (0 != lesson.getNumber()) {
+            number.setText(String.valueOf(lesson.getNumber()));
+        }
+
+        description.setText(lesson.getDescription());
+
+        if (lesson.getIsAbsenceUnexcused()) {
+            description.setTextColor(getResources().getColor(R.color.colorPrimaryDark));
+        }
+
+        return view;
+    }
+
+    @OnClick(R.id.attendance_dialog_close)
+    void onClickCloseButton() {
+        dismiss();
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java
index 6010d108..82bb2520 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java
@@ -2,30 +2,42 @@ package io.github.wulkanowy.ui.main.attendance;
 
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import javax.inject.Inject;
 
+import butterknife.BindView;
 import butterknife.ButterKnife;
 import io.github.wulkanowy.R;
 import io.github.wulkanowy.di.component.FragmentComponent;
 import io.github.wulkanowy.ui.base.BaseFragment;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
 
-public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
+public class AttendanceFragment extends BaseFragment implements AttendanceContract.View, TabLayout.OnTabSelectedListener {
+
+    @BindView(R.id.attendance_fragment_viewpager)
+    ViewPager viewPager;
+
+    @BindView(R.id.attendance_fragment_tab_layout)
+    TabLayout tabLayout;
+
+    @Inject
+    AttendancePagerAdapter pagerAdapter;
 
     @Inject
     AttendanceContract.Presenter presenter;
 
-    public AttendanceFragment() {
-        // empty constructor for fragment
-    }
-
+    @Nullable
     @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_attendance, container, false);
 
         FragmentComponent component = getFragmentComponent();
@@ -46,14 +58,60 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
         }
     }
 
+    @Override
+    public void onTabSelected(TabLayout.Tab tab) {
+        presenter.onTabSelected(tab.getPosition());
+    }
+
+    @Override
+    public void onTabUnselected(TabLayout.Tab tab) {
+        presenter.onTabUnselected(tab.getPosition());
+    }
+
+    @Override
+    public void onTabReselected(TabLayout.Tab tab) {
+        //do nothing
+    }
+
+    @Override
+    public void setTabDataToAdapter(TabsData tabsData) {
+        pagerAdapter.setTabsData(tabsData);
+    }
+
+    @Override
+    public void setAdapterWithTabLayout() {
+        viewPager.setAdapter(pagerAdapter);
+
+        tabLayout.setupWithViewPager(viewPager);
+        tabLayout.addOnTabSelectedListener(this);
+    }
+
+    @Override
+    public void setChildFragmentSelected(int position, boolean selected) {
+        ((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
+    }
+
+    @Override
+    public void scrollViewPagerToPosition(int position) {
+        viewPager.setCurrentItem(position, false);
+    }
+
     @Override
     public void setActivityTitle() {
-        setTitle(getString(R.string.dashboard_text));
+        setTitle(getString(R.string.attendance_text));
+    }
+
+    @Override
+    public void onError(String message) {
+        if (getActivity() != null) {
+            Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
+                    message, Snackbar.LENGTH_LONG).show();
+        }
     }
 
     @Override
     public void onDestroyView() {
-        super.onDestroyView();
         presenter.onDestroy();
+        super.onDestroyView();
     }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java
new file mode 100644
index 00000000..0270f7c1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java
@@ -0,0 +1,136 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import java.util.List;
+
+import butterknife.BindColor;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
+import eu.davidea.viewholders.ExpandableViewHolder;
+import io.github.wulkanowy.R;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+import io.github.wulkanowy.data.db.dao.entities.Day;
+
+public class AttendanceHeaderItem
+        extends AbstractExpandableHeaderItem<AttendanceHeaderItem.HeaderViewHolder, AttendanceSubItem> {
+
+    private Day day;
+
+    AttendanceHeaderItem(Day day) {
+        this.day = day;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AttendanceHeaderItem that = (AttendanceHeaderItem) o;
+
+        return new EqualsBuilder()
+                .append(day, that.day)
+                .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37)
+                .append(day)
+                .toHashCode();
+    }
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.attendance_header;
+    }
+
+    @Override
+    public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
+        return new HeaderViewHolder(view, adapter);
+    }
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
+        holder.onBind(day, getSubItems());
+    }
+
+    static class HeaderViewHolder extends ExpandableViewHolder {
+
+        @BindView(R.id.attendance_header_day)
+        TextView dayName;
+
+        @BindView(R.id.attendance_header_date)
+        TextView date;
+
+        @BindView(R.id.attendance_header_description)
+        TextView description;
+
+        @BindView(R.id.attendance_header_alert_image)
+        ImageView alert;
+
+        @BindView(R.id.attendance_header_free_name)
+        TextView freeName;
+
+        @BindColor(R.color.secondary_text)
+        int secondaryColor;
+
+        @BindColor(R.color.free_day)
+        int backgroundFreeDay;
+
+        HeaderViewHolder(View view, FlexibleAdapter adapter) {
+            super(view, adapter);
+            view.setOnClickListener(this);
+            ButterKnife.bind(this, view);
+        }
+
+        void onBind(Day item, List<AttendanceSubItem> subItems) {
+            dayName.setText(StringUtils.capitalize(item.getDayName()));
+            date.setText(item.getDate());
+
+            int numberOfHours = countNotPresentHours(subItems);
+            description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences,
+                    numberOfHours, numberOfHours)));
+            description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE);
+            alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE);
+            freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE);
+
+            if (item.getAttendanceLessons().isEmpty()) {
+                ((FrameLayout) getContentView()).setForeground(null);
+                getContentView().setBackgroundColor(backgroundFreeDay);
+                dayName.setTextColor(secondaryColor);
+            }
+        }
+
+        private int countNotPresentHours(List<AttendanceSubItem> subItems) {
+            int i = 0;
+            for (AttendanceSubItem subItem : subItems) {
+                if (subItem.getLesson().getIsAbsenceUnexcused()) {
+                    i++;
+                }
+            }
+
+            return i;
+        }
+
+        private boolean isSubItemsHasChanges(List<AttendanceSubItem> subItems) {
+            for (AttendanceSubItem subItem : subItems) {
+                if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson().getIsUnexcusedLateness()) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java
new file mode 100644
index 00000000..3cabff71
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java
@@ -0,0 +1,38 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+
+import io.github.wulkanowy.ui.main.TabsData;
+
+public class AttendancePagerAdapter extends FragmentStatePagerAdapter {
+
+    private TabsData tabsData;
+
+    public AttendancePagerAdapter(FragmentManager fragmentManager) {
+        super(fragmentManager);
+    }
+
+    void setTabsData(TabsData tabsData) {
+        this.tabsData = tabsData;
+    }
+
+    @Override
+    public Fragment getItem(int position) {
+        return tabsData.getFragment(position);
+    }
+
+
+    @Override
+    public int getCount() {
+        return tabsData.getFragmentsCount();
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return tabsData.getTitle(position);
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java
index da43e320..73dd44ca 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java
@@ -1,16 +1,33 @@
 package io.github.wulkanowy.ui.main.attendance;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.inject.Inject;
 
 import io.github.wulkanowy.data.RepositoryContract;
 import io.github.wulkanowy.ui.base.BasePresenter;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
+import io.github.wulkanowy.utils.TimeUtils;
+import io.github.wulkanowy.utils.async.AbstractTask;
+import io.github.wulkanowy.utils.async.AsyncListeners;
 
 public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
-        implements AttendanceContract.Presenter {
+        implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener {
+
+    private AbstractTask loadingTask;
+
+    private List<String> dates = new ArrayList<>();
+
+    private TabsData tabsData = new TabsData();
 
     private OnFragmentIsReadyListener listener;
 
+    private int positionToScroll;
+
+    private boolean isFirstSight = false;
+
     @Inject
     AttendancePresenter(RepositoryContract repository) {
         super(repository);
@@ -25,7 +42,18 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
             getView().setActivityTitle();
         }
 
-        this.listener.onFragmentIsReady();
+        if (dates.isEmpty()) {
+            dates = TimeUtils.getMondaysFromCurrentSchoolYear();
+        }
+        positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
+
+        if (!isFirstSight) {
+            isFirstSight = true;
+
+            loadingTask = new AbstractTask();
+            loadingTask.setOnFirstLoadingListener(this);
+            loadingTask.execute();
+        }
     }
 
     @Override
@@ -34,4 +62,50 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
             getView().setActivityTitle();
         }
     }
+
+    @Override
+    public void onTabSelected(int position) {
+        getView().setChildFragmentSelected(position, true);
+    }
+
+    @Override
+    public void onTabUnselected(int position) {
+        getView().setChildFragmentSelected(position, false);
+    }
+
+    @Override
+    public void onDoInBackgroundLoading() throws Exception {
+        for (String date : dates) {
+            tabsData.addTitle(date);
+            tabsData.addFragment(AttendanceTabFragment.newInstance(date));
+        }
+    }
+
+    @Override
+    public void onCanceledLoadingAsync() {
+        //do nothing
+
+    }
+
+    @Override
+    public void onEndLoadingAsync(boolean result, Exception exception) {
+        if (result) {
+            getView().setTabDataToAdapter(tabsData);
+            getView().setAdapterWithTabLayout();
+            getView().scrollViewPagerToPosition(positionToScroll);
+            listener.onFragmentIsReady();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        isFirstSight = false;
+
+        if (loadingTask != null) {
+            loadingTask.cancel(true);
+            loadingTask = null;
+        }
+
+        super.onDestroy();
+    }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java
new file mode 100644
index 00000000..6da35555
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java
@@ -0,0 +1,119 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import android.content.Context;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
+import eu.davidea.viewholders.FlexibleViewHolder;
+import io.github.wulkanowy.R;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+
+class AttendanceSubItem
+        extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeaderItem> {
+
+    private AttendanceLesson lesson;
+
+    AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) {
+        super(header);
+        this.lesson = lesson;
+    }
+
+    AttendanceLesson getLesson() {
+        return lesson;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AttendanceSubItem that = (AttendanceSubItem) o;
+
+        return new EqualsBuilder()
+                .append(lesson, that.lesson)
+                .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37)
+                .append(lesson)
+                .toHashCode();
+    }
+
+    @Override
+    public int getLayoutRes() {
+        return R.layout.attendance_subitem;
+    }
+
+    @Override
+    public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
+        return new AttendanceSubItem.SubItemViewHolder(view, adapter);
+    }
+
+    @Override
+    public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) {
+        holder.onBind(lesson);
+    }
+
+    static class SubItemViewHolder extends FlexibleViewHolder {
+
+        @BindView(R.id.attendance_subItem_lesson)
+        TextView lessonName;
+
+        @BindView(R.id.attendance_subItem_number)
+        TextView lessonNumber;
+
+        @BindView(R.id.attendance_subItem_description)
+        TextView lessonDescription;
+
+        @BindView(R.id.attendance_subItem_alert_image)
+        ImageView alert;
+
+        private Context context;
+
+        private AttendanceLesson item;
+
+        SubItemViewHolder(View view, FlexibleAdapter adapter) {
+            super(view, adapter);
+            ButterKnife.bind(this, view);
+            context = view.getContext();
+            view.setOnClickListener(this);
+        }
+
+        void onBind(AttendanceLesson lesson) {
+            item = lesson;
+
+            lessonName.setText(lesson.getSubject());
+            lessonNumber.setText((String.valueOf(lesson.getNumber())));
+            lessonDescription.setText(lesson.getDescription());
+            alert.setVisibility(lesson.getIsAbsenceUnexcused() || lesson.getIsUnexcusedLateness()
+                    ? View.VISIBLE : View.INVISIBLE);
+        }
+
+        @Override
+        public void onClick(View view) {
+            super.onClick(view);
+            showDialog();
+        }
+
+        private void showDialog() {
+            AttendanceDialogFragment dialogFragment = AttendanceDialogFragment.newInstance(item);
+            dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
+            dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString());
+        }
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java
new file mode 100644
index 00000000..1bac005d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java
@@ -0,0 +1,32 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import java.util.List;
+
+import io.github.wulkanowy.ui.base.BaseContract;
+
+public interface AttendanceTabContract {
+
+    interface View extends BaseContract.View {
+
+        void updateAdapterList(List<AttendanceHeaderItem> headerItems);
+
+        void onRefreshSuccess();
+
+        void hideRefreshingBar();
+
+        void showNoItem(boolean show);
+
+        void showProgressBar(boolean show);
+    }
+
+    interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> {
+
+        void onFragmentSelected(boolean isSelected);
+
+        void setArgumentDate(String date);
+
+        void onStart(AttendanceTabContract.View view, boolean isPrimary);
+
+        void onRefresh();
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java
new file mode 100644
index 00000000..90313daa
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java
@@ -0,0 +1,170 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
+import io.github.wulkanowy.R;
+import io.github.wulkanowy.di.component.FragmentComponent;
+import io.github.wulkanowy.ui.base.BaseFragment;
+
+public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View,
+        SwipeRefreshLayout.OnRefreshListener {
+
+    private static final String ARGUMENT_KEY = "date";
+
+    private static final String SAVED_KEY = "isSelected";
+
+    private boolean isPrimary = false;
+
+    private boolean isSelected = false;
+
+    @BindView(R.id.attendance_tab_fragment_recycler)
+    RecyclerView recyclerView;
+
+    @BindView(R.id.attendance_tab_fragment_swipe_refresh)
+    SwipeRefreshLayout refreshLayout;
+
+    @BindView(R.id.attendance_tab_fragment_progress_bar)
+    View progressBar;
+
+    @BindView(R.id.attendance_tab_fragment_no_item_container)
+    View noItemView;
+
+    @Inject
+    AttendanceTabContract.Presenter presenter;
+
+    @Inject
+    FlexibleAdapter<AttendanceHeaderItem> adapter;
+
+    public static AttendanceTabFragment newInstance(String date) {
+        AttendanceTabFragment fragmentTab = new AttendanceTabFragment();
+
+        Bundle bundle = new Bundle();
+        bundle.putString(ARGUMENT_KEY, date);
+        fragmentTab.setArguments(bundle);
+
+        return fragmentTab;
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false);
+
+        FragmentComponent component = getFragmentComponent();
+        if (component != null) {
+            component.inject(this);
+            setButterKnife(ButterKnife.bind(this, view));
+
+            if (getArguments() != null) {
+                presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
+            }
+
+            presenter.onStart(this, isPrimary);
+        }
+        return view;
+    }
+
+    @Override
+    protected void setUpOnViewCreated(View fragmentView) {
+        adapter.setAutoCollapseOnExpand(true);
+        adapter.setAutoScrollOnExpand(true);
+        adapter.expandItemsAtStartUp();
+
+        recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
+        recyclerView.setAdapter(adapter);
+
+        refreshLayout.setColorSchemeResources(android.R.color.black);
+        refreshLayout.setOnRefreshListener(this);
+
+    }
+
+    @Override
+    public void updateAdapterList(List<AttendanceHeaderItem> headerItems) {
+        adapter.updateDataSet(headerItems);
+    }
+
+    @Override
+    public void setMenuVisibility(boolean menuVisible) {
+        super.setMenuVisibility(menuVisible);
+        if (presenter != null && getView() != null) {
+            presenter.onFragmentSelected(isSelected);
+        } else if (isSelected) {
+            isPrimary = true;
+        }
+    }
+
+    @Override
+    public void onRefresh() {
+        presenter.onRefresh();
+    }
+
+    @Override
+    public void onRefreshSuccess() {
+        onError(R.string.timetable_refresh_success);
+    }
+
+    @Override
+    public void hideRefreshingBar() {
+        refreshLayout.setRefreshing(false);
+    }
+
+    @Override
+    public void showProgressBar(boolean show) {
+        progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    @Override
+    public void showNoItem(boolean show) {
+        noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    public void setSelected(boolean selected) {
+        isSelected = selected;
+    }
+
+    @Override
+    public void onError(String message) {
+        if (getActivity() != null) {
+            Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
+                    message, Snackbar.LENGTH_LONG).show();
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        outState.putBoolean(SAVED_KEY, isSelected);
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public void onDestroyView() {
+        isPrimary = false;
+        presenter.onDestroy();
+        super.onDestroyView();
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java
new file mode 100644
index 00000000..7f4766c9
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java
@@ -0,0 +1,175 @@
+package io.github.wulkanowy.ui.main.attendance;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import io.github.wulkanowy.data.RepositoryContract;
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
+import io.github.wulkanowy.data.db.dao.entities.Day;
+import io.github.wulkanowy.data.db.dao.entities.Week;
+import io.github.wulkanowy.ui.base.BasePresenter;
+import io.github.wulkanowy.utils.async.AbstractTask;
+import io.github.wulkanowy.utils.async.AsyncListeners;
+
+public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.View>
+        implements AttendanceTabContract.Presenter, AsyncListeners.OnRefreshListener,
+        AsyncListeners.OnFirstLoadingListener {
+
+    private AbstractTask refreshTask;
+
+    private AbstractTask loadingTask;
+
+    private List<AttendanceHeaderItem> headerItems = new ArrayList<>();
+
+    private String date;
+
+    private boolean isFirstSight = false;
+
+    @Inject
+    AttendanceTabPresenter(RepositoryContract repository) {
+        super(repository);
+    }
+
+    @Override
+    public void onStart(AttendanceTabContract.View view, boolean isPrimary) {
+        super.onStart(view);
+        getView().showProgressBar(true);
+        getView().showNoItem(false);
+        onFragmentSelected(isPrimary);
+    }
+
+    @Override
+    public void onFragmentSelected(boolean isSelected) {
+        if (!isFirstSight && isSelected) {
+            isFirstSight = true;
+
+            loadingTask = new AbstractTask();
+            loadingTask.setOnFirstLoadingListener(this);
+            loadingTask.execute();
+        }
+    }
+
+    @Override
+    public void onRefresh() {
+        if (getView().isNetworkConnected()) {
+            refreshTask = new AbstractTask();
+            refreshTask.setOnRefreshListener(this);
+            refreshTask.execute();
+        } else {
+            getView().onNoNetworkError();
+            getView().hideRefreshingBar();
+        }
+    }
+
+    @Override
+    public void onDoInBackgroundRefresh() throws Exception {
+        syncData();
+    }
+
+    @Override
+    public void onCanceledRefreshAsync() {
+        if (isViewAttached()) {
+            getView().hideRefreshingBar();
+        }
+    }
+
+    @Override
+    public void onEndRefreshAsync(boolean result, Exception exception) {
+        if (result) {
+            loadingTask = new AbstractTask();
+            loadingTask.setOnFirstLoadingListener(this);
+            loadingTask.execute();
+
+            getView().onRefreshSuccess();
+        } else {
+            getView().onError(getRepository().getErrorLoginMessage(exception));
+        }
+        getView().hideRefreshingBar();
+    }
+
+    @Override
+    public void onDoInBackgroundLoading() throws Exception {
+        Week week = getRepository().getWeek(date);
+
+        if (week == null || !week.getIsAttendanceSynced()) {
+            syncData();
+            week = getRepository().getWeek(date);
+        }
+
+        List<Day> dayList = week.getDayList();
+
+        headerItems = new ArrayList<>();
+
+        boolean isEmptyWeek = true;
+
+        for (Day day : dayList) {
+            AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day);
+
+            if (isEmptyWeek) {
+                isEmptyWeek = day.getAttendanceLessons().isEmpty();
+            }
+
+            List<AttendanceLesson> lessonList = day.getAttendanceLessons();
+
+            List<AttendanceSubItem> subItems = new ArrayList<>();
+
+            for (AttendanceLesson lesson : lessonList) {
+                lesson.setDescription(getRepository().getAttendanceLessonDescription(lesson));
+                subItems.add(new AttendanceSubItem(headerItem, lesson));
+            }
+
+            headerItem.setSubItems(subItems);
+            headerItem.setExpanded(false);
+            headerItems.add(headerItem);
+        }
+
+        if (isEmptyWeek) {
+            headerItems = new ArrayList<>();
+        }
+    }
+
+    @Override
+    public void onCanceledLoadingAsync() {
+        // do nothing
+    }
+
+    @Override
+    public void onEndLoadingAsync(boolean result, Exception exception) {
+        if (headerItems.isEmpty()) {
+            getView().showNoItem(true);
+            getView().updateAdapterList(null);
+        } else {
+            getView().updateAdapterList(headerItems);
+            getView().showNoItem(false);
+        }
+        getView().showProgressBar(false);
+    }
+
+    @Override
+    public void setArgumentDate(String date) {
+        this.date = date;
+    }
+
+    private void syncData() throws Exception {
+        getRepository().loginCurrentUser();
+        getRepository().syncAttendance(date);
+    }
+
+    @Override
+    public void onDestroy() {
+        isFirstSight = false;
+
+        if (refreshTask != null) {
+            refreshTask.cancel(true);
+            refreshTask = null;
+        }
+        if (loadingTask != null) {
+            loadingTask.cancel(true);
+            loadingTask = null;
+        }
+
+        super.onDestroy();
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java
index 18fa33f9..eaad0505 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java
@@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.main.timetable;
 import io.github.wulkanowy.di.annotations.PerActivity;
 import io.github.wulkanowy.ui.base.BaseContract;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
 
 public interface TimetableContract {
 
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java
index cfc1e073..b9390195 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java
@@ -15,13 +15,13 @@ import butterknife.BindView;
 import butterknife.ButterKnife;
 import butterknife.OnClick;
 import io.github.wulkanowy.R;
-import io.github.wulkanowy.data.db.dao.entities.Lesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
 
 public class TimetableDialogFragment extends DialogFragment {
 
     private static final String ARGUMENT_KEY = "Item";
 
-    private Lesson lesson;
+    private TimetableLesson lesson;
 
     @BindView(R.id.timetable_dialog_lesson_value)
     TextView lessonName;
@@ -54,7 +54,7 @@ public class TimetableDialogFragment extends DialogFragment {
         //empty constructor for fragment
     }
 
-    public static TimetableDialogFragment newInstance(Lesson lesson) {
+    public static TimetableDialogFragment newInstance(TimetableLesson lesson) {
         TimetableDialogFragment dialogFragment = new TimetableDialogFragment();
 
         Bundle bundle = new Bundle();
@@ -69,7 +69,7 @@ public class TimetableDialogFragment extends DialogFragment {
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         if (getArguments() != null) {
-            lesson = (Lesson) getArguments().getSerializable(ARGUMENT_KEY);
+            lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY);
         }
     }
 
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java
index a9a83391..8bef4fb2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java
@@ -18,6 +18,7 @@ import io.github.wulkanowy.R;
 import io.github.wulkanowy.di.component.FragmentComponent;
 import io.github.wulkanowy.ui.base.BaseFragment;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
 
 public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener {
 
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java
index 6393179e..39a8f012 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java
@@ -94,10 +94,10 @@ public class TimetableHeaderItem
             dayName.setText(StringUtils.capitalize(item.getDayName()));
             date.setText(item.getDate());
             alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE);
-            freeName.setVisibility(item.isFreeDay() ? View.VISIBLE : View.INVISIBLE);
+            freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE);
             freeName.setText(item.getFreeDayName());
 
-            if (item.isFreeDay()) {
+            if (item.getIsFreeDay()) {
                 ((FrameLayout) getContentView()).setForeground(null);
                 getContentView().setBackgroundColor(backgroundFreeDay);
                 dayName.setTextColor(secondaryColor);
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java
index 24e7582d..0b852342 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java
@@ -5,6 +5,8 @@ import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
 
+import io.github.wulkanowy.ui.main.TabsData;
+
 public class TimetablePagerAdapter extends FragmentStatePagerAdapter {
 
     private TabsData tabsData;
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java
index 5d17a442..e816a190 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java
@@ -8,6 +8,7 @@ import javax.inject.Inject;
 import io.github.wulkanowy.data.RepositoryContract;
 import io.github.wulkanowy.ui.base.BasePresenter;
 import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
+import io.github.wulkanowy.ui.main.TabsData;
 import io.github.wulkanowy.utils.TimeUtils;
 import io.github.wulkanowy.utils.async.AbstractTask;
 import io.github.wulkanowy.utils.async.AsyncListeners;
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java
index b4212940..ca82f783 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java
@@ -8,6 +8,9 @@ import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
 import java.util.List;
 
 import butterknife.BindView;
@@ -16,26 +19,41 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
 import eu.davidea.viewholders.FlexibleViewHolder;
 import io.github.wulkanowy.R;
-import io.github.wulkanowy.data.db.dao.entities.Lesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
 
 
 public class TimetableSubItem
         extends AbstractSectionableItem<TimetableSubItem.SubItemViewHolder, TimetableHeaderItem> {
 
-    private Lesson lesson;
+    private TimetableLesson lesson;
 
-    public TimetableSubItem(TimetableHeaderItem header, Lesson lesson) {
+    public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
         super(header);
         this.lesson = lesson;
     }
 
-    public Lesson getLesson() {
+    public TimetableLesson getLesson() {
         return lesson;
     }
 
     @Override
     public boolean equals(Object o) {
-        return this == o;
+        if (this == o) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TimetableSubItem that = (TimetableSubItem) o;
+
+        return new EqualsBuilder()
+                .append(lesson, that.lesson)
+                .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37)
+                .append(lesson)
+                .toHashCode();
     }
 
     @Override
@@ -72,7 +90,7 @@ public class TimetableSubItem
 
         private Context context;
 
-        private Lesson item;
+        private TimetableLesson item;
 
         SubItemViewHolder(View view, FlexibleAdapter adapter) {
             super(view, adapter);
@@ -81,7 +99,7 @@ public class TimetableSubItem
             view.setOnClickListener(this);
         }
 
-        void onBind(Lesson lesson) {
+        void onBind(TimetableLesson lesson) {
             item = lesson;
 
             lessonName.setText(lesson.getSubject());
diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java
index 43ead53e..00dcef6a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java
+++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java
@@ -8,7 +8,7 @@ import javax.inject.Inject;
 
 import io.github.wulkanowy.data.RepositoryContract;
 import io.github.wulkanowy.data.db.dao.entities.Day;
-import io.github.wulkanowy.data.db.dao.entities.Lesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
 import io.github.wulkanowy.data.db.dao.entities.Week;
 import io.github.wulkanowy.ui.base.BasePresenter;
 import io.github.wulkanowy.utils.async.AbstractTask;
@@ -96,7 +96,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
     public void onDoInBackgroundLoading() throws Exception {
         Week week = getRepository().getWeek(date);
 
-        if (week == null) {
+        if (week == null || !week.getIsTimetableSynced()) {
             syncData();
             week = getRepository().getWeek(date);
         }
@@ -111,14 +111,14 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
             TimetableHeaderItem headerItem = new TimetableHeaderItem(day);
 
             if (isFreeWeek) {
-                isFreeWeek = day.isFreeDay();
+                isFreeWeek = day.getIsFreeDay();
             }
 
-            List<Lesson> lessonList = day.getLessons();
+            List<TimetableLesson> lessonList = day.getTimetableLessons();
 
             List<TimetableSubItem> subItems = new ArrayList<>();
 
-            for (Lesson lesson : lessonList) {
+            for (TimetableLesson lesson : lessonList) {
                 subItems.add(new TimetableSubItem(headerItem, lesson));
             }
 
@@ -143,8 +143,10 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
         if (headerItems.isEmpty()) {
             getView().showNoItem(true);
             getView().setFreeWeekName(freeWeekName);
+            getView().updateAdapterList(null);
         } else {
             getView().updateAdapterList(headerItems);
+            getView().showNoItem(false);
         }
         getView().showProgressBar(false);
     }
diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java
index 1dd5f641..a22e4d8b 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java
+++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java
@@ -4,10 +4,11 @@ package io.github.wulkanowy.utils;
 import java.util.ArrayList;
 import java.util.List;
 
+import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
 import io.github.wulkanowy.data.db.dao.entities.Day;
 import io.github.wulkanowy.data.db.dao.entities.Grade;
-import io.github.wulkanowy.data.db.dao.entities.Lesson;
 import io.github.wulkanowy.data.db.dao.entities.Subject;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
 import io.github.wulkanowy.data.db.dao.entities.Week;
 
 public final class DataObjectConverter {
@@ -60,7 +61,7 @@ public final class DataObjectConverter {
         return new Day()
                 .setDate(day.getDate())
                 .setDayName(day.getDayName())
-                .setFreeDay(day.isFreeDay())
+                .setIsFreeDay(day.isFreeDay())
                 .setFreeDayName(day.getFreeDayName());
     }
 
@@ -75,8 +76,8 @@ public final class DataObjectConverter {
         return dayEntityList;
     }
 
-    public static Lesson lessonToLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) {
-        return new Lesson()
+    public static TimetableLesson lessonToTimetableLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) {
+        return new TimetableLesson()
                 .setNumber(lesson.getNumber())
                 .setSubject(lesson.getSubject())
                 .setTeacher(lesson.getTeacher())
@@ -94,12 +95,36 @@ public final class DataObjectConverter {
                 .setNewMovedInOrChanged(lesson.isNewMovedInOrChanged());
     }
 
-    public static List<Lesson> lessonsToLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
+    public static AttendanceLesson lessonToAttendanceLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) {
+        return new AttendanceLesson()
+                .setNumber(Integer.valueOf(lesson.getNumber()))
+                .setSubject(lesson.getSubject())
+                .setDate(lesson.getDate())
+                .setIsPresence(lesson.isPresence())
+                .setIsAbsenceUnexcused(lesson.isAbsenceUnexcused())
+                .setIsAbsenceExcused(lesson.isAbsenceExcused())
+                .setIsUnexcusedLateness(lesson.isUnexcusedLateness())
+                .setIsAbsenceForSchoolReasons(lesson.isAbsenceForSchoolReasons())
+                .setIsExcusedLateness(lesson.isExcusedLateness())
+                .setIsExemption(lesson.isExemption());
+    }
 
-        List<Lesson> lessonEntityList = new ArrayList<>();
+    public static List<TimetableLesson> lessonsToTimetableLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
+
+        List<TimetableLesson> lessonEntityList = new ArrayList<>();
 
         for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
-            lessonEntityList.add(lessonToLessonEntity(lesson));
+            lessonEntityList.add(lessonToTimetableLessonEntity(lesson));
+        }
+        return lessonEntityList;
+    }
+
+    public static List<AttendanceLesson> lessonsToAttendanceLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
+
+        List<AttendanceLesson> lessonEntityList = new ArrayList<>();
+
+        for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
+            lessonEntityList.add(lessonToAttendanceLessonEntity(lesson));
         }
         return lessonEntityList;
     }
diff --git a/app/src/main/res/layout/attendance_dialog.xml b/app/src/main/res/layout/attendance_dialog.xml
new file mode 100644
index 00000000..916c993c
--- /dev/null
+++ b/app/src/main/res/layout/attendance_dialog.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minWidth="300dp"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:id="@+id/attendance_dialog_relative_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingBottom="20dp"
+            android:paddingEnd="20dp"
+            android:paddingLeft="20dp"
+            android:paddingRight="20dp"
+            android:paddingStart="20dp"
+            android:paddingTop="10dp"
+            tools:ignore="UselessParent">
+
+            <TextView
+                android:id="@+id/attendance_dialog_details"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_alignParentTop="true"
+                android:layout_gravity="start"
+                android:gravity="center_vertical"
+                android:maxLines="5"
+                android:minHeight="60dp"
+                android:minLines="2"
+                android:paddingTop="10dp"
+                android:text="@string/generic_dialog_details"
+                android:textIsSelectable="true"
+                android:textSize="20sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_subject"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_details"
+                android:layout_marginTop="10dp"
+                android:text="@string/attendance_dialog_subject"
+                android:textIsSelectable="true"
+                android:textSize="17sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_subject_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_subject"
+                android:layout_marginTop="3dp"
+                android:text="@string/generic_app_no_data"
+                android:textIsSelectable="true"
+                android:textSize="12sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_subject_value"
+                android:layout_marginTop="10dp"
+                android:text="@string/attendance_dialog_description"
+                android:textSize="17sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_description_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_description"
+                android:layout_marginTop="3dp"
+                android:text="@string/generic_app_no_data"
+                android:textIsSelectable="true"
+                android:textSize="12sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_description_value"
+                android:layout_marginTop="10dp"
+                android:text="@string/attendance_dialog_date"
+                android:textSize="17sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_date_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_date"
+                android:layout_marginTop="3dp"
+                android:text="@string/generic_app_no_data"
+                android:textIsSelectable="true"
+                android:textSize="12sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_number"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_date_value"
+                android:layout_marginTop="10dp"
+                android:text="@string/attendance_dialog_number"
+                android:textSize="17sp" />
+
+            <TextView
+                android:id="@+id/attendance_dialog_number_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_below="@+id/attendance_dialog_number"
+                android:layout_marginTop="3dp"
+                android:text="@string/generic_app_no_data"
+                android:textIsSelectable="true"
+                android:textSize="12sp" />
+
+            <Button
+                android:id="@+id/attendance_dialog_close"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_alignTop="@+id/attendance_dialog_number_value"
+                android:layout_marginTop="25dp"
+                android:background="?attr/selectableItemBackground"
+                android:focusable="true"
+                android:text="@string/generic_dialog_close"
+                android:textAllCaps="true"
+                android:textSize="15sp" />
+
+        </RelativeLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/app/src/main/res/layout/attendance_header.xml b/app/src/main/res/layout/attendance_header.xml
new file mode 100644
index 00000000..61dac631
--- /dev/null
+++ b/app/src/main/res/layout/attendance_header.xml
@@ -0,0 +1,86 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/ic_border"
+    android:foreground="?attr/selectableItemBackgroundBorderless">
+
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingBottom="@dimen/activity_vertical_margin"
+        android:paddingLeft="@dimen/activity_horizontal_margin"
+        android:paddingRight="@dimen/activity_horizontal_margin"
+        android:paddingTop="@dimen/activity_vertical_margin">
+
+        <TextView
+            android:id="@+id/attendance_header_day"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:layout_marginEnd="15dp"
+            android:layout_marginRight="15dp"
+            android:layout_toLeftOf="@id/attendance_header_alert_image"
+            android:layout_toStartOf="@+id/attendance_header_alert_image"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="@string/app_name"
+            android:textSize="19sp" />
+
+        <TextView
+            android:id="@+id/attendance_header_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:layout_below="@+id/attendance_header_day"
+            android:layout_marginTop="5dp"
+            android:maxLines="1"
+            android:text="@string/app_name"
+            android:textColor="@color/secondary_text"
+            android:textSize="14sp" />
+
+        <TextView
+            android:id="@+id/attendance_header_description"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@+id/attendance_header_date"
+            android:layout_toRightOf="@+id/attendance_header_date"
+            android:layout_below="@+id/attendance_header_day"
+            android:layout_marginLeft="10dp"
+            android:layout_marginStart="10dp"
+            android:layout_marginTop="5dp"
+            android:maxLines="1"
+            android:text="@string/app_name"
+            android:textColor="@color/secondary_text"
+            android:textSize="14sp" />
+
+        <TextView
+            android:id="@+id/attendance_header_free_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="180dp"
+            android:layout_marginStart="180dp"
+            android:gravity="end"
+            android:maxLines="2"
+            android:text="@string/attendance_no_entries"
+            android:textColor="@color/secondary_text"
+            android:textSize="16sp" />
+
+        <ImageView
+            android:id="@+id/attendance_header_alert_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="10dp"
+            app:srcCompat="@drawable/ic_exclamation_24dp"
+            tools:ignore="contentDescription" />
+
+    </RelativeLayout>
+</FrameLayout>
diff --git a/app/src/main/res/layout/attendance_subitem.xml b/app/src/main/res/layout/attendance_subitem.xml
new file mode 100644
index 00000000..9509333f
--- /dev/null
+++ b/app/src/main/res/layout/attendance_subitem.xml
@@ -0,0 +1,78 @@
+<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    xmlns:tool="http://schemas.android.com/tools"
+    android:id="@+id/attendance_subItem_cardView"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="7dp"
+    android:layout_marginEnd="5dp"
+    android:layout_marginLeft="5dp"
+    android:layout_marginRight="5dp"
+    android:layout_marginStart="5dp"
+    android:foreground="?attr/selectableItemBackgroundBorderless"
+    card_view:cardElevation="0dp">
+
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="7dp"
+        android:layout_marginEnd="7dp"
+        android:layout_marginLeft="7dp"
+        android:layout_marginRight="7dp"
+        android:layout_marginStart="7dp"
+        android:layout_marginTop="7dp">
+
+        <TextView
+            android:id="@+id/attendance_subItem_number"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_centerVertical="true"
+            android:gravity="center"
+            android:maxLength="2"
+            android:text="0"
+            android:textSize="32sp"
+            tool:ignore="all"/>
+
+        <TextView
+            android:id="@+id/attendance_subItem_lesson"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_marginEnd="40dp"
+            android:layout_marginLeft="10dp"
+            android:layout_marginRight="40dp"
+            android:layout_marginStart="10dp"
+            android:layout_toEndOf="@+id/attendance_subItem_number"
+            android:layout_toRightOf="@+id/attendance_subItem_number"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="@string/app_name"
+            android:textSize="17sp"
+            tool:ignore="all"/>
+
+        <TextView
+            android:id="@+id/attendance_subItem_description"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignBottom="@+id/attendance_subItem_number"
+            android:layout_alignLeft="@id/attendance_subItem_lesson"
+            android:layout_alignStart="@id/attendance_subItem_lesson"
+            android:maxLines="1"
+            android:text="@string/grades_text"
+            android:textColor="@color/secondary_text"
+            android:textSize="12sp" />
+
+        <ImageView
+            android:id="@+id/attendance_subItem_alert_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="10dp"
+            app:srcCompat="@drawable/ic_exclamation_24dp"
+            tool:ignore="contentDescription"/>
+
+    </RelativeLayout>
+</android.support.v7.widget.CardView>
diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml
index 6df75c8e..a1d578ab 100644
--- a/app/src/main/res/layout/fragment_attendance.xml
+++ b/app/src/main/res/layout/fragment_attendance.xml
@@ -1,36 +1,25 @@
 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    tools:context="io.github.wulkanowy.ui.main.attendance.AttendanceFragment">
+    android:layout_alignParentBottom="true"
+    android:id="@+id/attendance_fragment_container">
 
     <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="center">
+        android:layout_height="match_parent">
 
-        <ImageView
-            android:id="@+id/wrench_under_construction"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/textView3"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="45dp"
-            android:contentDescription="@string/activity_dashboard_text"
-            android:minHeight="100dp"
-            android:minWidth="100dp"
-            app:srcCompat="@drawable/ic_wrench_24dp" />
-
-        <TextView
+        <android.support.design.widget.TabLayout
+            android:id="@+id/attendance_fragment_tab_layout"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="47dp"
-            android:text="@string/activity_under_construction"
-            android:textSize="17sp"
-            android:gravity="center"
-            android:textAlignment="center"
-            android:id="@+id/textView3" />
-    </RelativeLayout>
+            app:tabMinWidth="125dp"
+            app:tabMode="scrollable"/>
 
+        <android.support.v4.view.ViewPager
+            android:id="@+id/attendance_fragment_viewpager"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_below="@id/attendance_fragment_tab_layout" />
+    </RelativeLayout>
 </android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/fragment_attendance_tab.xml b/app/src/main/res/layout/fragment_attendance_tab.xml
new file mode 100644
index 00000000..d859102d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_attendance_tab.xml
@@ -0,0 +1,63 @@
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/attendance_tab_fragment_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="io.github.wulkanowy.ui.main.grades.GradesFragment">
+
+    <RelativeLayout
+        android:id="@+id/attendance_tab_fragment_progress_bar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:visibility="gone">
+
+        <ProgressBar
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:indeterminate="true" />
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:id="@+id/attendance_tab_fragment_no_item_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+        <ImageView
+            android:id="@+id/attendance_tab_fragment_no_item_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/attendance_tab_fragment_no_item_text"
+            android:layout_centerHorizontal="true"
+            android:layout_marginTop="40dp"
+            android:minHeight="100dp"
+            android:minWidth="100dp"
+            android:tint="@android:color/black"
+            app:srcCompat="@drawable/ic_menu_attendance_24dp"
+            tools:ignore="contentDescription" />
+
+        <TextView
+            android:id="@+id/attendance_tab_fragment_no_item_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="46dp"
+            android:text="@string/attendance_no_entries"
+            android:textAlignment="center"
+            android:textSize="20sp" />
+    </RelativeLayout>
+
+    <android.support.v4.widget.SwipeRefreshLayout
+        android:id="@+id/attendance_tab_fragment_swipe_refresh"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/attendance_tab_fragment_recycler"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+    </android.support.v4.widget.SwipeRefreshLayout>
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/fragment_timetable.xml b/app/src/main/res/layout/fragment_timetable.xml
index d16230c1..f78f1ab2 100644
--- a/app/src/main/res/layout/fragment_timetable.xml
+++ b/app/src/main/res/layout/fragment_timetable.xml
@@ -22,4 +22,4 @@
             android:layout_height="match_parent"
             android:layout_below="@id/timetable_fragment_tab_layout" />
     </RelativeLayout>
-</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 8fbe68fb..9642a53d 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -96,4 +96,22 @@
         <item quantity="many">Dostałeś %1$d ocen</item>
         <item quantity="other">Dostałeś %1$d ocen</item>
     </plurals>
+
+    <string name="attendance_absence_for_school_reasons">Nieobecny z przyczyn szkolnych</string>
+    <string name="attendance_absence_excused">Nieobecność usprawiedliwiona</string>
+    <string name="attendance_absence_unexcused">Nieobecność nieusprawiedliwiona</string>
+    <string name="attendance_exemption">Zwolniony</string>
+    <string name="attendance_excused_lateness">Spóźnienie usprawiedliowione</string>
+    <string name="attendance_unexcused_lateness">Spóźnienie nieusprawiedliwione</string>
+    <string name="attendance_present">Obecny</string>
+    <string name="attendance_dialog_description">Opis</string>
+    <string name="attendance_dialog_date">Data</string>
+    <string name="attendance_dialog_subject">Przedmiot</string>
+    <string name="attendance_dialog_number">Numer lekcji</string>
+    <string name="attendance_no_entries">Brak wpisów</string>
+    <plurals name="numberOfAbsences">
+        <item quantity="one">%1$d nieobecność</item>
+        <item quantity="few">%1$d nieobecności</item>
+        <item quantity="many">%1$d nieobecności</item>
+    </plurals>
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 56f4ffba..315fee4f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -91,4 +91,23 @@
         <item quantity="one">You received %1$d grade</item>
         <item quantity="other">You received %1$d grades</item>
     </plurals>
+
+    <string name="attendance_absence_for_school_reasons">Absence for school reasons</string>
+    <string name="attendance_absence_excused">Absence excused</string>
+    <string name="attendance_absence_unexcused">Unexcused absence</string>
+    <string name="attendance_exemption">Exemption</string>
+    <string name="attendance_excused_lateness">Excused lateness</string>
+    <string name="attendance_unexcused_lateness">Unexcused lateness</string>
+    <string name="attendance_present">Present</string>
+    <string name="attendance_dialog_description">Description</string>
+    <string name="attendance_dialog_date">Date</string>
+    <string name="attendance_dialog_subject">Subject</string>
+    <string name="attendance_dialog_number">Lesson number</string>
+    <string name="attendance_no_entries">No entries</string>
+    <plurals name="numberOfAbsences">
+        <item quantity="one">%1$d absence</item>
+        <item quantity="few">%1$d absences</item>
+        <item quantity="many">%1$d absences</item>
+        <item quantity="other">%1$d absences</item>
+    </plurals>
 </resources>
diff --git a/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java b/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java
index 8899ed33..f32877c5 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java
+++ b/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java
@@ -10,6 +10,7 @@ import io.github.wulkanowy.api.grades.Grade;
 import io.github.wulkanowy.api.grades.Subject;
 import io.github.wulkanowy.api.generic.Day;
 import io.github.wulkanowy.api.generic.Lesson;
+import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
 
 public class DataObjectConverterTest {
 
@@ -64,15 +65,15 @@ public class DataObjectConverterTest {
     @Test
     public void lessonConversionEmptyTest() {
         Assert.assertEquals(new ArrayList<>(),
-                DataObjectConverter.lessonsToLessonsEntities(new ArrayList<Lesson>()));
+                DataObjectConverter.lessonsToTimetableLessonsEntities(new ArrayList<Lesson>()));
     }
 
     @Test
     public void lessonConversionTest() {
         List<Lesson> lessonList = new ArrayList<>();
         lessonList.add(new Lesson().setRoom("20"));
-        List<io.github.wulkanowy.data.db.dao.entities.Lesson> lessonEntityList =
-                DataObjectConverter.lessonsToLessonsEntities(lessonList);
+        List<TimetableLesson> lessonEntityList =
+                DataObjectConverter.lessonsToTimetableLessonsEntities(lessonList);
 
         Assert.assertEquals("20", lessonEntityList.get(0).getRoom());
     }