[UI/Attendance] Fix counting issues. Add attendance details dialog.

This commit is contained in:
Kuba Szczodrzyński 2020-05-09 19:18:18 +02:00
parent 65e0e10db6
commit 771712da99
14 changed files with 381 additions and 63 deletions

View File

@ -151,11 +151,17 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
}
}
val typeName = types?.get(typeSymbol) ?: ""
val typeColor = when (typeSymbol) {
"e" -> 0xff673ab7
"en" -> 0xffec407a
"ep" -> 0xff4caf50
else -> null
}?.toInt()
val typeShort = when (baseType) {
TYPE_UNKNOWN -> typeSymbol
else -> data.app.attendanceManager.getTypeShort(baseType)
}
val typeShort = if (isCounted)
data.app.attendanceManager.getTypeShort(baseType)
else
typeSymbol
val semester = data.profile?.dateToSemester(lessonDate) ?: 1
@ -168,7 +174,7 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
typeName = typeName,
typeShort = typeShort,
typeSymbol = typeSymbol,
typeColor = null,
typeColor = typeColor,
date = lessonDate,
startTime = startTime,
semester = semester,

View File

@ -73,6 +73,6 @@ open class Attendance(
@delegate:Ignore
val typeObject by lazy {
AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor)
AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor).also { it.isCounted = isCounted }
}
}

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.Entity
import androidx.room.Ignore
@Entity(tableName = "attendanceTypes",
primaryKeys = ["profileId", "id"])
@ -23,6 +24,9 @@ data class AttendanceType (
val typeColor: Int?
) : Comparable<AttendanceType> {
@Ignore
var isCounted: Boolean = true
// attendance bar order:
// day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent,
override fun compareTo(other: AttendanceType): Int {

View File

@ -12,6 +12,7 @@ import android.util.AttributeSet
import android.view.View
import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.utils.Colors
import kotlin.math.roundToInt
/* https://github.com/JakubekWeg/Mobishit/blob/master/app/src/main/java/jakubweg/mobishit/view/AttendanceBarView.kt */
class AttendanceBar : View {
@ -34,7 +35,7 @@ class AttendanceBar : View {
mCornerRadius = 4.dp.toFloat()
if (isInEditMode)
setAttendanceData(mapOf(
setAttendanceData(listOf(
0xff43a047.toInt() to 23,
0xff009688.toInt() to 187,
0xff3f51b5.toInt() to 46,
@ -49,8 +50,8 @@ class AttendanceBar : View {
// color, count
private class AttendanceItem(var color: Int, var count: Int)
fun setAttendanceData(list: Map<Int, Int>) {
attendancesList = list.map { AttendanceItem(it.key, it.value) }
fun setAttendanceData(list: List<Pair<Int, Int>>) {
attendancesList = list.map { AttendanceItem(it.first, it.second) }
setWillNotDraw(false)
invalidate()
}
@ -91,11 +92,12 @@ class AttendanceBar : View {
mainPaint.color = e.color
canvas.drawRect(left, top, left + width, bottom, mainPaint)
val percentage = (100f * e.count / sum).roundToInt().toString() + "%"
val textBounds = Rect()
textPaint.getTextBounds(e.count.toString(), 0, e.count.toString().length, textBounds)
textPaint.getTextBounds(percentage, 0, percentage.length, textBounds)
if (width > textBounds.width() + 8.dp && height > textBounds.height() + 2.dp) {
textPaint.color = Colors.legibleTextColor(e.color)
canvas.drawText(e.count.toString(), left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint)
canvas.drawText(percentage, left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint)
}
left += width

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-5-9.
*/
package pl.szczodrzynski.edziennik.ui.modules.attendance
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.ColorUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
import pl.szczodrzynski.edziennik.setTintColor
import kotlin.coroutines.CoroutineContext
class AttendanceDetailsDialog(
val activity: AppCompatActivity,
val attendance: AttendanceFull,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "AttendanceDetailsDialog"
}
private lateinit var app: App
private lateinit var b: AttendanceDetailsDialogBinding
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = AttendanceDetailsDialogBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val manager = app.attendanceManager
val attendanceColor = manager.getAttendanceColor(attendance)
b.attendance = attendance
b.devMode = App.debugMode
b.attendanceName.setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt())
b.attendanceName.background.setTintColor(attendanceColor)
b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no)
}}
}

View File

@ -95,7 +95,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
}})
adapter.onAttendanceClick = {
//GradeDetailsDialog(activity, it)
AttendanceDetailsDialog(activity, it)
}
}; return true}
@ -174,15 +174,14 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
items.forEach { month ->
month.typeCountMap = month.items
.groupBy { it.typeObject }
.map { it.key to it.value.count { a -> a.isCounted } }
.map { it.key to it.value.size }
.sortedBy { it.first }
.toMap()
val totalCount = month.typeCountMap.entries.sumBy {
when (it.key.baseType) {
Attendance.TYPE_UNKNOWN -> 0
else -> it.value
}
if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
0
else it.value
}
val presenceCount = month.typeCountMap.entries.sumBy {
when (it.key.baseType) {
@ -190,7 +189,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
Attendance.TYPE_PRESENT_CUSTOM,
Attendance.TYPE_BELATED,
Attendance.TYPE_BELATED_EXCUSED,
Attendance.TYPE_RELEASED -> it.value
Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
else -> 0
}
}
@ -213,6 +212,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
type = it.key,
items = it.value.toMutableList()
) }
.sortedBy { it.items.size }
items.forEach { type ->
type.percentage = if (attendance.isEmpty())

View File

@ -76,7 +76,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true)
setHasFixedSize(false)
layoutManager = LinearLayoutManager(context)
isNestedScrollingEnabled = false
}
@ -103,7 +103,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
}})
adapter.onAttendanceClick = {
//GradeDetailsDialog(activity, it)
AttendanceDetailsDialog(activity, it)
}
b.toggleGroup.check(when (periodSelection) {
@ -184,15 +184,14 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
items.forEach { subject ->
subject.typeCountMap = subject.items
.groupBy { it.typeObject }
.map { it.key to it.value.count { a -> a.isCounted } }
.map { it.key to it.value.size }
.sortedBy { it.first }
.toMap()
val totalCount = subject.typeCountMap.entries.sumBy {
when (it.key.baseType) {
Attendance.TYPE_UNKNOWN -> 0
else -> it.value
}
if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
0
else it.value
}
val presenceCount = subject.typeCountMap.entries.sumBy {
when (it.key.baseType) {
@ -200,7 +199,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
Attendance.TYPE_PRESENT_CUSTOM,
Attendance.TYPE_BELATED,
Attendance.TYPE_BELATED_EXCUSED,
Attendance.TYPE_RELEASED -> it.value
Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
else -> 0
}
}
@ -218,7 +217,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
val typeCountMap = attendance
.groupBy { it.typeObject }
.map { it.key to it.value.count { a -> a.isCounted } }
.map { it.key to it.value.size }
.sortedBy { it.first }
.toMap()
@ -228,11 +227,11 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
presenceCountSum.toFloat() / totalCountSum.toFloat() * 100f
launch {
b.attendanceBar.setAttendanceData(typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
b.attendanceBar.setAttendanceData(typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
b.attendanceBar.isInvisible = typeCountMap.isEmpty()
b.previewContainer.removeAllViews()
val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
//val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
typeCountMap.forEach { (type, count) ->
val layout = LinearLayout(activity)
val attendanceObject = Attendance(
@ -252,7 +251,8 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
)
layout.addView(AttendanceView(activity, attendanceObject, manager))
layout.addView(TextView(activity).also {
it.setText(R.string.attendance_percentage_format, count/sum*100f)
//it.setText(R.string.attendance_percentage_format, count/sum*100f)
it.text = count.toString()
it.setPadding(0, 0, 5.dp, 0)
})
layout.setPadding(0, 8.dp, 0, 8.dp)

View File

@ -49,7 +49,7 @@ class MonthViewHolder(
b.unread.isVisible = item.hasUnseen
b.attendanceBar.setAttendanceData(item.typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
b.previewContainer.isInvisible = item.state != STATE_CLOSED
b.summaryContainer.isInvisible = item.state == STATE_CLOSED
@ -77,7 +77,8 @@ class MonthViewHolder(
)
layout.addView(AttendanceView(contextWrapper, attendance, manager))
layout.addView(TextView(contextWrapper).also {
it.setText(R.string.attendance_percentage_format, count/sum*100f)
//it.setText(R.string.attendance_percentage_format, count/sum*100f)
it.text = count.toString()
it.setPadding(0, 0, 5.dp, 0)
})
layout.setPadding(0, 8.dp, 0, 0)

View File

@ -42,7 +42,7 @@ class SubjectViewHolder(
b.unread.isVisible = item.hasUnseen
b.attendanceBar.setAttendanceData(item.typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
b.percentage.isVisible = true

View File

@ -5,13 +5,14 @@
package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.concat
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.databinding.AttendanceItemTypeBinding
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
@ -43,7 +44,11 @@ class TypeViewHolder(
b.unread.isVisible = item.hasUnseen
b.previewContainer.visibility = if (item.state == AttendanceAdapter.STATE_CLOSED) View.VISIBLE else View.INVISIBLE
b.details.text = listOf(
app.getString(R.string.attendance_percentage_format, item.percentage),
app.getString(R.string.attendance_type_yearly_format, item.items.size),
app.getString(R.string.attendance_type_semester_format, item.semesterCount)
).concat("")
b.type.setAttendance(Attendance(
profileId = 0,

View File

@ -51,16 +51,16 @@ class AttendanceManager(val app: App) : CoroutineScope {
}
}
fun getAttendanceColor(typeObject: AttendanceType): Int {
return (if (useSymbols) typeObject.typeColor else null) ?: when (typeObject.baseType) {
Attendance.TYPE_PRESENT_CUSTOM -> typeObject.typeColor ?: 0xff64b5f6.toInt()
else -> getAttendanceColor(typeObject.baseType)
}
return (if (useSymbols) typeObject.typeColor else null)
?: if (typeObject.baseType == Attendance.TYPE_PRESENT_CUSTOM || !typeObject.isCounted)
typeObject.typeColor ?: 0xff64b5f6.toInt()
else getAttendanceColor(typeObject.baseType)
}
fun getAttendanceColor(attendance: Attendance): Int {
return (if (useSymbols) attendance.typeColor else null) ?: when (attendance.baseType) {
Attendance.TYPE_PRESENT_CUSTOM -> attendance.typeColor ?: 0xff64b5f6.toInt()
else -> getAttendanceColor(attendance.baseType)
}
return (if (useSymbols) attendance.typeColor else null)
?: if (attendance.baseType == Attendance.TYPE_PRESENT_CUSTOM || !attendance.isCounted)
attendance.typeColor ?: 0xff64b5f6.toInt()
else getAttendanceColor(attendance.baseType)
}
/* _ _ _____ _____ _ __ _

View File

@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-5-9.
-->
<layout 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">
<data>
<import type="android.view.View"/>
<import type="pl.szczodrzynski.edziennik.utils.Utils"/>
<import type="pl.szczodrzynski.edziennik.utils.models.Date"/>
<import type="pl.szczodrzynski.edziennik.utils.models.Time"/>
<variable
name="attendance"
type="pl.szczodrzynski.edziennik.data.db.full.AttendanceFull" />
<variable
name="devMode"
type="boolean" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/attendanceName"
android:layout_width="72dp"
android:layout_height="72dp"
android:background="@drawable/bg_rounded_16dp"
android:gravity="center"
android:padding="8dp"
android:text="@{attendance.typeShort}"
android:textIsSelectable="true"
android:textSize="36sp"
app:autoSizeMinTextSize="18sp"
app:autoSizeMaxTextSize="56sp"
app:autoSizeTextType="uniform"
tools:background="#ff673ab7"
tools:text="e" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{@string/dialog_grade_details_semester_format(attendance.semester)}"
android:textAppearance="@style/NavView.TextView.Helper"
android:textIsSelectable="true"
tools:text="semestr 1" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{attendance.subjectLongName}"
android:textAppearance="@style/NavView.TextView.Title"
android:textIsSelectable="true"
tools:text="matematyka" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/attendance_details_teacher"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{attendance.teacherName}"
android:textIsSelectable="true"
tools:text="Andrzej Religijny" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_type"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{attendance.typeName}"
android:textIsSelectable="true"
tools:text="nieobecność nieusprawiedliwiona" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_date"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{attendance.date.formattedString}"
android:textIsSelectable="true"
tools:text="30 lutego 2002" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_time"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{attendance.startTime.stringHM}"
android:textIsSelectable="true"
tools:text="10:60" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_lesson_topic"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{attendance.lessonTopic == null ? View.GONE : View.VISIBLE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{attendance.lessonTopic}"
android:textIsSelectable="true"
android:visibility="@{attendance.lessonTopic == null ? View.GONE : View.VISIBLE}"
tools:text="Malowanie autoportretu na ścianie sali gimnastycznej. Modlitwa w terenie." />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_is_counted"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:id="@+id/attendanceIsCounted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:textIsSelectable="true"
tools:text="Tak" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false"
android:visibility="@{devMode ? View.VISIBLE : View.GONE}">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_id"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Long.toString(attendance.id)}"
android:textIsSelectable="true"
tools:text="12345" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/attendance_details_type_id"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Long.toString(attendance.baseType)}"
android:textIsSelectable="true"
tools:text="12345" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</layout>

View File

@ -27,7 +27,7 @@
android:orientation="horizontal">
<LinearLayout
android:layout_width="32dp"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
@ -81,26 +81,17 @@
tools:background="@android:drawable/ic_menu_more" />
</LinearLayout>
<FrameLayout
<TextView
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/previewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textSize="14sp"
android:visibility="gone"
tools:text="57,67% • 521 przez cały rok • 134 w tym semestrze"
tools:text1="Cały rok: 3 oceny • suma: 320 pkt"
tools:text2="Cały rok: 15 ocen • średnia: 2,62"
tools:visibility="visible" />
</FrameLayout>
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:textSize="14sp"
tools:text="57,67% • 521 przez cały rok • 134 w tym semestrze"
tools:text1="Cały rok: 3 oceny • suma: 320 pkt"
tools:text2="Cały rok: 15 ocen • średnia: 2,62" />
</LinearLayout>
</layout>

View File

@ -1305,4 +1305,14 @@
<string name="you_are_offline_text">Jesteś offline. Spróbuj włączyć Wi-Fi lub dane komórkowe.</string>
<string name="you_are_offline_title">Połączenie sieciowe</string>
<string name="attendance_tab_types">Wg typu</string>
<string name="attendance_type_yearly_format">%d przez cały rok</string>
<string name="attendance_type_semester_format">%d w tym semestrze</string>
<string name="attendance_details_teacher">Nauczyciel</string>
<string name="attendance_details_type">Rodzaj</string>
<string name="attendance_details_date">Data</string>
<string name="attendance_details_time">Godzina</string>
<string name="attendance_details_lesson_topic">Temat lekcji</string>
<string name="attendance_details_is_counted">Liczona do puli?</string>
<string name="attendance_details_id">ID frekwencji</string>
<string name="attendance_details_type_id">ID rodzaju podstawowego</string>
</resources>