/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2020 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ // oEvent.cpp: implementation of the oEvent class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include "oEvent.h" #include "gdioutput.h" #include "gdifonts.h" #include "meosexception.h" #include "inthashmap.h" #include "oDataContainer.h" #include "csvparser.h" #include "oFreeImport.h" #include "random.h" #include "SportIdent.h" #include "RunnerDB.h" #include "meos_util.h" #include "meos.h" #include "importformats.h" #include #include #include "localizer.h" #include "iof30interface.h" #include "gdiconstants.h" #include "MeosSQL.h" FlowOperation importFilterGUI(oEvent *oe, gdioutput & gdi, const set& stages, const vector &idProviders, set & filter, string &preferredIdProvider); string conv_is(int i) { char bf[256]; // if (i==0) // return ""; //else if (_itoa_s(i, bf, 10)==0) return bf; return ""; } int ConvertStatusToOE(int i) { switch(i) { case StatusOK: case StatusNoTiming: return 0; case StatusDNS: // Ej start case StatusCANCEL: case StatusNotCompetiting: case StatusOutOfCompetition: return 1; case StatusDNF: // Utg. return 2; case StatusMP: // Felst. return 3; case StatusDQ: //Disk return 4; case StatusMAX: //Maxtid return 5; } return 1;//Ej start...?! } wstring &getFirst(wstring &inout, int maxNames) { int s = inout.size(); for (int k = 0;k3 && maxNames<=1) { inout[k] = 0; maxNames--; return inout; } } return inout; } bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includeSplits) { enum { OEstno = 0, OEcard = 1, OEid = 2, OEsurname = 3, OEfirstname = 4, OEbirth = 5, OEsex = 6, OEnc = 8, OEstart = 9, OEfinish = 10, OEtime = 11, OEstatus = 12, OEclubno = 13, OEclub = 14, OEclubcity = 15, OEnat = 16, OEclassno = 17, OEclassshortname = 18, OEclassname = 19, OErent = 35, OEfee = 36, OEpaid = 37, OEcourseno = 38, OEcourse = 39, OElength = 40, OEclimb = 41, OEcoursecontrols = 42, OEpl = 43, OEstartpunch = 44, OEfinishpunch = 45 }; csvparser csv; oClass::initClassId(*this); if (!csv.openOutput(file)) return false; calculateResults({}, ResultType::ClassResult); sortRunners(SortOrder::ClassResult); oRunnerList::iterator it; string maleString; string femaleString; switch (languageTypeIndex) { case 1: // English csv.outputRow("Stno;Chip;Database Id;Surname;First name;YB;S;Block;nc;Start;Finish;Time;Classifier;Club no.;Cl.name;City;Nat;Cl. no.;Short;Long;Num1;Num2;Num3;Text1;Text2;Text3;Adr. name;Street;Line2;Zip;City;Phone;Fax;EMail;Id/Club;Rented;Start fee;Paid;Course no.;Course;km;m;Course controls;Pl;Start punch;Finish punch;Control1;Punch1;Control2;Punch2;Control3;Punch3;Control4;Punch4;Control5;Punch5;Control6;Punch6;Control7;Punch7;Control8;Punch8;Control9;Punch9;Control10;Punch10;(may be more) ..."); maleString = "M"; femaleString = "F"; break; case 2: // Svenska csv.outputRow("Startnr;Bricka;Databas nr.;Efternamn;Förnamn;År;K;Block;ut;Start;Mål;Tid;Status;Klubb nr.;Namn;Ort;Land;Klass nr.;Kort;Lång;Num1;Num2;Num3;Text1;Text2;Text3;Adr. namn;Gata;Rad 2;Post nr.;Ort;Tel;Fax;E-post;Id/Club;Hyrd;Startavgift;Betalt;Bana nr.;Bana;km;Hm;Bana kontroller;Pl;Startstämpling;Målstämpling;Kontroll1;Stämplar1;Kontroll2;Stämplar2;Kontroll3;Stämplar3;Kontroll4;Stämplar4;Kontroll5;Stämplar5;Kontroll6;Stämplar6;Kontroll7;Stämplar7;Kontroll8;Stämplar8;Kontroll9;Stämplar9;Kontroll10;Stämplar10;(kan fortsätta).."); maleString = "M"; femaleString = "K"; break; case 3: // Deutsch csv.outputRow("Stnr;Chip;Datenbank Id;Nachname;Vorname;Jg;G;Block;AK;Start;Ziel;Zeit;Wertung;Club-Nr.;Abk;Ort;Nat;Katnr;Kurz;Lang;Num1;Num2;Num3;Text1;Text2;Text3;Adr. Name;Straße;Zeile2;PLZ;Ort;Tel;Fax;EMail;Id/Verein;Gemietet;Startgeld;Bezahlt;Bahnnummer;Bahn;km;Hm;Bahn Posten;Pl;Startstempel;Zielstempel;Posten1;Stempel1;Posten2;Stempel2;Posten3;Stempel3;Posten4;Stempel4;Posten5;Stempel5;Posten6;Stempel6;Posten7;Stempel7;Posten8;Stempel8;Posten9;Stempel9;Posten10;Stempel10;(und weitere)..."); maleString = "M"; femaleString = "W"; break; case 4: // Dansk csv.outputRow("Stnr;Brik;Database ID;Efternavn;Fornavn;År;K;Blok;UFK;Start;Mål;Tid;Status;Klub nr.;Navn;Klub;Land;Klasse nr.;kort;Lang;Num1;Num2;Num3;Text1;Text2;Text3;Adr. navn;Gade;Linie2;Post nr.;Klub;Tlf.;Fax.;Email;Id/klub;Lejet;Startafgift;Betalt;Bane nr.;Bane;km;Hm;Poster på bane;Pl;Start-stempling;Mål-stempling;Post1;Klip1;Post2;Klip2;Post3;Klip3;Post4;Klip4;Post5;Klip5;Post6;Klip6;Post7;Klip7;Post8;Klip8;Post9;Klip9;Post10;Klip10;(måske mere)..."); maleString = "M"; femaleString = "K"; break; case 5: // Français csv.outputRow("N° dép.;Puce;Ident. base de données;Nom;Prénom;Né;S;Plage;nc;Départ;Arrivée;Temps;Evaluation;N° club;Nom;Ville;Nat;N° cat.;Court;Long;Num1;Num2;Num3;Text1;Text2;Text3;Adr. nom;Rue;Ligne2;Code Post.;Ville;Tél.;Fax;E-mail;Id/Club;Louée;Engagement;Payé;Circuit N°;Circuit;km;m;Postes du circuit;Pl;Poinçon de départ;Arrivée (P);Poste1;Poinçon1;Poste2;Poinçon2;Poste3;Poinçon3;Poste4;Poinçon4;Poste5;Poinçon5;Poste6;Poinçon6;Poste7;Poinçon7;Poste8;Poinçon8;Poste9;Poinçon9;Poste10;Poinçon10;(peut être plus) ..."); maleString = "H"; femaleString = "F"; break; case 6: // Russian csv.outputRow("Stnr;Chip;Datenbank Id;Nachname;Vorname;Jg;G_Sex;Block;AK_notclass;Start;Ziel;Zeit;Wertung;Club-Nr.;Abk;Ort;Nat;Katnr;Kurz;Lang;Num1;Num2;Num3;Text1;Text2;Text3;Adr. Name;Strasse;Zeile2;PLZ;Ort;Tel;Fax;EMail;Club_TIdNr;Gemietet;Startgeld;Bezahlt;Bahnnummer;Bahn;km_Kilometer;Hm_Climbmeter;Bahn Posten;Pl_Place;Startstempel;Zielstempel;Posten1;Stempel1;Posten2;Stempel2;Posten3;Stempel3;Posten4;Stempel4;Posten5;Stempel5;Posten6;Stempel6;Posten7;Stempel7;Posten8;Stempel8;Posten9;Stempel9;Posten10;Stempel10;(und weitere)..."); maleString = "M"; femaleString = "W"; break; default: csv.outputRow("Stno;Chip;Database Id;Surname;First name;YB;S;Block;nc;Start;Finish;Time;Classifier;Club no.;Cl.name;City;Nat;Cl. no.;Short;Long;Num1;Num2;Num3;Text1;Text2;Text3;Adr. name;Street;Line2;Zip;City;Phone;Fax;EMail;Id/Club;Rented;Start fee;Paid;Course no.;Course;km;m;Course controls;Pl;Start punch;Finish punch;Control1;Punch1;Control2;Punch2;Control3;Punch3;Control4;Punch4;Control5;Punch5;Control6;Punch6;Control7;Punch7;Control8;Punch8;Control9;Punch9;Control10;Punch10;(may be more) ..."); maleString = "M"; femaleString = "F"; } char bf[256]; for (it = Runners.begin(); it != Runners.end(); ++it) { vector row; row.resize(46); oDataInterface di = it->getDI(); row[OEstno] = conv_is(it->getId()); row[OEcard] = conv_is(it->getCardNo()); if (it->getExtIdentifier() != 0) row[OEid] = gdibase.recodeToNarrow(it->getExtIdentifierString()); row[OEsurname] = gdibase.recodeToNarrow(it->getFamilyName()); row[OEfirstname] = gdibase.recodeToNarrow(it->getGivenName()); row[OEbirth] = conv_is(di.getInt("BirthYear") % 100); // Specialized per language PersonSex s = it->getSex(); switch (s) { case sFemale: row[OEsex] = femaleString; break; case sMale: row[OEsex] = maleString; break; case sBoth: case sUnknown: default: row[OEsex] = gdibase.recodeToNarrow(di.getString("Sex")); break; } // nc / Runner shall not / doesn't want to be ranked if (it->getStatus() == StatusNotCompetiting) row[OEnc] = "X"; else row[OEnc] = "0"; // Excel format HH:MM:SS if (it->getStartTime() > 0) row[OEstart] = gdibase.recodeToNarrow(it->getStartTimeS()); // Excel format HH:MM:SS if (it->getFinishTime() > 0) row[OEfinish] = gdibase.recodeToNarrow(it->getFinishTimeS()); // Excel format HH:MM:SS if (it->getRunningTime(true) > 0) row[OEtime] = gdibase.recodeToNarrow(formatTimeHMS(it->getRunningTime(true))); row[OEstatus] = conv_is(ConvertStatusToOE(it->getStatusComputed())); row[OEclubno] = conv_is(it->getClubId()); if (it->getClubRef()) { row[OEclub] = gdibase.recodeToNarrow(it->getClubRef()->getDI().getString("ShortName")); row[OEclubcity] = gdibase.recodeToNarrow(it->getClub()); } row[OEnat] = gdibase.recodeToNarrow(di.getString("Nationality")); row[OEclassno] = conv_is(it->getClassId(true)); row[OEclassshortname] = gdibase.recodeToNarrow(it->getClass(true)); row[OEclassname] = gdibase.recodeToNarrow(it->getClass(true)); row[OErent] = conv_is(di.getInt("CardFee")); row[OEfee] = conv_is(di.getInt("Fee")); row[OEpaid] = conv_is(di.getInt("Paid")); pCourse pc = it->getCourse(true); if (pc) { row[OEcourseno] = conv_is(pc->getId()); row[OEcourse] = gdibase.recodeToNarrow(pc->getName()); if (pc->getLength()>0) { sprintf_s(bf, "%d.%d", pc->getLength() / 1000, pc->getLength() % 1000); row[OElength] = bf; } row[OEclimb] = conv_is(pc->getDI().getInt("Climb")); row[OEcoursecontrols] = conv_is(pc->nControls); } row[OEpl] = gdibase.recodeToNarrow(it->getPlaceS()); if (includeSplits && pc != NULL) { // Add here split times // row[45]: finish time row[OEfinishpunch] = row[OEfinish]; // row[46; 48; 50; ..]: control id // row[47; 49; 51; ..]: punch time of control id row[i-1] const vector &sp = it->getSplitTimes(true); bool hasRogaining = pc->hasRogaining(); int startIx = pc->useFirstAsStart() ? 1 : 0; int endIx = pc->useLastAsFinish() ? pc->nControls - 1 : pc->nControls; for (int k = startIx, m = 0; k < endIx; k++, m += 2) { if (pc->getControl(k)->isRogaining(hasRogaining)) continue; row.push_back(gdibase.recodeToNarrow(pc->getControl(k)->getIdS())); if (unsigned(k) < sp.size() && sp[k].time > 0) row.push_back(gdibase.recodeToNarrow(formatTimeHMS(sp[k].time - it->tStartTime))); else row.push_back("-----"); } // Extra punches vector punches; oe->getPunchesForRunner(it->getId(), true, punches); for (vector::iterator punchIt = punches.begin(); punchIt != punches.end(); ++punchIt) { pPunch punch = *punchIt; if (!punch->isUsed && !(punch->isFinish() && !pc->useLastAsFinish()) && !(punch->isStart() && !pc->useFirstAsStart()) && !punch->isCheck()) { row.push_back(gdibase.recodeToNarrow(punch->getType())); int t = punch->getAdjustedTime(); if (it->tStartTime > 0 && t > 0 && t > it->tStartTime) row.push_back(gdibase.recodeToNarrow(formatTimeHMS(t - it->tStartTime))); else return "-----"; } } } csv.outputRow(row); } csv.closeOutput(); return true; } void oEvent::importXML_EntryData(gdioutput &gdi, const wstring &file, bool updateClass, bool removeNonexisting, const set &filter, const string &preferredIdType) { vector< pair > runnersInTeam; for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved() && it->tInTeam) { runnersInTeam.push_back(make_pair(it->getId(), it->getClassId(false)) ); } } xmlparser xml; xml.read(file); xmlobject xo = xml.getObject("EntryList"); set matchedClasses; if (xo) { gdi.addString("", 0, "Importerar anmälningar (IOF, xml)"); gdi.refreshFast(); int ent = 0, fail = 0, removed = 0; if (xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); reader.setPreferredIdType(preferredIdType); reader.readEntryList(gdi, xo, removeNonexisting, filter, ent, fail, removed); for (auto &c : Clubs) { c.updateFromDB(); } } else { xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("ClubEntry")){ xmlList entries; //xmlobject xentry=it->getObject("Entry"); int ClubId = 0; xmlobject club = it->getObject("Club"); if (club) { addXMLClub(club, false); ClubId = club.getObjectInt("ClubId"); } else ClubId = it->getObjectInt("ClubId"); it->getObjects("Entry", entries); for (size_t k = 0; k0) gdi.addString("", 0, "Antal som inte importerades: X#" + itos(fail)).setColor(colorRed); gdi.dropLine(); gdi.refreshFast(); } xo = xml.getObject("StartList"); if (xo) { gdi.addString("", 0, "Importerar anmälningar (IOF, xml)"); gdi.refreshFast(); int ent = 0, fail = 0; if (xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); reader.readStartList(gdi, xo, ent, fail); } else { xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("ClassStart")){ xmlList entries; int clsId = it->getObjectInt("ClassId"); pClass cls = 0; if (clsId == 0) { wstring clsName; it->getObjectString("ClassShortName", clsName); if (!clsName.empty()) cls = getClassCreate(0, clsName, matchedClasses); } else { cls = getClassCreate(clsId, lang.tl(L"Klass ") + itow(clsId), matchedClasses); } it->getObjects("PersonStart", entries); for (size_t k = 0; k0) gdi.addString("", 0, "Antal som inte importerades: X#" + itos(fail)).setColor(colorRed); gdi.dropLine(); gdi.refreshFast(); } xo = xml.getObject("ClassData"); if (!xo) xo = xml.getObject("ClassList"); if (xo) { gdi.addString("", 0, "Importerar klasser (IOF, xml)"); gdi.refreshFast(); int imp = 0, fail = 0; if (xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); reader.readClassList(gdi, xo, imp, fail); } else { xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for (it=xl.begin(); it != xl.end(); ++it) { if (it->is("Class")) { if (addXMLClass(*it)) imp++; else fail++; } } } gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp)); if (fail>0) gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed); gdi.dropLine(); gdi.refreshFast(); } xo=xml.getObject("ClubList"); if (xo) { gdi.addString("", 0, "Importerar klubbar (IOF, xml)"); gdi.refreshFast(); int imp = 0, fail = 0; xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Club")){ if (addXMLClub(*it, false)) imp++; else fail++; } } gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp)); if (fail>0) gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed); gdi.dropLine(); gdi.refreshFast(); } xo=xml.getObject("RankList"); if (xo) { gdi.addString("", 0, "Importerar ranking..."); gdi.refreshFast(); int imp = 0, fail = 0; xmlList xl; xo.getObjects(xl); map<__int64, int> ext2Id; for (auto it = Runners.begin(); it != Runners.end(); ++it) { if (it->skip()) continue; __int64 ext = it->getExtIdentifier(); if (ext != 0) ext2Id[ext] = it->getId(); } map> rankList; for (auto it = xl.begin(); it != xl.end(); ++it) { if (it->is("Competitor")) { addXMLRank(*it, ext2Id, rankList); } } for (auto it : rankList) { if (it.second.second == RankStatus::Ambivalent) fail++; else { pRunner r = getRunner(it.first, 0); if (r) { r->getDI().setInt("Rank", it.second.first); r->synchronize(); imp++; } else { fail++; } } } gdi.addString("", 0, "Klart. X värden tilldelade.#" + itos(imp)); if (fail>0) gdi.addString("", 0, "Antal ignorerade: X#" + itos(fail)); gdi.dropLine(); gdi.refreshFast(); } xo=xml.getObject("CourseData"); if (xo) { gdi.addString("", 0, "Importerar banor (IOF, xml)"); gdi.refreshFast(); int imp = 0, fail = 0; if (xo && xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); reader.readCourseData(gdi, xo, updateClass, imp, fail); } else { xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Course")){ if (addXMLCourse(*it, updateClass, matchedClasses)) imp++; else fail++; } else if (it->is("Control")){ addXMLControl(*it, 0); } else if (it->is("StartPoint")){ addXMLControl(*it, 1); } else if (it->is("FinishPoint")){ addXMLControl(*it, 2); } } } gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp)); if (fail>0) gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed); gdi.dropLine(); gdi.refreshFast(); } xo=xml.getObject("EventList"); if (xo) { gdi.addString("", 0, "Importerar tävlingsdata (IOF, xml)"); gdi.refreshFast(); if (xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); reader.readEventList(gdi, xo); gdi.addString("", 0, L"Tävlingens namn: X#" + getName()); gdi.dropLine(); gdi.refreshFast(); } else { xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Event")){ addXMLEvent(*it); gdi.addString("", 0, L"Tävlingens namn: X#" + getName()); gdi.dropLine(); gdi.refreshFast(); break; } } } } xo = xml.getObject("ServiceRequestList"); if (xo) { gdi.addString("", 0, "Importerar tävlingsdata (IOF, xml)"); gdi.refreshFast(); if (xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); int imp = 0, fail = 0; reader.readServiceRequestList(gdi, xo, imp, fail); gdi.dropLine(); gdi.refreshFast(); } } vector toRemove; for (size_t k = 0; k < runnersInTeam.size(); k++) { int id = runnersInTeam[k].first; int classId = runnersInTeam[k].second; pRunner r = getRunner(id, 0); if (r && !r->tInTeam && r->getClassId(false) == classId) { toRemove.push_back(r->getId()); } } removeRunner(toRemove); } bool oEvent::addXMLCompetitorDB(const xmlobject &xentry, int clubId) { if (!xentry) return false; xmlobject person = xentry.getObject("Person"); if (!person) return false; wstring pids; person.getObjectString("PersonId", pids); __int64 extId = oBase::converExtIdentifierString(pids); int pid = oBase::idFromExtId(extId); xmlobject pname = person.getObject("PersonName"); if (!pname) return false; int cardno = 0; string tmp; xmlList cards; xentry.getObjects("CCard", cards); for (size_t k = 0; kgetRunnerById(extId); if (!rde) { rde = runnerDB->getRunnerByCard(cardno); if (rde && rde->getExtId() != 0) rde = 0; //Other runner, same card if (!rde) rde = runnerDB->addRunner(name.c_str(), pid, clubId, cardno); } if (rde) { rde->setExtId(extId); rde->setName(name.c_str()); rde->dbe().clubNo = clubId; rde->dbe().birthYear = extendYear(birth); rde->dbe().sex = sex[0]; memcpy(rde->dbe().national, national, 3); } return true; } bool oEvent::addOECSVCompetitorDB(const vector &row) { // Ident. base de données;Puce;Nom;Prénom;Né;S;N° club;Nom;Ville;Nat;N° cat.;Court;Long;Num1;Num2;Num3;E_Mail;Texte1;Texte2;Texte3;Adr. nom;Rue;Ligne2;Code Post.;Ville;Tél.;Fax;E-mail;Id/Club;Louée enum { OEid = 0, OEcard = 1, OEsurname = 2, OEfirstname = 3, OEbirth = 4, OEsex = 5, OEclubno = 6, OEclub = 7, OEclubcity = 8, OEnat = 9, OEclassno = 10, OEclassshort = 11, OEclasslong = 12 }; int pid = _wtoi(row[OEid].c_str()); wstring given = row[OEfirstname]; wstring family = row[OEsurname]; if (given.empty() && family.empty()) return false; wstring name = family + L", " + given; // Depending on the OE language, man = "H" (French) or "M" (English, Svenska, Dansk, Russian, Deutsch) // woman = "F" (English, French) or "W" (Deutsch, Russian) or "K" (Svenska, Dansk) char sex[2]; if (row[OEsex] == L"H" || row[OEsex] == L"M") strcpy_s(sex, "M"); else if (row[OEsex] == L"F" || row[OEsex] == L"K" || row[OEsex] == L"W") strcpy_s(sex, "W"); else strcpy_s(sex, ""); int birth = _wtoi(row[OEbirth].c_str()); // Hack to take care of inconsistency between FFCO licensees archive (France) and event registrations from FFCO (FR) char national[4] = { 0,0,0,0 }; if (row[OEnat] == L"France") { strcpy_s(national, "FRA"); } // Extract club data int clubId = _wtoi(row[OEclubno].c_str()); wstring clubName; wstring shortClubName; clubName = row[OEclubcity]; shortClubName = row[OEclub]; if (clubName.length() > 0 && IsCharAlphaNumeric(clubName[0])) { pClub pc = new oClub(this); pc->Id = clubId; pc->setName(clubName); pc->setExtIdentifier(clubId); oDataInterface DI = pc->getDI(); DI.setString("ShortName", shortClubName.substr(0, 8)); // Nationality? runnerDB->importClub(*pc, false); delete pc; } RunnerWDBEntry *rde = runnerDB->getRunnerById(pid); int cardno = _wtoi(row[OEcard].c_str()); if (!rde) { rde = runnerDB->getRunnerByCard(cardno); if (rde && rde->getExtId() != 0) rde = NULL; //Other runner, same card if (!rde) rde = runnerDB->addRunner(name.c_str(), pid, clubId, cardno); } if (rde) { rde->setExtId(pid); rde->setName(name.c_str()); rde->dbe().clubNo = clubId; rde->dbe().birthYear = extendYear(birth); rde->dbe().sex = sex[0]; memcpy(rde->dbe().national, national, 3); } return true; } bool oEvent::addXMLTeamEntry(const xmlobject &xentry, int clubId) { if (!xentry) return false; wstring name; xentry.getObjectString("TeamName", name); int id = xentry.getObjectInt("EntryId"); if (name.empty()) name = lang.tl("Lag X#" + itos(id)); xmlList teamCmp; xentry.getObjects("TeamCompetitor", teamCmp); xmlobject cls = xentry.getObject("EntryClass"); xmlobject edate = xentry.getObject("EntryDate"); pClass pc = getXMLClass(xentry); if (!pc) return false; pClub club = getClubCreate(clubId); pTeam t = getTeam(id); if (t == 0) { if ( id > 0) { oTeam or(this, id); t = addTeam(or, true); } else { oTeam or(this); t = addTeam(or, true); } t->setStartNo(Teams.size(), oBase::ChangeType::Update); } if (!t->hasFlag(oAbstractRunner::FlagUpdateName)) t->setName(name, false); if (!t->Class || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) t->setClassId(pc->getId(), false); t->Club = club; oDataInterface DI = t->getDI(); wstring date; if (edate) DI.setDate("EntryDate", edate.getObjectString("Date", date)); int maxleg = teamCmp.size(); for (size_t k = 0; k < teamCmp.size(); k++) { maxleg = max(maxleg, teamCmp[k].getObjectInt("TeamSequence")); } if (pc->getNumStages() < unsigned(maxleg)) { setupRelay(*pc, PRelay, maxleg, getAbsTime(3600)); } for (size_t k = 0; k < teamCmp.size(); k++) { int leg = teamCmp[k].getObjectInt("TeamSequence") - 1; if (leg>=0) { pRunner r = addXMLEntry(teamCmp[k], clubId, false); t->setRunner(leg, r, true); } } return true; } pClass oEvent::getXMLClass(const xmlobject &xentry) { xmlobject eclass = xentry.getObject("EntryClass"); if (eclass) { int cid = eclass.getObjectInt("ClassId"); pClass pc = getClass(cid); if ( pc == 0 && cid>0) { oClass cls(this, cid); cls.Name = lang.tl(L"Klass X#" + itow(cid)); pc = addClass(cls);//Create class if not found } return pc; } return 0; } pClub oEvent::getClubCreate(int clubId) { if (clubId) { pClub club = getClub(clubId); if (!club) { pClub dbClub = runnerDB->getClub(clubId); if (dbClub) { club = addClub(*dbClub); } if (!club) { club = addClub(lang.tl("Klubb X#" + itos(clubId))); } } return club; } return 0; } pRunner oEvent::addXMLPerson(const xmlobject &person) { xmlobject pname = person.getObject("PersonName"); if (!pname) return 0; wstring pids; person.getObjectString("PersonId", pids); __int64 extId = oBase::converExtIdentifierString(pids); int pid = oBase::idFromExtId(extId); pRunner r = 0; if (pid) r = getRunner(pid, 0); if (!r) { if ( pid > 0) { oRunner or(this, pid); r = addRunner(or, true); } else { oRunner or(this); r = addRunner(or, true); } } wstring given, family; pname.getObjectString("Given", given); pname.getObjectString("Family", family); r->setName(family + L", " + getFirst(given, 2), false); r->setExtIdentifier(extId); oDataInterface DI=r->getDI(); wstring tmp; r->setSex(interpretSex(person.getObjectString("sex", tmp))); xmlobject bd=person.getObject("BirthDate"); if (bd) r->setBirthYear(extendYear(bd.getObjectInt("Date"))); xmlobject nat=person.getObject("Nationality"); if (nat) { wchar_t national[4]; xmlobject natId = nat.getObject("CountryId"); if (natId) r->setNationality(natId.getObjectString("value", national, 4)); } return r; } pRunner oEvent::addXMLEntry(const xmlobject &xentry, int clubId, bool setClass) { if (!xentry) return 0; xmlobject person = xentry.getObject("Person"); if (!person) return 0; pRunner r = addXMLPerson(person); int cmpClubId = xentry.getObjectInt("ClubId"); if (cmpClubId != 0) clubId = cmpClubId; oDataInterface DI=r->getDI(); wstring given = r->getGivenName(); xmlList cards; xentry.getObjects("CCard", cards); wstring tmp; for (size_t k= 0; ksetCardNo(cardno, false); break; } } } pClass oldClass = r->Class; pClub oldClub = r->Club; if (setClass && !r->hasFlag(oAbstractRunner::FlagUpdateClass) ) r->Class = getXMLClass(xentry); classIdToRunnerHash.reset(); r->Club = getClubCreate(clubId); if (oldClass != r->Class || oldClub != r->Club) r->updateChanged(); xmlobject edate=xentry.getObject("EntryDate"); if (edate) DI.setDate("EntryDate", edate.getObjectString("Date", tmp)); r->addClassDefaultFee(false); r->synchronize(); xmlobject adjRunner = xentry.getObject("AllocationControl"); if (adjRunner) { xmlobject person2 = adjRunner.getObject("Person"); if (person2) { xmlobject pname2 = person2.getObject("PersonName"); wstring pids2; person.getObjectString("PersonId", pids2); const __int64 extId2 = oBase::converExtIdentifierString(pids2); int pid2 = oBase::idFromExtId(extId2); pRunner r2 = getRunner(pid2, 0); if (!r2) { if ( pid2 > 0) { oRunner or(this, pid2); r2 = addRunner(or, true); } else { oRunner or(this); r2 = addRunner(or, true); } } wstring given2, family2; if (pname2) { pname2.getObjectString("Given", given2); pname2.getObjectString("Family", family2); r2->setName(family2 + L", " + getFirst(given2, 2), false); } r2->setExtIdentifier(pid2); // Create patrol if (r->Class) { bool createTeam = false; pClass pc = r->Class; if (pc->getNumStages() <= 1) { setupRelay(*pc, PPatrolOptional, 2, getAbsTime(3600)); createTeam = true; } pTeam t = r->tInTeam; if (t == 0) t = r2->tInTeam; if (t == 0) { autoAddTeam(r); t = r2->tInTeam; createTeam = true; } if (t != 0) { if (t->getRunner(0) == r2) { t->setRunner(0, r2, true); t->setRunner(1, r, true); } else { t->setRunner(0, r, true); t->setRunner(1, r2, true); } } if (createTeam && t && !t->hasFlag(oAbstractRunner::FlagUpdateName)) { t->setName(given + L" / " + given2, false); } } } } else { if (r->Class && r->Class->getNumStages()>=2) { autoAddTeam(r); } } return r; } pRunner oEvent::addXMLStart(const xmlobject &xstart, pClass cls) { if (!xstart) return 0; xmlobject person_ = xstart.getObject("Person"); if (!person_) return 0; pRunner r = addXMLPerson(person_); pClass oldClass = r->Class; pClub oldClub = r->Club; r->Class = cls; classIdToRunnerHash.reset(); xmlobject xclub = xstart.getObject("Club"); int clubId = xstart.getObjectInt("ClubId"); wstring cname; if (xclub) { clubId = xclub.getObjectInt("ClubId"); xclub.getObjectString("ShortName", cname); } if (clubId > 0) { r->Club = getClubCreate(clubId, cname); } xmlobject xstrt = xstart.getObject("Start"); if (!xstrt) return r; oDataInterface DI=r->getDI(); int cardno = xstrt.getObjectInt("CCardId"); if (cardno == 0) { xmlList cards; xstrt.getObjects("CCard", cards); string tmp; for (size_t k= 0; ksetCardNo(cardno, false); break; } } } } else r->setCardNo(cardno, false); wstring tmp; xmlobject xstarttime = xstrt.getObject("StartTime"); if (xstarttime) r->setStartTimeS(xstarttime.getObjectString("Clock", tmp)); if (oldClass != r->Class || oldClub != r->Club) r->updateChanged(); r->addClassDefaultFee(false); r->synchronize(); if (r->Class && r->Class->getNumStages()>=2) { autoAddTeam(r); } return r; } void oEvent::importOECSV_Data(const wstring &oecsvfile, bool clear) { // Clear DB if needed if (clear) { runnerDB->clearClubs(); runnerDB->clearRunners(); } csvparser cp; list< vector > data; gdibase.addString("",0,"Läser löpare..."); gdibase.refresh(); cp.parse(oecsvfile, data); gdibase.addString("", 0, "Behandlar löpardatabasen").setColor(colorGreen); gdibase.refresh(); list>::iterator it; for (it = ++(data.begin()); it != data.end(); ++it) { addOECSVCompetitorDB(*it); } gdibase.addString("", 0, "Klart. Antal importerade: X#" + itos(data.size())); gdibase.refresh(); setProperty("DatabaseUpdate", getRelativeDay()); // Save DB saveRunnerDatabase(L"database", true); if (hasDBConnection()) { gdibase.addString("", 0, "Uppdaterar serverns databas..."); gdibase.refresh(); OpFailStatus stat = sqlConnection->uploadRunnerDB(this); if (stat == opStatusFail) { string err; sqlConnection->getErrorMessage(err); string error = string("Kunde inte ladda upp löpardatabasen (X).#") + err; throw meosException(error); } else if (stat == opStatusWarning) { string err; sqlConnection->getErrorMessage(err); gdibase.addInfoBox("", wstring(L"Kunde inte ladda upp löpardatabasen (X).#") + lang.tl(err), 5000); } gdibase.addString("", 0, "Klart"); gdibase.refresh(); } } void oEvent::importXML_IOF_Data(const wstring &clubfile, const wstring &competitorfile, bool clear) { if (!clubfile.empty()) { xmlparser xml_club; xml_club.setProgress(gdibase.getHWNDTarget()); if (clear && !competitorfile.empty()) runnerDB->clearClubs(); gdibase.addString("",0,"Läser klubbar..."); gdibase.refresh(); xml_club.read(clubfile); gdibase.addString("",0,"Lägger till klubbar..."); gdibase.refresh(); xmlobject xo = xml_club.getObject("ClubList"); int clubCount = 0; if (!xo) { xo = xml_club.getObject("OrganisationList"); if (xo) { IOF30Interface reader(this, false); reader.readClubList(gdibase, xo, clubCount); } } else { xmlList xl; if (xo) xo.getObjects(xl); xmlList::const_iterator it; for (it=xl.begin(); it != xl.end(); ++it) if (it->is("Club")) { if (addXMLClub(*it, true)) clubCount++; } } gdibase.addStringUT(0, lang.tl("Antal importerade: ") + itow(clubCount)); } if (!competitorfile.empty()) { xmlparser xml_cmp; xml_cmp.setProgress(gdibase.getHWNDTarget()); gdibase.dropLine(); gdibase.addString("",0,"Läser löpare..."); gdibase.refresh(); xml_cmp.read(competitorfile); if (clear) { runnerDB->clearRunners(); } gdibase.addString("",0,"Lägger till löpare..."); gdibase.refresh(); int personCount = 0; xmlobject xo=xml_cmp.getObject("CompetitorList"); if (xo && xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); vector idProviders; reader.prescanCompetitorList(xo); reader.getIdTypes(idProviders); if (idProviders.size() > 1) { string preferredIdProvider; set dmy; FlowOperation op = importFilterGUI(oe, gdibase, {}, idProviders, dmy, preferredIdProvider); if (op != FlowContinue) return; reader.setPreferredIdType(preferredIdProvider); } reader.readCompetitorList(gdibase, xo, personCount); } else { xmlList xl; if (xo) xo.getObjects(xl); xmlList::const_iterator it; for (it=xl.begin(); it != xl.end(); ++it) { if (it->is("Competitor")){ int ClubId=it->getObjectInt("ClubId"); if (addXMLCompetitorDB(*it, ClubId)) personCount++; } } } gdibase.addStringUT(0, lang.tl("Antal importerade: ") + itow(personCount)); gdibase.refresh(); setProperty("DatabaseUpdate", getRelativeDay()); } saveRunnerDatabase(L"database", true); if (hasDBConnection()) { gdibase.addString("", 0, "Uppdaterar serverns databas..."); gdibase.refresh(); OpFailStatus stat = (OpFailStatus)sqlConnection->uploadRunnerDB(this); if (stat == opStatusFail) { string err; sqlConnection->getErrorMessage(err); string error = string("Kunde inte ladda upp löpardatabasen (X).#") + err; throw meosException(error); } else if (stat == opStatusWarning) { string err; sqlConnection->getErrorMessage(err); gdibase.addInfoBox("", wstring(L"Kunde inte ladda upp löpardatabasen (X).#") + lang.tl(err), 5000); } gdibase.addString("", 0, "Klart"); gdibase.refresh(); } } /* 6 D18 Elit D18 Elit E 600 600 A Adult 65 2001-10-18 */ bool oEvent::addXMLEvent(const xmlobject &xevent) { if (!xevent) return false; int id = xevent.getObjectInt("EventId"); wstring name; xevent.getObjectString("Name", name); xmlobject date = xevent.getObject("StartDate"); if (id>0) setExtIdentifier(id); if (!hasFlag(TransferFlags::FlagManualName)) setName(name, false); if (date) { wstring dateStr; date.getObjectString("Date", dateStr); if (!hasFlag(TransferFlags::FlagManualDateTime)) setDate(dateStr, false); } synchronize(); return true; } /* Bana 02 1 D21E 0 8500 0 S1 1 104 310 2 102 360 M1 157 */ int getLength(const xmlobject &xlen) { if (xlen.isnull()) return 0; return xlen.getInt(); } int getStartIndex(int sn) { return sn + 211100; } int getFinishIndex(int sn) { return sn + 311100; } wstring getStartName(const wstring &start) { int num = getNumberSuffix(start); if (num == 0 && start.length()>0) num = int(start[start.length()-1])-'0'; if (num > 0 && num < 10) return lang.tl(L"Start ") + itow(num); else if (start.length() == 1) return lang.tl(L"Start"); else return start; } /* 120 */ bool oEvent::addXMLControl(const xmlobject &xcontrol, int type) { // type: 0 control, 1 start, 2 finish if (!xcontrol) return false; xmlobject pos = xcontrol.getObject("MapPosition"); int xp = 0, yp = 0; if (pos) { string x,y; pos.getObjectString("x", x); pos.getObjectString("y", y); xp = int(10.0 * atof(x.c_str())); yp = int(10.0 * atof(y.c_str())); } if (type == 0) { int code = xcontrol.getObjectInt("ControlCode"); if (code>=30 && code<1024) { pControl pc = getControl(code, true); pc->getDI().setInt("xpos", xp); pc->getDI().setInt("ypos", yp); pc->synchronize(); } } else if (type == 1) { wstring start; xcontrol.getObjectString("StartPointCode", start); start = getStartName(trim(start)); int num = getNumberSuffix(start); if (num == 0 && start.length()>0) num = int(start[start.length()-1])-'0'; pControl pc = getControl(getStartIndex(num), true); pc->setNumbers(L""); pc->setName(start); pc->setStatus(oControl::StatusStart); pc->getDI().setInt("xpos", xp); pc->getDI().setInt("ypos", yp); } else if (type == 2) { wstring finish; xcontrol.getObjectString("FinishPointCode", finish); finish = trim(finish); int num = getNumberSuffix(finish); if (num == 0 && finish.length()>0) num = int(finish[finish.length()-1])-'0'; if (num > 0) finish = lang.tl("Mål ") + itow(num); pControl pc = getControl(getFinishIndex(num), true); pc->setNumbers(L""); pc->setName(finish); pc->setStatus(oControl::StatusFinish); pc->getDI().setInt("xpos", xp); pc->getDI().setInt("ypos", yp); } return true; } bool oEvent::addXMLCourse(const xmlobject &xcrs, bool addClasses, set &matchedClasses) { if (!xcrs) return false; int cid = xcrs.getObjectInt("CourseId"); wstring name; xcrs.getObjectString("CourseName", name); name = trim(name); vector cls; xmlList obj; xcrs.getObjects("ClassShortName", obj); for (size_t k = 0; k courses; xcrs.getObjects("CourseVariation", obj); for (size_t k = 0; k ctrlCode(ctrl.size()); vector legLen(ctrl.size()); for (size_t i = 0; i0 && seq<=ctrl.size()) index = seq-1; ctrlCode[index] = ctrl[i].getObjectInt("ControlCode"); legLen[index] = getLength(ctrl[i].getObject("LegLength")); } legLen.push_back(getLength(obj[k].getObject("DistanceToFinish"))); int actualId = ((cid+1)*100) + variationId; wstring cname=name; if (!varName.empty()) cname += L" " + varName; else if (obj.size() > 1) cname += L" " + itow(k+1); pCourse pc = 0; if (cid > 0) pc = getCourseCreate(actualId); else { pc = getCourse(cname); if (pc == 0) pc = addCourse(cname); } pc->setName(cname); pc->setLength(len); pc->importControls("", true, false); for (size_t i = 0; i=30 && ctrlCode[i]<1024) pc->addControl(ctrlCode[i]); } if (pc->getNumControls() + 1 == legLen.size()) pc->setLegLengths(legLen); pc->getDI().setInt("Climb", climb); pc->setStart(start, true); pc->synchronize(); string finish; obj[k].getObjectString("FinishPointCode", finish); finish = trim(finish); pc->synchronize(); courses.push_back(pc); } if (addClasses) { for (size_t k = 0; kgetNumStages()==0) { pCls->setCourse(courses[0]); } else { for (size_t i = 0; igetNumStages(); i++) pCls->addStageCourse(i, courses[0]->getId(), -1); } } else { if (courses.size() == pCls->getNumStages()) { for (size_t i = 0; iaddStageCourse(i, courses[i]->getId(), -1); } else { for (size_t i = 0; iaddStageCourse(0, courses[i]->getId(), -1); } } } } } return true; } bool oEvent::addXMLClass(const xmlobject &xclass) { if (!xclass) return false; int classid=xclass.getObjectInt("ClassId"); wstring name, shortName; xclass.getObjectString("Name", name); xclass.getObjectString("ClassShortName", shortName); if (!shortName.empty()) name = shortName; pClass pc=0; if (classid) { pc = getClass(classid); if (!pc) { oClass c(this, classid); pc = addClass(c); } } else pc = addClass(name); if (!pc->hasFlag(oClass::TransferFlags::FlagManualName)) pc->setName(name, false); oDataInterface DI=pc->getDI(); wstring tmp; DI.setInt("LowAge", xclass.getObjectInt("lowAge")); DI.setInt("HighAge", xclass.getObjectInt("highAge")); DI.setString("Sex", xclass.getObjectString("sex", tmp)); DI.setString("ClassType", xclass.getObjectString("ClassTypeId", tmp)); xmlList xFee; xclass.getObjects("EntryFee", xFee); vector fee; for (size_t k = 0; k0) { fee.push_back(oe->interpretCurrency(f, cur)); } } } // XXX Eventor studpid hack if (fee.size() == 2 && fee[1]synchronize(); return true; } bool oEvent::addXMLClub(const xmlobject &xclub, bool savetoDB) { if (!xclub) return false; int clubid=xclub.getObjectInt("ClubId"); wstring Name, shortName; xclub.getObjectString("Name", Name); xclub.getObjectString("ShortName", shortName); if (!shortName.empty() && shortName.length() < Name.length()) swap(Name, shortName); int district = xclub.getObjectInt("OrganisationId"); if (Name.length()==0 || !IsCharAlphaNumeric(Name[0])) return false; xmlobject address=xclub.getObject("Address"); wstring str; wstring co; if (address) { address.getObjectString("street", str); address.getObjectString("careOf", co); } pClub pc=0; if ( !savetoDB ) { if (clubid) pc = getClubCreate(clubid, Name); if (!pc) return false; } else { pc = new oClub(this); pc->Id = clubid; } pc->setName(Name); pc->setExtIdentifier(clubid); oDataInterface DI=pc->getDI(); wstring tmp; DI.setString("CareOf", co); DI.setString("Street", str); if (address) { DI.setString("City", address.getObjectString("city", tmp)); DI.setString("ZIP", address.getObjectString("zipCode", tmp)); } DI.setInt("District", district); xmlobject tele=xclub.getObject("Tele"); if (tele){ DI.setString("EMail", tele.getObjectString("mailAddress", tmp)); DI.setString("Phone", tele.getObjectString("phoneNumber", tmp)); } xmlobject country=xclub.getObject("Country"); if (country) { xmlobject natId = country.getObject("CountryId"); wchar_t national[4]; if (natId) DI.setString("Nationality", natId.getObjectString("value", national, 4)); } if (savetoDB) { runnerDB->importClub(*pc, false); delete pc; } else { pc->synchronize(); } return true; } bool oEvent::addXMLRank(const xmlobject &xrank, const map<__int64, int> &externIdToRunnerId, map> &output) { if (!xrank) return false; xmlobject person;//xrank->getObject("Person"); xmlobject club;//xrank->getObject("Club"); xmlobject rank; xmlobject vrank; xmlList x; xrank.getObjects(x); xmlList::const_iterator cit=x.begin(); string tmp; while(cit!=x.end()){ if (cit->is("Person")) person=*cit; else if (cit->is("Club")) club=*cit; else if (cit->is("Rank")){ if (cit->getObjectString("Name", tmp).find("Vacancy") != string::npos) vrank=*cit; else rank = *cit; } ++cit; } if (!person) return false; wstring pid; person.getObjectString("PersonId", pid); const __int64 extId = oBase::converExtIdentifierString(pid); int id = oBase::idFromExtId(extId); auto res = externIdToRunnerId.find(extId); if (res != externIdToRunnerId.end()) id = res->second; pRunner r = getRunner(id, 0); bool idMatch = r != nullptr; if (r == nullptr){ xmlobject pname = person.getObject("PersonName"); if (!pname) return false; wstring given, family; wstring name=pname.getObjectString("Family", family) + L", " + getFirst(pname.getObjectString("Given", given), 2); if (!club) r=getRunnerByName(name); else { wstring cn, cns; club.getObjectString("ShortName", cns); club.getObjectString("Name", cn); if (cns.empty()) cns = cn; if (!cn.empty() && cn.length()getId()] = make_pair(pos, RankStatus::IdMatch); else { auto ores = output.find(r->getId()); if (ores == output.end()) output[r->getId()] = make_pair(pos, RankStatus::NameMatch); else if (ores->second.second == RankStatus::NameMatch) output[r->getId()] = make_pair(pos, RankStatus::Ambivalent); // Not clear. Do not match. } } /* oDataInterface DI=r->getDI(); if (rank) DI.setInt("Rank", rank.getObjectInt("RankPosition")); r->synchronize();*/ return true; } void oEvent::exportIOFEventList(xmlparser &xml) { xml.startTag("EventList"); xml.write("IOFVersion", "version", L"2.0.3"); exportIOFEvent(xml); xml.endTag(); //EventList } void oEvent::exportIOFEvent(xmlparser &xml) { // (IndSingleDay|IndMultiDay|teamSingleDay|teamMultiDay|relay) xml.startTag("Event", "eventForm", "IndSingleDay"); xml.write("EventId", getExtIdentifierString()); xml.write("Name", getName()); xml.write("EventClassificationId", "type", L"other", L"MeOS"); { xml.startTag("StartDate"); xml.write("Date", "dateFormat", L"YYYY-MM-DD", getDate()); xml.write("Clock", "clockFormat", L"HH:MM:SS", getZeroTime()); xml.endTag(); // StartDate } wstring url = getDCI().getString("Homepage"); if (!url.empty()) xml.write("WebURL", url); wstring account = getDCI().getString("Account"); if (!account.empty()) xml.write("Account", "type", L"other", account); xml.endTag(); //Event } void oEvent::exportIOFClass(xmlparser &xml) { xml.startTag("ClassData"); set cls; for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) { if (!it->getType().empty()) cls.insert(it->getType()); } int id = 1; map idMap; for (set::iterator it = cls.begin(); it != cls.end(); ++it) { xml.startTag("ClassType"); idMap[*it] = id; xml.write("ClassTypeId", id++); xml.write("Name", *it); xml.endTag(); } for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) { vector pv; pClass pc = &*it; int low = 0; int high = 0; pc->getAgeLimit(low, high); if (low>0) { pv.push_back(L"lowAge"); pv.push_back(itow(low)); } if (high>0) { pv.push_back(L"highAge"); pv.push_back(itow(high)); } wstring sex = encodeSex(pc->getSex()); if (sex.empty()) sex = L"B"; pv.push_back(L"sex"); pv.push_back(sex); if (pc->getNumStages()>1) { pv.push_back(L"numberInTeam"); pv.push_back(itow(pc->getNumStages())); } if (pc->getClassType() == oClassRelay) { pv.push_back(L"teamEntry"); pv.push_back(L"Y"); } if (pc->getNoTiming()) { pv.push_back(L"timePresentation"); pv.push_back(L"N"); } xml.startTag("Class", pv); xml.write("ClassId", itow(pc->getId())); xml.write("ClassShortName", pc->getName()); if (!pc->getType().empty()) xml.write("ClassTypeId", itow(idMap[pc->getType()])); xml.endTag(); } xml.endTag(); } void oEvent::exportIOFClublist(xmlparser &xml) { xml.startTag("ClubList"); xml.write("IOFVersion", "version", L"2.0.3"); for (oClubList::iterator it = Clubs.begin(); it!=Clubs.end(); ++it) { it->exportIOFClub(xml, true); } xml.endTag(); } void cWrite(xmlparser &xml, const char *tag, const wstring &value) { if (!value.empty()) { xml.write(tag, value); } } void oClub::exportClubOrId(xmlparser &xml) const { if (getExtIdentifier() != 0) xml.write("ClubId", getExtIdentifierString()); else { exportIOFClub(xml, true); } } void oClub::exportIOFClub(xmlparser &xml, bool compact) const { xml.startTag("Club"); if (getExtIdentifier() != 0) xml.write("ClubId", getExtIdentifierString()); else xml.write("ClubId"); xml.write("ShortName", getName()); if (compact) { xml.endTag(); return; } wstring country = getDCI().getString("Nationality"); if (!country.empty()) xml.write("CountryId", "value", country); int district = getDCI().getInt("District"); if (district>0) xml.write("OrganisationId", district); vector pv; // Address wstring co = getDCI().getString("CareOf"); wstring street = getDCI().getString("Street"); wstring city = getDCI().getString("City"); wstring zip = getDCI().getString("ZIP"); if (!co.empty()) { pv.push_back(L"careOf"); pv.push_back(co); } if (!street.empty()) { pv.push_back(L"street"); pv.push_back(street); } if (!city.empty()) { pv.push_back(L"city"); pv.push_back(city); } if (!zip.empty()) { pv.push_back(L"zipCode"); pv.push_back(zip); } if (!pv.empty()) { xml.startTag("Address", pv); xml.endTag(); } pv.clear(); //Tele wstring mail = getDCI().getString("EMail"); wstring phone = getDCI().getString("Phone"); if (!mail.empty()) { pv.push_back(L"mailAddress"); pv.push_back(mail); } if (!phone.empty()) { pv.push_back(L"phoneNumber"); pv.push_back(phone); } if (!pv.empty()) { xml.startTag("Tele", pv); xml.endTag(); } //Club xml.endTag(); } void oEvent::exportIOFStartlist(xmlparser &xml) { xml.startTag("StartList"); xml.write("IOFVersion", "version", L"2.0.3"); exportIOFEvent(xml); for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) { xml.startTag("ClassStart"); //xml.write("ClassId", itos(it->getId())); xml.write("ClassShortName", it->getName()); it->exportIOFStart(xml); xml.endTag(); } xml.endTag(); } void oClass::exportIOFStart(xmlparser &xml) { bool useEventor = oe->getPropertyInt("UseEventor", 0) == 1; if (getClassType() == oClassIndividual || getClassType() == oClassIndividRelay) { for (oRunnerList::iterator it = oe->Runners.begin(); it!=oe->Runners.end(); ++it) { if (it->getClassId(true) != getId() || it->isRemoved()) continue; xml.startTag("PersonStart"); it->exportIOFRunner(xml, true); if (it->getClubId()>0) it->Club->exportClubOrId(xml); int rank = it->getDCI().getInt("Rank"); if (rank>0) { //Ranking xml.startTag("Rank"); xml.write("Name", L"MeOS"); xml.write("RankPosition", rank); xml.write("RankValue", rank); xml.endTag(); } int multi = it->getNumMulti(); if (multi==0) it->exportIOFStart(xml); else { xml.startTag("RaceStart"); xml.write("EventRaceId", L"1"); it->exportIOFStart(xml); xml.endTag(); for (int k = 0; k < multi; k++) { pRunner r = it->getMultiRunner(k+1); if (r) { xml.startTag("RaceStart"); xml.write("EventRaceId", k+2); r->exportIOFStart(xml); xml.endTag(); } } } xml.endTag(); } } else if (getClassType() == oClassRelay || getClassType() == oClassPatrol) { // A bug in Eventor / OLA results in an internal error if a patrol has a team name. // Set writeTeamName to true (or remove) when this bug is fixed. bool writeTeamName = !useEventor || getClassType() != oClassPatrol; for (oTeamList::iterator it = oe->Teams.begin(); it!=oe->Teams.end(); ++it) { if (it->getClassId(true) != getId() || it->isRemoved()) continue; xml.startTag("TeamStart"); if (writeTeamName) xml.write("TeamName", it->getName()); wstring nat = it->getDCI().getString("Nationality"); if (!nat.empty()) xml.write("CountryId", "value", nat); for (size_t k=0; kRunners.size(); k++) { if (it->Runners[k]) { xml.startTag("PersonStart"); pRunner parent = it->Runners[k]->getMultiRunner(0); if (parent != 0) parent->exportIOFRunner(xml, true); if (it->Runners[k]->getClubId()>0) { it->Runners[k]->Club->exportClubOrId(xml); } else if (it->getClubId()>0) { it->Club->exportClubOrId(xml); } it->Runners[k]->exportIOFStart(xml); xml.endTag(); } } xml.endTag(); } } } void oRunner::exportIOFStart(xmlparser &xml) { xml.startTag("Start"); int sno = getStartNo(); if (sno>0) xml.write("StartNumber", sno); xml.startTag("StartTime"); xml.write("Clock", "clockFormat", L"HH:MM:SS", formatTimeIOF(getStartTime(), oe->ZeroTime)); xml.endTag(); wstring bib = getBib(); if (!bib.empty()) xml.write("BibNumber", bib); if (getCardNo() > 0) xml.write("CCardId", getCardNo()); int len = 0; if (getCourse(false)) { len = getCourse(false)->getLength(); if (len>0) xml.write("CourseLength", "unit", L"m", itow(len)); wstring start = getCourse(false)->getStart(); if (!start.empty()) xml.write("StartId", max(1, getNumberSuffix(start))); } if (tInTeam) { xml.write("TeamSequence", tLeg+1); } xml.endTag(); } void oRunner::exportIOFRunner(xmlparser &xml, bool compact) { wstring sex = encodeSex(getSex()); if (sex.length()==1) xml.startTag("Person", "sex", sex); else xml.startTag("Person"); if (getExtIdentifier() != 0) xml.write("PersonId", getExtIdentifierString()); else xml.write("PersonId"); xml.startTag("PersonName"); xml.write("Family", getFamilyName()); xml.write("Given", "sequence", L"1", getGivenName()); xml.endTag(); int year = getBirthYear(); if (year>0 && !compact) { xml.startTag("BirthDate"); xml.write("Date", "dateFormat", L"YYYY", itow(extendYear(year))); xml.endTag(); } xml.endTag(); } void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set &classes, int leg, bool oldStylePatol) { vector dummy; xml.startTag("ResultList"); xml.write("IOFVersion", "version", L"2.0.3"); wstring hhmmss = L"HH:MM:SS"; exportIOFEvent(xml); bool ClassStarted=false; int Id=-1; bool skipClass=false; if (oldStylePatol) { // OLD STYLE PATROL EXPORT for (oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; if (it->Runners.size()>=2 && it->Runners[0] && it->Runners[1]) { if (it->getClassId(true)!=Id) { if (ClassStarted) xml.endTag(); if (!it->Class || it->Class->getClassType()!=oClassPatrol) { skipClass=true; ClassStarted=false; continue; } if ((!classes.empty() && classes.count(it->getClassId(true)) == 0) || leg != -1) { skipClass=true; ClassStarted=false; continue; } skipClass=false; xml.startTag("ClassResult"); ClassStarted=true; Id=it->getClassId(true); xml.write("ClassShortName", it->getClass(true)); } if (skipClass) continue; xml.startTag("PersonResult"); it->Runners[0]->exportIOFRunner(xml, true); xml.startTag("Club"); xml.write("ClubId", 0); xml.write("ShortName", it->Runners[1]->getName()); xml.write("CountryId", "value", it->getDI().getString("Nationality")); xml.endTag(); xml.startTag("Result"); xml.startTag("StartTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getStartTime(), ZeroTime)); xml.endTag(); xml.startTag("FinishTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getLegFinishTime(-1), ZeroTime)); xml.endTag(); xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getLegRunningTime(-1, true, false), 0)); xml.write("ResultPosition", it->getLegPlaceS(-1, false)); xml.write("CompetitorStatus", "value", it->Runners[0]->getIOFStatusS()); const vector &sp=it->Runners[0]->getSplitTimes(true); pCourse pc=it->Runners[0]->getCourse(false); if (pc) xml.write("CourseLength", "unit", L"m", pc->getLengthS()); pCourse pcourse=pc; auto legStatus = it->getLegStatus(-1, true, false); if (pcourse && legStatus>0 && legStatus!=StatusDNS && legStatus!=StatusCANCEL) { int no = 1; bool hasRogaining = pcourse->hasRogaining(); int startIx = pcourse->useFirstAsStart() ? 1 : 0; int endIx = pcourse->useLastAsFinish() ? pcourse->nControls - 1 : pcourse->nControls; for (int k=startIx;kControls[k]->isRogaining(hasRogaining)) continue; xml.startTag("SplitTime", "sequence", itos(no++)); xml.write("ControlCode", pcourse->Controls[k]->getFirstNumber()); if (unsigned(k)0) xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(sp[k].time-it->tStartTime, 0)); else xml.write("Time", L"--:--:--"); xml.endTag(); } } xml.endTag(); xml.endTag(); } } if (ClassStarted) { xml.endTag(); ClassStarted = false; } } // OldStylePatrol if (leg == -1) exportTeamSplits(xml, classes, oldStylePatol); skipClass=false; Id=-1; for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved() || (leg != -1 && it->tLeg != leg) || it->isVacant()) continue; if (it->getClassId(true)!=Id) { if (ClassStarted) xml.endTag(); if (!it->Class) { skipClass=true; ClassStarted=false; continue; } ClassType ct = it->Class->getClassType(); if (leg == -1 && (ct == oClassPatrol || ct ==oClassRelay || ct == oClassIndividRelay) ) { skipClass=true; ClassStarted=false; continue; } if ( (!classes.empty() && classes.count(it->getClassId(true)) == 0) ) { skipClass=true; ClassStarted=false; continue; } xml.startTag("ClassResult"); ClassStarted=true; skipClass=false; Id=it->getClassId(true); xml.write("ClassShortName", it->getClass(true)); } if (skipClass) continue; xml.startTag("PersonResult"); it->exportIOFRunner(xml, true); if (it->Club) it->Club->exportIOFClub(xml, true); xml.startTag("Result"); xml.startTag("CCard"); xml.write("CCardId", it->getCardNo()); xml.endTag(); xml.startTag("StartTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getStartTime(), ZeroTime)); xml.endTag(); xml.startTag("FinishTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(true),0)); xml.write("ResultPosition", it->getPlaceS()); xml.write("CompetitorStatus", "value", it->getIOFStatusS()); const vector &sp=it->getSplitTimes(true); pCourse pc=it->getCourse(false); if (pc) xml.write("CourseLength", "unit", L"m", pc->getLengthS()); pCourse pcourse=it->getCourse(true); if (pcourse && it->getStatus()>0 && it->getStatus()!=StatusDNS && it->getStatus()!=StatusNotCompetiting && it->getStatus() != StatusCANCEL) { bool hasRogaining = pcourse->hasRogaining(); int no = 1; int startIx = pcourse->useFirstAsStart() ? 1 : 0; int endIx = pcourse->useLastAsFinish() ? pcourse->nControls - 1 : pcourse->nControls; for (int k=startIx;kControls[k]->isRogaining(hasRogaining)) continue; xml.startTag("SplitTime", "sequence", itos(no++)); xml.write("ControlCode", pcourse->Controls[k]->getFirstNumber()); if (unsigned(k)0) xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(sp[k].time - it->tStartTime, 0)); else xml.write("Time", L"--:--:--"); xml.endTag(); } } xml.endTag(); xml.endTag(); } if (ClassStarted) { xml.endTag(); ClassStarted = false; } xml.endTag(); } void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldStylePatrol) { wstring hhmmss = L"HH:MM:SS"; vector dummy; bool ClassStarted=false; int Id=-1; bool skipClass=false; sortTeams(ClassResult, -1, true); for(oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; if (it->getClassId(true)!=Id) { if (ClassStarted) { xml.endTag(); ClassStarted = false; } if (!it->Class) { skipClass=true; continue; } ClassType ct = it->Class->getClassType(); if (oldStylePatrol && ct == oClassPatrol) { skipClass=true; continue; } if (ct != oClassRelay && ct != oClassIndividRelay && ct != oClassPatrol) { skipClass=true; continue; } if (!classes.empty() && classes.count(it->getClassId(true)) == 0) { skipClass=true; continue; } skipClass=false; xml.startTag("ClassResult"); ClassStarted=true; Id=it->getClassId(true); xml.write("ClassShortName", it->getClass(true)); } if (skipClass) continue; xml.startTag("TeamResult"); { /* Sundsvalls OK 10:00:00 11:45:52 1 */ wstring nat = it->getDCI().getString("Nationality"); if (!nat.empty()) xml.write("CountryId", "value", nat); xml.write("TeamName", it->getName()); xml.write("BibNumber", it->getStartNo()); xml.startTag("StartTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getStartTime(), ZeroTime)); xml.endTag(); xml.startTag("FinishTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(true), 0)); xml.write("ResultPosition", it->getPlaceS()); xml.write("TeamStatus", "value", it->getIOFStatusS()); for (size_t k=0;kRunners.size();k++) { if (!it->Runners[k]) continue; pRunner r=it->Runners[k]; xml.startTag("PersonResult"); { r->exportIOFRunner(xml, true); if (r->Club) r->Club->exportIOFClub(xml, true); xml.startTag("Result"); { xml.write("TeamSequence", k+1); xml.startTag("StartTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(r->getStartTime(), ZeroTime)); xml.endTag(); xml.startTag("FinishTime"); xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(r->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(r->getRunningTime(true), 0)); xml.write("ResultPosition", r->getPlaceS()); xml.write("CompetitorStatus", "value", r->getIOFStatusS()); const vector &sp = r->getSplitTimes(true); pCourse pc=r->getCourse(false); if (pc) { xml.startTag("CourseVariation"); xml.write("CourseVariationId", pc->getId()); xml.write("CourseLength", "unit", L"m", pc->getLengthS()); xml.endTag(); } pCourse pcourse=pc; if (pcourse && r->getStatus()>0 && r->getStatus()!=StatusDNS && r->getStatus()!=StatusNotCompetiting && r->getStatus() != StatusCANCEL) { int no = 1; bool hasRogaining = pcourse->hasRogaining(); int startIx = pcourse->useFirstAsStart() ? 1 : 0; int endIx = pcourse->useLastAsFinish() ? pcourse->nControls - 1 : pcourse->nControls; for (int k=startIx;kControls[k]->isRogaining(hasRogaining)) continue; xml.startTag("SplitTime", "sequence", itos(no++)); xml.write("ControlCode", pcourse->Controls[k]->getFirstNumber()); if (unsigned(k)0) xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(sp[k].time - it->tStartTime, 0)); else xml.write("Time", L"--:--:--"); xml.endTag(); } //Loop over splits } } xml.endTag(); } xml.endTag(); } //Loop over team members } xml.endTag(); // Team result } if (ClassStarted) { xml.endTag(); ClassStarted = false; } } void oEvent::exportIOFSplits(IOFVersion version, const wchar_t *file, bool oldStylePatrolExport, bool useUTC, const set &classes, int leg, bool teamsAsIndividual, bool unrollLoops, bool includeStageInfo, bool forceSplitFee) { xmlparser xml; xml.openOutput(file, false); oClass::initClassId(*this); reEvaluateAll(classes, true); if (version != IOF20) calculateResults(classes, ResultType::ClassCourseResult); calculateResults(classes, ResultType::TotalResult); calculateResults(classes, ResultType::ClassResult); calculateTeamResults(classes, ResultType::TotalResult); calculateTeamResults(classes, ResultType::ClassResult); sortRunners(SortOrder::ClassResult); sortTeams(SortOrder::ClassResult, -1, false); if (version == IOF20) exportIOFResults(xml, true, classes, leg, oldStylePatrolExport); else { IOF30Interface writer(this, forceSplitFee); writer.writeResultList(xml, classes, leg, useUTC, teamsAsIndividual, unrollLoops, includeStageInfo); } xml.closeOut(); } void oEvent::exportIOFStartlist(IOFVersion version, const wchar_t *file, bool useUTC, const set &classes, bool teamsAsIndividual, bool includeStageInfo, bool forceSplitFee) { xmlparser xml; oClass::initClassId(*this); xml.openOutput(file, false); if (version == IOF20) exportIOFStartlist(xml); else { IOF30Interface writer(this, forceSplitFee); writer.writeStartList(xml, classes, useUTC, teamsAsIndividual, includeStageInfo); } xml.closeOut(); }