package service;
import api.v1.Position;
import api.v1.PositionSearch;
import api.v1.PositionTag;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
import exceptions.PoseidonException;
import mapper.PositionMapper;
import mapper.PositionTagMapper;
import models.OrderModel;
import models.PositionModel;
import models.PositionTagCount;
import models.PositionTagModel;
import play.db.DB;
import play.mvc.Http;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class PositionService extends PoseidonService {
public PositionService(String username) {
super(username);
}
public List<PositionModel> findAll() {
List<PositionModel> mPositionList = PositionModel.find.where().isNull("deleted").findList();
for (PositionModel position : mPositionList) {
position.isDeletable = isDeletable(position.id);
}
return mPositionList;
}
public PositionModel getById(long id) {
PositionModel mPosition = PositionModel.find.byId(id);
mPosition.isDeletable = isDeletable(id);
return mPosition;
}
public PositionModel findByName(String name) {
PositionModel mPosition = PositionModel.find.where().ieq("name", name).isNull("deleted").findUnique();
if (mPosition != null) {
mPosition.isDeletable = isDeletable(mPosition.id);
}
return mPosition;
}
public PositionModel deletePosition(PositionModel mPosition) {
if (mPosition.isDeletable) {
// dont physically delete, just mark as deleted
mPosition.deleted = PoseidonService.getNow().toDate();
mPosition.deletedBy = getUser();
mPosition.save();
} else {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Kan ikke slette posisjonen, da denne refereres av en eller flere bestillinger");
}
return mPosition;
}
public PositionModel createOrUpdate(Position apiPosition) {
PositionModel mPosition;
if ((apiPosition.id != null) && (apiPosition.id > 0)) {
mPosition = updateExisting(apiPosition);
} else {
PositionModel p = PositionModel.findByName(apiPosition.name);
if (p != null && p.deleted == null) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Posisjonens navn er ikke unikt");
}
if (p != null) { // exists, but deleted, resurrect it
mPosition = p;
mPosition.deleted = null;
mPosition.deletedBy = null;
} else {
if (apiPosition.tags.length == 0) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Posisjonen må ha minst 1 tag");
}
mPosition = new PositionMapper().mapToModel(apiPosition);
}
mPosition.isDeletable = true;
mPosition.created =PoseidonService.getNow().toDate();
mPosition.createdBy = getUser();
}
mPosition.save();
return mPosition;
}
private PositionModel updateExisting(Position aPosition) {
PositionModel mPosition = getById(aPosition.id);
PositionMapper mapper = new PositionMapper();
if (aPosition.tags.length == 0) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Posisjonen må ha minst 1 tag");
}
mPosition.name = aPosition.name;
mapper.mapCoordinate(aPosition, mPosition);
mPosition.isDeletable = isDeletable(aPosition.id);
mPosition.tags = new ArrayList<>();
for (PositionTag tag : aPosition.tags) {
PositionTagModel mTag;
if (tag.id == null || tag.id == 0) {
mTag = PositionTagModel.findByName(tag.name);
if (mTag == null) {
mTag = new PositionTagModel(tag.name);
mTag.save();
}
} else {
mTag = PositionTagModel.find.byId(tag.id);
if (mTag == null) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Invalid position tag id specified");
}
}
if (mTag.positionModels == null) mTag.positionModels = new ArrayList<>();
mTag.positionModels.add(mPosition);
mPosition.tags.add(mTag);
}
mPosition.updated = PoseidonService.getNow().toDate();
mPosition.updatedBy = getUser();
return mPosition;
}
public List<PositionModel> getPositionsForCustomer(Long id) {
String positionsSql =
"select distinct " +
" p.id, " +
" p.name, " +
" o.position_name, " +
" p.latitude, " +
" p.longitude " +
"from pos_position p, " +
" pos_order o " +
"where o.customer_id = ? " +
"and o.position_id = p.id " +
"and p.deleted is null " +
"and o.deleted is null ";
// Her brukes ren JDBC pga en bug i Ebean med hensyn på transiente verdier (position.alias)
Connection connection = DB.getConnection(PoseidonPropertyService.getDefaultDatasource());
PreparedStatement positionStatement = null;
PreparedStatement tagStatement = null;
List<PositionModel> positions = new ArrayList<>();
try {
int i = 1;
positionStatement = connection.prepareStatement(positionsSql);
positionStatement.setLong(i, id);
String tagSql = "select distinct t.id,t.name " +
"from pos_position p, pos_position_tag t, pos_position_tags pt " +
"where p.id = ? " +
"and pt.position_id = p.id " +
"and pt.tag_id = t.id " +
"ORDER by t.name";
tagStatement = connection.prepareStatement(tagSql);
ResultSet rs = positionStatement.executeQuery();
while (rs.next()) {
PositionModel p = new PositionModel();
p.id = rs.getLong(1);
p.name = rs.getString(2);
p.alias = rs.getString(3);
p.latitude = rs.getDouble(4);
p.longitude = rs.getDouble(5);
tagStatement.setLong(i, p.id);
ResultSet tagRs = null;
try {
tagRs = tagStatement.executeQuery();
while (tagRs.next()) {
PositionTagModel tagModel = new PositionTagModel();
tagModel.id = tagRs.getLong(1);
tagModel.name = tagRs.getString(2);
p.tags.add(tagModel);
}
} finally {
if (tagRs != null) {
tagRs.close();
}
}
positions.add(p);
}
} catch (SQLException e) {
throw new PoseidonException(Http.Status.INTERNAL_SERVER_ERROR, "En feil oppstod i kommunikasjon med databasen. Feilen har blitt logget", e);
} finally {
if (positionStatement != null) try {
positionStatement.close();
if (tagStatement != null) tagStatement.close();
connection.close();
} catch (SQLException e) {
throw new PoseidonException(Http.Status.INTERNAL_SERVER_ERROR, "En feil oppstod i kommunikasjon med databasen. Feilen har blitt logget", e);
}
}
for (PositionModel p : positions) {
p.isDeletable = isDeletable(p.id);
}
return positions;
}
public boolean isDeletable(long positionId) {
boolean isDeletable = false;
String sql = "select distinct o.id " +
"from pos_position p, pos_order o " +
"where p.id = :id " +
"and p.id = o.position_id " +
"and p.deleted is null " +
"and o.deleted is null ";
RawSql rawSql = RawSqlBuilder.parse(sql).
columnMapping("o.id", "id").create();
Query<OrderModel> query = Ebean.find(OrderModel.class);
query.setRawSql(rawSql);
query.setParameter("id",positionId);
List<OrderModel> referencedOrders = query.findList();
if (referencedOrders.size() == 0) {
isDeletable = true;
}
return isDeletable;
}
public List<PositionModel> search(PositionSearch searchParams) {
String select = "select distinct p.id, p.name, p.latitude, p.longitude ";
String from = " from pos_position p ";
String where = " where p.deleted is null ";
if ((searchParams.radius != null && searchParams.radius > 0) &&
(searchParams.position_alias != null && !searchParams.position_alias.isEmpty()) &&
(searchParams.position_name != null && !searchParams.position_name.isEmpty())) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Søkeradius kan ikke angis når både posisjonsnavn og alias er angitt");
}
if ((searchParams.radius != null && searchParams.radius > 0) &&
(searchParams.position_alias == null || searchParams.position_alias.isEmpty()) &&
(searchParams.position_name == null || searchParams.position_name.isEmpty())) {
searchParams.radius = null; // ignore search radius if no position name/alias is specified
}
if (searchParams.radius != null && searchParams.radius > 0.0) {
from = "from (select p.id, p.name, p.latitude, p.longitude " +
" from pos_position p where lower(p.name) like '%" + searchParams.position_name.toLowerCase() + "%' ) as p1,\n" +
" pos_position p\n";
if (where.length() > 7) where += " and ";
where += " acos( sin(radians(p1.latitude))*sin(radians(p.latitude)) + cos(radians(p1.latitude))*cos(radians(p.latitude))*cos(radians(p.longitude-p1.longitude))) * 6371 < " + searchParams.radius;
} else {
if (searchParams.position_name != null && !searchParams.position_name.isEmpty()) {
if (where.length() > 7) where += " and ";
where += "lower(p.name) like '%" + searchParams.position_name.toLowerCase() + "%'";
}
if (searchParams.searchArea != null && searchParams.searchArea.isValid()) {
if (where.length() > 7) where += " and ";
where += " p.latitude between " + searchParams.searchArea.latitude_lower_left + " and " + searchParams.searchArea.latitude_upper_right +
"and p.longitude between " + searchParams.searchArea.longitude_lower_left + " and " + searchParams.searchArea.longitude_upper_right;
}
if (searchParams.tags != null && searchParams.tags.length > 0) {
from += ", pos_position_tag tag, pos_position_tags tags ";
if (where.length() > 7) where += " and ";
String taglist = "";
for (int i = 0; i < searchParams.tags.length; i++) {
taglist += "'" + searchParams.tags[i].name + "'";
if (i < searchParams.tags.length - 1)
taglist += ",";
}
where += " tag.id = tags.tag_id " +
"and tags.position_id = p.id " +
"and tag.name in (" + taglist + ")";
}
}
String sql = select + " " + from + " ";
if (where.length() > 7) sql += where;
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
RawSqlBuilder columnMapping = rawSqlBuilder
.columnMapping("p.id", "id")
.columnMapping("p.name", "name")
.columnMapping("p.latitude", "latitude")
.columnMapping("p.longitude", "longitude");
RawSql rawSql = columnMapping.create();
Query<PositionModel> query = Ebean.find(PositionModel.class);
query.setRawSql(rawSql);
List<PositionModel> positions = query.findList();
for (PositionModel p : positions) {
p.isDeletable = isDeletable(p.id);
}
return positions;
}
public List<PositionTagModel> findAllTags() {
return PositionTagModel.find.all();
}
public PositionTagModel getTagById(Long id) {
return PositionTagModel.find.byId(id);
}
public List<PositionTagModel> findAllMatchingTags(String query) {
return PositionTagModel.find.where().icontains("name", query).findList();
}
public PositionTagModel createOrUpdateTag(PositionTag apiTag) {
PositionTagModel mTag;
if ((apiTag.id != null) && (apiTag.id > 0)) {
mTag = updateExistingTag(apiTag);
} else {
PositionTagModel tag = PositionTagModel.findByName(apiTag.name);
if (tag != null) {
throw new PoseidonException(Http.Status.BAD_REQUEST, "Tagens navn er ikke unikt");
}
mTag = new PositionTagMapper().mapToModel(apiTag);
mTag.created = PoseidonService.getNow().toDate();
mTag.createdBy = getUser();
}
mTag.save();
return mTag;
}
private PositionTagModel updateExistingTag(PositionTag apiTag) {
PositionTagModel mTag = getTagById(apiTag.id);
mTag.name = apiTag.name;
mTag.updated = PoseidonService.getNow().toDate();
mTag.updatedBy = getUser();
return mTag;
}
public List<PositionTagCount> getTagsForCustomer(Long id) {
String select = "select distinct t.id, t.name, count(t.id)";
String from = " from pos_position p , pos_position_tag t, pos_position_tags pt, pos_order o, pos_customer c";
String where = " where " +
" t.id = pt.tag_id " +
" and pt.position_id = p.id " +
" and o.position_id = p.id " +
" and p.deleted is null " +
" and o.deleted is null " +
" and c.id = o.customer_id " +
" and c.id = " + id + "" +
" group by t.name, t.id ";
String sql = select + from + where;
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
RawSqlBuilder columnMapping = rawSqlBuilder
.columnMapping("t.id", "tag.id")
.columnMapping("t.name", "tag.name")
.columnMapping("count(t.id)", "count");
RawSql rawSql = columnMapping.create();
Query<PositionTagCount> query = Ebean.find(PositionTagCount.class);
query.setRawSql(rawSql);
List<PositionTagCount> tags = query.findList();
return tags;
}
}