Changeset df7f390
- Timestamp:
- 01/13/25 14:18:51 (44 hours ago)
- Branches:
- master
- Parents:
- 0a7426e
- Files:
-
- 2 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
backend/GlobeGuru-backend/src/main/java/DatabaseUtil.java
r0a7426e rdf7f390 53 53 "price REAL," + 54 54 "FOREIGN KEY(optionId) REFERENCES options(id))" 55 ); 56 PreparedStatement stmt5 = conn.prepareStatement( 57 "CREATE TABLE IF NOT EXISTS changeLog(" + 58 "id INTEGER PRIMARY KEY AUTOINCREMENT," + 59 "detail_id INTEGER NOT NULL," + 60 "attribute TEXT NOT NULL," + 61 "oldValue TEXT," + 62 "newValue TEXT," + 63 "FOREIGN KEY(detail_id) REFERENCES optionDetails(id))" 55 64 )) { 56 65 … … 59 68 stmt3.executeUpdate(); 60 69 stmt4.executeUpdate(); 70 stmt5.executeUpdate(); 61 71 } 62 72 } … … 201 211 } 202 212 } 203 204 205 206 213 public static List<Option> poolOptionDetails(int id) throws SQLException{ 207 214 String sql = "SELECT * FROM optionDetails WHERE optionId = ?"; … … 283 290 } 284 291 public static int saveOptionToDatabase(Option option) { 285 String sql = "INSERT INTO options (link, imgSrc, hotelName, country, dateRange,numberOfPeople , isPriceChanged, newPrice) VALUES (?, ?,?, ?, ?, ?, ?, ?)";292 String sql = "INSERT INTO options (link, imgSrc, hotelName, country, dateRange,numberOfPeople) VALUES (?, ?, ?, ?, ?, ?)"; 286 293 int id = 0; 287 294 try (Connection conn = DriverManager.getConnection("jdbc:sqlite:globe_guru.db"); … … 293 300 stmt.setString(5, option.getDateRange()); 294 301 stmt.setInt(6,option.getNumPeople()); 295 stmt.setBoolean(7, option.isPriceChanged());296 stmt.setFloat(8, option.getNewPrice());297 302 stmt.executeUpdate(); 298 303 try(ResultSet genKey = stmt.getGeneratedKeys()){ … … 331 336 } 332 337 } 333 334 338 public static void updateOptionDetails(int detail_id, String type, String board, String amenities, float price) { 335 String sql = "UPDATE optionDetails SET "; 336 337 List<String> updates = new ArrayList<>(); 338 if (type != null && !type.isEmpty()) updates.add("type = '" + type + "'"); 339 if (board != null && !board.isEmpty()) updates.add("board = '" + board + "'"); 340 if (amenities != null && !amenities.isEmpty()) updates.add("amenities = '" + amenities + "'"); 341 updates.add("price = " + price); 342 343 sql += String.join(", ", updates); 344 sql += " WHERE id = " + detail_id; 345 346 try (Connection conn = getConnection(); 347 PreparedStatement stmt = conn.prepareStatement(sql)) { 348 stmt.executeUpdate(); 339 try (Connection conn = getConnection()) { 340 String selectSql = "SELECT * FROM optionDetails WHERE id = ?"; 341 PreparedStatement selectStmt = conn.prepareStatement(selectSql); 342 selectStmt.setInt(1, detail_id); 343 ResultSet rs = selectStmt.executeQuery(); 344 345 if (rs.next()) { 346 String oldType = rs.getString("type"); 347 String oldBoard = rs.getString("board"); 348 String oldAmenities = rs.getString("amenities"); 349 float oldPrice = rs.getFloat("price"); 350 351 insertChangeLog(detail_id, "type", oldType, type); 352 insertChangeLog(detail_id, "board", oldBoard, board); 353 insertChangeLog(detail_id, "amenities", oldAmenities, amenities); 354 insertChangeLog(detail_id, "price", String.valueOf(oldPrice), String.valueOf(price)); 355 } 356 357 String sql = "UPDATE optionDetails SET "; 358 List<String> updates = new ArrayList<>(); 359 if (type != null && !type.isEmpty()) updates.add("type = '" + type + "'"); 360 if (board != null && !board.isEmpty()) updates.add("board = '" + board + "'"); 361 if (amenities != null && !amenities.isEmpty()) updates.add("amenities = '" + amenities + "'"); 362 if (price > 0) updates.add("price = " + price); 363 364 sql += String.join(", ", updates); 365 sql += " WHERE id = " + detail_id; 366 367 try (PreparedStatement stmt = conn.prepareStatement(sql)) { 368 stmt.executeUpdate(); 369 } 349 370 } catch (SQLException e) { 350 371 e.printStackTrace(); 351 372 } 352 373 } 353 354 public static void updateOptionInDatabase(Option option) { 355 String sql = "UPDATE options SET link = ?, imgSrc = ?, hotelName = ?, country = ?, price = ?, dateRange = ?, isPriceChanged = ?, newPrice = ? WHERE id = ?"; 356 try (Connection conn = DriverManager.getConnection("jdbc:sqlite:globe_guru.db"); 357 PreparedStatement stmt = conn.prepareStatement(sql)) { 358 stmt.setString(1, option.getLink()); 359 stmt.setString(2, option.getImgSrc()); 360 stmt.setString(3, option.getHotelName()); 361 stmt.setString(4, option.getCountry()); 362 stmt.setFloat(5, option.getPrice()); 363 stmt.setString(6, option.getDateRange()); 364 stmt.setBoolean(7, option.isPriceChanged()); 365 stmt.setFloat(8, option.getNewPrice()); 366 stmt.setInt(9, option.getId()); 367 stmt.executeUpdate(); 368 } catch (SQLException e) { 369 e.printStackTrace(); 370 } 371 } 372 public static Option findOption(Option option) { 373 String sql = "SELECT * FROM options WHERE id = ?"; 374 try (Connection conn = DriverManager.getConnection("jdbc:sqlite:globe_guru.db"); 375 PreparedStatement stmt = conn.prepareStatement(sql)) { 376 stmt.setInt(1, option.getId()); 377 try (ResultSet rs = stmt.executeQuery()) { 378 if (rs.next()) { 379 Option existingOption = new Option(); 380 existingOption.setId(rs.getInt("id")); 381 existingOption.setLink(rs.getString("link")); 382 existingOption.setImgSrc(rs.getString("imgSrc")); 383 existingOption.setHotelName(rs.getString("hotelName")); 384 existingOption.setCountry(rs.getString("country")); 385 existingOption.setPrice(rs.getFloat("price")); 386 existingOption.setDateRange(rs.getString("dateRange")); 387 existingOption.setPriceChanged(rs.getBoolean("isPriceChanged")); 388 existingOption.setNewPrice(rs.getInt("newPrice")); 389 existingOption.setNumPeople(rs.getInt("numberOfPeople")); 390 return existingOption; 391 } 392 } 393 } catch (SQLException e) { 394 e.printStackTrace(); 395 } 396 return null; 397 } 398 399 374 private static void insertChangeLog(int detail_id, String attribute, String oldValue, String newValue) { 375 if (!oldValue.equals(newValue)) { 376 String sql = "INSERT INTO changeLog (detail_id, attribute, oldValue, newValue) VALUES (?, ?, ?, ?)"; 377 try (Connection conn = getConnection(); 378 PreparedStatement stmt = conn.prepareStatement(sql)) { 379 stmt.setInt(1, detail_id); 380 stmt.setString(2, attribute); 381 stmt.setString(3, oldValue); 382 stmt.setString(4, newValue); 383 stmt.executeUpdate(); 384 } catch (SQLException e) { 385 e.printStackTrace(); 386 } 387 } 388 } 400 389 public static int getCurrentOptionsCount() throws SQLException { 401 390 String sql = "SELECT COUNT(*) AS optionsCount FROM options"; -
backend/GlobeGuru-backend/src/main/java/Option.java
r0a7426e rdf7f390 1 import java.util.ArrayList; 2 import java.util.List; 1 3 import java.util.Objects; 2 4 … … 12 14 private String board; 13 15 private String amenities; 16 private int numPeople; 17 private String dateRange; 18 private List<Change> changes = new ArrayList<>(); 14 19 15 private int numPeople; 16 //Price changing 17 private float newPrice = 0; 18 private boolean isPriceChanged = false; 19 private String dateRange; 20 // Constructor 21 public Option(){ 22 price = 0; 20 public String getDateRange() { 21 return dateRange; 23 22 } 24 23 … … 31 30 } 32 31 32 public int getId() { 33 return id; 34 } 35 36 public void setId(int id) { 37 this.id = id; 38 } 39 33 40 public void setDetail_id(int detail_id) { 34 41 this.detail_id = detail_id; 35 }36 37 public String getDateRange() {38 return dateRange;39 42 } 40 43 … … 63 66 } 64 67 65 public boolean isEmpty(){66 return (hotelName == null || country == null || link == null || imgSrc == null);68 public int getNumPeople() { 69 return numPeople; 67 70 } 71 72 public void setNumPeople(int numPeople) { 73 this.numPeople = numPeople; 74 } 75 68 76 public String getHotelName() { 69 77 return hotelName; … … 98 106 } 99 107 108 public String getImgSrc() { 109 return imgSrc; 110 } 111 100 112 public void setImgSrc(String imgSrc) { 101 113 this.imgSrc = imgSrc; 102 114 } 103 115 104 public String getImgSrc() {105 return imgSrc;116 public void addChange(String attribute, String oldValue, String newValue) { 117 this.changes.add(new Change(attribute, oldValue, newValue)); 106 118 } 107 119 108 public int getNumPeople() { 109 return numPeople; 110 } 111 112 public void setNumPeople(int numPeople) { 113 this.numPeople = numPeople; 120 public List<Change> getChanges() { 121 return changes; 114 122 } 115 123 116 124 @Override 117 125 public boolean equals(Object obj) { 118 if (this==obj) return true;119 if (obj == null || getClass() != obj.getClass()) return false;126 if (this == obj) return true; 127 if (obj == null || getClass() != obj.getClass()) return false; 120 128 Option option = (Option) obj; 121 129 return Float.compare(option.price, price) == 0 … … 127 135 @Override 128 136 public int hashCode() { 129 return Objects.hash(hotelName, country,price,link);137 return Objects.hash(hotelName, country, price, link); 130 138 } 131 139 132 public int getId() {133 return id;134 }135 136 public void setId(int id) {137 this.id = id;138 }139 140 //debug141 140 @Override 142 141 public String toString() { 143 142 return "Option{" + 144 "id= '" + id + '\''+145 " dateRange='" + dateRange + '\'' +146 " hotelName='" + hotelName + '\'' +143 "id=" + id + 144 ", dateRange='" + dateRange + '\'' + 145 ", hotelName='" + hotelName + '\'' + 147 146 ", country='" + country + '\'' + 148 ", price= '" + price + '\''+147 ", price=" + price + 149 148 ", link='" + link + '\'' + 150 ", image='" + imgSrc + 149 ", imgSrc='" + imgSrc + '\'' + 150 ", type='" + type + '\'' + 151 ", board='" + board + '\'' + 152 ", amenities='" + amenities + '\'' + 153 ", numPeople=" + numPeople + 154 ", changes=" + changes + 151 155 '}'; 152 156 } 153 157 154 public void setPriceChanged(boolean a){ 155 isPriceChanged = a; 156 } 157 public void setNewPrice(float a){ 158 newPrice = a; 159 } 160 161 public boolean isPriceChanged() { 162 return isPriceChanged; 163 } 164 165 public float getNewPrice() { 166 return newPrice; 158 public boolean isEmpty() { 159 return (link.isEmpty() || country.isEmpty() || hotelName.isEmpty()); 167 160 } 168 161 } 162 -
backend/GlobeGuru-backend/src/main/java/Scraper.java
r0a7426e rdf7f390 14 14 15 15 private List<String> urls; 16 private ConcurrentLinkedQueue<Option> optionsQueue;17 16 private CountDownLatch latch; 18 17 19 18 public Scraper() { 20 19 urls = new ArrayList<>(); 21 this.optionsQueue = new ConcurrentLinkedQueue<>();22 20 ObjectMapper mapper = new ObjectMapper(); 23 21 try { … … 44 42 System.out.println("Scraper has started "); 45 43 for (String url : urls) { 46 new ScraperThread(url, optionsQueue,latch).start();44 new ScraperThread(url, latch).start(); 47 45 } 48 46 return null; -
backend/GlobeGuru-backend/src/main/java/ScraperThread.java
r0a7426e rdf7f390 29 29 public class ScraperThread extends Thread { 30 30 private String url; 31 private ConcurrentLinkedQueue<Option> uniqueOptions;32 31 private CountDownLatch latch; 33 private Set<Option> optionSet; 34 35 public ScraperThread(String url, ConcurrentLinkedQueue<Option> optionsQueue, CountDownLatch latch) { 32 33 public ScraperThread(String url, CountDownLatch latch) { 36 34 this.url = url; 37 this.uniqueOptions = optionsQueue;38 35 this.latch = latch; 39 this.optionSet = new HashSet<>();40 36 } 41 37 … … 86 82 for (Element div : childDivs) { 87 83 String data = div.outerHtml(); 88 Option option = optionParser(data, numPeople);84 Option option = optionParser(data, numPeople); 89 85 if (option != null) { 90 Option existingOption = DatabaseUtil.findOption(option); 91 if (existingOption != null) { 92 if (existingOption.equals(option)) { 93 option.setPriceChanged(true); 94 option.setNewPrice(option.getPrice()); 95 } 96 DatabaseUtil.updateOptionInDatabase(option); 97 } else if (optionSet.add(option)) { 98 uniqueOptions.add(option); 99 option.setId(DatabaseUtil.saveOptionToDatabase(option)); 100 scrapeOptionInfo(option); 101 System.out.println("Parsed " + option); 102 } 86 option.setId(DatabaseUtil.saveOptionToDatabase(option)); 87 scrapeOptionInfo(option); 88 System.out.println("Parsed " + option); 103 89 } 104 90 } … … 115 101 for (Element div : childDivs) { 116 102 String data = div.outerHtml(); 117 Option newOption = optionParser(data, numPeople);103 Option newOption = optionParser(data, numPeople); 118 104 if (newOption != null) { 119 if (optionSet.add(newOption)) { 120 uniqueOptions.add(newOption); 121 122 newOption.setId(DatabaseUtil.saveOptionToDatabase(newOption)); 123 scrapeOptionInfo(newOption); 124 System.out.println("Parsed " + newOption); 125 } 126 } 127 } 128 129 } else { 105 newOption.setId(DatabaseUtil.saveOptionToDatabase(newOption)); 106 scrapeOptionInfo(newOption); 107 System.out.println("Parsed " + newOption); 108 } 109 } 110 } else { 130 111 System.out.println("Parent div not found"); 131 112 } … … 134 115 System.out.println("URL not recognized for parsing."); 135 116 } 117 136 118 } 137 119 private void scrapeOptionInfo(Option option) { -
backend/GlobeGuru-backend/target/classes/lastUpdateTime.json
r0a7426e rdf7f390 1 1 { 2 "lastUpdateTime" : "2025-01-1 1T00:07:21.694581100"2 "lastUpdateTime" : "2025-01-13T13:53:28.872595600" 3 3 } -
frontend/index.html
r0a7426e rdf7f390 54 54 <span class="close">×</span> 55 55 <div class="saved-trips-container"> 56 <h2> Saved Trips</h2>56 <h2>Зачувани патувања</h2> 57 57 <div id="savedTripsList"></div> 58 58 </div> 59 59 <div class="price-changes-container"> 60 <h2> Price Changes</h2>60 <h2>Промени во понудата</h2> 61 61 <div id="priceChangesList"></div> 62 62 </div> … … 107 107 <div class="loading-content"> 108 108 <div class="spinner"></div> 109 <p> Loading, please wait...</p>109 <p>Пребарувам. ве молиме почекајте...</p> 110 110 </div> 111 111 </div> 112 112 <div id="AccountDetailsOverlay" class="popup accDetails"> 113 113 <div class="popup-content"> 114 <h1> My account</h1>114 <h1>Мој профил</h1> 115 115 <div class="popup-content" style="display: none" id="confDel"> 116 116 <div class="delete" > 117 <h1> Are you sure you want to delete your account?</h1>118 <button onclick="deleteAccount()" class="button" id="confirmDelete" style="background-color: red"> Yes</button>119 <button id="cancel" class="button" onclick="closeConfirmDelete()"> No</button>117 <h1>Дали сте сигурни дека сакате да го избришете профилот?</h1> 118 <button onclick="deleteAccount()" class="button" id="confirmDelete" style="background-color: red">Да</button> 119 <button id="cancel" class="button" onclick="closeConfirmDelete()">Не</button> 120 120 </div> 121 121 </div> 122 <button class="button" id="pswdChange">Change password</button> 123 <button class="button" style="background-color: red" id="deleteAcc" onclick="confirmDelete()">Delete Account</button> 122 <button class="button" style="background-color: red" id="deleteAcc" onclick="confirmDelete()">Избриши профил</button> 124 123 </div> 125 124 </div> -
frontend/js/formHandler.js
r0a7426e rdf7f390 51 51 const dataList = document.getElementById('givenOptions'); 52 52 dataList.innerHTML = ''; 53 53 54 if (data.length === 0) { 54 55 const wrapper = document.createElement('div'); … … 66 67 img.src = item.imgSrc || ''; 67 68 optionDiv.appendChild(img); 68 const WrapperDiv = document.createElement('div');69 optionDiv.appendChild( WrapperDiv);69 const wrapperDiv = document.createElement('div'); 70 optionDiv.appendChild(wrapperDiv); 70 71 71 72 const nameParagraph = document.createElement('p'); 72 73 nameParagraph.id = 'name'; 73 74 nameParagraph.textContent = item.hotelName || 'N/A'; 74 WrapperDiv.appendChild(nameParagraph);75 wrapperDiv.appendChild(nameParagraph); 75 76 76 77 const countryParagraph = document.createElement('p'); … … 78 79 countryParagraph.style.fontSize = '20px'; 79 80 countryParagraph.textContent = item.country || 'N/A'; 80 WrapperDiv.appendChild(countryParagraph);81 wrapperDiv.appendChild(countryParagraph); 81 82 82 83 const dateParagraph = document.createElement('h2'); 83 84 dateParagraph.id = 'date'; 84 85 dateParagraph.textContent = item.dateRange || 'N/A'; 85 WrapperDiv.appendChild(dateParagraph);86 wrapperDiv.appendChild(dateParagraph); 86 87 87 88 const peopleParagraph = document.createElement('p'); 88 89 peopleParagraph.id = 'numPeople'; 89 90 peopleParagraph.textContent = item.numPeople === 1 ? item.numPeople + " лице" : item.numPeople + " лица"; 90 WrapperDiv.appendChild(peopleParagraph);91 wrapperDiv.appendChild(peopleParagraph); 91 92 92 93 const priceHeading = document.createElement('h1'); 93 94 priceHeading.textContent = 'Цена:'; 94 WrapperDiv.appendChild(priceHeading);95 wrapperDiv.appendChild(priceHeading); 95 96 const priceParagraph = document.createElement('h2'); 96 97 priceParagraph.id = 'price'; 97 98 priceParagraph.textContent = (item.price || 0) + " EUR"; 98 WrapperDiv.appendChild(priceParagraph);99 wrapperDiv.appendChild(priceParagraph); 99 100 100 101 const infoDiv = document.createElement('div'); … … 110 111 infoDiv.appendChild(amenities); 111 112 optionDiv.appendChild(infoDiv); 113 114 const changesDiv = document.createElement('div'); 115 item.changes.forEach(change => { 116 const changeParagraph = document.createElement('p'); 117 changeParagraph.textContent = change.attribute + " changed from " + change.oldValue + " to " + change.newValue; 118 changesDiv.appendChild(changeParagraph); 119 }); 120 optionDiv.appendChild(changesDiv); 112 121 113 122 const link = document.createElement('a'); -
frontend/js/siteFlow.js
r0a7426e rdf7f390 168 168 .then(data => { 169 169 console.log('Option saved:', data); 170 // Update the UI to reflect that the option was saved171 170 updateFavoriteButtons(); 172 171 }) … … 243 242 tripDiv.appendChild(price); 244 243 244 // Add change indicator 245 if(trip.changes && trip.changes.length > 0) { 246 const changeDiv = document.createElement('div'); 247 changeDiv.classList.add('changes'); 248 trip.changes.forEach(change => { 249 const changeParagraph = document.createElement('p'); 250 changeParagraph.textContent = `${change.attribute} се смени од ${change.oldValue} во ${change.newValue}`; 251 changeDiv.appendChild(changeParagraph); 252 }); 253 tripDiv.appendChild(changeDiv); 254 } 255 245 256 savedTripsList.appendChild(tripDiv); 246 257 }); 247 248 258 249 259 const priceChangesList = document.getElementById('priceChangesList'); … … 258 268 }); 259 269 } else { 260 priceChangesList.textContent = "нема промена на цената.";270 priceChangesList.textContent = "нема промена во зачуваните патувања."; 261 271 } 262 272 }) … … 265 275 }); 266 276 } 277 267 278 function removeFromSaved(optionId) { 268 279 const userEmail = sessionStorage.getItem('user').replace(/^"|"$/g, '');
Note:
See TracChangeset
for help on using the changeset viewer.