mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-27 14:27:15 +00:00
Merge branch 'master' into design2015
This commit is contained in:
commit
0cccd0deb2
15 files changed
+85
-82
No files matched your search
+1
-1
@@ -1259,7 +1259,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
logger.info("Removing persistent competitor info for competitor "+competitor.getName()+" with ID "+competitor.getId());
|
||||
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
|
||||
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
|
||||
collection.remove(query);
|
||||
collection.remove(query, WriteConcern.SAFE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-14
@@ -391,20 +391,17 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
}
|
||||
|
||||
private void onCourseDesignChangedEvent(RaceLogCourseDesignChangedEvent event) {
|
||||
if (trackedRace == null)
|
||||
return;
|
||||
|
||||
CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog()).analyze();
|
||||
List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>();
|
||||
|
||||
for (Waypoint waypoint : base.getWaypoints()) {
|
||||
update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions()));
|
||||
}
|
||||
|
||||
try {
|
||||
trackedRace.getRace().getCourse().update(update, params.getDomainFactory());
|
||||
} catch (PatchFailedException e) {
|
||||
logger.log(Level.WARNING, "Could not update course for race " + trackedRace.getRace().getName());
|
||||
if (trackedRace != null) {
|
||||
CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog()).analyze();
|
||||
List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>();
|
||||
for (Waypoint waypoint : base.getWaypoints()) {
|
||||
update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions()));
|
||||
}
|
||||
try {
|
||||
trackedRace.getRace().getCourse().update(update, params.getDomainFactory());
|
||||
} catch (PatchFailedException e) {
|
||||
logger.log(Level.WARNING, "Could not update course for race " + trackedRace.getRace().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -23,7 +23,6 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
|
||||
@Override
|
||||
public List<Leg> getLegs() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -72,7 +71,6 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
|
||||
@Override
|
||||
public Leg getFirstLeg() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,11 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
*/
|
||||
public interface Entry {
|
||||
int getTrackedRank();
|
||||
Double getNetPoints() throws NoWindException;
|
||||
Double getNetPointsUncorrected() throws NoWindException;
|
||||
Double getTotalPoints() throws NoWindException;
|
||||
Double getNetPoints();
|
||||
Double getNetPointsUncorrected();
|
||||
Double getTotalPoints();
|
||||
MaxPointsReason getMaxPointsReason();
|
||||
boolean isDiscarded() throws NoWindException;
|
||||
boolean isDiscarded();
|
||||
/**
|
||||
* Tells if the net points have been corrected by a {@link ScoreCorrection}
|
||||
*/
|
||||
|
||||
+12
-21
@@ -219,7 +219,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
return fleet;
|
||||
}
|
||||
@Override
|
||||
public Double getNetPointsUncorrected() throws NoWindException {
|
||||
public Double getNetPointsUncorrected() {
|
||||
try {
|
||||
return netPointsUncorrectedProvider.call();
|
||||
} catch (Exception e) {
|
||||
@@ -1280,21 +1280,12 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
final Set<RaceColumn> discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint);
|
||||
for (final RaceColumn raceColumn : this.getRaceColumns()) {
|
||||
RunnableFuture<LeaderboardEntryDTO> future = new FutureTask<LeaderboardEntryDTO>(() -> {
|
||||
try {
|
||||
Entry entry = AbstractSimpleLeaderboardImpl.this.getEntry(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
return getLeaderboardEntryDTO(entry, raceColumn, competitor, timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails != null
|
||||
&& namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory,
|
||||
fillNetPointsUncorrected, cache);
|
||||
} catch (NoWindException e) {
|
||||
logger.info("Exception trying to compute leaderboard entry for competitor "
|
||||
+ competitor.getName() + " in race column " + raceColumn.getName() + ": "
|
||||
+ e.getMessage());
|
||||
logger.throwing(AbstractSimpleLeaderboardImpl.class.getName(),
|
||||
"computeLeaderboardByName.future.call()", e);
|
||||
throw new NoWindError(e);
|
||||
}
|
||||
Entry entry = AbstractSimpleLeaderboardImpl.this.getEntry(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
return getLeaderboardEntryDTO(entry, raceColumn, competitor, timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails != null
|
||||
&& namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory,
|
||||
fillNetPointsUncorrected, cache);
|
||||
});
|
||||
executor.execute(future);
|
||||
futuresForColumnName.put(raceColumn.getName(), future);
|
||||
@@ -1365,7 +1356,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
private LeaderboardEntryDTO getLeaderboardEntryDTO(Entry entry, RaceColumn raceColumn, Competitor competitor,
|
||||
TimePoint timePoint, boolean addLegDetails, boolean waitForLatestAnalyses,
|
||||
Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache, DomainFactory baseDomainFactory,
|
||||
boolean fillNetPointsUncorrected, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
boolean fillNetPointsUncorrected, WindLegTypeAndLegBearingCache cache) {
|
||||
LeaderboardEntryDTO entryDTO = new LeaderboardEntryDTO();
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
|
||||
entryDTO.race = trackedRace == null ? null : trackedRace.getRaceIdentifier();
|
||||
@@ -1541,7 +1532,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
*/
|
||||
private RaceDetails getRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws InterruptedException, ExecutionException {
|
||||
final RaceDetails raceDetails;
|
||||
if (trackedRace.getEndOfTracking() != null && trackedRace.getEndOfTracking().compareTo(timePoint) < 0) {
|
||||
raceDetails = getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(trackedRace, competitor, legRanksCache, rankingInfo, cache);
|
||||
@@ -1553,7 +1544,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private RaceDetails getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(final TrackedRace trackedRace,
|
||||
final Competitor competitor, final Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, final WindLegTypeAndLegBearingCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
RankingInfo rankingInfo, final WindLegTypeAndLegBearingCache cache) throws InterruptedException, ExecutionException {
|
||||
final com.sap.sse.common.Util.Pair<TrackedRace, Competitor> key = new com.sap.sse.common.Util.Pair<TrackedRace, Competitor>(trackedRace, competitor);
|
||||
RunnableFuture<RaceDetails> raceDetails;
|
||||
synchronized (raceDetailsAtEndOfTrackingCache) {
|
||||
@@ -1585,7 +1576,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private RaceDetails calculateRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
WindLegTypeAndLegBearingCache cache, final RankingInfo rankingInfo) throws NoWindException {
|
||||
WindLegTypeAndLegBearingCache cache, final RankingInfo rankingInfo) {
|
||||
final List<LegEntryDTO> legDetails = new ArrayList<LegEntryDTO>();
|
||||
final Course course = trackedRace.getRace().getCourse();
|
||||
course.lockForRead(); // hold back any course re-configurations while looping over the legs
|
||||
@@ -1633,7 +1624,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private LegEntryDTO createLegEntry(TrackedLegOfCompetitor trackedLeg, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) {
|
||||
LegEntryDTO result;
|
||||
final Duration time = trackedLeg.getTime(timePoint);
|
||||
if (trackedLeg == null || time == null) {
|
||||
|
||||
+2
-2
@@ -195,7 +195,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* <code>timePoint</code>, returns the VMG value for this time point. If the competitor has already finished the
|
||||
* leg, the VMG at the time when the competitor finished the leg is returned.
|
||||
*/
|
||||
Speed getVelocityMadeGood(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
Speed getVelocityMadeGood(TimePoint timePoint, WindPositionMode windPositionMode);
|
||||
|
||||
/**
|
||||
* Same as {@link #getVelocityMadeGood(TimePoint, WindPositionMode)}, only that a cache for wind data and leg type and bearing
|
||||
@@ -212,7 +212,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
/**
|
||||
* Same as {@link #getEstimatedTimeToNextMark(TimePoint, WindPositionMode)}, only that a cache for leg type calculation is passed.
|
||||
*/
|
||||
Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
/**
|
||||
* Returns <code>null</code> in case this leg's competitor hasn't started the leg yet. If in the leg at
|
||||
|
||||
+13
-9
@@ -284,11 +284,13 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
// as fallback in the absence of wind information, project to leg bearing
|
||||
projectToBearing = cache.getLegBearing(getTrackedLeg(), at);
|
||||
}
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
if (speed.getBearing() != null && projectToBearing != null) {
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -338,10 +340,12 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
final Position endPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(to, /* extrapolate */false);
|
||||
if (endPos != null) {
|
||||
Distance d = getTrackedLeg().getAbsoluteWindwardDistance(
|
||||
getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(start.getTimePoint(), false),
|
||||
endPos, to, WindPositionMode.EXACT, cache);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
final Position startPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(start.getTimePoint(), false);
|
||||
if (startPos != null) {
|
||||
Distance d = getTrackedLeg().getAbsoluteWindwardDistance(startPos, endPos, to,
|
||||
WindPositionMode.EXACT, cache);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -613,7 +617,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode) {
|
||||
return getVelocityMadeGood(at, windPositionMode, new LeaderboardDTOCalculationReuseCache(at));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public interface BoatClassImageResources extends ClientBundle {
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/470.png")
|
||||
ImageResource _470Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.png")
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.jpg")
|
||||
ImageResource _49erFXIcon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER.png")
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB |
+14
-5
@@ -8,6 +8,7 @@ import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.DB;
|
||||
@@ -16,6 +17,7 @@ import com.mongodb.MongoException;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
|
||||
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
@@ -25,11 +27,18 @@ import com.sap.sailing.domain.test.AbstractLeaderboardTest;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
|
||||
private DomainFactory domainFactory;
|
||||
|
||||
public StoreCompetitorTest() throws UnknownHostException, MongoException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// clear the domainFactory competitor store for a clean start:
|
||||
domainFactory = new DomainFactoryImpl();
|
||||
}
|
||||
|
||||
private void dropCompetitorCollection() {
|
||||
DB db = getMongoService().getDB();
|
||||
DBCollection competitorCollection = db.getCollection(CollectionNames.COMPETITORS.name());
|
||||
@@ -45,7 +54,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
String competitorName2 = "Hasso Plattner";
|
||||
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor(competitorName1);
|
||||
@@ -72,7 +81,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndUpdateCompetitorWithUUIDAsId() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID());
|
||||
@@ -86,7 +95,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndRemoveCompetitor() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso");
|
||||
@@ -100,7 +109,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndRemoveCompetitorWithUUIDAsId() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID());
|
||||
|
||||
+4
-8
@@ -287,14 +287,10 @@ public class RegattaAndRaceDataJsonGetServlet extends AbstractJsonHttpServlet {
|
||||
jsonCompetitorInLeg.put("distanceTraveledOverGroundInMeters",
|
||||
distanceTraveled.getMeters());
|
||||
}
|
||||
try {
|
||||
Speed velocityMadeGood = trackedLegOfCompetitor.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
|
||||
if (velocityMadeGood != null) {
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInKnots", velocityMadeGood.getKnots());
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInMetersPerSecond", velocityMadeGood.getMetersPerSecond());
|
||||
}
|
||||
} catch (NoWindException e) {
|
||||
// well, we don't know the wind direction... then no VMG will be shown...
|
||||
Speed velocityMadeGood = trackedLegOfCompetitor.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
|
||||
if (velocityMadeGood != null) {
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInKnots", velocityMadeGood.getKnots());
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInMetersPerSecond", velocityMadeGood.getMetersPerSecond());
|
||||
}
|
||||
Speed averageVelocityMadeGood = trackedLegOfCompetitor
|
||||
.getAverageVelocityMadeGood(timePoint);
|
||||
|
||||
@@ -246,12 +246,16 @@ if [[ $OPERATION == "auto-install" ]]; then
|
||||
activate_user_data
|
||||
# then download and install environment and append to env.sh
|
||||
install_environment
|
||||
# finally, append user data to env.sh as it shall take precedence over the installed environment's defaults
|
||||
append_user_data_to_envsh
|
||||
# finally, append user data to env.sh as it shall take precedence over the installed environment's defaults
|
||||
append_user_data_to_envsh
|
||||
|
||||
# make sure to reload data
|
||||
source `pwd`/env.sh
|
||||
|
||||
# Append Apache macro invocation for /internal-server-status based on mod_status and INSTANCE_DNS to /etc/httpd/conf.d/001-events.conf
|
||||
APACHE_CONFIG_DIR=/etc/httpd/conf.d
|
||||
echo "Appending macro usage for /internal-server-status URL for mod_status based Apache monitoring to $APACHE_CONFIG_DIR/001-events.conf"
|
||||
echo "Use Status $INSTANCE_DNS internal-server-status" >>$APACHE_CONFIG_DIR/001-events.conf
|
||||
|
||||
if [[ $INSTALL_FROM_RELEASE == "" ]] && [[ $BUILD_BEFORE_START != "True" ]]; then
|
||||
echo "I could not find any option telling me to download a release or to build! Possible cause: Your environment contains empty values for these variables!"
|
||||
|
||||
Reference in new issue
Block a user