Skip to content

Commit

Permalink
Merge pull request #62 from kuri-team/release-3.1-05-24-21
Browse files Browse the repository at this point in the history
Release 3.1 05/24/21
  • Loading branch information
miketvo authored May 24, 2021
2 parents 1a1c740 + 3a7ab69 commit 4cf1c7c
Show file tree
Hide file tree
Showing 62 changed files with 2,045 additions and 1,488 deletions.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"email": "ngocanhthu20102002@gmail.com"
}
],
"config": {
"optimize-autoloader": true
},
"require": {
"php": "~7.4.0"
},
Expand Down
80 changes: 80 additions & 0 deletions private/browse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/*
*
* Yabe database browsing interface library.
* MIT License. Copyright (c) 2021 栗 KURI 栗
*
*/

/**
* Retrieve store name and store, then insert them into the store card
* @param $store_name
* @param $store_id
*/
function display_store($store_name, $store_id) {
$store_href = url_for("/store/store-template/?id={$store_id}");
echo "
<div class='store-card'>
<a href='$store_href'><img class='store-card-thumbnail' alt='image representation of a shop' src='../../media/image/profile-placeholder_143x143.png'></a>
<a class='store-card-name' href=$store_href>$store_name</a>
</div>
";
}

/**
* Retrieve a list of stores and the maximum number of cards that will be display
* Display store cards with a number of cards that is <= the maximum number of cards
* The cards will be ordered ascending based on its position in the list
* @param $stores
* @param $max_cards
*/
function each_page($stores, $max_cards) {
$min = 0;
$max = $max_cards - $min - 1;
$page = $_GET["page"];
$list_length = count($stores);
$min += $max_cards * ($page-1);
$max += $max_cards * ($page-1);
if ($max > $list_length) {
$max = $list_length - 1;
}
for ($i = $min; $i <= $max; $i++) {
if (isset($stores[$i])) {
display_store($stores[$i]["store_name"],$stores[$i]["store_id"]);
}
}

}

/**
* Use the number of the current page to get the previous page
* @return int
*/
function prev_page() {
$prev = $_GET["page"] - 1;
// page number cannot be lower than 1
if ($prev < 1) {
$prev = 1;
}
return $prev;
}

/**
* Use the number of the current page to get the next page
* @return int
*/
function next_page($list_length, $max_cards) {
$next = $_GET["page"] + 1;
// calculate the required pages for each browse option
if ($list_length % $max_cards != 0) {
$max_page = floor($list_length / $max_cards) + 1;
} else {
$max_page = $list_length / $max_cards;
}
// page number cannot exceed the maximum number of pages
if ($next > $max_page) {
$next = $max_page;
}
return $next;
}
82 changes: 82 additions & 0 deletions private/classes/Database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

require_once(PRIVATE_PATH . "/csv.php");


class Database {
public const CATEGORY_DATABASE = 0;
public const STORE_DATABASE = 1;
public const PRODUCT_DATABASE = 2;

private static string $category_database_path = PRIVATE_PATH . "/database/categories.csv";
private static string $store_database_path = PRIVATE_PATH . "/database/stores.csv";
private static string $product_database_path = PRIVATE_PATH . "/database/products.csv";

private int $type;
private array $data = [];


public function __construct(int $type) {
$this->type = $type;
switch ($this->type) {
case self::CATEGORY_DATABASE:
$data = read_csv(self::$category_database_path, true);
foreach ($data as $entry) {
$this->data[] = new DatabaseCategory($entry["id"], $entry["name"]);
}
break;
case self::STORE_DATABASE:
$categories = new Database(self::CATEGORY_DATABASE);
$data = read_csv(self::$store_database_path, true);
foreach ($data as $entry) {
$this->data[] = new DatabaseStore(
$entry["id"],
$entry["name"],
$categories->getEntryById($entry["category_id"]),
strtotime($entry["created_time"]),
boolval($entry["featured"])
);
}
break;
case self::PRODUCT_DATABASE:
$stores = new Database(self::STORE_DATABASE);
$data = read_csv(self::$product_database_path, true);
foreach ($data as $entry) {
$this->data[] = new DatabaseProduct(
$entry["id"],
$entry["name"],
floatval($entry["price"]),
$stores->getEntryById($entry["store_id"]),
strtotime($entry["created_time"]),
boolval($entry["featured_in_mall"]),
boolval($entry["featured_in_store"])
);
}
break;
}
}


public static function merge(Database $database_1, Database $database_2) {
$result = clone $database_1;
$result->setAllEntries(array_merge($result->getAllEntries(), $database_2->getAllEntries()));
return $result;
}

public function getEntryById(string $id) {
foreach ($this->data as $entry) {
if ($entry->id === $id) {
return $entry;
}
}
return null;
}

public function getAllEntries(): array {
return $this->data;
}

private function setAllEntries($data) {
$this->data = $data;
}
}
11 changes: 11 additions & 0 deletions private/classes/DatabaseCategory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

class DatabaseCategory extends DatabaseEntry {
private static int $count = 0;


public function __construct(string $id, string $name) {
parent::__construct($id, $name);
self::$count++;
}
}
25 changes: 25 additions & 0 deletions private/classes/DatabaseEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

class DatabaseEntry {
public string $id;
public string $name;
public int $search_relevance = 0;

public function __construct(string $id, string $name) {
$this->id = $id;
$this->name = $name;
}


public function isSearchTermMatch(string $search_term, int $levenshtein_match_threshold=0): bool {
$name_elements = preg_split("/[\s,]+/", $this->name);
foreach ($name_elements as $name_element) {
if (levenshtein(strtoupper($name_element), strtoupper($search_term)) <= $levenshtein_match_threshold) {
$this->search_relevance++;
return true;
}
}
$this->search_relevance--;
return false;
}
}
47 changes: 47 additions & 0 deletions private/classes/DatabaseProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

class DatabaseProduct extends DatabaseEntry {
private static int $count = 0;

public int $price;
public int $created_time; // MUST be an Unix timestamp (number of seconds since the Unix Epoch - January 1 1970 00:00:00 GMT)
public DatabaseStore $store;
public bool $featured_in_mall;
public bool $featured_in_store;


public function __construct(
string $id,
string $name,
int $price,
DatabaseStore $store,
int $created_time=null,
bool $featured_in_mall=false,
bool $featured_in_store=false
) {
parent::__construct($id, $name);
$this->price = $price;
if ($created_time === null) {
$this->created_time = time();
} else {
$this->created_time = $created_time;
}
$this->store = $store;
$this->featured_in_mall = $featured_in_mall;
$this->featured_in_store = $featured_in_store;

self::$count++;
}

public function isSearchTermMatch(string $search_term, int $levenshtein_match_threshold=0): bool {
$name_elements = preg_split("/[\s,]+/", $this->name);
foreach ($name_elements as $name_element) {
if (levenshtein(strtoupper($name_element), strtoupper($search_term), 1, 100, 10000) <= $levenshtein_match_threshold) {
$this->search_relevance -= $this->store->search_relevance * 4;
return true;
}
}
$this->search_relevance += $this->store->search_relevance;
return $this->store->isSearchTermMatch($search_term, $levenshtein_match_threshold);
}
}
35 changes: 35 additions & 0 deletions private/classes/DatabaseStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

class DatabaseStore extends DatabaseEntry {
private static int $count = 0;

public DatabaseCategory $category;
public int $created_time; // MUST be an Unix timestamp (number of seconds since the Unix Epoch - January 1 1970 00:00:00 GMT)
public bool $featured; // true if this store is featured on mall home, false otherwise TODO: This OOP feature is not yet implemented due to time constraint. Right now it is implemented functionally in /public/mall/index.php


public function __construct(string $id, string $name, DatabaseCategory $category, int $created_time=null, bool $featured=false) {
parent::__construct($id, $name);
if ($created_time === null) {
$this->created_time = time();
} else {
$this->created_time = $created_time;
}
$this->category = $category;
$this->featured = $featured;

self::$count++;
}

public function isSearchTermMatch(string $search_term, int $levenshtein_match_threshold=0): bool {
$name_elements = preg_split("/[\s,]+/", $this->name);
foreach ($name_elements as $name_element) {
if (levenshtein(strtoupper($name_element), strtoupper($search_term), 1, 10, 100) <= $levenshtein_match_threshold) {
$this->search_relevance -= $this->category->search_relevance * 2;
return true;
}
}
$this->search_relevance += $this->category->search_relevance;
return $this->category->isSearchTermMatch($search_term, $levenshtein_match_threshold);
}
}
73 changes: 73 additions & 0 deletions private/classes/Search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

class Search {
public const FILTER_ALL = "All";
public const FILTER_PRODUCTS = "Products";
public const FILTER_STORES = "Stores";

public string $query;
public string $filter;
public array $results = [];

private array $search_terms = [];
private Database $data;


public function __construct(string $query, string $filter=self::FILTER_ALL) {
$this->query = $query;
$search_terms = preg_split("/[\s,]+/", $this->query);

if ($filter === "Filter") {
$this->filter = self::FILTER_ALL;
} else {
$this->filter = $filter;
}
switch ($this->filter) {
case self::FILTER_ALL:
$this->data = new Database(Database::STORE_DATABASE);
$this->data = Database::merge($this->data, new Database(Database::PRODUCT_DATABASE));
break;
case self::FILTER_PRODUCTS:
$this->data = new Database(Database::PRODUCT_DATABASE);
break;
case self::FILTER_STORES:
default:
$this->data = new Database(Database::STORE_DATABASE);
break;
}

foreach ($search_terms as $search_term) {
$this->search_terms[] = new SearchTerm($search_term, $this->data);
}

$this->generateResults();
}


public function generateResults() {
$match_results = [];
foreach ($this->search_terms as $search_term) {
foreach ($search_term->getAllMatches() as $match) {
if (isset($match_results[$match->id])) {
$match_results[$match->id]++;
} else {
$match_results[$match->id] = 1;
}
}
}

foreach ($match_results as $match_id => $match_count) {
if ($match_count === count($this->search_terms)) {
if ($this->filter === self::FILTER_ALL || $this->filter === self::FILTER_STORES || $this->filter === self::FILTER_PRODUCTS) {
$this->results[] = $this->data->getEntryById($match_id);
} elseif (levenshtein(explode(" ", $this->data->getEntryById($match_id)->category->name)[0], explode(" ", $this->filter)[2]) < 5) { // TODO: This is a temporary workaround. Replace this with an exact match mechanism.
$this->results[] = $this->data->getEntryById($match_id);
}
}
}

usort($this->results, function (DatabaseEntry $entry_1, DatabaseEntry $entry_2) {
return $entry_2->search_relevance - $entry_1->search_relevance;
});
}
}
Loading

0 comments on commit 4cf1c7c

Please sign in to comment.