Beispiel #1
0
void RunsTableModel::switchStartTimes(int r1, int r2)
{
	qfLogFuncFrame() << r1 << r2;
	int col_stime = columnIndex("startTimeMs");
	QF_ASSERT(col_stime >= 0, "Bad startTimeMs column!", return);

	int id1 = value(r1, "runs.id").toInt();
	int id2 = value(r2, "runs.id").toInt();
	QString err_msg;
	QModelIndex ix1 = index(r1, col_stime);
	QVariant v1 = ix1.data(Qt::EditRole);
	QModelIndex ix2 = index(r2, col_stime);
	QVariant v2 = ix2.data(Qt::EditRole);

	bool is_single_user = sqlConnection().driverName().endsWith(QLatin1String("SQLITE"), Qt::CaseInsensitive);
	if(is_single_user) {
		setData(ix1, v2);
		setData(ix2, v1);
		postRow(r1, true);
		postRow(r2, true);
	}
	else {
		//qf::core::sql::Transaction transaction(sqlConnection());
		quickevent::core::og::TimeMs t1 = v1.value<quickevent::core::og::TimeMs>();
		quickevent::core::og::TimeMs t2 = v2.value<quickevent::core::og::TimeMs>();
		int msec1 = -1, msec2 = -1;
		qf::core::sql::Query q(sqlConnection());
		QString qs = "SELECT id, startTimeMs FROM runs WHERE id IN (" QF_IARG(id1) ", " QF_IARG(id2) ")";
		q.exec(qs, qf::core::Exception::Throw);
		while(q.next()) {
			int id = q.value("id").toInt();
			if(id == id1)
				msec1 = q.value("startTimeMs").toInt();
			else if(id == id2)
				msec2 = q.value("startTimeMs").toInt();
		}
		qfDebug() << t1.msec() << msec1 << t2.msec() << msec2;
		if(msec1 == t1.msec() && msec2 == t2.msec()) {
			setData(ix1, v2);
			setData(ix2, v1);
			postRow(r1, qf::core::Exception::Throw);
			postRow(r2, qf::core::Exception::Throw);
			//transaction.commit();
		}
		else {
			err_msg = tr("Mid-air collision switching start times, reload table and try it again.");
		}
	}
	emit startTimesSwitched(id1, id2, err_msg);
}
Beispiel #2
0
bool RunsTableModel::postRow(int row_no, bool throw_exc)
{
	bool is_single_user = sqlConnection().driverName().endsWith(QLatin1String("SQLITE"), Qt::CaseInsensitive);
	if(is_single_user)
		return Super::postRow(row_no, throw_exc);

	int col_stime = columnIndex("startTimeMs");
	QF_ASSERT(col_stime >= 0, "Bad startTimeMs column!", return false);
	if(isDirty(row_no, col_stime)) {
		int id = value(row_no, "runs.id").toInt();
		int orig_msec = origValue(row_no, col_stime).toInt();
		int db_msec = 0;

		qf::core::sql::Transaction transaction(sqlConnection());
		QString qs = "SELECT id, startTimeMs FROM runs WHERE id=" QF_IARG(id) " FOR UPDATE";
		qf::core::sql::Query q(transaction.connection());
		q.exec(qs, qf::core::Exception::Throw);
		if(q.next()) {
			db_msec = q.value("startTimeMs").toInt();
		}
		if(orig_msec == db_msec) {
			bool ret = Super::postRow(row_no, throw_exc);
			transaction.commit();
			return ret;
		}
		else {
			QString err_msg = tr("Mid-air collision setting start time, reload table and try it again.");
			revertRow(row_no);
			if(throw_exc)
				QF_EXCEPTION(err_msg);
			return false;
		}
	}
	return Super::postRow(row_no, throw_exc);
}
Beispiel #3
0
void ClassesWidget::reload()
{
	if(eventPlugin()->eventName().isEmpty()) {
		m_classesModel->clearRows();
		m_courseCodesModel->clearRows();
		return;
	}
	int stage_id = selectedStageId();
	{
		qf::core::sql::QueryBuilder qb1;
		qb1.select("COUNT(*)")
				.from("runs")
				.join("runs.competitorId", "competitors.id")
				.where("competitors.classId=classdefs.classId")
				.where("NOT runs.offRace")
				.where("runs.stageId=" QF_IARG(stage_id));
		qfs::QueryBuilder qb;
		qb.select2("classes", "*")
				.select2("classdefs", "*")
				.select2("courses", "id, name, length, climb")
				.select("(" + qb1.toString() + ") AS runsCount")
				.from("classes")
				.joinRestricted("classes.id", "classdefs.classId", "classdefs.stageId=" QF_IARG(stage_id))
				.join("classdefs.courseId", "courses.id")
				.orderBy("classes.name");//.limit(10);
		/*
		int class_id = m_cbxClasses->currentData().toInt();
		if(class_id > 0) {
			qb.where("competitors.classId=" + QString::number(class_id));
		}
		*/
		m_classesModel->setQueryBuilder(qb);
		m_classesModel->reload();
	}
	{
		qf::core::sql::Query q(m_classesModel->sqlConnection());
		q.exec("SELECT COUNT(*) FROM classdefs WHERE stageId=" QF_IARG(stage_id));
		bool ro = true;
		if(q.next())
			ro = (q.value(0).toInt() == 0);
		if(ro) {
			qfWarning() << tr("Courses are not imported, class table is read only.");
		}
		ui->tblClasses->setReadOnly(ro);
	}
	reloadCourseCodes();
}
Beispiel #4
0
QVariantMap CardChecker::courseCodesForRunId(int run_id)
{
	QVariantMap ret;
	if(run_id <= 0) {
		qfError() << "Run ID == 0";
		return ret;
	}
	qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
	auto runs_plugin = qobject_cast<Runs::RunsPlugin *>(fwk->plugin("Runs"));
	QF_ASSERT(runs_plugin != nullptr, "Bad plugin", return QVariantMap());

	int course_id = runs_plugin->courseForRun(run_id);
	if(course_id <= 0) {
		qfError() << "Course ID == 0";
		return ret;
	}
	{
		qfs::QueryBuilder qb;
		qb.select2("courses", "*")
				.from("courses")
				.where("courses.id=" QF_IARG(course_id));
		qfs::Query q;
		q.exec(qb.toString(), qf::core::Exception::Throw);
		if(q.next())
			ret = q.values();
	}
	{
		qfs::QueryBuilder qb;
		qb.select2("coursecodes", "position")
				.select2("codes", "code, altCode, outOfOrder")
				.from("coursecodes")
				.join("coursecodes.codeId", "codes.id")
				.where("coursecodes.courseId=" QF_IARG(course_id))
				.orderBy("coursecodes.position");
		qfs::Query q;
		//qfWarning() << qb.toString();
		q.exec(qb.toString(), qf::core::Exception::Throw);
		QVariantList codes;
		while (q.next()) {
			codes << q.values();
		}
		ret["codes"] = codes;
	}
	return ret;
}
void RunsTableItemDelegate::setHighlightedClassId(int class_id, int stage_id)
{
	if(m_highlightedClassId == class_id && m_stageId == stage_id)
		return;
	m_stageId = stage_id;
	m_highlightedClassId = 0;
	qf::core::sql::QueryBuilder qb;
	qb.select2("classdefs", "startTimeMin, startIntervalMin, vacantsBefore, vacantEvery, vacantsAfter")
			.from("classdefs")
			.where("stageId=" QF_IARG(stage_id))
			.where("classId=" QF_IARG(class_id));
	qfs::Query q(qfs::Connection::forName());
	q.exec(qb.toString(), qf::core::Exception::Throw);
	if(q.next()) {
		m_highlightedClassId = class_id;
		m_classInterval = q.value("startIntervalMin").toInt() * 60 * 1000;
		m_classStart = q.value("startTimeMin").toInt() * 60 * 1000;
	}
}
void RunsTableItemDelegate::reloadHighlightedClassId()
{
	qf::core::sql::QueryBuilder qb;
	qb.select2("classdefs", "startTimeMin, lastStartTimeMin, startIntervalMin, vacantsBefore, vacantEvery, vacantsAfter")
			.from("classdefs")
			.where("stageId=" QF_IARG(m_stageId))
			.where("classId=" QF_IARG(m_highlightedClassId));
	qfs::Query q(qfs::Connection::forName());
	q.exec(qb.toString(), qf::core::Exception::Throw);
	if(q.next()) {
		m_classInterval = q.value("startIntervalMin").toInt() * 60 * 1000;
		m_classStartFirst = q.value("startTimeMin").toInt() * 60 * 1000;
		m_classStartLast = q.value("lastStartTimeMin").toInt() * 60 * 1000;
	}
	else {
		m_classInterval = 0;
		m_classStartFirst = 0;
		m_classStartLast = 0;
	}
}
Beispiel #7
0
int CardChecker::startTimeSec(int run_id)
{
	int ret = 0;
	qfs::QueryBuilder qb;
	qb.select2("runs", "startTimeMs")
			.from("runs")
			.where("runs.id=" QF_IARG(run_id));
	qfs::Query q;
	q.exec(qb.toString(), qf::core::Exception::Throw);
	if(q.next())
		ret = q.value(0).toInt() / 1000;
	else
		qfError() << "Cannot find runs record for id:" << run_id;
	return ret;
}
Beispiel #8
0
void ReceiptsWidget::reload()
{
	int current_stage = currentStageId();
	qfs::QueryBuilder qb;
	qb.select2("cards", "id, siId, printerConnectionId")
			.select2("runs", "startTimeMs, timeMs")
			.select2("competitors", "registration")
			.select2("classes", "name")
			.select("COALESCE(lastName, '') || ' ' || COALESCE(firstName, '') AS competitorName")
			.from("cards")
			.join("cards.runId", "runs.id")
			.join("runs.competitorId", "competitors.id")
			.join("competitors.classId", "classes.id")
			.where("cards.stageId=" QF_IARG(current_stage))
			.orderBy("cards.id DESC");
	m_cardsModel->setQueryBuilder(qb);
	m_cardsModel->reload();
}
Beispiel #9
0
void ClassesWidget::reloadCourseCodes()
{
	int current_course_id = 0;
	auto row = ui->tblClasses->selectedRow();
	if(!row.isNull())
		current_course_id = row.value("classdefs.courseId").toInt();
	if(current_course_id == 0) {
		ui->lblCourseCodes->setText("---");
	}
	else {
		ui->lblCourseCodes->setText(row.value("courses.name").toString());
	}
	{
		qfs::QueryBuilder qb;
		qb.select2("codes", "*")
				.select2("coursecodes", "position")
				.from("coursecodes")
				.join("coursecodes.codeId", "codes.id")
				.where("coursecodes.courseId=" QF_IARG(current_course_id))
				.orderBy("coursecodes.position");
		m_courseCodesModel->setQueryBuilder(qb);
		m_courseCodesModel->reload();
	}
}
Beispiel #10
0
QVariantMap ReceiptsPlugin::receiptTablesData(int card_id)
{
	qfLogFuncFrame() << card_id;
	QF_TIME_SCOPE("receiptTablesData()");
	QVariantMap ret;
	CardReader::ReadCard read_card = cardReaderPlugin()->readCard(card_id);
	CardReader::CheckedCard checked_card = cardReaderPlugin()->checkCard(read_card);
	int current_stage_id = eventPlugin()->currentStageId();
	int run_id = checked_card.runId();
	int course_id = checked_card.courseId();
	int current_standings = 0;
	int competitors_finished = 0;
	QMap<int, int> best_laps; //< position->time
	///QMap<int, int> missing_codes; //< pos->code
	///QSet<int> out_of_order_codes;
	{
		qf::core::model::SqlTableModel model;
		qf::core::sql::QueryBuilder qb;
		qb.select2("competitors", "*")
				.select2("runs", "*")
				.select2("classes", "name")
				.select("COALESCE(competitors.lastName, '') || ' ' || COALESCE(competitors.firstName, '') AS competitorName")
				.from("runs")
				.join("runs.competitorId", "competitors.id")
				.join("competitors.classId", "classes.id")
				.where("runs.id=" QF_IARG(run_id));
		model.setQuery(qb.toString());
		model.reload();
		if(model.rowCount() == 1) {
			int class_id = model.value(0, "competitors.classId").toInt();
			{
				// find best laps for competitors class
				qf::core::sql::QueryBuilder qb_minlaps;
				// TODO: remove position field from DB in 0.1.5
				qb_minlaps.select("runlaps.position, MIN(runlaps.lapTimeMs) AS minLapTimeMs")
						.from("competitors")
						.joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id), "JOIN")
						.joinRestricted("runs.id", "runlaps.runId", "runlaps.position > 0 AND runlaps.lapTimeMs > 0", "JOIN")
						.groupBy("runlaps.position");
				QString qs = qb_minlaps.toString();
				//qfInfo() << qs;
				qf::core::sql::Query q;
				q.exec(qs);
				while(q.next()) {
					int position = q.value("position").toInt();
					if(position == 0) {
						qfWarning() << "position == 0 in best runlaps";
						continue;
					}
					int lap = q.value("minLapTimeMs").toInt();
					if(lap == 0) {
						qfWarning() << "minLapTimeMs == 0 in best runlaps";
						continue;
					}
					best_laps[position] = lap;
					//qfInfo() << "bestlaps[" << pos << "] =" << lap;
				}
			}
			if(checked_card.isOk()) {
				// find current standings
				qf::core::sql::QueryBuilder qb;
				qb.select2("runs", "timeMs")
						.select("runs.disqualified OR NOT runs.isRunning OR runs.isRunning IS NULL OR runs.misPunch AS dis")
						.from("competitors")
						.joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id))
						.where("runs.finishTimeMs > 0")
						.orderBy("misPunch, disqualified, isRunning, runs.timeMs");
				//qfInfo() << qb.toString();
				qf::core::sql::Query q;
				q.exec(qb.toString(), qf::core::Exception::Throw);
				while (q.next()) {
					bool dis = q.value("dis").toBool();
					int time = q.value("timeMs").toInt();
					if(!dis) {
						if(time <= checked_card.timeMs())
							current_standings++;
					}
					competitors_finished++;
				}
			}
		}
		qfu::TreeTable tt = model.toTreeTable();
		{
			qf::core::sql::QueryBuilder qb;
			qb.select2("courses", "length, climb")
					.select("(SELECT COUNT(*) FROM coursecodes WHERE courseId=courses.id) AS controlCount")
					.from("courses")
					.where("courses.id=" QF_IARG(course_id));
			qf::core::sql::Query q;
			q.exec(qb.toString());
			if(q.next()) {
				QSqlRecord rec = q.record();
				for (int i = 0; i < rec.count(); ++i) {
					QString fld_name = rec.fieldName(i);
					tt.setValue(fld_name, rec.value(i));
				}
			}
		}
		{
			qf::core::sql::QueryBuilder qb;
			qb.select2("config", "ckey, cvalue, ctype")
					.from("config")
					.where("ckey LIKE 'event.%'");
			qf::core::sql::Query q;
			q.exec(qb.toString());
			while(q.next()) {
				QVariant v = qf::core::Utils::retypeStringValue(q.value("cvalue").toString(), q.value("ctype").toString());
				tt.setValue(q.value("ckey").toString(), v);
			}
		}
		tt.setValue("stageCount", eventPlugin()->stageCount());
		tt.setValue("currentStageId", eventPlugin()->currentStageId());
		qfDebug() << "competitor:\n" << tt.toString();
		ret["competitor"] = tt.toVariant();
	}
	{
		qfu::TreeTable tt;
		tt.appendColumn("position", QVariant::Int);
		tt.appendColumn("code", QVariant::Int);
		tt.appendColumn("stpTimeMs", QVariant::Int);
		tt.appendColumn("lapTimeMs", QVariant::Int);
		tt.appendColumn("lossMs", QVariant::Int);
 		QMapIterator<QString, QVariant> it(checked_card);
		while(it.hasNext()) {
			it.next();
			if(it.key() != QLatin1String("punches"))
				tt.setValue(it.key(), it.value());
		}
		tt.setValue("isOk", checked_card.isOk());
		int position = 0;
		for(auto v : checked_card.punches()) {
			CardReader::CheckedPunch punch(v.toMap());
			qfu::TreeTableRow ttr = tt.appendRow();
			++position;
			int code = punch.code();
			ttr.setValue("position", position);
			ttr.setValue("code", code);
			ttr.setValue("stpTimeMs", punch.stpTimeMs());
			int lap = punch.lapTimeMs();
			ttr.setValue("lapTimeMs", lap);
			int best_lap = best_laps.value(position);
			if(lap > 0 && best_lap > 0) {
				int loss = lap - best_lap;
				ttr.setValue("lossMs", loss);
			}
		}
		/*
		{
			// runlaps table contains also finish time entry, it is under FINISH_PUNCH_POS
			// currently best_laps[999] contains best finish lap time for this class
			int loss = 0;
			int best_lap = best_laps.value(CardReader::CardReaderPlugin::FINISH_PUNCH_POS);
			if(best_lap > 0)
				loss = checked_card.finishLapTimeMs() - best_lap;
			//qfInfo() << "control_count:" << control_count << "finishLapTimeMs:" << checked_card.finishLapTimeMs() << "- best_lap:" << best_lap << "=" << loss;
			tt.setValue("finishLossMs", loss);
		}
		*/
		{
			QSet<int> correct_codes;
			for (int i = 0; i < checked_card.punchCount(); ++i) {
				correct_codes << checked_card.punchAt(i).code();
			}
			QVariantList xc;
			for (int i = 0; i < read_card.punchCount(); ++i) {
				int code = read_card.punchAt(i).code();
				if(!correct_codes.contains(code)) {
					xc.insert(xc.count(), QVariantList() << (i+1) << code);
				}
			}
			tt.setValue("extraCodes", xc);
		}
		tt.setValue("currentStandings", current_standings);
		tt.setValue("competitorsFinished", competitors_finished);
		tt.setValue("timeMs", checked_card.timeMs());

		qfDebug() << "card:\n" << tt.toString();
		ret["card"] = tt.toVariant();
	}
	return ret;
}
Beispiel #11
0
qf::core::utils::TreeTable RelaysPlugin::nlegsResultsTable(int class_id, int leg_count, int places, bool exclude_not_finish)
{
	int max_leg = 0;
	qfs::Query q;
	{
		qfs::QueryBuilder qb;
		qb.select("relayLegCount")
			.from("classdefs")
			.where("classId=" QF_IARG(class_id));
		q.execThrow(qb.toString());
		if(q.next())
			max_leg = q.value(0).toInt();
	}
	if(max_leg == 0) {
		qfError() << "Leg count not defined for class id:" << class_id;
		return qf::core::utils::TreeTable();
	}
	if(leg_count > max_leg)
		leg_count = max_leg;

	QList<Relay> relays;
	//QStringList relay_ids;
	{
		qfs::QueryBuilder qb;
		qb.select2("relays", "id, club, name, number")
				.select2("clubs", "name")
				.from("relays")
				.join("relays.club", "clubs.abbr")
				.where("relays.classId=" QF_IARG(class_id));
		q.execThrow(qb.toString());
		while(q.next()) {
			Relay r;
			r.relayId = q.value("relays.id").toInt();
			r.name = (q.value("relays.number").toString()
					+ ' ' + q.value("relays.club").toString()
					+ ' ' + q.value("relays.name").toString()
					+ ' ' + q.value("clubs.name").toString()).trimmed();
			for (int i = 0; i < leg_count; ++i)
				r.legs << Leg();
			relays << r;
			//relay_ids << QString::number(r.relayId);
		}
	}
	{
		qfs::QueryBuilder qb;
		qb.select2("competitors", "id, registration")
				.select2("runs", "id, relayId, leg")
				.select("COALESCE(competitors.lastName, '') || ' ' || COALESCE(competitors.firstName, '') AS competitorName")
				.from("runs")
				.join("runs.competitorId", "competitors.id")
				.joinRestricted("runs.relayId", "relays.id", "relays.classId=" QF_IARG(class_id), qfs::QueryBuilder::INNER_JOIN)
				//.where("runs.relayId IN (" + relay_ids.join(',') + ")")
				.where("runs.leg>0 AND runs.leg<=" + QString::number(leg_count))
				.orderBy("runs.relayId, runs.leg");
		q.execThrow(qb.toString());
		while(q.next()) {
			int relay_id = q.value("runs.relayId").toInt();
			for (int i = 0; i < relays.count(); ++i) {
				if(relays[i].relayId == relay_id) {
					Relay &relay = relays[i];
					int legno = q.value("runs.leg").toInt();
					Leg &leg = relay.legs[legno - 1];
					leg.name = q.value("competitorName").toString();
					leg.runId = q.value("runs.id").toInt();
					leg.reg = q.value("competitors.registration").toString();
					break;
				}
			}
		}
	}
	for (int legno = 1; legno <= leg_count; ++legno) {
		qfs::QueryBuilder qb;
		qb.select2("runs", "id, relayId, timeMs, disqualified")
				.from("runs")
				.joinRestricted("runs.relayId", "relays.id",
								"relays.classId=" QF_IARG(class_id)
								" AND runs.leg=" QF_IARG(legno)
								" AND runs.isRunning"
								" AND NOT runs.notCompeting"
								" AND runs.finishTimeMs>0"
								, qfs::QueryBuilder::INNER_JOIN)
				.orderBy("runs.disqualified, runs.timeMs");
		q.execThrow(qb.toString());
		int run_pos = 1;
		while(q.next()) {
			int relay_id = q.value("runs.relayId").toInt();
			for (int i = 0; i < relays.count(); ++i) {
				if(relays[i].relayId == relay_id) {
					int run_id = q.value("runs.id").toInt();
					Relay &relay = relays[i];
					Leg &leg = relay.legs[legno - 1];
					if(leg.runId != run_id) {
						qfError() << "internal error, leg:" << legno << "runId check:" << leg.runId << "should equal" << run_id;
					}
					else {
						leg.notfinish = false;
						leg.disq = q.value("runs.disqualified").toBool();
						leg.time = q.value("timeMs").toInt();
						leg.pos = leg.disq? 0: run_pos;
						run_pos++;
					}
					break;
				}
			}
		}
	}
	/// compute overal legs positions
	for (int legno = 1; legno <= leg_count; ++legno) {
		QList<QPair<int, int>> relay_stime;
		for (int i = 0; i < relays.count(); ++i) {
			Relay &relay = relays[i];
			Leg &leg = relay.legs[legno - 1];
			if(!leg.notfinish && !leg.disq) {
				if(legno == 1)
					leg.stime = leg.time;
				else if(relay.legs[legno-2].stime > 0)
					leg.stime = leg.time + relay.legs[legno-2].stime;
			}
			if(leg.stime > 0)
				relay_stime << QPair<int, int>(relay.relayId, leg.stime);
		}
		std::sort(relay_stime.begin(), relay_stime.end(), [](const QPair<int, int> &a, const QPair<int, int> &b) {return a.second < b.second;});
		int pos = 0;
		for(const QPair<int, int> &p : relay_stime) {
			int relay_id = p.first;
			for (int i = 0; i < relays.count(); ++i) {
				if(relays[i].relayId == relay_id) {
					Relay &relay = relays[i];
					Leg &leg = relay.legs[legno - 1];
					leg.spos = ++pos;
					break;
				}
			}
		}
	}
	if(exclude_not_finish) {
		/*
		relays.erase(std::remove_if(relays.begin(),
									  relays.end(),
									  [](const Relay &r){return r.time(leg_count) == TIME_NOT_FINISH;}),
					   relays.end());
		*/
		QMutableListIterator<Relay> i(relays);
		while (i.hasNext()) {
			const Relay &r = i.next();
			if(r.time(leg_count) == qog::TimeMs::NOT_FINISH_TIME_MSEC)
				i.remove();
		}
	}
	/// sort relays
	std::sort(relays.begin(), relays.end(), [leg_count](const Relay &a, const Relay &b) {
		return a.time(leg_count) < b.time(leg_count);
	});

	int time0 = 0;
	qf::core::utils::TreeTable tt;
	tt.appendColumn("pos", QVariant::Int);
	tt.appendColumn("name", QVariant::String);
	tt.appendColumn("time", QVariant::Int);
	tt.appendColumn("loss", QVariant::Int);
	for (int i = 0; i < qMin(relays.count(), places); ++i) {
		qf::core::utils::TreeTableRow rr = tt.appendRow();
		Relay &relay = relays[i];
		int time = relay.time(leg_count);
		if(i == 0)
			time0 = time;
		int prev_time = (i > 0)? relays[i-1].time(leg_count): 0;
		rr.setValue("pos", (time <= qog::TimeMs::MAX_REAL_TIME_MSEC && time > prev_time)? i+1: 0);
		rr.setValue("name", relay.name);
		rr.setValue("time", time);
		rr.setValue("loss", (time <= qog::TimeMs::MAX_REAL_TIME_MSEC)?time - time0: 0);
		qf::core::utils::TreeTable tt2;
		tt.appendColumn("name", QVariant::String);
		tt.appendColumn("reg", QVariant::String);
		tt.appendColumn("time", QVariant::Int);
		tt.appendColumn("pos", QVariant::Int);
		tt.appendColumn("stime", QVariant::Int);
		tt.appendColumn("spos", QVariant::Int);
		//tt.appendColumn("disq", QVariant::Bool);
		for (int j = 0; j < relay.legs.count(); ++j) {
			Leg &leg = relay.legs[j];
			qf::core::utils::TreeTableRow rr2 = tt2.appendRow();
			rr2.setValue("competitorName", leg.name);
			rr2.setValue("registration", leg.reg);
			rr2.setValue("time",
						 leg.disq? qog::TimeMs::DISQ_TIME_MSEC
								: (leg.time == 0)? qog::TimeMs::NOT_FINISH_TIME_MSEC
												: leg.time);
			rr2.setValue("pos", leg.pos);
			rr2.setValue("stime", leg.stime);
			rr2.setValue("spos", leg.spos);
			//rr2.setValue("disq", leg.disq);
		}
		rr.appendTable(tt2);
	}
	//qfInfo() << tt.toString();
	return tt;
}