Compare commits
7 commits
2fb72f1701
...
3be2418102
Author | SHA1 | Date | |
---|---|---|---|
3be2418102 | |||
d321a5d95e | |||
c7d8eddf86 | |||
d50e496f79 | |||
e04a24f7a1 | |||
f838977283 | |||
91357d7c7f |
13 changed files with 229 additions and 38 deletions
|
@ -14,6 +14,7 @@ set(CORE_SOURCES
|
||||||
seller.cpp
|
seller.cpp
|
||||||
article.cpp
|
article.cpp
|
||||||
sale.cpp
|
sale.cpp
|
||||||
|
marketplace.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(core STATIC ${CORE_SOURCES})
|
add_library(core STATIC ${CORE_SOURCES})
|
||||||
|
|
|
@ -20,4 +20,8 @@ std::string Article::getDescription() { return description_; }
|
||||||
|
|
||||||
Seller* Article::getSeller() { return sellerPtr_; }
|
Seller* Article::getSeller() { return sellerPtr_; }
|
||||||
|
|
||||||
int Article::getPrice() { return price_; }
|
int Article::getPrice() { return price_; }
|
||||||
|
|
||||||
|
int Article::getArticleNo() {
|
||||||
|
return articleNo_;
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ class Article : public Entity
|
||||||
//void setSeller(std::shared_ptr<Seller> sellerPtr);
|
//void setSeller(std::shared_ptr<Seller> sellerPtr);
|
||||||
void setSeller(Seller* sellerPtr);
|
void setSeller(Seller* sellerPtr);
|
||||||
|
|
||||||
|
int getArticleNo();
|
||||||
std::string getDescription();
|
std::string getDescription();
|
||||||
Seller* getSeller();
|
Seller* getSeller();
|
||||||
int getPrice();
|
int getPrice();
|
||||||
|
|
|
@ -145,7 +145,7 @@ void Database::beginTransaction() { exec("BEGIN TRANSACTION"); }
|
||||||
|
|
||||||
void Database::endTransaction() { exec("END TRANSACTION"); }
|
void Database::endTransaction() { exec("END TRANSACTION"); }
|
||||||
|
|
||||||
unsigned int Database::storeSellers(std::vector<Seller>& sellers)
|
unsigned int Database::storeSellers(std::vector<std::shared_ptr<Seller>>& sellers)
|
||||||
{
|
{
|
||||||
int retCode{};
|
int retCode{};
|
||||||
int count{};
|
int count{};
|
||||||
|
@ -154,7 +154,7 @@ unsigned int Database::storeSellers(std::vector<Seller>& sellers)
|
||||||
beginTransaction();
|
beginTransaction();
|
||||||
|
|
||||||
for (auto& seller : sellers) {
|
for (auto& seller : sellers) {
|
||||||
if (seller.getState() == Entity::State::NEW) {
|
if (seller->getState() == Seller::State::NEW) {
|
||||||
retCode = sqlite3_prepare_v2(
|
retCode = sqlite3_prepare_v2(
|
||||||
db_,
|
db_,
|
||||||
"INSERT INTO sellers"
|
"INSERT INTO sellers"
|
||||||
|
@ -165,16 +165,17 @@ unsigned int Database::storeSellers(std::vector<Seller>& sellers)
|
||||||
if (retCode != SQLITE_OK)
|
if (retCode != SQLITE_OK)
|
||||||
throw std::runtime_error(sqlite3_errmsg(db_));
|
throw std::runtime_error(sqlite3_errmsg(db_));
|
||||||
|
|
||||||
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
|
int test = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
|
||||||
boost::uuids::to_string(seller.getUuid()).c_str(), -1, nullptr);
|
boost::uuids::to_string(seller->getUuid()).c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
std::cout << "!!! TEST: " << test << "\n";
|
||||||
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
|
||||||
seller.getSellerNo());
|
seller->getSellerNo());
|
||||||
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
|
||||||
seller.getFirstName().c_str(), -1, nullptr);
|
seller->getFirstName().c_str(), -1, SQLITE_TRANSIENT);
|
||||||
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":last_name"),
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":last_name"),
|
||||||
seller.getLastName().c_str(), -1, nullptr);
|
seller->getLastName().c_str(), -1, SQLITE_TRANSIENT);
|
||||||
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":num_offered_articles"),
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":num_offered_articles"),
|
||||||
seller.numArticlesOffered());
|
seller->numArticlesOffered());
|
||||||
|
|
||||||
retCode = sqlite3_step(stmt);
|
retCode = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
@ -184,12 +185,103 @@ unsigned int Database::storeSellers(std::vector<Seller>& sellers)
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
throw std::runtime_error(errMsg);
|
throw std::runtime_error(errMsg);
|
||||||
}
|
}
|
||||||
seller.setState(Seller::State::OK);
|
seller->setState(Seller::State::OK);
|
||||||
++count;
|
++count;
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
} else if (seller->getState() == Seller::State::UPDATE) {
|
||||||
|
// TODO
|
||||||
|
retCode = sqlite3_prepare_v2(
|
||||||
|
db_,
|
||||||
|
"UPDATE sellers SET"
|
||||||
|
" seller_no = :seller_no, first_name = :first_name,"
|
||||||
|
" last_name = :last_name, num_offered_articles = :num_offered_articles"
|
||||||
|
" WHERE id = :uuid",
|
||||||
|
-1, &stmt, nullptr);
|
||||||
|
|
||||||
|
if (retCode != SQLITE_OK)
|
||||||
|
throw std::runtime_error(sqlite3_errmsg(db_));
|
||||||
|
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
|
||||||
|
boost::uuids::to_string(seller->getUuid()).c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
|
||||||
|
seller->getSellerNo());
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
|
||||||
|
seller->getFirstName().c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":last_name"),
|
||||||
|
seller->getLastName().c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":num_offered_articles"),
|
||||||
|
seller->numArticlesOffered());
|
||||||
|
|
||||||
|
retCode = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
if (retCode != SQLITE_DONE) {
|
||||||
|
|
||||||
|
std::string errMsg(sqlite3_errmsg(db_));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
throw std::runtime_error(errMsg);
|
||||||
|
}
|
||||||
|
seller->setState(Seller::State::OK);
|
||||||
|
++count;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else if (seller->getState() == Seller::State::DELETE) {
|
||||||
|
count += static_cast<int>(seller->getArticles(false).size());
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seller->getState() != Seller::State::DELETE) {
|
||||||
|
count += storeArticles(stmt, seller->getArticles(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endTransaction();
|
endTransaction();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Database::storeArticles(sqlite3_stmt* stmt, std::vector<Article*> articles)
|
||||||
|
{
|
||||||
|
int retCode{};
|
||||||
|
int count{};
|
||||||
|
|
||||||
|
for (auto& article : articles) {
|
||||||
|
if (article->getState() == Article::State::NEW) {
|
||||||
|
retCode = sqlite3_prepare_v2(
|
||||||
|
db_,
|
||||||
|
"INSERT INTO articles"
|
||||||
|
" (id, seller_id, source_no, article_no, description, price)"
|
||||||
|
" VALUES (:uuid, :seller_id, :source_no, :article_no, :desc, :price)",
|
||||||
|
-1, &stmt, nullptr);
|
||||||
|
|
||||||
|
if (retCode != SQLITE_OK)
|
||||||
|
throw std::runtime_error(sqlite3_errmsg(db_));
|
||||||
|
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
|
||||||
|
boost::uuids::to_string(article->getUuid()).c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"),
|
||||||
|
boost::uuids::to_string(article->getSeller()->getUuid()).c_str(), -1,
|
||||||
|
SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
|
||||||
|
article->getSourceNo());
|
||||||
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"),
|
||||||
|
article->getArticleNo());
|
||||||
|
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":desctiption"),
|
||||||
|
article->getDescription().c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":price"),
|
||||||
|
article->getPrice());
|
||||||
|
|
||||||
|
retCode = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
if (retCode != SQLITE_DONE) {
|
||||||
|
|
||||||
|
std::string errMsg(sqlite3_errmsg(db_));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
throw std::runtime_error(errMsg);
|
||||||
|
}
|
||||||
|
article->setState(Seller::State::OK);
|
||||||
|
++count;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else if (article->getState() == Article::State::UPDATE) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
|
@ -9,21 +9,23 @@
|
||||||
|
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Database(const std::string& dbname);
|
Database(const std::string& dbname);
|
||||||
~Database();
|
~Database();
|
||||||
Database(const Database&) = delete;
|
Database(const Database&) = delete;
|
||||||
Database& operator=(const Database&) = delete;
|
Database& operator=(const Database&) = delete;
|
||||||
void exec(const std::string& sql);
|
void exec(const std::string& sql);
|
||||||
void init();
|
void init();
|
||||||
unsigned int storeSellers(std::vector<Seller>& sellers);
|
unsigned int storeSellers(std::vector<std::shared_ptr<Seller>>& sellers);
|
||||||
private:
|
|
||||||
sqlite3 *db_;
|
private:
|
||||||
|
sqlite3* db_;
|
||||||
std::string dbname_;
|
std::string dbname_;
|
||||||
void beginTransaction();
|
void beginTransaction();
|
||||||
void endTransaction();
|
void endTransaction();
|
||||||
void createNew();
|
void createNew();
|
||||||
int getVersion();
|
int getVersion();
|
||||||
|
unsigned int storeArticles(sqlite3_stmt* stmt, std::vector<Article*> articles);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DATABASE_H
|
#endif // DATABASE_H
|
|
@ -22,4 +22,12 @@ void Entity::setUuidFromString(const std::string& uuidString)
|
||||||
inline Entity::State Entity::getState() const
|
inline Entity::State Entity::getState() const
|
||||||
{
|
{
|
||||||
return state_;
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::setSourceNo(int sourceNo) {
|
||||||
|
sourceNo_ = sourceNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Entity::getSourceNo() const {
|
||||||
|
return sourceNo_;
|
||||||
}
|
}
|
|
@ -12,15 +12,20 @@ class Entity
|
||||||
enum class State { NEW, UPDATE, DELETE, OK };
|
enum class State { NEW, UPDATE, DELETE, OK };
|
||||||
|
|
||||||
virtual ~Entity() = 0;
|
virtual ~Entity() = 0;
|
||||||
const boost::uuids::uuid& getUuid() const { return uuid_; };
|
|
||||||
void createUuid();
|
void createUuid();
|
||||||
void setUuidFromString(const std::string& uuidString);
|
void setUuidFromString(const std::string& uuidString);
|
||||||
virtual State getState() const;
|
|
||||||
void setState(State state) { state_ = state; }
|
void setState(State state) { state_ = state; }
|
||||||
|
void setSourceNo(int sourceNo);
|
||||||
|
|
||||||
|
const boost::uuids::uuid& getUuid() const { return uuid_; };
|
||||||
|
virtual State getState() const;
|
||||||
|
virtual int getSourceNo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::uuids::uuid uuid_{};
|
boost::uuids::uuid uuid_{};
|
||||||
State state_{State::NEW};
|
State state_{State::NEW};
|
||||||
|
int sourceNo_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ENTITY_H
|
#endif // ENTITY_H
|
9
src/core/marketplace.cpp
Normal file
9
src/core/marketplace.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include "marketplace.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
void Marketplace::storeToDb() {
|
||||||
|
const std::string DB_PATH{"/tmp/kima2.db"};
|
||||||
|
|
||||||
|
Database db(DB_PATH);
|
||||||
|
db.storeSellers(sellers_);
|
||||||
|
}
|
21
src/core/marketplace.h
Normal file
21
src/core/marketplace.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef MARKETPLACE_H
|
||||||
|
#define MARKETPLACE_H
|
||||||
|
|
||||||
|
#include "article.h"
|
||||||
|
#include "sale.h"
|
||||||
|
#include "seller.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Marketplace
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void storeToDb();
|
||||||
|
void loadFromDb();
|
||||||
|
private:
|
||||||
|
std::vector<std::shared_ptr<Seller>> sellers_;
|
||||||
|
//std::vector<std::shared_ptr<Article>> articles_;
|
||||||
|
std::vector<std::shared_ptr<Sale>> sales_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -8,9 +8,33 @@ void Sale::addArticle(std::shared_ptr<Article> articlePtr)
|
||||||
articles_.push_back(articlePtr);
|
articles_.push_back(articlePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Article*> Sale::getArticles()
|
||||||
|
{
|
||||||
|
std::vector<Article*> articles(articles_.size());
|
||||||
|
for (const auto& article : articles_) {
|
||||||
|
articles.push_back(article.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return articles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sale::removeArticle(const Article* articlePtr)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(articles_.begin(), articles_.end(),
|
||||||
|
[&articlePtr](auto art) { return art.get() == articlePtr; });
|
||||||
|
if (it != articles_.end()) {
|
||||||
|
(*it)->setSale(nullptr);
|
||||||
|
articles_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Sale::sumInCents()
|
int Sale::sumInCents()
|
||||||
{
|
{
|
||||||
int sum = std::accumulate(articles_.begin(), articles_.end(), 0,
|
int sum = std::accumulate(articles_.begin(), articles_.end(), 0,
|
||||||
[](int a, std::shared_ptr<Article> b) { return a + b->getPrice(); });
|
[](int a, std::shared_ptr<Article> b) { return a + b->getPrice(); });
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Sale::getTimestamp() { return timestamp_; }
|
||||||
|
|
||||||
|
void Sale::setTimestamp(const std::string& timestamp) { timestamp_ = timestamp; }
|
|
@ -5,17 +5,25 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/date_time.hpp>
|
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||||
|
|
||||||
class Article;
|
class Article;
|
||||||
|
|
||||||
class Sale : public Entity
|
class Sale : public Entity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int sumInCents();
|
|
||||||
void addArticle(std::shared_ptr<Article> articlePtr);
|
void addArticle(std::shared_ptr<Article> articlePtr);
|
||||||
|
void setTimestamp(const std::string& timestamp);
|
||||||
|
|
||||||
|
std::vector<Article*> getArticles();
|
||||||
|
std::string getTimestamp();
|
||||||
|
int sumInCents();
|
||||||
|
|
||||||
|
void removeArticle(const Article* articlePtr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::posix_time::ptime systemTime_{boost::posix_time::second_clock::local_time()};
|
std::string timestamp_{
|
||||||
|
boost::posix_time::to_iso_extended_string(boost::posix_time::second_clock::local_time())};
|
||||||
std::vector<std::shared_ptr<Article>> articles_{};
|
std::vector<std::shared_ptr<Article>> articles_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,23 +17,28 @@ BOOST_AUTO_TEST_CASE(store_seller_fail)
|
||||||
{
|
{
|
||||||
Database db(":memory:");
|
Database db(":memory:");
|
||||||
db.init();
|
db.init();
|
||||||
std::vector<Seller> sellers;
|
std::vector<std::shared_ptr<Seller>> sellers;
|
||||||
sellers.push_back({});
|
sellers.push_back(std::make_shared<Seller>());
|
||||||
sellers.push_back({});
|
sellers.push_back(std::make_shared<Seller>());
|
||||||
BOOST_CHECK_THROW(db.storeSellers(sellers), std::runtime_error);
|
BOOST_CHECK_THROW(db.storeSellers(sellers), std::runtime_error);
|
||||||
}
|
}
|
||||||
BOOST_AUTO_TEST_CASE(store_sellers_succ)
|
BOOST_AUTO_TEST_CASE(store_sellers_succ)
|
||||||
{
|
{
|
||||||
Database db(":memory:");
|
Database db(":memory:");
|
||||||
db.init();
|
db.init();
|
||||||
std::vector<Seller> sellers;
|
std::vector<std::shared_ptr<Seller>> sellers;
|
||||||
Seller a{};
|
auto a = std::make_shared<Seller>();
|
||||||
a.createUuid();
|
a->createUuid();
|
||||||
a.setSellerNo(1);
|
a->setSellerNo(1);
|
||||||
Seller b{};
|
auto b = std::make_shared<Seller>("Max", "Mustermann");
|
||||||
b.createUuid();
|
b->createUuid();
|
||||||
b.setSellerNo(2);
|
b->setSellerNo(2);
|
||||||
BOOST_TEST(a.getUuid() != b.getUuid());
|
auto c = std::make_shared<Article>();
|
||||||
|
c->createUuid();
|
||||||
|
c->setPrice(500);
|
||||||
|
c->setDescription("Test");
|
||||||
|
b->addArticle(c);
|
||||||
|
BOOST_TEST(a->getUuid() != b->getUuid());
|
||||||
sellers.push_back(a);
|
sellers.push_back(a);
|
||||||
sellers.push_back(b);
|
sellers.push_back(b);
|
||||||
BOOST_CHECK_NO_THROW(db.storeSellers(sellers));
|
BOOST_CHECK_NO_THROW(db.storeSellers(sellers));
|
||||||
|
@ -43,14 +48,14 @@ BOOST_AUTO_TEST_CASE(seller_states)
|
||||||
{
|
{
|
||||||
Database db(":memory:");
|
Database db(":memory:");
|
||||||
db.init();
|
db.init();
|
||||||
std::vector<Seller> sellers;
|
std::vector<std::shared_ptr<Seller>> sellers;
|
||||||
Seller a;
|
auto a = std::make_shared<Seller>();
|
||||||
a.setSellerNo(3);
|
a->setSellerNo(3);
|
||||||
a.createUuid();
|
a->createUuid();
|
||||||
sellers.push_back(a);
|
sellers.push_back(a);
|
||||||
std::cout << "Anzahl sellers: " << sellers.size() << "\n";
|
std::cout << "Anzahl sellers: " << sellers.size() << "\n";
|
||||||
BOOST_TEST((sellers.at(0).getState() == Entity::State::NEW));
|
BOOST_TEST((sellers.at(0)->getState() == Entity::State::NEW));
|
||||||
BOOST_TEST(db.storeSellers(sellers) == 1);
|
BOOST_TEST(db.storeSellers(sellers) == 1);
|
||||||
BOOST_TEST((sellers.at(0).getState() == Entity::State::OK));
|
BOOST_TEST((sellers.at(0)->getState() == Entity::State::OK));
|
||||||
BOOST_TEST(db.storeSellers(sellers) == 0);
|
BOOST_TEST(db.storeSellers(sellers) == 0);
|
||||||
}
|
}
|
|
@ -30,4 +30,15 @@ BOOST_AUTO_TEST_CASE(articles_sum)
|
||||||
|
|
||||||
BOOST_TEST(sale.sumInCents() == 550);
|
BOOST_TEST(sale.sumInCents() == 550);
|
||||||
BOOST_TEST(seller.getArticles(true).size() == 10);
|
BOOST_TEST(seller.getArticles(true).size() == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(remove_article) {
|
||||||
|
auto art = std::make_shared<Article>();
|
||||||
|
Sale sale{};
|
||||||
|
|
||||||
|
BOOST_TEST(art->isSold() == false);
|
||||||
|
sale.addArticle(art);
|
||||||
|
BOOST_TEST(art->isSold() == true);
|
||||||
|
sale.removeArticle(art.get());
|
||||||
|
BOOST_TEST(art->isSold() == false);
|
||||||
}
|
}
|
Loading…
Reference in a new issue