profile
viewpoint

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.HashSet;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Servlet that returns bucket list content */+@WebServlet("/generate-hunt")+public class GenerateServlet extends HttpServlet {++  private static final String FILTER_ARRAY = "clicked-array";++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    // Get array of clicked filters and convert to ArrayList<String>+    Gson gson = new Gson();+    HashSet<String> clickedFilters =+        gson.fromJson(request.getParameter(FILTER_ARRAY), HashSet.class);++    response.setContentType("text/html;");+    response.getWriter().println(clickedFilters);

Chatted offline - @shreyachatterjee00 and @lilymzhou should meet to discuss how to make this one request flow instead of 2.

shreyachatterjee00

comment created time in an hour

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.HashSet;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Servlet that returns bucket list content */+@WebServlet("/generate-hunt")+public class GenerateServlet extends HttpServlet {++  private static final String FILTER_ARRAY = "clicked-array";++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    // Get array of clicked filters and convert to ArrayList<String>+    Gson gson = new Gson();+    HashSet<String> clickedFilters =+        gson.fromJson(request.getParameter(FILTER_ARRAY), HashSet.class);++    response.setContentType("text/html;");+    response.getWriter().println(clickedFilters);

What is the longer-term plan for this return value? Right now you are just returning what was requested, but eventually you'll want to start the scavenger hunt if you were able to create one. Is the plan that you get the filters, query datastore for destinations, pick some, and create a scavenger hunt, store it in datastore and... return an ID? return the scavenger hunt? It's my understanding that @lilymzhou is working on querying the datastore for a hunt, but if her code also has the client requesting the code from the datastore, wouldn't it make more sense to just return the hunt directly from this request instead (what I suspect might be happening) only using this POST request to store the data and another GET request to get the hunt you just created from the datastore?

I want to make sure these two pieces are actually connected together and not just tangentially related via datastore. Thanks!

shreyachatterjee00

comment created time in 2 hours

pull request commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

Please address the changes I left, but don't wait for Tim's review to merge this in. Go ahead as soon as you've made the changes I've suggested.

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {+  private static final String NAME_PARAMETER = "name";+  private static final String LAT_PARAMETER = "latitude";+  private static final String LNG_PARAMETER = "longitude";+  private static final String CITY_PARAMETER = "city";+  private static final String DESCRIPTION_PARAMETER = "description";+  private static final String RIDDLE_PARAMETER = "riddle";+  private static final String HINT1_PARAMETER = "hint1";+  private static final String HINT2_PARAMETER = "hint2";+  private static final String HINT3_PARAMETER = "hint3";+  private static final String OBSCURITY_PARAMETER = "obscurity";+  private static final String TAG_PARAMETER = "tag";+  private static final String REDIRECT_URL = "/destination-data";++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter(NAME_PARAMETER);++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter(LAT_PARAMETER)))+            .withLng(Double.parseDouble(request.getParameter(LNG_PARAMETER)))+            .build();++    String city = request.getParameter(CITY_PARAMETER);+    String description = request.getParameter(DESCRIPTION_PARAMETER);++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter(RIDDLE_PARAMETER))+            .withHint(request.getParameter(HINT1_PARAMETER))+            .withHint(request.getParameter(HINT2_PARAMETER))+            .withHint(request.getParameter(HINT3_PARAMETER))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    String obscureLevel = request.getParameter(OBSCURITY_PARAMETER);+    Destination.Obscurity level = convertLevelToEnum(obscureLevel);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues(TAG_PARAMETER))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination destination =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    /* TODO: Store the data in datastore rather than locally */+    destinations.add(destination);+    /* TODO: redirect back index.html once the data is stored in datastore */+    response.sendRedirect(REDIRECT_URL);+  }++  /* Retrieves the most recent destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {+    Set<Destination.Tag> tagEnums = new HashSet<Destination.Tag>();+    for (String tag : tags) {+      switch (tag) {+        case "art":+          tagEnums.add(Destination.Tag.ART);+          break;+        case "sports":+          tagEnums.add(Destination.Tag.SPORT);+          break;+        case "historical":+          tagEnums.add(Destination.Tag.HISTORICAL);+          break;+        case "food":+          tagEnums.add(Destination.Tag.FOOD);+          break;+        case "family":+          tagEnums.add(Destination.Tag.FAMILY);+          break;+        case "tourist":+          tagEnums.add(Destination.Tag.TOURIST);+          break;+      }+    }++    return tagEnums;+  }++  public Destination.Obscurity convertLevelToEnum(String obscureLevel) {+    switch (obscureLevel) {+      case "easy":+        return Destination.Obscurity.EASY;+      case "medium":+        return Destination.Obscurity.MEDIUM;+      case "hard":+        return Destination.Obscurity.HARD;+    }++    return Destination.Obscurity.UNDEFINED;

You can add a default case which will cover everything else

    switch (obscureLevel) {
      case "easy":
        return Destination.Obscurity.EASY;
      case "medium":
        return Destination.Obscurity.MEDIUM;
      case "hard":
        return Destination.Obscurity.HARD;
      default:
        return Destination.Obscurity.UNDEFINED; 
    }
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.data;++import java.util.ArrayList;+import java.util.HashSet;+import java.util.Set;++// Represents a destination submitted by a user.+public class Destination {++  public enum Tag {+    FOOD,+    SPORT,+    TOURIST,+    HISTORICAL,+    ART,+    FAMILY,+    UNDEFINED;+  }++  public enum Obscurity {+    EASY,+    MEDIUM,+    HARD,+    UNDEFINED;+  }++  private String name;+  private LatLng location;+  private String city;+  private String description;+  private ArrayList<Riddle> riddles = new ArrayList<>();+  private Obscurity level;+  private Set<Tag> categories = new HashSet<>();++  private Destination() {}++  public static class Builder {+    private String name;+    private LatLng location;+    private String city;+    private String description;+    private ArrayList<Riddle> riddles = new ArrayList<>();+    private Obscurity level;+    private Set<Tag> categories = new HashSet<>();++    public Builder withName(String name) {+      this.name = name;+      return this;+    }++    public Builder withLocation(LatLng location) {+      this.location = location;+      return this;+    }++    public Builder withCity(String city) {+      this.city = city;+      return this;+    }++    public Builder withDescription(String description) {+      this.description = description;+      return this;+    }++    public Builder withRiddle(Riddle riddle) {+      riddles.add(riddle);+      return this;+    }++    public Builder withTags(Set<Tag> categories) {+      for (Tag tag : categories) {+        this.categories.add(tag);+      }+      return this;+    }++    public Builder withObscurity(Obscurity level) {+      this.level = level;+      return this;+    }++    public Destination build() {+      Destination destination = new Destination();+      destination.name = this.name;+      destination.location = this.location;+      destination.city = this.city;+      destination.description = this.description;+      for (Riddle riddle : this.riddles) {+        destination.riddles.add(riddle);+      }+      for (Tag tag : this.categories) {+        destination.categories.add(tag);+      }

same here!

      destination.categories.addAll(this.categories);
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.data;++import java.util.ArrayList;+import java.util.HashSet;+import java.util.Set;++// Represents a destination submitted by a user.+public class Destination {++  public enum Tag {+    FOOD,+    SPORT,+    TOURIST,+    HISTORICAL,+    ART,+    FAMILY,+    UNDEFINED;+  }++  public enum Obscurity {+    EASY,+    MEDIUM,+    HARD,+    UNDEFINED;+  }++  private String name;+  private LatLng location;+  private String city;+  private String description;+  private ArrayList<Riddle> riddles = new ArrayList<>();+  private Obscurity level;+  private Set<Tag> categories = new HashSet<>();++  private Destination() {}++  public static class Builder {+    private String name;+    private LatLng location;+    private String city;+    private String description;+    private ArrayList<Riddle> riddles = new ArrayList<>();+    private Obscurity level;+    private Set<Tag> categories = new HashSet<>();++    public Builder withName(String name) {+      this.name = name;+      return this;+    }++    public Builder withLocation(LatLng location) {+      this.location = location;+      return this;+    }++    public Builder withCity(String city) {+      this.city = city;+      return this;+    }++    public Builder withDescription(String description) {+      this.description = description;+      return this;+    }++    public Builder withRiddle(Riddle riddle) {+      riddles.add(riddle);+      return this;+    }++    public Builder withTags(Set<Tag> categories) {+      for (Tag tag : categories) {+        this.categories.add(tag);+      }+      return this;+    }++    public Builder withObscurity(Obscurity level) {+      this.level = level;+      return this;+    }++    public Destination build() {+      Destination destination = new Destination();+      destination.name = this.name;+      destination.location = this.location;+      destination.city = this.city;+      destination.description = this.description;+      for (Riddle riddle : this.riddles) {+        destination.riddles.add(riddle);+      }

There's a shortcut for this, the addAll method: https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#addAll(java.util.Collection)

      destination.riddles.addAll(this.riddles);
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.data;++import java.util.ArrayList;+import java.util.HashSet;+import java.util.Set;++// Represents a destination submitted by a user.+public class Destination {++  public enum Tag {+    FOOD,+    SPORT,+    TOURIST,+    HISTORICAL,+    ART,+    FAMILY,+    UNDEFINED;

It's common practice to put UNDEFINED as the first type.

    UNDEFINED,
    FOOD,
    SPORT,
    TOURIST,
    HISTORICAL,
    ART,
    FAMILY;
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {+  private static final String NAME_PARAMETER = "name";+  private static final String LAT_PARAMETER = "latitude";+  private static final String LNG_PARAMETER = "longitude";+  private static final String CITY_PARAMETER = "city";+  private static final String DESCRIPTION_PARAMETER = "description";+  private static final String RIDDLE_PARAMETER = "riddle";+  private static final String HINT1_PARAMETER = "hint1";+  private static final String HINT2_PARAMETER = "hint2";+  private static final String HINT3_PARAMETER = "hint3";+  private static final String OBSCURITY_PARAMETER = "obscurity";+  private static final String TAG_PARAMETER = "tag";+  private static final String REDIRECT_URL = "/destination-data";++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter(NAME_PARAMETER);++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter(LAT_PARAMETER)))+            .withLng(Double.parseDouble(request.getParameter(LNG_PARAMETER)))+            .build();++    String city = request.getParameter(CITY_PARAMETER);+    String description = request.getParameter(DESCRIPTION_PARAMETER);++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter(RIDDLE_PARAMETER))+            .withHint(request.getParameter(HINT1_PARAMETER))+            .withHint(request.getParameter(HINT2_PARAMETER))+            .withHint(request.getParameter(HINT3_PARAMETER))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    String obscureLevel = request.getParameter(OBSCURITY_PARAMETER);+    Destination.Obscurity level = convertLevelToEnum(obscureLevel);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues(TAG_PARAMETER))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination destination =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    /* TODO: Store the data in datastore rather than locally */+    destinations.add(destination);+    /* TODO: redirect back index.html once the data is stored in datastore */+    response.sendRedirect(REDIRECT_URL);+  }++  /* Retrieves the most recent destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {

this method should be private, not public, since it's not used by any other classes

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {+  private static final String NAME_PARAMETER = "name";+  private static final String LAT_PARAMETER = "latitude";+  private static final String LNG_PARAMETER = "longitude";+  private static final String CITY_PARAMETER = "city";+  private static final String DESCRIPTION_PARAMETER = "description";+  private static final String RIDDLE_PARAMETER = "riddle";+  private static final String HINT1_PARAMETER = "hint1";+  private static final String HINT2_PARAMETER = "hint2";+  private static final String HINT3_PARAMETER = "hint3";+  private static final String OBSCURITY_PARAMETER = "obscurity";+  private static final String TAG_PARAMETER = "tag";+  private static final String REDIRECT_URL = "/destination-data";++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter(NAME_PARAMETER);++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter(LAT_PARAMETER)))+            .withLng(Double.parseDouble(request.getParameter(LNG_PARAMETER)))+            .build();++    String city = request.getParameter(CITY_PARAMETER);+    String description = request.getParameter(DESCRIPTION_PARAMETER);++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter(RIDDLE_PARAMETER))+            .withHint(request.getParameter(HINT1_PARAMETER))+            .withHint(request.getParameter(HINT2_PARAMETER))+            .withHint(request.getParameter(HINT3_PARAMETER))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    String obscureLevel = request.getParameter(OBSCURITY_PARAMETER);+    Destination.Obscurity level = convertLevelToEnum(obscureLevel);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues(TAG_PARAMETER))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination destination =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    /* TODO: Store the data in datastore rather than locally */+    destinations.add(destination);+    /* TODO: redirect back index.html once the data is stored in datastore */+    response.sendRedirect(REDIRECT_URL);+  }++  /* Retrieves the most recent destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {+    Set<Destination.Tag> tagEnums = new HashSet<Destination.Tag>();+    for (String tag : tags) {+      switch (tag) {+        case "art":+          tagEnums.add(Destination.Tag.ART);+          break;+        case "sports":+          tagEnums.add(Destination.Tag.SPORT);+          break;+        case "historical":+          tagEnums.add(Destination.Tag.HISTORICAL);+          break;+        case "food":+          tagEnums.add(Destination.Tag.FOOD);+          break;+        case "family":+          tagEnums.add(Destination.Tag.FAMILY);+          break;+        case "tourist":+          tagEnums.add(Destination.Tag.TOURIST);+          break;+      }+    }++    return tagEnums;+  }++  public Destination.Obscurity convertLevelToEnum(String obscureLevel) {

same with this one - make it private

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.data;++import java.util.ArrayList;+import java.util.HashSet;+import java.util.Set;++// Represents a destination submitted by a user.+public class Destination {++  public enum Tag {+    FOOD,+    SPORT,+    TOURIST,+    HISTORICAL,+    ART,+    FAMILY,+    UNDEFINED;+  }++  public enum Obscurity {+    EASY,+    MEDIUM,+    HARD,+    UNDEFINED;

same here - move UNDEFINED to the first type

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))

My mistake, the method is getParameter: https://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html#getParameter(java.lang.String)

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);+    response.sendRedirect("/destination-data");+  }++  /* Retrieves the destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {+    Set<Destination.Tag> tagEnums = new HashSet<Destination.Tag>();+    for (String tag : tags) {+      switch (tag) {+        case "art":+          tagEnums.add(Destination.Tag.ART);+          break;+        case "sports":+          tagEnums.add(Destination.Tag.SPORT);+          break;+        case "historical":+          tagEnums.add(Destination.Tag.HISTORICAL);+          break;+        case "food":+          tagEnums.add(Destination.Tag.FOOD);+          break;+        case "family":+          tagEnums.add(Destination.Tag.FAMILY);+          break;+        case "tourist":+          tagEnums.add(Destination.Tag.TOURIST);+          break;+        default:+          tagEnums.add(Destination.Tag.UNDEFINED);+      }+    }++    return tagEnums;+  }++  public Destination.Obscurity convertLevelToEnum(List<String> levels) {

Also I missed this, but this should just be a string with the 1 level, right? Not a list of strings.

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))

I imagine you'd just want to use getParameterValue instead of getParameterValues, right?

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);+    response.sendRedirect("/destination-data");+  }++  /* Retrieves the destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {+    Set<Destination.Tag> tagEnums = new HashSet<Destination.Tag>();+    for (String tag : tags) {+      switch (tag) {+        case "art":+          tagEnums.add(Destination.Tag.ART);+          break;+        case "sports":+          tagEnums.add(Destination.Tag.SPORT);+          break;+        case "historical":+          tagEnums.add(Destination.Tag.HISTORICAL);+          break;+        case "food":+          tagEnums.add(Destination.Tag.FOOD);+          break;+        case "family":+          tagEnums.add(Destination.Tag.FAMILY);+          break;+        case "tourist":+          tagEnums.add(Destination.Tag.TOURIST);+          break;+        default:+          tagEnums.add(Destination.Tag.UNDEFINED);

Yes, it's good practice to have an UNDEFINED value when you define the enum.

In this particular case, you're taking in a list of strings the user has given you and converting it to a set of enums. Since the user isn't ever going to be specifying "undefined" as a tag, there's no point in adding it to your enum set.

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);+    response.sendRedirect("/destination-data");+  }++  /* Retrieves the destination created by the user, turns it into a JSON formatted string,

Leave a comment in the code, not the PR. :)

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.ArrayList;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;+import java.util.List;+import java.util.Arrays;+import java.util.stream.Collectors;+import java.util.Set;+import java.util.HashSet;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {+  +  // Temporarily stores the destination created by the user+  public List<com.google.sps.data.Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    com.google.sps.data.LatLng location = new com.google.sps.data.LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    com.google.sps.data.Riddle riddle = new com.google.sps.data.Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();+            +    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels = Arrays.stream(request.getParameterValues("obscurity")).filter(level -> level != null).collect(Collectors.toList());+    com.google.sps.data.Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags = Arrays.stream(request.getParameterValues("tag")).filter(tag -> tag != null).collect(Collectors.toList());+    Set<com.google.sps.data.Destination.Tag> checkedTags = convertTagsToEnum(tags);++    com.google.sps.data.Destination d1 = new com.google.sps.data.Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();+    +    destinations.add(d1);+    response.sendRedirect("/destination-data");

leave a TODO comment to indicate that you're planning on changing it to redirect to an HTML file

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);+    response.sendRedirect("/destination-data");+  }++  /* Retrieves the destination created by the user, turns it into a JSON formatted string,+   *and displays the JSON-ified String on /destination-data+   */+  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    Gson gson = new Gson();+    String json = gson.toJson(destinations.get(destinations.size() - 1));+    response.setContentType("application/json;");+    response.getWriter().println(json);+  }++  public Set<Destination.Tag> convertTagsToEnum(List<String> tags) {+    Set<Destination.Tag> tagEnums = new HashSet<Destination.Tag>();+    for (String tag : tags) {+      switch (tag) {+        case "art":+          tagEnums.add(Destination.Tag.ART);+          break;+        case "sports":+          tagEnums.add(Destination.Tag.SPORT);+          break;+        case "historical":+          tagEnums.add(Destination.Tag.HISTORICAL);+          break;+        case "food":+          tagEnums.add(Destination.Tag.FOOD);+          break;+        case "family":+          tagEnums.add(Destination.Tag.FAMILY);+          break;+        case "tourist":+          tagEnums.add(Destination.Tag.TOURIST);+          break;+        default:+          tagEnums.add(Destination.Tag.UNDEFINED);

i don't think you want to add UNDEFINED to your set of tags, right?

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);

The idea is that eventually this would write to the datastore, right? If so, please leave a TODO comment so that it's clear what the ulitmate goal of this code is.

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =+        new Destination.Builder()+            .withName(name)+            .withLocation(location)+            .withCity(city)+            .withDescription(description)+            .withRiddle(riddle)+            .withTags(checkedTags)+            .withObscurity(level)+            .build();++    destinations.add(d1);+    response.sendRedirect("/destination-data");+  }++  /* Retrieves the destination created by the user, turns it into a JSON formatted string,

This actually just retrieves the last destination that was created, by any person. Update the comment to reflect what this method is doing, or leave a TODO to indicate what you plan to do.

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))+            .filter(level -> level != null)+            .collect(Collectors.toList());+    Destination.Obscurity level = convertLevelToEnum(levels);++    /* Retrieves the tags selected by the user as a List of Strings and converts them into a Set of Enum Tags */+    List<String> tags =+        Arrays.stream(request.getParameterValues("tag"))+            .filter(tag -> tag != null)+            .collect(Collectors.toList());+    Set<Destination.Tag> checkedTags = convertTagsToEnum(tags);++    Destination d1 =

the doPost method will only ever create 1 destination at a time, so you can just call this variable destination

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");++    LatLng location =+        new LatLng.Builder()+            .withLat(Double.parseDouble(request.getParameter("latitude")))+            .withLng(Double.parseDouble(request.getParameter("longitude")))+            .build();++    String city = request.getParameter("city");+    String description = request.getParameter("description");++    Riddle riddle =+        new Riddle.Builder()+            .withPuzzle(request.getParameter("riddle"))+            .withHint(request.getParameter("hint1"))+            .withHint(request.getParameter("hint2"))+            .withHint(request.getParameter("hint3"))+            .build();++    /* Retrieves the obscurity level chosen by the user as a List of Strings and converts the level to an Enum Obscurity value */+    List<String> levels =+        Arrays.stream(request.getParameterValues("obscurity"))

this implies there can be more than 1 level of obsurity?

rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {++  // Temporarily stores the destination created by the user+  public List<Destination> destinations = new ArrayList<>();++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String name = request.getParameter("name");

Create constants for these parameter names and declare them at the top, like I did in the suggestion above. Do this for all parameter names.

    String name = request.getParameter(NAME_PARAMETER);
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.data;++import java.util.ArrayList;+import java.util.HashSet;+import java.util.Set;++// Represents a destination submitted by a user.+public class Destination {++  public enum Tag {+    FOOD,+    SPORT,+    TOURIST,+    HISTORICAL,+    ART,+    FAMILY,+    UNDEFINED;+  }++  public enum Obscurity {+    EASY,+    MEDIUM,+    HARD,+    UNDEFINED;+  }++  private String name;+  private LatLng location;+  private String city;+  private String description;+  private ArrayList<com.google.sps.data.Riddle> riddles = new ArrayList<>();

If two classes are in the same package (in this case, com.google.sps.data), you don't need to prefix the class name like this, nor do you need to add an import statement at the top. DestinationDataServlet, by contrast, is in the com.google.sps.servlets package, so you do need to add the import statement in that case.

  private ArrayList<Riddle> riddles = new ArrayList<>();
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Servlet that retrieves user submitted information and creates a destination from this data

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.Destination;+import com.google.sps.data.LatLng;+import com.google.sps.data.Riddle;+import java.io.IOException;+import java.util.ArrayList;+import java.util.Arrays;+import java.util.HashSet;+import java.util.List;+import java.util.Set;+import java.util.stream.Collectors;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/* Returns a destination created from user submitted information */+@WebServlet("/destination-data")+public class DestinationDataServlet extends HttpServlet {+
  private static final NAME_PARAMETER = "name";
rysimone

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

 function createLine(text) {  /**  * Show or hide the proceed button.- *  * @param {boolean} hide Whether the proceed button should be hidden or shown.  */ function toggleProceedButton(hide) {   const proceedButton = document.getElementById(PROCEED_BUTTON);   if (hide) {     proceedButton.classList.add(INVISIBLE_CLASS);   } else {+    if (destIndex == huntArr.length - 1) {+      document.querySelector('#' + PROCEED_BUTTON).innerText =+          PROCEED_FINAL_MSSG;+    }     proceedButton.classList.remove(INVISIBLE_CLASS);   } } +/**+ * Show or hide the hint button.+ * @param {boolean} hide Whether the proceed button should be hidden or shown.+ * This function is implemented in the adjacent PR.
 * TODO: Implement this function.
lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

 window.onload = getHunt(); function getHunt() {   fetch(DATA_URL).then((response) => response.json()).then((mssg) => {     destIndex = mssg.index;-    for (let i = 0; i < mssg.items.length; i++) {-      puzzleArr.push(mssg.items[i].riddle.puzzle);-      destArr.push(mssg.items[i].name);+    if (huntArr.length == 0) {+      for (let i = 0; i < mssg.items.length; i++) {+        const cur = mssg.items[i];+        huntArr.push(new Destination(cur.name, cur.description,+            cur.riddle.puzzle, cur.riddle.hints, cur.location.lat,+            cur.location.lng));+      }     }+    createMap();     updateToCurrentState(destIndex);+    if (destIndex >= 0) {+      handleDestinationAnswer(mssg.guess);+    }   }); }  /**- * Updates the hunt to the current destination that the user is on.- *- * @param {int} index The index of the destination that the user needs- * to find.+ * Determines whether the user entered the correct destination, and+ * adjusts the display accordingly.+ * @param {String} guess The destination name entered by the user.  */-async function updateToCurrentState(index) {-  if (index == 0) { // User presses the start button.-    sendIndexToServlet(index);-  }-  if (index >= 0) { // The user has already begun the hunt.-    hideStartButton();-    changeRiddleMessage(puzzleArr[index]);-    const isCorrect = await checkCorrectDestination();-    if (isCorrect) {-      toggleProceedButton(/* hide = */ false);-    }-  } else { // The user has not yet pressed the start button.-    toggleProceedButton(/* hide = */ true);+function handleDestinationAnswer(guess) {+  if (guess === huntArr[destIndex].name) {+    toggleProceedButton(/* hide = */ false);+    toggleHintButton(/* hide = */ true);+    updateMessage(SUBMIT_DISPLAY, CORRECT_MSSG);+    updateMessage(RIDDLE_DISPLAY, huntArr[destIndex].name + ': ' ++        huntArr[destIndex].description);+    addMarkerToMap(huntArr[destIndex].lat, huntArr[destIndex].lng,+        huntArr[destIndex].name);+  } else if (guess.length != 0) {+    updateMessage(SUBMIT_DISPLAY, WRONG_MSSG);   } }  /**- * Retrieves the string the user submitted as the destination name.- * This data is fetched from the server because entity extraction- * will be used in the MVP.- * @return {boolean} Whether or not the user entered the correct name.+ * Add a marker to the map at the specified location.+ * @param {Double} destLat: latitude of location.+ * @param {Double} destLng: longitude of location.+ * @param {String} destName: Name of location.+ * This function is implemented in another PR.  */-async function checkCorrectDestination() {-  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {-    if (mssg === destArr[destIndex]) { // The user entered the correct name.-      return true;-    }-    return false;-  });+function addMarkerToMap(destLat, destLng, destName) {}++/**+ * Creates a map and adds it to the page.+ * This function is implemented in another PR.
 * TODO: Implement this function
lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

 function createLine(text) {  /**  * Show or hide the proceed button.- *  * @param {boolean} hide Whether the proceed button should be hidden or shown.  */ function toggleProceedButton(hide) {   const proceedButton = document.getElementById(PROCEED_BUTTON);   if (hide) {     proceedButton.classList.add(INVISIBLE_CLASS);   } else {+    if (destIndex == huntArr.length - 1) {

You might want to use === instead of == here because it will enforce value and type equality

    if (destIndex === huntArr.length - 1) {
lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

 window.onload = getHunt(); function getHunt() {   fetch(DATA_URL).then((response) => response.json()).then((mssg) => {     destIndex = mssg.index;-    for (let i = 0; i < mssg.items.length; i++) {-      puzzleArr.push(mssg.items[i].riddle.puzzle);-      destArr.push(mssg.items[i].name);+    if (huntArr.length == 0) {+      for (let i = 0; i < mssg.items.length; i++) {+        const cur = mssg.items[i];+        huntArr.push(new Destination(cur.name, cur.description,+            cur.riddle.puzzle, cur.riddle.hints, cur.location.lat,+            cur.location.lng));+      }     }+    createMap();     updateToCurrentState(destIndex);+    if (destIndex >= 0) {+      handleDestinationAnswer(mssg.guess);+    }   }); }  /**- * Updates the hunt to the current destination that the user is on.- *- * @param {int} index The index of the destination that the user needs- * to find.+ * Determines whether the user entered the correct destination, and+ * adjusts the display accordingly.+ * @param {String} guess The destination name entered by the user.  */-async function updateToCurrentState(index) {-  if (index == 0) { // User presses the start button.-    sendIndexToServlet(index);-  }-  if (index >= 0) { // The user has already begun the hunt.-    hideStartButton();-    changeRiddleMessage(puzzleArr[index]);-    const isCorrect = await checkCorrectDestination();-    if (isCorrect) {-      toggleProceedButton(/* hide = */ false);-    }-  } else { // The user has not yet pressed the start button.-    toggleProceedButton(/* hide = */ true);+function handleDestinationAnswer(guess) {+  if (guess === huntArr[destIndex].name) {+    toggleProceedButton(/* hide = */ false);+    toggleHintButton(/* hide = */ true);+    updateMessage(SUBMIT_DISPLAY, CORRECT_MSSG);+    updateMessage(RIDDLE_DISPLAY, huntArr[destIndex].name + ': ' ++        huntArr[destIndex].description);+    addMarkerToMap(huntArr[destIndex].lat, huntArr[destIndex].lng,+        huntArr[destIndex].name);+  } else if (guess.length != 0) {+    updateMessage(SUBMIT_DISPLAY, WRONG_MSSG);   } }  /**- * Retrieves the string the user submitted as the destination name.- * This data is fetched from the server because entity extraction- * will be used in the MVP.- * @return {boolean} Whether or not the user entered the correct name.+ * Add a marker to the map at the specified location.+ * @param {Double} destLat: latitude of location.+ * @param {Double} destLng: longitude of location.+ * @param {String} destName: Name of location.+ * This function is implemented in another PR.
 * TODO: Implement this function.
lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

   @Override   public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {     String indexStr = request.getParameter(INDEX_PARAMETER);-    index = Integer.parseInt(indexStr);+    try {+      index = Integer.parseInt(indexStr);+    } catch (Exception e) {+    }++    name = request.getParameter(NAME_PARAMETER);

It's not clear that name is a guess, especially considering the new argument to the ScavengerHunt constructor is called "guess".

    nameGuess = request.getParameter(NAME_PARAMETER);
lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Add enhancements to existing prototype features in the go feature.

 const PROCEED_BUTTON = 'proceed-button';  // URLs that data should be fetched from. const DATA_URL = '/go-data';-const NAME_URL = '/name-data';  // Div IDs that text or a map should be inserted into.+const HINT_DISPLAY = 'hint-area'; const RIDDLE_DISPLAY = 'riddle-area';+const SUBMIT_DISPLAY = 'submit-area';  // Hard-coded messages to be displayed to the user.-const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';+const PROCEED_FINAL_MSSG = 'Finish the Hunt';+const CORRECT_MSSG = 'Correct!';+const WRONG_MSSG = 'Wrong. Try again!';  // Other constants. const INDEX_PARAM = 'new-index'; const INVISIBLE_CLASS = 'invisible';  // Global variables.-const puzzleArr = [];-let destIndex;-const destArr = [];+let destIndex; // Marks the destination that the user currently needs to find.+let hintIndex = 0; // Marks the hint that the user will see next.+const huntArr = []; // Stores scavenger hunt data retrieved from the server.++/**+ * Represents a destination on the scavenger hunt.+ */+class Destination {

create a new file for this new class, destination.js

lilymzhou

comment created time in a day

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+const UNCLICKED = 'unclicked-filter';+const CLICKED = 'clicked-filter';+const CLICKED_ARRAY_URL = 'clicked-array=';++/**+* Turn a filter a different color when pressed, and change class accordingly.+**/+function turnBlueWhenClicked() { //eslint-disable-line+  const allFilters = document.querySelectorAll('.unclicked-filter');+  for (let i = 0; i < allFilters.length; i++) {+    allFilters[i].onclick = function() {+      const currFilter = allFilters[i];+      if (currFilter.classList.contains(UNCLICKED)) {+        currFilter.classList.remove(UNCLICKED);+        currFilter.classList.add(CLICKED);+      } else {+        currFilter.classList.remove(CLICKED);+        currFilter.classList.add(UNCLICKED);+      }+    };+  }+}++/**+* Get all filters that have been clicked when user presses submit button,+* and pass array to servlet, and TODO: return success or error message.+**/+function getClickedFilters() { //eslint-disable-line+  const clickedArray = [];+  const clickedFilters = document.querySelectorAll('.clicked-filter');+  for (let i = 0; i < clickedFilters.length; i++) {+    clickedArray[i] = clickedFilters[i].innerHTML;

innerText might be better? What if you add something later that makes innerHTML not just text?

shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+const UNCLICKED = 'unclicked-filter';+const CLICKED = 'clicked-filter';+const CLICKED_ARRAY_URL = 'clicked-array=';++/**+* Turn a filter a different color when pressed, and change class accordingly.+**/+function turnBlueWhenClicked() { //eslint-disable-line+  const allFilters = document.querySelectorAll('.unclicked-filter');+  for (let i = 0; i < allFilters.length; i++) {+    allFilters[i].onclick = function() {+      const currFilter = allFilters[i];+      if (currFilter.classList.contains(UNCLICKED)) {+        currFilter.classList.remove(UNCLICKED);+        currFilter.classList.add(CLICKED);+      } else {+        currFilter.classList.remove(CLICKED);+        currFilter.classList.add(UNCLICKED);+      }+    };+  }+}++/**+* Get all filters that have been clicked when user presses submit button,+* and pass array to servlet, and TODO: return success or error message.+**/+function getClickedFilters() { //eslint-disable-line+  const clickedArray = [];+  const clickedFilters = document.querySelectorAll('.clicked-filter');+  for (let i = 0; i < clickedFilters.length; i++) {+    clickedArray[i] = clickedFilters[i].innerHTML;+  }++  const JSONArray = JSON.stringify(clickedArray);

non-constant variables shouldn't start with a capital letter

shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+const UNCLICKED = 'unclicked-filter';+const CLICKED = 'clicked-filter';+const CLICKED_ARRAY_URL = 'clicked-array=';++/**+* Turn a filter a different color when pressed, and change class accordingly.+**/+function turnBlueWhenClicked() { //eslint-disable-line+  const allFilters = document.querySelectorAll('.unclicked-filter');+  for (let i = 0; i < allFilters.length; i++) {+    allFilters[i].onclick = function() {+      const currFilter = allFilters[i];+      if (currFilter.classList.contains(UNCLICKED)) {+        currFilter.classList.remove(UNCLICKED);+        currFilter.classList.add(CLICKED);+      } else {+        currFilter.classList.remove(CLICKED);+        currFilter.classList.add(UNCLICKED);+      }+    };+  }+}++/**+* Get all filters that have been clicked when user presses submit button,+* and pass array to servlet, and TODO: return success or error message.+**/+function getClickedFilters() { //eslint-disable-line+  const clickedArray = [];+  const clickedFilters = document.querySelectorAll('.clicked-filter');
  const clickedFilters = document.querySelectorAll('.' + CLICKED);
shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+const UNCLICKED = 'unclicked-filter';+const CLICKED = 'clicked-filter';+const CLICKED_ARRAY_URL = 'clicked-array=';++/**+* Turn a filter a different color when pressed, and change class accordingly.+**/+function turnBlueWhenClicked() { //eslint-disable-line+  const allFilters = document.querySelectorAll('.unclicked-filter');
  const allFilters = document.querySelectorAll('.' + UNCLICKED);
shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.HashSet;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Servlet that returns bucket list content */+@WebServlet("/generate")+public class generateServlet extends HttpServlet {

Class names should be UpperCamelCase. Please rename the class and the file.

shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.HashSet;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Servlet that returns bucket list content */+@WebServlet("/generate")+public class generateServlet extends HttpServlet {++  private static final String FILTER_ARRAY = "clicked-array";+  String JSONclickedArray;+  HashSet<String> clickedFilters;++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    // Get array of clicked filters and convert to ArrayList<String>+    JSONclickedArray = request.getParameter(FILTER_ARRAY);

non constant variables shouldn't start with a capital letter like this

shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+const UNCLICKED = 'unclicked-filter';+const CLICKED = 'clicked-filter';+const CLICKED_ARRAY_URL = 'clicked-array=';++/**+* Turn a filter a different color when pressed, and change class accordingly.+**/+function turnBlueWhenClicked() { //eslint-disable-line+  const allFilters = document.querySelectorAll('.unclicked-filter');+  for (let i = 0; i < allFilters.length; i++) {+    allFilters[i].onclick = function() {+      const currFilter = allFilters[i];+      if (currFilter.classList.contains(UNCLICKED)) {+        currFilter.classList.remove(UNCLICKED);+        currFilter.classList.add(CLICKED);+      } else {+        currFilter.classList.remove(CLICKED);+        currFilter.classList.add(UNCLICKED);+      }+    };

No need to change it, but if you haven't seen arrow functions in javascript before, let me introduce you to them! Arrow functions are nice because they produce shorter code, and also handle the this binding differently. If you're not familiar with javascript scope and the this keyword, you can read this article.

shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import java.io.IOException;+import java.util.HashSet;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Servlet that returns bucket list content */+@WebServlet("/generate")+public class generateServlet extends HttpServlet {++  private static final String FILTER_ARRAY = "clicked-array";+  String JSONclickedArray;+  HashSet<String> clickedFilters;++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    // Get array of clicked filters and convert to ArrayList<String>+    JSONclickedArray = request.getParameter(FILTER_ARRAY);+    Gson gson = new Gson();+    clickedFilters = gson.fromJson(JSONclickedArray, HashSet.class);

The doPost method is the only place this variable is used, so no need to make this a class variable. In fact, you don't need a local variable at all. Same for clickedFilters. If it's not used elsewhere, you should contain the scope of variables to the methods they are used in.

    HashSet<String> clickedFilters = gson.fromJson(request.getParameter(FILTER_ARRAY), HashSet.class);
shreyachatterjee00

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Add functionality for map and hint button.

 function getHunt() {       puzzleArr.push(mssg.items[i].riddle.puzzle);       destArr.push(mssg.items[i].name);     }+    createMap();     updateToCurrentState(destIndex);   }); } +/**+ * Add a marker to the map at the specified location.+ * @param {Double} destLat: latitude of location.+ * @param {Double} destLng: longitude of location.+ * @param {String} destName: Name of location.+ */+function addMarkerToMap(destLat, destLng, destName) {+  const coord = new google.maps.LatLng(destLat, destLng);+  const marker = new google.maps.Marker({+    position: coord,+    title: destName,+  });+  marker.setMap(map);+}++/**+ * Creates a map and adds it to the page.+ */+function createMap() {+  map = new google.maps.Map(+      document.getElementById(MAP_DISPLAY),+      // Centered at GooglePlex (updated below).+      {center: {lat: 37.422, lng: -122.084}, zoom: 7},+  );+  if (navigator.geolocation) {+    navigator.geolocation.getCurrentPosition(function(position) {+      const pos = {lat: position.coords.latitude,+        lng: position.coords.longitude};+      addMarkerToMap(position.coords.latitude, position.coords.longitude,+          'Your current location');+      map.setCenter(pos);+    }, function() {+      updateMessage(MAP_MSSG_DISPLAY, 'Error: I can\'t find your location.');+    });+  } else {+    updateMessage(MAP_MSSG_DISPLAY, 'Error: Your browser doesn\'t' ++        'support geolocation.');+  }+}++/** PR #2+ * Show or hide the hint button.+ * @param {boolean} hide Whether the proceed button should be hidden or shown.+ * Disable lint check because toggleHintButton() is called in PR #1.

say where it's called in the code, not in what PR you introduced it

lilymzhou

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Add functionality for map and hint button.

 function getHunt() {       puzzleArr.push(mssg.items[i].riddle.puzzle);       destArr.push(mssg.items[i].name);     }+    createMap();     updateToCurrentState(destIndex);   }); } +/**+ * Add a marker to the map at the specified location.+ * @param {Double} destLat: latitude of location.+ * @param {Double} destLng: longitude of location.+ * @param {String} destName: Name of location.+ */+function addMarkerToMap(destLat, destLng, destName) {+  const coord = new google.maps.LatLng(destLat, destLng);+  const marker = new google.maps.Marker({+    position: coord,+    title: destName,+  });+  marker.setMap(map);+}++/**+ * Creates a map and adds it to the page.+ */+function createMap() {+  map = new google.maps.Map(+      document.getElementById(MAP_DISPLAY),+      // Centered at GooglePlex (updated below).+      {center: {lat: 37.422, lng: -122.084}, zoom: 7},+  );+  if (navigator.geolocation) {+    navigator.geolocation.getCurrentPosition(function(position) {+      const pos = {lat: position.coords.latitude,+        lng: position.coords.longitude};+      addMarkerToMap(position.coords.latitude, position.coords.longitude,+          'Your current location');+      map.setCenter(pos);+    }, function() {+      updateMessage(MAP_MSSG_DISPLAY, 'Error: I can\'t find your location.');+    });+  } else {+    updateMessage(MAP_MSSG_DISPLAY, 'Error: Your browser doesn\'t' ++        'support geolocation.');+  }+}++/** PR #2

What is this comment for?

lilymzhou

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Add functionality for map and hint button.

 function getHunt() {       puzzleArr.push(mssg.items[i].riddle.puzzle);       destArr.push(mssg.items[i].name);     }+    createMap();     updateToCurrentState(destIndex);   }); } +/**+ * Add a marker to the map at the specified location.+ * @param {Double} destLat: latitude of location.+ * @param {Double} destLng: longitude of location.+ * @param {String} destName: Name of location.+ */+function addMarkerToMap(destLat, destLng, destName) {+  const coord = new google.maps.LatLng(destLat, destLng);+  const marker = new google.maps.Marker({+    position: coord,+    title: destName,+  });+  marker.setMap(map);+}++/**+ * Creates a map and adds it to the page.+ */+function createMap() {+  map = new google.maps.Map(+      document.getElementById(MAP_DISPLAY),+      // Centered at GooglePlex (updated below).+      {center: {lat: 37.422, lng: -122.084}, zoom: 7},+  );+  if (navigator.geolocation) {+    navigator.geolocation.getCurrentPosition(function(position) {+      const pos = {lat: position.coords.latitude,+        lng: position.coords.longitude};+      addMarkerToMap(position.coords.latitude, position.coords.longitude,+          'Your current location');+      map.setCenter(pos);+    }, function() {+      updateMessage(MAP_MSSG_DISPLAY, 'Error: I can\'t find your location.');+    });

This is more of a suggestion than a directive, but this might be a good opportunity to use arrow functions here! Arrow functions are nice because they produce shorter code, and also handle the this binding differently. If you're not familiar with javascript scope and the this keyword, you can read this article. Let me know if you run into any issues.

    navigator.geolocation.getCurrentPosition((position) => {
      const pos = {lat: position.coords.latitude,
        lng: position.coords.longitude};
      addMarkerToMap(position.coords.latitude, position.coords.longitude,
          'Your current location');
      map.setCenter(pos);
    }, 
    () => updateMessage(MAP_MSSG_DISPLAY, 'Error: I can\'t find your location.'));
lilymzhou

comment created time in 2 days

Pull request review commentgoogleinterns/step183-2020

Add functionality for map and hint button.

 const DATA_URL = '/go-data'; const NAME_URL = '/name-data';  // Div IDs that text or a map should be inserted into.+const HINT_DISPLAY = 'hint-area'; const RIDDLE_DISPLAY = 'riddle-area';+const MAP_DISPLAY = 'map-area';+const MAP_MSSG_DISPLAY = 'map-message-area';  // Hard-coded messages to be displayed to the user. const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';  // Other constants. const INDEX_PARAM = 'new-index'; const INVISIBLE_CLASS = 'invisible';+const mapKey = config.MAP_KEY;  // Global variables. const puzzleArr = []; let destIndex; const destArr = [];+let map; -window.onload = getHunt();+window.onload = function() {+  addScriptToHead();+  getHunt();+};++/**+ * Add the Map API key to the head.+ */+function addScriptToHead() {+  const newScript = document.createElement('script');+  newScript.src = 'https://maps.googleapis.com/maps/api/js?key=' + mapKey;

probably not much value in creating the mapKey variable... would just use config.MAP_KEY directly here.

lilymzhou

comment created time in 2 days

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 978c26e3dafa1999b0b1acb1fd9a6655532aad91

Create javabuild.yaml

view details

Megan Johnson

commit sha 348e36a989816634e46e3d3a3d4af6115c36048e

Merge pull request #32 from googleinterns/builder Add a presubmit to check that the java builds.

view details

push time in 3 days

PR merged googleinterns/step183-2020

Add a presubmit to check that the java builds.

This is an additional workflow that will run when you create a pull request to check that the Java you are adding will build.

From PR #1 you'll see that I didn't add this originally because pom.xml hadn't been created. It has now, so now I'm doing this!

+16 -0

0 comment

1 changed file

megankj

pr closed time in 3 days

create barnchgoogleinterns/step183-2020

branch : builder

created branch time in 3 days

delete branch googleinterns/step183-2020

delete branch : owners

delete time in 3 days

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 28d45735b7024ed7f544ef4ddfaaa18a506df44d

Delete CODEOWNERS

view details

Megan Johnson

commit sha cb436d2e6f7d14bd7ae1501493d062eb95a860f8

Merge branch 'master' into owners

view details

Megan Johnson

commit sha 9ecd1cb7446e2a0a5d0b3512cf752754324a0526

Merge pull request #31 from googleinterns/owners Delete CODEOWNERS

view details

push time in 3 days

PR merged googleinterns/step183-2020

Reviewers
Delete CODEOWNERS

It was a good idea at the time but it seems like it caused more trouble than it was work. Removing so it will stop auto-adding Tim and I at the start of the code review.

+0 -5

0 comment

1 changed file

megankj

pr closed time in 3 days

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 3bf19e9ac6b8e2789dab45b61d7c826b520bca0a

Add lint workflows

view details

Megan Johnson

commit sha 4798cfd0c0377aa66f119fd572580785c025fa98

Merge pull request #2 from googleinterns/owners Create OWNERS for the repo.

view details

Megan Johnson

commit sha c04b0079a3cc5cf712605fad07d7d718b8f21844

Merge pull request #1 from googleinterns/lint Add lint workflows

view details

tjgoog

commit sha b0939b2828793a837c2820fb46621c45f2558502

Add `target/` to .gitignore.

view details

tjgoog

commit sha f760d28f6a8a9a060ee4866fd444f725b330eb6d

Merge pull request #4 from googleinterns/gitignore Add `target/` to .gitignore.

view details

Lily Zhou

commit sha 7e7f4f89d09da95a747f2fb7ef13f688b73d56cb

Add pom.xml and appengine-web.xml files to be able to run code.

view details

Lily Zhou

commit sha 2453b9d3330fd801e6d27b1e66fe758e10b17793

Create and retrieve fake scavenger hunt item from servlet.

view details

Lily Zhou

commit sha e3de69c93f0cadff9e6013c2f20f7b30f2f47b76

Reformat java files.

view details

Lily Zhou

commit sha a1320be6b7107d7863929e592d50693e2295812d

Reformat JS and Java files.

view details

Lily Zhou

commit sha 0c295accc2113fc3cd69de0bf6cad6bc6292da2f

Add basic components to go.html.

view details

Lily Zhou

commit sha 05d051c6b23fe149805b6f84f972789613d3889a

Fix formatting in go.css, go.js, and index.js.

view details

Lily Zhou

commit sha d7998fb1a609992cbc4610bd718e3bc8a5f55a63

Fix formatting issues in go.css, DataServlet.java, and go.js.

view details

Lily Zhou

commit sha 65674ad4cd875818de1699b007881f4231028f27

Fix formatting errors in DataServlet.java and go.js.

view details

Lily Zhou

commit sha 16e5b58a85813cfe2070c3f1722759eb5fc85beb

Fix formatting in javascript files.

view details

Lily Zhou

commit sha 65952b9081ee82976317d62138ba08a736cfd810

Add Javadoc comment and fix java formatting.

view details

Lily Zhou

commit sha 5cb9c6c1fc7b89338fb8f4c4406d51603f7e40b3

Fix Javadoc comment.

view details

Lily Zhou

commit sha e5fc87f4a862b165b90078bb98bb0b2e69d88bc5

Change returns to return in Javadoc comment.

view details

Lily Zhou

commit sha 457c829ae6612daef533a3d802a8156aa0ebe7d6

Remove comma for formatting in .htmllintrc.

view details

Lily Zhou

commit sha 8783efa1854ffa82ea8f75b1020073cc03ff002b

Remove comma for formatting in .htmllintrc (v2).

view details

Lily Zhou

commit sha e40b120b21937267ce7c0efea81f98f970af4465

Change end of file in go.html and index.html.

view details

push time in 3 days

PR opened googleinterns/step183-2020

Reviewers
Delete CODEOWNERS

It was a good idea at the time but it seems like it caused more trouble than it was work. Removing so it will stop auto-adding Tim and I at the start of the code review.

+0 -5

0 comment

1 changed file

pr created time in 3 days

create barnchgoogleinterns/step183-2020

branch : owners

created branch time in 3 days

pull request commentgoogleinterns/step183-2020

Create a servlet for filtering, turn filters dark blue when clicked

Removed @tjgoog and I for now - plz add back when the others approve.

shreyachatterjee00

comment created time in 3 days

Pull request review commentgoogleinterns/step183-2020

Creates an html page where users can input information to create a new destination for scavenger hunts

+<!DOCTYPE html>+<html lang="en">+  <head>+    <meta charset="UTF-8"/>+    <title>Destination Creation</title>+    <link rel="stylesheet" href="destinationCreation.css"/>

are you missing the css file for this PR?

rysimone

comment created time in 3 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+<!DOCTYPE html>+<html lang="en">+  <head>+    <meta charset="UTF-8" />+    <title>Go On Scavenger Hunt</title>+    <link rel="stylesheet" href="go.css" />+    <script src="config.js"></script>+    <script src="go.js"></script>+  </head>+  <body>+    <div class="content">+      <div class="center-block">+        <div id="map-area"></div>+      </div>+      <div class="center-row">+        <div id="map-message-area"></div>+      </div>+      <form action="/name-data" method="POST">

I think that there's something non-obvious here about the way this data flows.

When a user submits a guess here, it isn't immediately checked. Here's what happens:

  1. user types a guess and clicks submit
  2. /name-data stores that name and redirects back to go.html
  3. go.html calls getHunt on load, which makes a call to /go-data to get the scavenger hunt object
  4. updateToCurrentState is the method that actually does the checking to see if needs to proceed to the next destination

This might be simpler if this could be handled in one request instead of two, something like /check-name?input&Golden%20gate%20bridge&hunt=5&index=2 that would return whether the guess is correct or not. In any case, we won't make this blocking, but something to think about!

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  toggleProceedButton(/* hide = */ true);

but if index >= 0, after it checks the destination name, if it matches it will show the proceed button. but then you always hide the proceed button after this if statement.

should this second part only be fore index < 0?

since getDestName is using a fetch, you might need to wait for the promise to resolve. what happens if you make getDestName async and await it?

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  hideProceedButton();+}++/**+ * Retrieves the string the user submitted as the destination name.+ */+function getDestName() {+  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {

I think this function might also be renamed checkCorrectDestination or something since that better describes what is happening. And now that I think about it, this method has a side-effect: if the name matches, it shows the proceed button. It not, it does nothing. But to the caller of this method, updateToCurrentState, it doesn't appear that way.

It would be clearer for checkCorrectDestination to simply return a boolean (the method would need to be async in this case, since it's doing a fetch). Then in updateToCurrentState, you would know whether to show the proceed button or not. I think this will make updateToCurrentState easier to understand since the logic flow is outlined better.

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  hideProceedButton();+}++/**+ * Retrieves the string the user submitted as the destination name.+ */+function getDestName() {+  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {

I think it's fine to send to the server if it's going to be used for the MVP. It would be helpful to comment that that's the intention so that it's clear why.

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  hideProceedButton();+}++/**+ * Retrieves the string the user submitted as the destination name.+ */+function getDestName() {+  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {+    if (mssg === destArr[destIndex]) { // The user entered the correct name.+      showProceedButton();+    }+  });+}++/**+ * Creates a new paragraph element from text.+ *+ * @param {String} text to be displayed on main page.+ * @return {String} newLine New element to be appended to main page.+ */+function createLine(text) {+  const newLine = document.createElement('p');+  newLine.innerText = text;+  return newLine;+}++/**+ * Hide the proceed button.+ */+function hideProceedButton() {+  const proceedButton = document.getElementById(PROCEED_ID);+  proceedButton.classList.add(INVISIBLE_CLASS);+}++/**+ * Show the proceed button.+ */+function showProceedButton() {+  const proceedButton = document.getElementById(PROCEED_ID);+  proceedButton.classList.remove(INVISIBLE_CLASS);+}

Suggestion: collapse these two methods into a single method that just toggles the element's visibility.

/**
 * Show or hide the proceed button.
 */
function toggleProceedButton(const hide) {
  const proceedButton = document.getElementById(PROCEED_ID);
  if (hide) {
    proceedButton.classList.add(INVISIBLE_CLASS);
  } else {
    proceedButton.classList.remove(INVISIBLE_CLASS);
  }
}

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  hideProceedButton();+}++/**+ * Retrieves the string the user submitted as the destination name.+ */+function getDestName() {+  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {+    if (mssg === destArr[destIndex]) { // The user entered the correct name.+      showProceedButton();+    }+  });+}++/**+ * Creates a new paragraph element from text.+ *+ * @param {String} text to be displayed on main page.+ * @return {String} newLine New element to be appended to main page.+ */+function createLine(text) {+  const newLine = document.createElement('p');+  newLine.innerText = text;+  return newLine;+}++/**+ * Hide the proceed button.+ */+function hideProceedButton() {+  const proceedButton = document.getElementById(PROCEED_ID);+  proceedButton.classList.add(INVISIBLE_CLASS);+}++/**+ * Show the proceed button.+ */+function showProceedButton() {+  const proceedButton = document.getElementById(PROCEED_ID);+  proceedButton.classList.remove(INVISIBLE_CLASS);+}++/**+ * Hide the start button.+ */+function hideStartButton() {+  const startButton = document.getElementById(START_ID);+  startButton.classList.add(INVISIBLE_CLASS);+}++/**+ * Change the riddle text displayed on the mmain page.+ * @param {String} text Text that the riddle should be changed to.+ */+function changeRiddleMessage(text) {+  const riddle = document.getElementById(RIDDLE_ID);+  riddle.innerHTML = '';+  riddle.appendChild(createLine(text));+}++/**+ * Update the scavenger hunt data with the current destination that+ * the user is on.+ * @param {int} index Index of the current destination the user needs to find.+ */+function sendIndexToServlet(index) {+  const params = new URLSearchParams();+  params.append(INDEX_PARAM, index);+  fetch(GO_URL, {method: 'POST', body: params});+}++/**+ * After the user correctly names the destination, proceed to+ * the next destination in the hunt.+ */+function proceed() { //eslint-disable-line+  destIndex++;+  sendIndexToServlet(destIndex);

right now the servlet isn't doing anything with the updated index, right? the purpose is just to keep track of progress?

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.HuntItem;+import com.google.sps.data.Riddle;+import com.google.sps.data.ScavengerHunt;+import java.io.IOException;+import java.util.ArrayList;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++/** Constructs a fake scavenger hunt. */+@WebServlet("/go-data")+public class GoDataServlet extends HttpServlet {+  private int index = -1;++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String indexStr = request.getParameter("new-index");+    index = Integer.parseInt(indexStr);+  }++  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    ScavengerHunt hunt = buildScavengerHunt();++    response.setContentType(Constants.JSON_TYPE);+    Gson gson = new Gson();+    String json = gson.toJson(hunt);+    response.getWriter().println(json);+  }++  public ScavengerHunt buildScavengerHunt() {+    // Constructing the first HuntItem.+    Riddle firstRiddle =+        new Riddle.Builder()+            .withPuzzle("I was constructed in 1933")+            .withHint("I am at the periphery of SF")+            .withHint("I am golden in color")+            .build();+    HuntItem firstHunt =+        new HuntItem.Builder()+            .withName("Golden Gate Bridge")+            .atLocation("San Francisco")+            .withDescription("A famous bridge in San Francisco")+            .withRiddle(firstRiddle)+            .build();++    // Constructing the second HuntItem.+    Riddle secondRiddle =+        new Riddle.Builder()+            .withPuzzle("A famous tower in Paris")+            .withHint("I am very tall")+            .withHint("I am a popular tourist destination")+            .build();+    HuntItem secondHunt =+        new HuntItem.Builder()+            .withName("Eiffel Tower")+            .atLocation("Paris")+            .withDescription("A famous tower in Paris")+            .withRiddle(secondRiddle)+            .build();++    // Constructing the third HuntItem.+    Riddle thirdRiddle =+        new Riddle.Builder()+            .withPuzzle("I am often associated with America")+            .withHint("I am a statue")+            .withHint("I am very tall")+            .build();+    HuntItem thirdHunt =+        new HuntItem.Builder()+            .withName("Statue of Liberty")+            .atLocation("NYC")+            .withDescription("A statue in New York City")+            .withRiddle(thirdRiddle)+            .build();++    // Constructing the scavenger hunt.+    ArrayList<HuntItem> items = new ArrayList<HuntItem>();+    items.add(firstHunt);+    items.add(secondHunt);+    items.add(thirdHunt);+    ScavengerHunt hunt = new ScavengerHunt(items, index, "San Francisco");

are you ever checking that the index doesn't equal -1?

lilymzhou

comment created time in 7 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++const GO_URL = '/go-data';++const INVISIBLE_CLASS = 'slide-invisible';+const NAME_URL = '/name-data';+const START_ID = 'start-button';+const PROCEED_ID = 'proceed-button';+const RIDDLE_ID = 'riddle-area';+const INDEX_PARAM = 'new-index';+const FINAL_MSSG = 'Congrats, you\'ve finished the hunt!';++const riddleArr = [];+let destIndex;+const destArr = [];++window.onload = getHunt();++/**+ * Retrieves scavenger hunt data, and updates to the current destination+ * to reflect the current state of the hunt.+ */+function getHunt() {+  fetch(GO_URL).then((response) => response.json()).then((mssg) => {+    destIndex = mssg.index;+    for (let i = 0; i < mssg.items.length; i++) {+      riddleArr.push(mssg.items[i].riddle.puzzle);+      destArr.push(mssg.items[i].name);+    }+    updateToCurrentState(destIndex);+  });+}++/**+ * Updates the hunt to the current destination that the user is on.+ *+ * @param {int} index The index of the destination that the user needs+ * to find.+ */+function updateToCurrentState(index) {+  if (index >= 0) {+    hideStartButton();+    changeRiddleMessage(riddleArr[index]);+    getDestName();+  }+  sendIndexToServlet(index);+  hideProceedButton();+}++/**+ * Retrieves the string the user submitted as the destination name.+ */+function getDestName() {+  fetch(NAME_URL).then((response) => response.json()).then((mssg) => {

It looks like in this design, the user submits a guess which is then stored on the server. When this method is called in updateToCurrentState, it retrieves the stored guess to check it.

I am wondering if this needs to be stored on the server at all? Will it need to be stored on the server in the future? Instead, you could just check that the value of the input field matches destArr[destIndex], which could all be done client-side.

lilymzhou

comment created time in 7 days

pull request commentgoogleinterns/step183-2020

Basic UI for scavenger hunt generation page

I removed @tjgoog and myself from review requested, and it doesn't look like it's added us back. When the approvals from your peers are in you can add us back and we'll take a look!

shreyachatterjee00

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Creates an html page that allows users to submit a form to create a destination for scavenger hunts

+<!DOCTYPE html>+<html lang="en">+    <head>+      <meta charset="UTF-8"/>+      <title>Destination Creation</title>+      <link rel="stylesheet" href="destinationCreation.css"/>+      <script src="destinationCreation.js"></script>+    </head>+    <header>+    </header>+    <body>+      <div id="content">+        <h1>Destination Creation Form</h1>+        <div id="form-container">+          <form action="/data" method="post">+            <label for="name-input">Enter the name of the destination: *</label>

Instead of adding a "*" to each required input, you can do this with a class like this:

.required {
  color: red;
}
.required::after {
  content: "*";
}

You can make a required class and add it to the labels that are required. This will let you style all the required labels the same way and add the little asterisk at the end! https://www.w3schools.com/cssref/sel_after.asp

rysimone

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Creates an html page that allows users to submit a form to create a destination for scavenger hunts

+<!DOCTYPE html>+<html lang="en">+    <head>+      <meta charset="UTF-8"/>+      <title>Destination Creation</title>+      <link rel="stylesheet" href="destinationCreation.css"/>+      <script src="destinationCreation.js"></script>+    </head>+    <header>+    </header>+    <body>+      <div id="content">

does this need to be an ID?

rysimone

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Creates an html page that allows users to submit a form to create a destination for scavenger hunts

+<!DOCTYPE html>+<html lang="en">+    <head>+      <meta charset="UTF-8"/>+      <title>Destination Creation</title>+      <link rel="stylesheet" href="destinationCreation.css"/>+      <script src="destinationCreation.js"></script>+    </head>+    <header>+    </header>+    <body>+      <div id="content">+        <h1>Destination Creation Form</h1>+        <div id="form-container">+          <form action="/data" method="post">

/data will be the path for the POST request, so I think giving it a more descriptive name like /create-destination might make more sense

rysimone

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.HuntItem;+import com.google.sps.data.Riddle;+import com.google.sps.data.ScavengerHunt;+import java.io.IOException;+import java.util.ArrayList;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++@WebServlet("/go-data")+public class DataServlet extends HttpServlet {+  private static final String JSON_TYPE = "application/json";++  private ScavengerHunt hunt;+  private int index = -1;++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String indexStr = request.getParameter("new-index");+    index = Integer.parseInt(indexStr);+  }++  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    buildScavengerHunt();++    response.setContentType(JSON_TYPE);+    Gson gson = new Gson();+    String json = gson.toJson(hunt);+    response.getWriter().println(json);+  }++  public void buildScavengerHunt() {+    // Constructing the first HuntItem.+    ArrayList<String> firstHints = new ArrayList<String>();+    firstHints.add("I am at the periphery of SF");+    firstHints.add("I am golden in color");+    Riddle firstRiddle = new Riddle("I was constructed in 1933", firstHints);+    HuntItem firstHunt =+        new HuntItem(+            "Golden Gate Bridge", "San Francisco", "A famous bridge in San Francisco", firstRiddle);++    // Constructing the second HuntItem.+    ArrayList<String> secondHints = new ArrayList<String>();+    secondHints.add("I am very tall");+    secondHints.add("I am a popular tourist destination");+    Riddle secondRiddle = new Riddle("A famous tower in Paris", secondHints);+    HuntItem secondHunt =+        new HuntItem("Eiffel Tower", "Paris", "A famous tower in Paris", secondRiddle);++    // Constructing the third HuntItem.+    ArrayList<String> thirdHints = new ArrayList<String>();+    thirdHints.add("I am a statue");+    thirdHints.add("I am very tall");+    Riddle thirdRiddle = new Riddle("I am an item often associated with America", thirdHints);+    HuntItem thirdHunt =+        new HuntItem("Statue of Liberty", "NYC", "A statue in New York City", thirdRiddle);

Since you're trying to build these HuntItem objects, I think a builder pattern would be great here: https://dzone.com/articles/design-patterns-the-builder-pattern

What you would do is in the HuntItem class, create a Builder sub-class with a couple methods like withName, withRiddles, etc.

Your code would be much more readable:

HuntItem item = new HuntItem.Builder().withName("Statue of Liberty").withCity("NYC").build();

Same for the Riddle class. You could have something that looked like this: new Riddle.Builder().withPuzzle("I am an item often associated with America").withRiddle("I am very tall").withRiddle("I am a statue").build();

This is all dummy data for now, but eventually you would need to construct these objects with data pulled from the datastore. I think if your classes used the builder pattern it might make constructing these objects from the datastore objects easier.

Let me know if you have any questions!

lilymzhou

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.

is this needed for something?

lilymzhou

comment created time in 9 days

Pull request review commentgoogleinterns/step183-2020

Support basic functionality: retrieving scavenger hunt data, beginning the hunt, confirming the name of the destination, proceeding to the next destination.

+// Copyright 2019 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+//     https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package com.google.sps.servlets;++import com.google.gson.Gson;+import com.google.sps.data.HuntItem;+import com.google.sps.data.Riddle;+import com.google.sps.data.ScavengerHunt;+import java.io.IOException;+import java.util.ArrayList;+import javax.servlet.annotation.WebServlet;+import javax.servlet.http.HttpServlet;+import javax.servlet.http.HttpServletRequest;+import javax.servlet.http.HttpServletResponse;++@WebServlet("/go-data")+public class DataServlet extends HttpServlet {+  private static final String JSON_TYPE = "application/json";++  private ScavengerHunt hunt;+  private int index = -1;++  @Override+  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {+    String indexStr = request.getParameter("new-index");+    index = Integer.parseInt(indexStr);+  }++  @Override+  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {+    buildScavengerHunt();++    response.setContentType(JSON_TYPE);+    Gson gson = new Gson();+    String json = gson.toJson(hunt);

I'm wondering if the hunt variable here needs to be a member field in this class. It appears that buildScavengerHunt() is building object and setting the member field, which has some side effects that aren't obvious.

I would recommend just having buildScavengerHunt return the ScavengerHunt object instead of setting a class variable like this.

lilymzhou

comment created time in 9 days

PR closed tjgoog/step

Try adding lint checks as workflows
+277 -0

0 comment

11 changed files

megankj

pr closed time in 15 days

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 3bf19e9ac6b8e2789dab45b61d7c826b520bca0a

Add lint workflows

view details

Megan Johnson

commit sha c04b0079a3cc5cf712605fad07d7d718b8f21844

Merge pull request #1 from googleinterns/lint Add lint workflows

view details

push time in 16 days

PR merged googleinterns/step183-2020

Add lint workflows

This is based on this pull request and this document.

This PR is missing the java builder and java linter. The java linter was added in an earlier commit (before I figured out that I needed to set up branch protection, whoops). The java builder says to only add if there's a pom.xml file, which there isn't currently. We can add that later.

+4879 -0

0 comment

10 changed files

megankj

pr closed time in 16 days

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 07b106d2ba4eb02f4558a0e313d6503f8aff1f59

Create CODEOWNERS

view details

Megan Johnson

commit sha 8cec75d3a739b0bff54d69d3b050ee8794a47642

Update CODEOWNERS

view details

Megan Johnson

commit sha 4798cfd0c0377aa66f119fd572580785c025fa98

Merge pull request #2 from googleinterns/owners Create OWNERS for the repo.

view details

push time in 16 days

MemberEvent

push eventgoogleinterns/step183-2020

Megan Johnson

commit sha 8cec75d3a739b0bff54d69d3b050ee8794a47642

Update CODEOWNERS

view details

push time in 16 days

create barnchgoogleinterns/step183-2020

branch : owners

created branch time in 16 days

MemberEvent
MemberEvent

PR opened googleinterns/step183-2020

Add lint workflows

This is based on this pull request and this document.

This PR is missing the java builder and java linter. The java linter was added in an earlier commit (before I figured out that I needed to set up branch protection, whoops). The java builder says to only add if there's a pom.xml file, which there isn't currently. We can add that later.

+4879 -0

0 comment

10 changed files

pr created time in 16 days

create barnchgoogleinterns/step183-2020

branch : lint

created branch time in 16 days

create barnchgoogleinterns/step183-2020

branch : master

created branch time in 16 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections;+import java.util.stream.Stream;+  public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;++  /** +  * Returns a Collection of {@code TimeRange} objects that span any available meeting times for all people in the MeetingRequest request.+  * For example, if 4pm - 8pm is free for a meeting, +  * the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +  */   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;

is there a reason you make a copy of this?

shreyachatterjee00

comment created time in 20 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections;+import java.util.stream.Stream;+  public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;++  /** +  * Returns a Collection of {@code TimeRange} objects that span any available meeting times for all people in the MeetingRequest request.+  * For example, if 4pm - 8pm is free for a meeting, +  * the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +  */   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    // Mandatory events = events that must be attended by an attended in the Meeting Request+    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }++  /** +  * Creates a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //check if any people in meeting request is present in this set+      boolean mtngAttendeeInEvent = attendees.stream().anyMatch(attendee -> event.getAttendees().contains(attendee));++      if (mtngAttendeeInEvent) {+        // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +        int eventStartTime = event.getWhen().start();+        int eventEndTime = event.getWhen().end();+        if (mandatoryEvents.containsKey(eventStartTime)) {+          if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        } else {+          mandatoryEvents.put(eventStartTime, eventEndTime);+        }+      }+    }+    return mandatoryEvents;+  }++  /**+  * Creates and returns an ArrayList of TimeRange objects that span available meeting times.
  * Creates and returns an ArrayList of {@code TimeRange} objects that span available meeting times.
shreyachatterjee00

comment created time in 20 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections;+import java.util.stream.Stream;+  public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;++  /** +  * Returns a Collection of {@code TimeRange} objects that span any available meeting times for all people in the MeetingRequest request.+  * For example, if 4pm - 8pm is free for a meeting, +  * the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +  */   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;

this is a small nit, but none of these need to be local variables, you can just inline them in the makeMandatoryEventMap and findMeetingTimes methods below

shreyachatterjee00

comment created time in 20 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 import java.util.HashMap; import java.util.ArrayList; import java.util.Collections;+import java.util.stream.Stream;+ -/** -* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.-* For example, if 4pm - 8pm is free for a meeting, -* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. -*/ public final class FindMeetingQuery {   public static final int MIN_INCREMENT = 10; +  /** +  * Returns a Collection of {@code TimeRange} objects that span any available meeting times for all people in the MeetingRequest request.+  * For example, if 4pm - 8pm is free for a meeting, +  * the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +  */   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {     long mtngDuration = request.getDuration();     Collection<String> attendees = request.getAttendees();     Collection<Event> allEvents = events; +    // Mandatory events = events that must be attended by an attended in the Meeting Request
    // Mandatory events are events that must be attended by an attendee in the {@code MeetingRequest}.
shreyachatterjee00

comment created time in 20 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 import java.util.HashMap; import java.util.ArrayList; import java.util.Collections;+import java.util.stream.Stream;+ -/** -* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.-* For example, if 4pm - 8pm is free for a meeting, -* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. -*/ public final class FindMeetingQuery {   public static final int MIN_INCREMENT = 10; +  /** +  * Returns a Collection of {@code TimeRange} objects that span any available meeting times for all people in the MeetingRequest request.+  * For example, if 4pm - 8pm is free for a meeting, +  * the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +  */

can you reformat this comment so it's less than 100 chars per line?

shreyachatterjee00

comment created time in 20 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.+  */+  public Collection<TimeRange> findMeetingTimes (long mtngDuration, HashMap<Integer, Integer> mandatoryEvents) {+    Collection<TimeRange> meetingTimes = new ArrayList<TimeRange>();+    int currTime = TimeRange.START_OF_DAY;+    int startTime = TimeRange.START_OF_DAY;+    int currDuration = 0;++    while (currTime < TimeRange.END_OF_DAY) {+      // If there's an event starting at this time, compare the current duration and meeting duration to see if there is a possible meeting time. +      // Reset the current duration variable, move the current time to the end time of the event, and repeat loop. +      if (mandatoryEvents.containsKey(currTime)) {+        int longestMeetingEnd = mandatoryEvents.get(currTime);+        if (currDuration >= mtngDuration) {+          // Not inclusive because a meeting starts at this time +          meetingTimes.add(TimeRange.fromStartEnd(startTime, currTime, /* inclusive = */ false));+        }+        currDuration = 0;++        // Check if there is a conflict: another meeting that starts during this one, and goes later+        longestMeetingEnd = isConflict(currTime, longestMeetingEnd, mandatoryEvents);+            +        currTime = longestMeetingEnd;+        startTime = longestMeetingEnd;+      }+      // If there is no event at this time, add MIN_INCREMENT minutes to the duration and current time, and run the loop again. +      else {+        currDuration += MIN_INCREMENT;+        currTime += MIN_INCREMENT;+      }+    }++    // When reaching the end of loop, if the current duration is longer than what is necessary, add it to the return Collection, inclusive. +    if (currTime > TimeRange.END_OF_DAY && currDuration >= mtngDuration) {+      meetingTimes.add(TimeRange.fromStartEnd(startTime, TimeRange.END_OF_DAY, /* inclusive = */ true));+    }+    return meetingTimes;+  }++  /**+  * Check if there is a conflict: another meeting that starts during this one, and goes later. +  * Return the later time if there is a conflicting meeting. +  **/+  public int isConflict (int currTime, int longestMeetingEnd, HashMap<Integer, Integer> mandatoryEvents) {+    int tempTime = currTime + 10;

use MIN_INCREMENT?

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.+  */+  public Collection<TimeRange> findMeetingTimes (long mtngDuration, HashMap<Integer, Integer> mandatoryEvents) {+    Collection<TimeRange> meetingTimes = new ArrayList<TimeRange>();+    int currTime = TimeRange.START_OF_DAY;+    int startTime = TimeRange.START_OF_DAY;+    int currDuration = 0;++    while (currTime < TimeRange.END_OF_DAY) {+      // If there's an event starting at this time, compare the current duration and meeting duration to see if there is a possible meeting time. +      // Reset the current duration variable, move the current time to the end time of the event, and repeat loop. +      if (mandatoryEvents.containsKey(currTime)) {+        int longestMeetingEnd = mandatoryEvents.get(currTime);+        if (currDuration >= mtngDuration) {+          // Not inclusive because a meeting starts at this time +          meetingTimes.add(TimeRange.fromStartEnd(startTime, currTime, /* inclusive = */ false));+        }+        currDuration = 0;++        // Check if there is a conflict: another meeting that starts during this one, and goes later+        longestMeetingEnd = isConflict(currTime, longestMeetingEnd, mandatoryEvents);

Methods with names like isFoo are expected to return booleans. Instead, since this returns an int, it should be something like getLastEventEnd or something that better describes what int you're getting here.

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.+  */+  public Collection<TimeRange> findMeetingTimes (long mtngDuration, HashMap<Integer, Integer> mandatoryEvents) {+    Collection<TimeRange> meetingTimes = new ArrayList<TimeRange>();+    int currTime = TimeRange.START_OF_DAY;+    int startTime = TimeRange.START_OF_DAY;+    int currDuration = 0;++    while (currTime < TimeRange.END_OF_DAY) {+      // If there's an event starting at this time, compare the current duration and meeting duration to see if there is a possible meeting time. +      // Reset the current duration variable, move the current time to the end time of the event, and repeat loop. +      if (mandatoryEvents.containsKey(currTime)) {+        int longestMeetingEnd = mandatoryEvents.get(currTime);+        if (currDuration >= mtngDuration) {+          // Not inclusive because a meeting starts at this time +          meetingTimes.add(TimeRange.fromStartEnd(startTime, currTime, /* inclusive = */ false));+        }+        currDuration = 0;++        // Check if there is a conflict: another meeting that starts during this one, and goes later+        longestMeetingEnd = isConflict(currTime, longestMeetingEnd, mandatoryEvents);+            +        currTime = longestMeetingEnd;+        startTime = longestMeetingEnd;+      }+      // If there is no event at this time, add MIN_INCREMENT minutes to the duration and current time, and run the loop again. +      else {

I mentioned this in our 1:1, but it's better to keep the code a little closer together like this:

      } else {
      // If there is no event at this time, add MIN_INCREMENT minutes to the duration and current time, and run the loop again. 

Please update here and elsewhere.

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.+  */+  public Collection<TimeRange> findMeetingTimes (long mtngDuration, HashMap<Integer, Integer> mandatoryEvents) {+    Collection<TimeRange> meetingTimes = new ArrayList<TimeRange>();+    int currTime = TimeRange.START_OF_DAY;+    int startTime = TimeRange.START_OF_DAY;+    int currDuration = 0;++    while (currTime < TimeRange.END_OF_DAY) {+      // If there's an event starting at this time, compare the current duration and meeting duration to see if there is a possible meeting time. +      // Reset the current duration variable, move the current time to the end time of the event, and repeat loop. +      if (mandatoryEvents.containsKey(currTime)) {+        int longestMeetingEnd = mandatoryEvents.get(currTime);

Does need to be declared here? It's immediately re-assigned on line 97.

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.+  */+  public Collection<TimeRange> findMeetingTimes (long mtngDuration, HashMap<Integer, Integer> mandatoryEvents) {+    Collection<TimeRange> meetingTimes = new ArrayList<TimeRange>();+    int currTime = TimeRange.START_OF_DAY;+    int startTime = TimeRange.START_OF_DAY;+    int currDuration = 0;++    while (currTime < TimeRange.END_OF_DAY) {+      // If there's an event starting at this time, compare the current duration and meeting duration to see if there is a possible meeting time. 

nit: make this comment shorter than 100 characters so doesn't wrap the line like this

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {+        if (people.contains(person)) {+          // If there's already an event with this start time in the map, but the current end time is later, add the current instead.  +          int eventStartTime = event.getWhen().start();+          int eventEndTime = event.getWhen().end();+          if (mandatoryEvents.containsKey(eventStartTime)) {+            if (eventEndTime > mandatoryEvents.get(eventStartTime)) {+              mandatoryEvents.put(eventStartTime, eventEndTime);+            }+            else {+              break;+            }+          }+          else {+            mandatoryEvents.put(eventStartTime, eventEndTime);+          }+        }  +      }+    }++    return mandatoryEvents;+  }++  /**+  * Create and return an ArrayList of TimeRange objects that span available meeting times.

nit: Method comments should be in the form "Creates and returns.." instead of "Create and return"

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);+    Collection<TimeRange> meetingTimes = findMeetingTimes(mtngDuration, mandatoryEvents);+    return meetingTimes;+  }+++  /** +  * Create a hash map <event start time, event end time> that ONLY contains meetings where 1 or more people from attendees must attend. +  **/+  public HashMap<Integer, Integer> makeMandatoryEventMap (Collection<Event> allEvents, Collection<String> attendees) {+    HashMap<Integer, Integer> mandatoryEvents = new HashMap<Integer, Integer>();++    for (Event event : allEvents) {+      //people who must attend +      Set<String> people = event.getAttendees();+      //check if any people in meeting request is present in this set+      for (String person : attendees) {

I don't think you actually need to iterate through every single attendee, right? You just need to determine if this event contains any of the provided attendees, and if so, add the event time to your map.

You may be able to remove some of this nesting by using java streams anyMatch to find out of this event is potentially conflicting with the request. anyMatch is nice because it's short-circuiting and will terminate if it finds a match, not having to go through all attendees.

boolean eventHasAttendee = attendees.stream().anyMatch(attendee -> event.getAttendees().contains(attendee));

if (eventHasAttendee) { addMandatoryEvent(event); }

And then your addMandatory event function can add the event to your map (unless it's shorter than the existing entry).

(As a side note, code with lots of nesting has the potential to introduce bugs since it makes it hard to follow the conditional branching that may or may not occur)

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.+* For example, if 4pm - 8pm is free for a meeting, +* the Time Range object will represent that entire chunk of time rather than 4pm - 5pm, even if the duration of meeting is 1 hour. +*/ public final class FindMeetingQuery {+  public static final int MIN_INCREMENT = 10;+   public Collection<TimeRange> query(Collection<Event> events, MeetingRequest request) {-    throw new UnsupportedOperationException("TODO: Implement this method.");+    long mtngDuration = request.getDuration();+    Collection<String> attendees = request.getAttendees();+    Collection<Event> allEvents = events;++    HashMap<Integer, Integer> mandatoryEvents = makeMandatoryEventMap(allEvents, attendees);

I think it would be good to explain what mandatory means

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.

nit: use javadoc formatting {@code TimeRange} which will help link all these objects together when looking at the codebase in github!

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

 package com.google.sps;  import java.util.Collection;+import java.util.HashSet;+import java.util.Set;+import java.util.HashMap;+import java.util.ArrayList;+import java.util.Collections; +/** +* Returns a Collection of TimeRange objects that span any available meeting times for all people in the MeetingRequest request.

also this javadoc should probably go above the query method, not the entire class

shreyachatterjee00

comment created time in 21 days

Pull request review commentshreyachatterjee00/step

Create algorithm to find all possible meeting times

  /** Servlet that deletes bucket list content*/ @WebServlet("/delete-data")-public class DeleteDataServlet extends HttpServlet +public class DeleteDataServlet extends HttpServlet {

alright, that's fine. thanks for the explanation!

shreyachatterjee00

comment created time in 21 days

Pull request review commentlilymzhou/stepfolio

Week 6: Implement calendar that can schedule meetings among multiple people.

 public void notEnoughRoom() {      Assert.assertEquals(expected, actual);   }-} +  @Test+  public void impossibleOptionalAttendee() {+    // Have two people have separate events, as in everyAttendeeIsConsidered(). +    // Also have person C have an all-day event, and have C added as an optional +    // attendee to the meeting request. The resulting list of time slots should +    // not change, as it is impossible to find a meeting time where C is present.+    // +    //+    // Events  :       |--A--|     |--B--|+    // Events  : |--------------C--------------|+    // Day     : |-----------------------------|+    // Options : |--1--|     |--2--|     |--3--|++    Collection<Event> events = Arrays.asList(+        new Event("Event 1", TimeRange.WHOLE_DAY, Arrays.asList(PERSON_C)),+        new Event("Event 2", TimeRange.fromStartDuration(TIME_0800AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_A)),+        new Event("Event 3", TimeRange.fromStartDuration(TIME_0900AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_B))+    );++    MeetingRequest request =+        new MeetingRequest(Arrays.asList(PERSON_A, PERSON_B), DURATION_30_MINUTES);+    request.addOptionalAttendee(PERSON_C);++    Collection<TimeRange> actual = query.query(events, request);+    Collection<TimeRange> expected =+        Arrays.asList(TimeRange.fromStartEnd(TimeRange.START_OF_DAY, TIME_0800AM, false),+            TimeRange.fromStartEnd(TIME_0830AM, TIME_0900AM, false),+            TimeRange.fromStartEnd(TIME_0930AM, TimeRange.END_OF_DAY, true));++    Assert.assertEquals(expected, actual);+  }++  @Test+  public void possibleOptionalAttendee() {+    // A, B, and C have separate events that do not overlap. C is an optional attendee+    // in the meeting request, while A and B are required. Because it is possible +    // to find a meeting slot where A, B, and C, are all available, the resulting +    // list of time slots should be non-empty.+    // +    //+    // Events  :       |--A--|--C--|--B--|+    // Day     : |-----------------------------|+    // Options : |--1--|                 |--2--|++    Collection<Event> events = Arrays.asList(+        new Event("Event 1", TimeRange.fromStartDuration(TIME_0800AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_A)),+        new Event("Event 2", TimeRange.fromStartDuration(TIME_0900AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_B)),+        new Event("Event 3", TimeRange.fromStartDuration(TIME_0830AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_C))+    );++    MeetingRequest request =+        new MeetingRequest(Arrays.asList(PERSON_A, PERSON_B), DURATION_30_MINUTES);+    request.addOptionalAttendee(PERSON_C);++    Collection<TimeRange> actual = query.query(events, request);++    Collection<TimeRange> expected =+        Arrays.asList(TimeRange.fromStartEnd(TimeRange.START_OF_DAY, TIME_0800AM, false),+            TimeRange.fromStartEnd(TIME_0930AM, TimeRange.END_OF_DAY, true));++    Assert.assertEquals(expected, actual);+  }++  @Test+  public void justEnoughRoomWithOptionalAttendee() {+    // Have one required attendee A, such that there is just enough room +    // at one point in the day to have the meeting. B is added an optional+    // attendee, but it is impossible to schedule a meeting with B; thus,+    // B is ignored.+    //+    // Events  : |--A--|-B-| |----A----|+    // Day     : |---------------------|+    // Options :++    Collection<Event> events = Arrays.asList(+        new Event("Event 1", TimeRange.fromStartEnd(TimeRange.START_OF_DAY, TIME_0830AM, false),+            Arrays.asList(PERSON_A)),+        new Event("Event 2", TimeRange.fromStartEnd(TIME_0830AM, TIME_0845AM, false),+            Arrays.asList(PERSON_B)),+        new Event("Event 3", TimeRange.fromStartEnd(TIME_0900AM, TimeRange.END_OF_DAY, true),+            Arrays.asList(PERSON_A))+    );++    MeetingRequest request = new MeetingRequest(Arrays.asList(PERSON_A), DURATION_30_MINUTES);+    request.addOptionalAttendee(PERSON_B);++    Collection<TimeRange> actual = query.query(events, request);+    Collection<TimeRange> expected =+        Arrays.asList(TimeRange.fromStartDuration(TIME_0830AM, DURATION_30_MINUTES));++    Assert.assertEquals(expected, actual);+  }++  @Test+  public void optionalAttendeesWithGaps() {+    // Have each person have different events. We should see two options because each person has+    // split the restricted times.+    //+    // Events  :       |--A--|     |--B--|+    // Day     : |-----------------------------|+    // Options : |--1--|     |--2--|     |--3--|++    Collection<Event> events = Arrays.asList(+        new Event("Event 1", TimeRange.fromStartDuration(TIME_0800AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_A)),+        new Event("Event 2", TimeRange.fromStartDuration(TIME_0900AM, DURATION_30_MINUTES),+            Arrays.asList(PERSON_B)));++    MeetingRequest request =+        new MeetingRequest(Arrays.asList(), DURATION_30_MINUTES);++    request.addOptionalAttendee(PERSON_A);+    request.addOptionalAttendee(PERSON_B);++    Collection<TimeRange> actual = query.query(events, request);+    Collection<TimeRange> expected =+        Arrays.asList(TimeRange.fromStartEnd(TimeRange.START_OF_DAY, TIME_0800AM, false),+            TimeRange.fromStartEnd(TIME_0830AM, TIME_0900AM, false),+            TimeRange.fromStartEnd(TIME_0930AM, TimeRange.END_OF_DAY, true));++    Assert.assertEquals(expected, actual);+  }++  @Test+  public void optionalAttendeesWithNoGaps() {+    // Have each person have different events. We should see two options because each person has

we should see no options, not two, right?

lilymzhou

comment created time in 22 days

more