void EventEditorDelegate::paint( QPainter* painter,
                                 const QStyleOptionViewItem& option,
                                 const QModelIndex& index ) const
{
    const Event& event = m_model->eventForIndex( index );
    Q_ASSERT( event.isValid() );
    const TaskTreeItem& item = DATAMODEL->taskTreeItem( event.taskId() );

    if ( event.isValid() ) {
        bool locked = DATAMODEL->isEventActive( event.id() );
        QString dateAndDuration;
        QTextStream dateStream( &dateAndDuration );
        QDate date = event.startDateTime().date();
        QTime time = event.startDateTime().time();
        QTime endTime = event.endDateTime().time();
        dateStream << date.toString( Qt::SystemLocaleDate )
               << " " << time.toString( "h:mm" )
               << " - " << endTime.toString( "h:mm" )
               << " (" << hoursAndMinutes( event.duration() ) << ") Week "
               << date.weekNumber();

        QString taskName;
        QTextStream taskStream( &taskName );
        // print leading zeroes for the TaskId
        const int taskIdLength = CONFIGURATION.taskPaddingLength;
        taskStream << QString( "%1" ).arg( item.task().id(), taskIdLength, 10, QChar( '0' ) )
                   << " " << DATAMODEL->smartTaskName( item.task() );

        paint( painter, option,
               taskName,
               dateAndDuration,
               logDuration( event.duration() ),
               locked ? EventState_Locked : EventState_Default );
    }
}
Exemple #2
0
	Response BaseDiscordClient::request(const RequestMethod method, Route path, const std::string jsonParameters/*,
		cpr::Parameters httpParameters*/, const std::initializer_list<Part>& multipartParameters) {
		//check if rate limited
		Response response;
		const time_t currentTime = getEpochTimeMillisecond();
		if (isGlobalRateLimited) {
			if (nextRetry <= currentTime) {
				isGlobalRateLimited = false;
			} else {
				onExceededRateLimit(isGlobalRateLimited, nextRetry - currentTime, { *this, method, path, jsonParameters, multipartParameters });
				response.statusCode = TOO_MANY_REQUESTS;
				setError(response.statusCode);
				return response;
			}
		}
		const std::string bucket = path.bucket(method);
		auto bucketResetTimestamp = buckets.find(bucket);
		if (bucketResetTimestamp != buckets.end()) {
			if (bucketResetTimestamp->second <= currentTime) {
				buckets.erase(bucketResetTimestamp);
			} else {
				onExceededRateLimit(false, bucketResetTimestamp->second - currentTime, { *this, method, path, jsonParameters, multipartParameters });
				response.statusCode = TOO_MANY_REQUESTS;
				setError(response.statusCode);
				return response;
			}
		}
		{	//the { is used so that onResponse is called after session is removed to make debugging performance issues easier
			//request starts here
			Session session;
			session.setUrl("https://discordapp.com/api/v6/" + path.url());
			std::vector<HeaderPair> header = {
				{ "Authorization", bot ? "Bot " + getToken() : getToken() },
				{ "User-Agent", "DiscordBot (https://github.com/yourWaifu/SleepyDiscord, vtheBestVersion)" },
			};
			if (jsonParameters != "") {
				session.setBody(&jsonParameters);
				header.push_back({ "Content-Type"  , "application/json"                      });
				header.push_back({ "Content-Length", std::to_string(jsonParameters.length()) });
			//} else if (httpParameters.content != "") {	//this is broken for now
			//	session.SetParameters(httpParameters);
			} else if (0 < multipartParameters.size()) {
				session.setMultipart(multipartParameters);
				header.push_back({ "Content-Type", "multipart/form-data" });
			} else {
				header.push_back({ "Content-Length", "0" });
			}
			session.setHeader(header);
			//Response response;
			switch (method) {
			case Post:   response = session.Post();   break;
			case Patch:  response = session.Patch();  break;
			case Delete: response = session.Delete(); break;
			case Get:    response = session.Get();    break;
			case Put:    response = session.Put();    break;
			default:     response.statusCode = BAD_REQUEST; break; //unexpected method
			}
			//status checking
			switch (response.statusCode) {
			case OK: case CREATED: case NO_CONTENT: case NOT_MODIFIED: break;
			case TOO_MANY_REQUESTS: {   //this should fall down to default
				int retryAfter = std::stoi(response.header["Retry-After"]);
				isGlobalRateLimited = response.header["X-RateLimit-Global"] == "true";
				nextRetry = getEpochTimeMillisecond() + retryAfter;
				if (!isGlobalRateLimited)
					buckets[bucket] = nextRetry;
				onExceededRateLimit(isGlobalRateLimited, retryAfter, { *this, method, path, jsonParameters, multipartParameters });
			}
			default:
			{		//error
				const ErrorCode code = static_cast<ErrorCode>(response.statusCode);
				setError(code);		//https error
				std::vector<std::string> values = json::getValues(response.text.c_str(),
				{ "code", "message" });	//parse json to get code and message
				if (!values.empty() && values[0] != "")
					onError(static_cast<ErrorCode>(std::stoi(values[0])), values[1]);	//send message to the error event
				else
					onError(ERROR_NOTE, response.text);
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
				throw code;
#endif
			} break;
			}
			//rate limit check
			if (response.header["X-RateLimit-Remaining"] == "0" && response.statusCode != TOO_MANY_REQUESTS) {
				std::tm date = {};
				//for some reason std::get_time requires gcc 5
				std::istringstream dateStream(response.header["Date"]);
				dateStream >> std::get_time(&date, "%a, %d %b %Y %H:%M:%S GMT");
				const time_t reset = std::stoi(response.header["X-RateLimit-Reset"]);
#if defined(_WIN32) || defined(_WIN64)
				std::tm gmTM;
				std::tm*const resetGM = &gmTM;
				gmtime_s(resetGM, &reset);
#else
				std::tm* resetGM = std::gmtime(&reset);
#endif
				const time_t resetDelta = (std::mktime(resetGM) - std::mktime(&date)) * 1000;
				buckets[bucket] = resetDelta + getEpochTimeMillisecond();
				onDepletedRequestSupply(resetDelta, { *this, method, path, jsonParameters, multipartParameters });
			}
		}